// Copyright (C) 1999-2000 Id Software, Inc.
//

#include "g_local.h"

extern void SP_misc_ammo_station( gentity_t *ent );
extern void ammo_station_finish_spawning ( gentity_t *self );
extern int	borgQueenClientNum;
extern qboolean BG_BorgTransporting( playerState_t *ps );

static int lastTimedMessage; //TiM - Moved here from g_local.h

/*
==============
TryUse

Try and use an entity in the world, directly ahead of us
==============
*/

#define USE_DISTANCE	64.0f
void TryUse( gentity_t *ent )
{
	gentity_t	*target; //, *newent;
	trace_t		trace;
	vec3_t		src, dest, vf;
	int			i;

	VectorCopy( ent->r.currentOrigin, src );
	src[2] += ent->client->ps.viewheight;

	AngleVectors( ent->client->ps.viewangles, vf, NULL, NULL );

	//extend to find end of use trace
	VectorMA( src, -6, vf, src );//in case we're inside something?
	VectorMA( src, 134, vf, dest );//128+6

	//Trace ahead to find a valid target
	trap_Trace( &trace, src, vec3_origin, vec3_origin, dest, ent->s.number, MASK_OPAQUE|CONTENTS_BODY|CONTENTS_ITEM|CONTENTS_CORPSE );

	if ( trace.fraction == 1.0f || trace.entityNum < 0 )
	{
		//FIXME: Play a failure sound
		return;
	}

	target = &g_entities[trace.entityNum];

	//Check for a use command
	if ( target->client )
	{//if a bot, make it follow me, if a teammate, give stuff
		if ( target->client->sess.sessionTeam == ent->client->sess.sessionTeam )
		{//on my team
//			gitem_t	*regen = BG_FindItemForPowerup( PW_REGEN );
//			gitem_t	*invis = BG_FindItemForPowerup( PW_INVIS );

			switch( ent->client->sess.sessionClass )
			{
			case PC_MEDIC://medic gives regen
				break;
			case PC_TECH://tech gives invisibility
				break;
			}
			switch( target->client->sess.sessionClass )
			{
			case PC_TECH://using tech gives you full ammo
				G_Sound(ent, G_SoundIndex("sound/player/suitenergy.wav") );
				for ( i = 0 ; i < MAX_WEAPONS ; i++ )
				{
					ent->client->ps.ammo[i] = Max_Ammo[i];
				}
				return;
				break;
			}
		}
	}
	else if ( target->use && Q_stricmp("func_usable", target->classname) == 0 )
	{//usable brush
		if ( target->team && atoi( target->team ) != 0 )
		{//usable has a team
			if ( atoi( target->team ) != ent->client->sess.sessionTeam )
			{//not on my team
				if ( ent->client->sess.sessionClass != PC_TECH )
				{//only a tech can use enemy usables
					//FIXME: Play a failure sound
					return;
				}
			}
		}
		//FIXME: play sound?
		target->use( target, ent, ent );
		return;
	}
	else if ( target->use && Q_stricmp("misc_ammo_station", target->classname) == 0 )
	{//ammo station
		if ( ent->client->sess.sessionTeam )
		{
			if ( target->team )
			{
				if ( atoi( target->team ) != ent->client->sess.sessionTeam )
				{
					//FIXME: play sound?
					return;
				}
			}
		}
		target->use( target, ent, ent );
		return;
	}
	else if ( target->s.number == ENTITYNUM_WORLD || (target->s.pos.trType == TR_STATIONARY && !(trace.surfaceFlags & SURF_NOIMPACT) && !target->takedamage) )
	{
		switch( ent->client->sess.sessionClass )
		{
		case PC_TECH://tech can place an ammo station
			break;
		}
		//FIXME: play sound
		return;
	}
	//FIXME: Play a failure sound
}

/*
===============
G_DamageFeedback

Called just before a snapshot is sent to the given player.
Totals up all damage and generates both the player_state_t
damage values to that client for pain blends and kicks, and
global pain sound events for all clients.
===============
*/
void P_DamageFeedback( gentity_t *player ) {
	gclient_t	*client;
	float	count;
	vec3_t	angles;

	client = player->client;
	if ( client->ps.pm_type == PM_DEAD ) {
		return;
	}

	// total points of damage shot at the player this frame
	count = client->damage_blood + client->damage_armor;
	if ( count == 0 ) {
		return;		// didn't take any damage
	}

	if ( count > 255 ) {
		count = 255;
	}

	// send the information to the client

	// world damage (falling, slime, etc) uses a special code
	// to make the blend blob centered instead of positional
	if ( client->damage_fromWorld ) {
		client->ps.damagePitch = 255;
		client->ps.damageYaw = 255;

		client->damage_fromWorld = qfalse;
	} else {
		vectoangles( client->damage_from, angles );
		client->ps.damagePitch = angles[PITCH]/360.0 * 256;
		client->ps.damageYaw = angles[YAW]/360.0 * 256;
	}

	// play an apropriate pain sound
	if ( (level.time > player->pain_debounce_time) && !(player->flags & FL_GODMODE) ) {
		player->pain_debounce_time = level.time + 700;
		G_AddEvent( player, EV_PAIN, player->health );
		client->ps.damageEvent++;
	}

	client->ps.damageCount = client->damage_blood;
	if (client->ps.damageCount > 255)
	{
		client->ps.damageCount = 255;
	}

	client->ps.damageShieldCount = client->damage_armor;
	if (client->ps.damageShieldCount > 255)
	{
		client->ps.damageShieldCount = 255;
	}

	//
	// clear totals
	//
	client->damage_blood = 0;
	client->damage_armor = 0;
	client->damage_knockback = 0;
}



/*
=============
P_WorldEffects

Check for lava / slime contents and drowning
=============
*/
void P_WorldEffects( gentity_t *ent ) {
	//qboolean	envirosuit;
	int			waterlevel;

	if ( ent->client->noclip ) {
		ent->client->airOutTime = level.time + 12000;	// don't need air
		return;
	}

	waterlevel = ent->waterlevel;

	//envirosuit = ent->client->ps.powerups[PW_BOLTON] > level.time;

	//
	// check for drowning
	//
	if ( waterlevel == 3 && !(ent->watertype&CONTENTS_LADDER)) {
		// envirosuit give air, techs can't drown
		if ( /*envirosuit ||*/ ent->client->sess.sessionClass == PC_ALPHAOMEGA22 ) {
			ent->client->airOutTime = level.time + 10000;
		}

		// if out of air, start drowning
		if ( ent->client->airOutTime < level.time) {
			// drown!
			ent->client->airOutTime += 1000;
			if ( ent->health > 1 ) { //TiM : used to be 0, but to fix red's medic code
				// take more damage the longer underwater
				ent->damage += 2;
				if (ent->damage > 15)
					ent->damage = 15;

				// play a gurp sound instead of a normal pain sound
				if (ent->health <= ent->damage) {
					G_Sound(ent, G_SoundIndex("*drown.wav"));
				} else if (rand()&1) {
					G_Sound(ent, G_SoundIndex("sound/player/gurp1.wav"));
				} else {
					G_Sound(ent, G_SoundIndex("sound/player/gurp2.wav"));
				}

				// don't play a normal pain sound
				ent->pain_debounce_time = level.time + 200;

				G_Damage (ent, NULL, NULL, NULL, NULL,
					ent->damage, DAMAGE_NO_ARMOR, MOD_WATER);
			}
		}
	} else {
		ent->client->airOutTime = level.time + 12000;
		ent->damage = 2;
	}

	//
	// check for sizzle damage (move to pmove?)
	//
	if (waterlevel &&
		(ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) ) {
		if (ent->health > 0
			&& ent->pain_debounce_time < level.time	) {

/*			if ( envirosuit ) {
				G_AddEvent( ent, EV_POWERUP_BATTLESUIT, 0 );
			} else {*/
				if (ent->watertype & CONTENTS_LAVA) {
					G_Damage (ent, NULL, NULL, NULL, NULL,
						30*waterlevel, 0, MOD_LAVA);
				}

				if (ent->watertype & CONTENTS_SLIME) {
					G_Damage (ent, NULL, NULL, NULL, NULL,
						10*waterlevel, 0, MOD_SLIME);
				}
			//}
		}
	}
}



/*
===============
G_SetClientSound
===============
*/
void G_SetClientSound( gentity_t *ent )
{	// 3/28/00 kef -- this is dumb.
	if (ent->waterlevel && (ent->watertype&(CONTENTS_LAVA|CONTENTS_SLIME)) )
		ent->s.loopSound = level.snd_fry;
	else
		ent->s.loopSound = 0;
}



//==============================================================

/*
==============
ClientImpacts
==============
*/
void ClientImpacts( gentity_t *ent, pmove_t *pm ) {
	int		i, j;
	trace_t	trace;
	gentity_t	*other;

	memset( &trace, 0, sizeof( trace ) );
	for (i=0 ; i<pm->numtouch ; i++) {
		for (j=0 ; j<i ; j++) {
			if (pm->touchents[j] == pm->touchents[i] ) {
				break;
			}
		}
		if (j != i) {
			continue;	// duplicated
		}
		other = &g_entities[ pm->touchents[i] ];

		if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
			ent->touch( ent, other, &trace );
		}

		if ( !other->touch ) {
			continue;
		}

		other->touch( other, ent, &trace );
	}

}

/*
============
G_TouchTriggers

Find all trigger entities that ent's current position touches.
Spectators will only interact with teleporters.
============
*/
void	G_TouchTriggers( gentity_t *ent ) {
	int			i, num;
	int			touch[MAX_GENTITIES];
	gentity_t	*hit;
	trace_t		trace;
	vec3_t		mins, maxs;
	static vec3_t	range = { 40, 40, 52 };

	if ( !ent->client ) {
		return;
	}

	// dead clients don't activate triggers!
	if ( ent->client->ps.stats[STAT_HEALTH] <= 0 ) {
		return;
	}

	VectorSubtract( ent->client->ps.origin, range, mins );
	VectorAdd( ent->client->ps.origin, range, maxs );

	num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );

	// can't use ent->absmin, because that has a one unit pad
	VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
	VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );

	for ( i=0 ; i<num ; i++ ) {
		hit = &g_entities[touch[i]];

		if ( !hit->touch && !ent->touch ) {
			continue;
		}
		if ( !( hit->r.contents & CONTENTS_TRIGGER ) ) {
			continue;
		}

		// ignore most entities if a spectator
		if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || (ent->client->ps.eFlags&EF_ELIMINATED) )
		{
			//RPG-X: J2J - No clip spectating doesnt need to use transporter entities or door triggers!
			if( rpg_noclipspectating.integer == 1 )
				continue;
				
			// this is ugly but adding a new ET_? type will
			// most likely cause network incompatibilities
			if ( hit->s.eType != ET_TELEPORT_TRIGGER &&	hit->touch != Touch_DoorTrigger) 
			{
				continue;
			}
		}

		// use seperate code for determining if an item is picked up
		// so you don't have to actually contact its bounding box
		if ( hit->s.eType == ET_ITEM ) {
			if ( !BG_PlayerTouchesItem( &ent->client->ps, &hit->s, level.time ) ) {
				continue;
			}
		} else {
			if ( !trap_EntityContact( mins, maxs, hit ) ) {
				continue;
			}
		}

		memset( &trace, 0, sizeof(trace) );

		if ( hit->touch ) {
			hit->touch (hit, ent, &trace);
		}

		if ( ( ent->r.svFlags & SVF_BOT ) && ( ent->touch ) ) {
			ent->touch( ent, hit, &trace );
		}
	}
}

/*
=================
SpectatorThink
=================
*/
void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) {
	pmove_t	pm;
	gclient_t	*client;

	client = ent->client;

	if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) {
		client->ps.pm_type = PM_SPECTATOR;
		client->ps.speed = 400;	// faster than normal

		// set up for pmove
		memset (&pm, 0, sizeof(pm));
		pm.ps = &client->ps;
		pm.cmd = *ucmd;
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;	// spectators can fly through bodies
		pm.trace = trap_Trace;
		pm.pointcontents = trap_PointContents;

		// perform a pmove
		Pmove (&pm);

		// save results of pmove
		VectorCopy( client->ps.origin, ent->s.origin );

		G_TouchTriggers( ent );
		trap_UnlinkEntity( ent );
	}

	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;

	// attack button cycles through spectators
	if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) {
		Cmd_FollowCycle_f( ent, 1 );
	}
	else if ( ( client->buttons & BUTTON_ALT_ATTACK ) && ! ( client->oldbuttons & BUTTON_ALT_ATTACK ) )
	{
		if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
			StopFollowing( ent );
		}
	}
}



/*
=================
ClientInactivityTimer

Returns qfalse if the client is dropped
=================
*/
qboolean ClientInactivityTimer( gclient_t *client )
{
	if ( ! g_inactivity.integer )
	{
		// give everyone some time, so if the operator sets g_inactivity during
		// gameplay, everyone isn't kicked
		client->inactivityTime = level.time + 60 * 1000;
		client->inactivityWarning = qfalse;
	}
	else if (	client->pers.cmd.forwardmove ||
				client->pers.cmd.rightmove ||
				client->pers.cmd.upmove ||
				(client->pers.cmd.buttons & BUTTON_ATTACK) ||
				(client->pers.cmd.buttons & BUTTON_ALT_ATTACK) )
	{
		client->inactivityTime = level.time + g_inactivity.integer * 1000;
		client->inactivityWarning = qfalse;
	}
	else if ( !client->pers.localClient )
	{
		if ( level.time > client->inactivityTime )
		{
			trap_DropClient( client - level.clients, "Dropped due to inactivity" );
			return qfalse;
		}
		if ( level.time > client->inactivityTime - 10000 && !client->inactivityWarning )
		{
			client->inactivityWarning = qtrue;
			trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
		}
	}
	return qtrue;
}


/*
==================
TimedMessage

RPG-X - RedTechie: Returns the message requested.
If the message is blank go to next. (Based off of SFEFMOD)

TiM: Huh... O_o.  Damn Red, you're right.  If the admin
puts in values, but not in a consistent consecutive order,
we'll get some mighty painful errors. >.<

I guess what we really need is a for loop that goes
thru, and checks to see if each and every CVAR has a value
currently in it....
==================
*/

//First off let's store the CVAR data in a local array.
//This'll let us perform for loop operations on it.
//Sigh, I wish I knew a quick way to convert variable names
//to strings.  Then this wouldn't be necessary O_o
static char *rpg_message[] = { rpg_message1.string,
							   rpg_message2.string,
							   rpg_message3.string,
							   rpg_message4.string,
							   rpg_message5.string,
							   rpg_message6.string,
							   rpg_message7.string,
							   rpg_message8.string,
							   rpg_message9.string,
							   rpg_message10.string };

char *TimedMessage( void ){
	int i = lastTimedMessage;

	while ( 1 ) { //Okay, start from the number we want, and loop thru all of them
		if ( i == 11 ) { //reset loop
			i = 1;
		}

		if ( strlen( rpg_message[i-1] ) >= 1 ) { //i-1 coz arrays start @ 0, but we started @ 1
			lastTimedMessage = i;

			return rpg_message[i-1]; //return the string
		}
	
		if ( i == lastTimedMessage - 1 ) { //okay, we've obviously gone thru the whole loop and come up with nothing. So screw it.
			return NULL;
		}

		//TiM: Cheap hack to stop it freezing if string0 is empty.  THe above condition don't work here
		if ( ( i == lastTimedMessage == 1 ) && !strlen( rpg_message[i-1] ) ) {
			return NULL;
		}

		i++;
	}

	//TiM: Hopefully it'll never reach here, but we have this anyway to shut the compiler up
	return "^1RPG-X ERROR: No messages to display";
	

/*
	switch( Msg ){
		case 1:
			if(strlen(rpg_message1.string) >= 1){
				lastTimedMessage = 1;
				return rpg_message1.string;
			}else{
				return TimedMessage( 2 );
			}
			break;

		case 2:
			if(strlen(rpg_message2.string) >= 1){
				lastTimedMessage = 2;
				return rpg_message2.string;
			}else{
				return TimedMessage( 3 );
			}
			break;

		case 3:
			if(strlen(rpg_message3.string) >= 1){
				lastTimedMessage = 3;
				return rpg_message3.string;
			}else{
				return TimedMessage( 4 );
			}
			break;

		case 4:
			if(strlen(rpg_message4.string) >= 1){
				lastTimedMessage = 4;
				return rpg_message4.string;
			}else{
				return TimedMessage( 5 );
			}
			break;

		case 5:
			if(strlen(rpg_message5.string) >= 1){
				lastTimedMessage = 5;
				return rpg_message5.string;
			}else{
				return TimedMessage( 6 );
			}
			break;

		case 6:
			if(strlen(rpg_message6.string) >= 1){
				lastTimedMessage = 6;
				return rpg_message6.string;
			}else{
				return TimedMessage( 7 );
			}
			break;

		case 7:
			if(strlen(rpg_message7.string) >= 1){
				lastTimedMessage = 7;
				return rpg_message7.string;
			}else{
				return TimedMessage( 8 );
			}
			break;

		case 8:
			if(strlen(rpg_message8.string) >= 1){
				lastTimedMessage = 8;
				return rpg_message8.string;
			}else{
				return TimedMessage( 9 );
			}
			break;

		case 9:
			if(strlen(rpg_message9.string) >= 1){
				lastTimedMessage = 9;
				return rpg_message9.string;
			}else{
				return TimedMessage( 10 );
			}
			break;

		case 10:
			if(strlen(rpg_message10.string) >= 1){
				lastTimedMessage = 0; //Start over again
				return rpg_message10.string;
			}else{
				return TimedMessage( 1 );
			}
			break;

		default:
			return "^1RPG-X ERROR: No messages to display";
			break;
	}*/
}


/*
==================
ClientTimerActions

Actions that happen once a second
==================
*/

void ClientTimerActions( gentity_t *ent, int msec ) {
	gclient_t	*client;
	char		*message;
	float		messageTime;

	client = ent->client;
	client->timeResidual += msec;

	//RPG-X - RedTechie: Heavily Modifyed Message system (Based off of phenix's old code)
	/*if ( level.time >= (level.message + (rpg_timedmessagetime.integer * 60000)) ) {
		level.message = level.time;
		trap_SendServerCommand( -1, va("cp \"%s\n\"", rpg_timedmessage.string) );
		//trap_SendServerCommand( -1, va( "print \"%s\n\"", rpg_timedmessage ) );
	}*/
	if( rpg_timedmessagetime.value ){
		//Make sure its not less then one //TiM: Well... we can have under 1, just not toooo far under 1
		
		//TiM : Init/reset TimedMessage's value
		if ( lastTimedMessage <= 0 || lastTimedMessage > 10 ) {
			lastTimedMessage = 1;
		}

		if(rpg_timedmessagetime.value < 0.2) { //1
			messageTime = 0.2;
		}else{
			messageTime = rpg_timedmessagetime.value;
		}

		if (level.time > (level.message + (messageTime * 60000)) ) {
			level.message = level.time;
			
			//TiM - There.  So with this working in conjunction with that reset
			//code above, this should be more efficient. :)
			message = TimedMessage(); //Since we're working with a gloabl scope variable, there's no need for this thing to have parameters:)
			lastTimedMessage++;

			/*//Decide what timed message to display
			if( lastTimedMessage && lastTimedMessage != 0 ){
				//A message has been displayed
				if(lastTimedMessage == 1){
					//Display message 2
					message = TimedMessage( 2 );
				}else if(lastTimedMessage == 2){
					//Display message 3
					message = TimedMessage( 3 );
				}else if(lastTimedMessage == 3){
					//Display message 4
					message = TimedMessage( 4 );
				}else if(lastTimedMessage == 4){
					//Display message 5
					message = TimedMessage( 5 );
				}else if(lastTimedMessage == 5){
					//Display message 6
					message = TimedMessage( 6 );
				}else if(lastTimedMessage == 6){
					//Display message 7
					message = TimedMessage( 7 );
				}else if(lastTimedMessage == 7){
					//Display message 8
					message = TimedMessage( 8 );
				}else if(lastTimedMessage == 8){
					//Display message 9
					message = TimedMessage( 9 );
				}else if(lastTimedMessage == 9){
					//Display message 10
					message = TimedMessage( 10 );
				}else{
					//BAD
					message = "^1RPG-X ERROR: lastTimedMessage not set correctly.";
				}
			}else{
				//A message hasnt been displayed yet
				message = TimedMessage( 1 );
			}*/
			
			//Alright send the message now
			if ( message != NULL ) {
				trap_SendServerCommand( -1, va("cp \"%s\n\"", message) );  //Shows the message on their main screen
			}

			//trap_SendServerCommand( -1, va("print \"\n \n%s \n\"", message));  //Shows the message in console
		}
	}



	while ( client->timeResidual >= 1000 ) {
		client->timeResidual -= 1000;

		// regenerate
		//TiM - removed so we can use the REGEN powerup elsewhere
		/*if ( client->ps.powerups[PW_REGEN] )
		{
			if ( client->sess.sessionClass != PC_NOCLASS && client->sess.sessionClass != PC_BORG && client->sess.sessionClass != PC_ACTIONHERO )
			{
				if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] )
				{
					ent->health += 2;
					G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
				}
			}
			else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH])
			{
				ent->health += 15;
				if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 1.1 )
				{
					ent->health = client->ps.stats[STAT_MAX_HEALTH] * 1.1;
				}
				G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
			}
			else if ( ent->health < client->ps.stats[STAT_MAX_HEALTH] * 2)
			{
				ent->health += 5;
				if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] * 2 )
				{
					ent->health = client->ps.stats[STAT_MAX_HEALTH] * 2;
				}
				G_AddEvent( ent, EV_POWERUP_REGEN, 0 );
			}
		}*/
		/*else if (ent->flags & FL_CLOAK)
		{
			//RPG-X: RedTechie - Health dosnt matter 
			if ( ent->health < 41)
			{
				ent->flags ^= FL_CLOAK;
				ent->client->ps.powerups[PW_INVIS]  = level.time + 1000000000;
			}
		}
		else if (ent->flags & FL_FLY)
		{
			//RPG-X: RedTechie - Health dosnt matter 
			if ( ent->health < 41)
			{
				ent->flags ^= FL_FLY;
				ent->client->ps.powerups[PW_FLIGHT] = level.time + 1000000000;
			}
		}*/
		//else
		//{
			// count down health when over max and not cloaked.
			if ( ent->health > client->ps.stats[STAT_MAX_HEALTH] )
			{
				ent->health--;
			}
		//}

		// THIS USED TO count down armor when over max, more slowly now (every other second)
//		if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH] && ((int)(level.time/1000))&0x01) {

		// NOW IT ONCE AGAIN counts down armor when over max, once per second
		if ( client->ps.stats[STAT_ARMOR] > client->ps.stats[STAT_MAX_HEALTH]) {
			client->ps.stats[STAT_ARMOR]--;
		}

		// if we've got the seeker powerup, see if we can shoot it at someone
		//if ( client->ps.powerups[PW_SEEKER] )
		//{
			//vec3_t	seekerPos;

			//TiM - Because I commented out the scav code, I'm going to remove all other ref to it
			/*if (SeekerAcquiresTarget(ent, seekerPos)) // set the client's enemy to a valid target
			{
				FireSeeker( ent, ent->enemy, seekerPos );
				G_AddEvent( ent, EV_POWERUP_SEEKER_FIRE, 0 ); // draw the thingy
			}*/
		//}

		if ( !client->ps.stats[STAT_HOLDABLE_ITEM] )
		{//holding nothing...
			if ( client->ps.stats[STAT_USEABLE_PLACED] > 0 )
			{//we're in some kind of countdown
				//so count down
				client->ps.stats[STAT_USEABLE_PLACED]--;
			}
		}

	}
}

/*
====================
ClientIntermissionThink
====================
*/
void ClientIntermissionThink( gclient_t *client ) {
	client->ps.eFlags &= ~EF_TALK;
	client->ps.eFlags &= ~EF_FIRING;

	// the level will exit when everyone wants to or after timeouts

	// swap and latch button actions
	client->oldbuttons = client->buttons;
	client->buttons = client->pers.cmd.buttons;
	if (g_gametype.integer != GT_SINGLE_PLAYER)
	{
		if ( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) ) {
			client->readyToExit ^= 1;
		}
	}
}

/*
====================
Cmd_Ready_f
====================

  This function is called from the ui_sp_postgame.c as a result of clicking on the
  "next" button in non GT_TOURNAMENT games.  This replaces the old system of waiting
  for the user to click an ATTACK or USE button to signal ready
  (see ClientIntermissionThink() above)

  when all clients have signaled ready, the game continues to the next match.
*/
void Cmd_Ready_f (gentity_t *ent)
{
	gclient_t *client;
	client = ent->client;

	client->readyToExit ^= 1;
}



typedef struct detHit_s
{
	gentity_t	*detpack;
	gentity_t	*player;
	int			time;
} detHit_t;

#define MAX_DETHITS		32		// never define this to be 0

detHit_t	detHits[MAX_DETHITS];
qboolean	bDetInit = qfalse;

extern qboolean FinishSpawningDetpack( gentity_t *ent, int itemIndex );
//-----------------------------------------------------------------------------DECOY TEMP
extern qboolean FinishSpawningDecoy( gentity_t *ent, int itemIndex );
//-----------------------------------------------------------------------------DECOY TEMP
void DetonateDetpack(gentity_t *ent);

#define DETPACK_DAMAGE			750
#define DETPACK_RADIUS			500

void detpack_shot( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
{
	int i = 0;
	gentity_t *ent = NULL;

	//so we can't be blown up by things we're blowing up
	self->takedamage = 0;

	G_TempEntity(self->s.origin, EV_GRENADE_EXPLODE);
	G_RadiusDamage( self->s.origin, self->parent?self->parent:self, DETPACK_DAMAGE*0.125, DETPACK_RADIUS*0.25,
		self, DAMAGE_ALL_TEAMS, MOD_DETPACK );
	// we're blowing up cuz we've been shot, so make sure we remove ourselves
	//from our parent's inventory (so to speak)
	for (i = 0; i < MAX_CLIENTS; i++)
	{
		if (((ent = &g_entities[i])!=NULL) && ent->inuse && (self->parent == ent))
		{
			ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
			ent->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
			break;
		}
	}
	G_FreeEntity(self);
}

qboolean PlaceDetpack(gentity_t *ent)
{
	gentity_t	*detpack = NULL;
	static gitem_t *detpackItem = NULL;
	float		detDistance = 80;
	trace_t		tr;
	vec3_t		fwd, right, up, end, mins = {-16,-16,-16}, maxs = {16,16,16};

	if (detpackItem == NULL)
	{
		detpackItem = BG_FindItemForHoldable(HI_DETPACK);
	}

	// make sure our detHit info is init'd
	if (!bDetInit)
	{
		memset(detHits, 0, MAX_DETHITS*sizeof(detHit_t));
		bDetInit = 1;
	}

	// can we place this in front of us?
	AngleVectors (ent->client->ps.viewangles, fwd, right, up);
	fwd[2] = 0;
	VectorMA(ent->client->ps.origin, detDistance, fwd, end);
	trap_Trace (&tr, ent->client->ps.origin, mins, maxs, end, ent->s.number, MASK_SHOT );
	if (tr.fraction > 0.9)
	{
		// got enough room so place the detpack
		detpack = G_Spawn();
		G_SpawnItem(detpack, detpackItem);
		detpack->physicsBounce = 0.0f;//detpacks are *not* bouncy
		VectorMA(ent->client->ps.origin, detDistance + mins[0], fwd, detpack->s.origin);
		if ( !FinishSpawningDetpack(detpack, detpackItem - bg_itemlist) )
		{
			return qfalse;
		}
		VectorNegate(fwd, fwd);
		vectoangles(fwd, detpack->s.angles);
		detpack->think = DetonateDetpack;
		detpack->nextthink = level.time + 120000;	// if not used after 2 minutes it blows up anyway
		detpack->parent = ent;
		return qtrue;
	}
	else
	{
		// no room
		return qfalse;
	}
}

qboolean PlayerHitByDet(gentity_t *det, gentity_t *player)
{
	int i = 0;

	if (!bDetInit)
	{
		// detHit stuff not initialized. who knows what's going on?
		return qfalse;
	}
	for (i = 0; i < MAX_DETHITS; i++)
	{
		if ( (detHits[i].detpack == det) && (detHits[i].player == player) )
		{
			return qtrue;
		}
	}
	return qfalse;
}

void AddPlayerToDetHits(gentity_t *det, gentity_t *player)
{
	int			i = 0;
	detHit_t	*lastHit = NULL, *curHit = NULL;

	for (i = 0; i < MAX_DETHITS; i++)
	{
		if (0 == detHits[i].time)
		{
			// empty slot. add our player here.
			detHits[i].detpack = det;
			detHits[i].player = player;
			detHits[i].time = level.time;
		}
		lastHit = &detHits[i];
	}
	// getting here means we've filled our list of detHits, so begin recycling them, starting with the oldest hit.
	curHit = &detHits[0];
	while (lastHit->time < curHit->time)
	{
		lastHit = curHit;
		curHit++;
		// just a safety check here
		if (curHit == &detHits[0])
		{
			break;
		}
	}
	curHit->detpack = det;
	curHit->player = player;
	curHit->time = level.time;
}

void ClearThisDetpacksHits(gentity_t *det)
{
	int			i = 0;

	for (i = 0; i < MAX_DETHITS; i++)
	{
		if (detHits[i].detpack == det)
		{
			detHits[i].player = NULL;
			detHits[i].detpack = NULL;
			detHits[i].time = 0;
		}
	}
}

void DetpackBlammoThink(gentity_t *ent)
{
	int i = 0, lifetime = 3000;	// how long (in msec) the shockwave lasts
	int			knockback = 400;
	float		curBlastRadius = 50.0*ent->count, radius = 0;
	vec3_t		vTemp;
	trace_t		tr;
	gentity_t	*pl = NULL;

	if (ent->count++ > (lifetime*0.01))
	{
		ClearThisDetpacksHits(ent);
		G_FreeEntity(ent);
		return;
	}

	// get a list of players within the blast radius at this time.
	// only hit the ones we can see from the center of the explosion.
	for (i=0; i<MAX_CLIENTS; i++)
	{
		if ( g_entities[i].client && g_entities[i].takedamage)
		{
			pl = &g_entities[i];
			VectorSubtract(pl->s.pos.trBase, ent->s.origin, vTemp);
			radius = VectorNormalize(vTemp);
			if ( (radius <= curBlastRadius) && !PlayerHitByDet(ent, pl) )
			{
				// within the proper radius. do we have LOS to the player?
				trap_Trace (&tr, ent->s.origin, NULL, NULL, pl->s.pos.trBase, ent->s.number, MASK_SHOT );
				if (tr.entityNum == i)
				{
					// oh yeah. you're gettin' hit.
					AddPlayerToDetHits(ent, pl);
					VectorMA(pl->client->ps.velocity, knockback, vTemp, pl->client->ps.velocity);
					// make sure the player goes up some
					if (pl->client->ps.velocity[2] < 100)
					{
						pl->client->ps.velocity[2] = 100;
					}
					if ( !pl->client->ps.pm_time )
					{
						int		t;

						t = knockback * 2;
						if ( t < 50 ) {
							t = 50;
						}
						if ( t > 200 ) {
							t = 200;
						}
						pl->client->ps.pm_time = t;
						pl->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
					}
				}
			}
			// this was just for testing. looks pretty neat, though.
/*
			else
			{
				VectorMA(ent->s.origin, curBlastRadius, vTemp, vTemp);
				G_TempEntity(vTemp, EV_FX_STEAM);
			}
*/
		}
	}

	ent->nextthink = level.time + FRAMETIME;
}

void DetonateDetpack(gentity_t *ent)
{
	// find all detpacks. the one whose parent is ent...blow up
	gentity_t	*detpack = NULL;
	char		*classname = BG_FindClassnameForHoldable(HI_DETPACK);

	if (!classname)
	{
		return;
	}
	while ((detpack = G_Find (detpack, FOFS(classname), classname)) != NULL)
	{
		if (detpack->parent == ent)
		{
			// found it. BLAMMO!
			// play explosion sound to all clients
			gentity_t	*te = NULL;

			te = G_TempEntity( detpack->s.pos.trBase, EV_GLOBAL_SOUND );
			te->s.eventParm = G_SoundIndex( "sound/weapons/explosions/detpakexplode.wav" );//cgs.media.detpackExplodeSound
			te->r.svFlags |= SVF_BROADCAST;

			//so we can't be blown up by things we're blowing up
			detpack->takedamage = 0;

			G_AddEvent(detpack, EV_DETPACK, 0);
			G_RadiusDamage( detpack->s.origin, detpack->parent, DETPACK_DAMAGE, DETPACK_RADIUS,
				detpack, DAMAGE_HALF_NOTLOS|DAMAGE_ALL_TEAMS, MOD_DETPACK );
			// just turn the model invisible and let the entity think for a bit to deliver a shockwave
			//G_FreeEntity(detpack);
			detpack->classname = NULL;
			detpack->s.modelindex = 0;
			detpack->think = DetpackBlammoThink;
			detpack->count = 1;
			detpack->nextthink = level.time + FRAMETIME;
			return;
		}
		else if (detpack == ent)	// if detpack == ent, we're blowing up this detpack cuz it's been sitting too long
		{
			detpack_shot(detpack, NULL, NULL, 0, 0);
			return;
		}
	}
	// hmm. couldn't find it.
	detpack = NULL;
}





#define SHIELD_HEALTH				250
#define SHIELD_HEALTH_DEC			10		// 25 seconds
#define MAX_SHIELD_HEIGHT			254
#define MAX_SHIELD_HALFWIDTH		255
#define SHIELD_HALFTHICKNESS		4
#define SHIELD_PLACEDIST			64


static qhandle_t	shieldAttachSound=0;
static qhandle_t	shieldActivateSound=0;
static qhandle_t	shieldDamageSound=0;
//RPG-X: - RedTechie Added shild ZAPZPZAAAAAAppPPP sound!
static qhandle_t	shieldMurderSound=0;


void ShieldRemove(gentity_t *self)
{
	self->think = G_FreeEntity;
	self->nextthink = level.time + 100;

	// Play raising sound...
	G_AddEvent(self, EV_GENERAL_SOUND, shieldActivateSound);

	return;
}


// Count down the health of the shield.
void ShieldThink(gentity_t *self)
{
	self->s.eFlags &= ~(EF_ITEMPLACEHOLDER | EF_NODRAW);
//	self->health -= SHIELD_HEALTH_DEC;
	self->nextthink = level.time + 1000;
	if (self->health <= 0)
	{
		ShieldRemove(self);
	}
	return;
}


// The shield was damaged to below zero health.
void ShieldDie(gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int mod)
{
	// Play damaging sound...
	G_AddEvent(self, EV_GENERAL_SOUND, shieldDamageSound);

	ShieldRemove(self);
}


// The shield had damage done to it.  Make it flicker.
void ShieldPain(gentity_t *self, gentity_t *attacker, int damage)
{
	// Set the itemplaceholder flag to indicate the the shield drawing that the shield pain should be drawn.
	self->s.eFlags |= EF_ITEMPLACEHOLDER;
	self->think = ShieldThink;
	self->nextthink = level.time + 400;

	// Play damaging sound...
	G_AddEvent(self, EV_GENERAL_SOUND, shieldDamageSound);

	return;
}


// Try to turn the shield back on after a delay.
void ShieldGoSolid(gentity_t *self)
{
	trace_t		tr;
	//gentity_t  *other;
	//other = G_Spawn();
	
	// see if we're valid
	//self->health--;
	if (self->health <= 0)
	{
		ShieldRemove(self);
		return;
	}

	trap_Trace (&tr, self->r.currentOrigin, self->r.mins, self->r.maxs, self->r.currentOrigin, self->s.number, CONTENTS_BODY );
	if(tr.startsolid)
	{	// gah, we can't activate yet
		self->nextthink = level.time + 200;
		self->think = ShieldGoSolid;
		trap_LinkEntity(self);
	}
	else
	{ // get hard... huh-huh...
		self->r.contents = CONTENTS_SOLID;
		self->s.eFlags &= ~(/*EF_NODRAW | */EF_ITEMPLACEHOLDER);
		self->nextthink = level.time + 1000;
		self->think = ShieldThink;
		self->takedamage = qfalse;//RPG-X: - RedTechie use to be qtrue
		
		trap_LinkEntity(self);


		// Play raising sound...
		G_AddEvent(self, EV_GENERAL_SOUND, shieldActivateSound);
	}

	return;
}


// Turn the shield off to allow a friend to pass through.
//RPG-X J2J EDIT here:
void ShieldGoNotSolid(gentity_t *self)
{
	// make the shield non-solid very briefly
	self->r.contents = CONTENTS_NONE;
//	self->s.eFlags |= EF_NODRAW;					//Commenting this should make it look like the player passes through..
	// nextthink needs to have a large enough interval to avoid excess accumulation of Activate messages
	self->nextthink = level.time + 200;
	self->think = ShieldGoSolid;


	self->takedamage = qfalse;
	trap_LinkEntity(self);

	// Play raising sound...
	G_AddEvent(self, EV_GENERAL_SOUND, shieldActivateSound);
}


// Somebody (a player) has touched the shield.  See if it is a "friend".
void ShieldTouch(gentity_t *self, gentity_t *other, trace_t *trace)
{

/*	if (g_gametype.integer >= GT_TEAM)
	{ // let teammates through
		// compare the parent's team to the "other's" team
		if (( self->parent->client) && (other->client))
		{
			if ( self->s.otherEntityNum2 == other->client->sess.sessionTeam || other->client->sess.sessionClass == PC_ALPHAOMEGA22 )
			{
				ShieldGoNotSolid(self);
			}
		}
	}
	else*/
	{//let the person who dropped the shield through
		if (other->client->sess.sessionClass == PC_ADMIN )
		{
			ShieldGoNotSolid(self);
		}
		//RPG-X:J2J Damage for shields 
		else
		{
			other->flags |= EF_MOVER_STOP;	//Attempt to not let non admins thru.
			//RPG-X: RedTechie - Added code for zap sound also a cvar to control damage a non admin walks into if cvar is set to 0 disables health from being drained (happens every 1 second)
			//if ( level.time >= level.message + 1000 ) {
			//	level.message = level.time;
				G_AddEvent(self, EV_GENERAL_SOUND, shieldMurderSound);//RPG-X: RedTechie - ZAPtacular! sound to my ears
				if(rpg_forcefielddamage.integer != 0){
					if(rpg_forcefielddamage.integer > 999){
						rpg_forcefielddamage.integer = 999;
					}
					other->health -= rpg_forcefielddamage.integer;
					
					//RPG-X: RedTechie - Fixed free ent if medic revive on
					if(rpg_medicsrevive.integer == 1){
						if(other->health <= 1){
							other->client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );
							other->client->ps.stats[STAT_HOLDABLE_ITEM] = HI_NONE;
							other->client->ps.stats[STAT_HEALTH] = other->health = 1;
							player_die (other, other, other, 1, MOD_FORCEFIELD);
						}
					}else{
						if(other->health <= 1){
							other->client->ps.stats[STAT_HEALTH] = other->health = 0;
							player_die (other, other, other, 100000, MOD_FORCEFIELD);
						}
					}
				}
			//}
		}

	}
}


// After a short delay, create the shield by expanding in all directions.
void CreateShield(gentity_t *ent)
{
	trace_t		tr;
	vec3_t		mins, maxs, end, posTraceEnd, negTraceEnd, start;
	int			height, posWidth, negWidth, halfWidth = 0;
	qboolean	xaxis;
	int			paramData = 0;
	static int	shieldID;

	// trace upward to find height of shield
	VectorCopy(ent->r.currentOrigin, end);
	end[2] += MAX_SHIELD_HEIGHT;
	trap_Trace (&tr, ent->r.currentOrigin, NULL, NULL, end, ent->s.number, MASK_SHOT );
	height = (int)(MAX_SHIELD_HEIGHT * tr.fraction);

	// use angles to find the proper axis along which to align the shield
	VectorSet(mins, -SHIELD_HALFTHICKNESS, -SHIELD_HALFTHICKNESS, 0);
	VectorSet(maxs, SHIELD_HALFTHICKNESS, SHIELD_HALFTHICKNESS, height);
	VectorCopy(ent->r.currentOrigin, posTraceEnd);
	VectorCopy(ent->r.currentOrigin, negTraceEnd);

	if ((int)(ent->s.angles[YAW]) == 0) // shield runs along y-axis
	{
		posTraceEnd[1]+=MAX_SHIELD_HALFWIDTH;
		negTraceEnd[1]-=MAX_SHIELD_HALFWIDTH;
		xaxis = qfalse;
	}
	else  // shield runs along x-axis
	{
		posTraceEnd[0]+=MAX_SHIELD_HALFWIDTH;
		negTraceEnd[0]-=MAX_SHIELD_HALFWIDTH;
		xaxis = qtrue;
	}

	// trace horizontally to find extend of shield
	// positive trace
	VectorCopy(ent->r.currentOrigin, start);
	start[2] += (height>>1);
	trap_Trace (&tr, start, 0, 0, posTraceEnd, ent->s.number, MASK_SHOT );
	posWidth = MAX_SHIELD_HALFWIDTH * tr.fraction;
	// negative trace
	trap_Trace (&tr, start, 0, 0, negTraceEnd, ent->s.number, MASK_SHOT );
	negWidth = MAX_SHIELD_HALFWIDTH * tr.fraction;

	// kef -- monkey with dimensions and place origin in center
	halfWidth = (posWidth + negWidth)>>1;
	if (xaxis)
	{
		ent->r.currentOrigin[0] = ent->r.currentOrigin[0] - negWidth + halfWidth;
	}
	else
	{
		ent->r.currentOrigin[1] = ent->r.currentOrigin[1] - negWidth + halfWidth;
	}
	ent->r.currentOrigin[2] += (height>>1);

	// set entity's mins and maxs to new values, make it solid, and link it
	if (xaxis)
	{
		VectorSet(ent->r.mins, -halfWidth, -SHIELD_HALFTHICKNESS, -(height>>1));
		VectorSet(ent->r.maxs, halfWidth, SHIELD_HALFTHICKNESS, height>>1);
	}
	else
	{
		VectorSet(ent->r.mins, -SHIELD_HALFTHICKNESS, -halfWidth, -(height>>1));
		VectorSet(ent->r.maxs, SHIELD_HALFTHICKNESS, halfWidth, height>>1);
	}
	ent->clipmask = MASK_SHOT;

	// Information for shield rendering.

//	xaxis - 1 bit
//	height - 0-254 8 bits
//	posWidth - 0-255 8 bits
//  negWidth - 0 - 255 8 bits

	paramData = (xaxis << 24) | (height << 16) | (posWidth << 8) | (negWidth);
	ent->s.time2 = paramData;

	if ( ent->s.otherEntityNum2 == TEAM_RED )
	{
		ent->team = "1";
	}
	else if ( ent->s.otherEntityNum2 == TEAM_BLUE )
	{
		ent->team = "2";
	}
	
	//RPG-X: - RedTechie no shield count down
	if ( g_pModSpecialties.integer != 0 )
	{
		//ent->health = ceil(SHIELD_HEALTH*4*g_dmgmult.value);
	}
	else
	{
		ent->health = ceil(SHIELD_HEALTH*g_dmgmult.value);
	}
	
	
	ent->s.time = ent->health;//???
	ent->pain = ShieldPain;
	ent->die = ShieldDie;
	ent->touch = ShieldTouch;
	
	ent->r.svFlags |= SVF_SHIELD_BBOX;

	// see if we're valid
	trap_Trace (&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, CONTENTS_BODY );

	if (tr.startsolid)
	{	// Something in the way!
		// make the shield non-solid very briefly
		ent->r.contents = CONTENTS_NONE;
		ent->s.eFlags |= EF_NODRAW;
		// nextthink needs to have a large enough interval to avoid excess accumulation of Activate messages
		ent->nextthink = level.time + 200;
		ent->think = ShieldGoSolid;
		ent->takedamage = qfalse;
		trap_LinkEntity(ent);
	}
	else
	{	// Get solid.
		ent->r.contents = CONTENTS_PLAYERCLIP|CONTENTS_SHOTCLIP;//CONTENTS_SOLID;

		ent->nextthink = level.time + 1000;
		ent->think = ShieldThink;

		ent->takedamage = qfalse;//RPG-X: - RedTechie Use to be qtrue
		trap_LinkEntity(ent);

		// Play raising sound...
		G_AddEvent(ent, EV_GENERAL_SOUND, shieldActivateSound);
	}

	return;
}

qboolean PlaceShield(gentity_t *playerent)
{
	static const gitem_t *shieldItem = NULL;
	gentity_t	*shield = NULL;
	trace_t		tr;
	vec3_t		fwd, pos, dest, mins = {-16,-16, 0}, maxs = {16,16,16};

	if (shieldAttachSound==0)
	{
		shieldAttachSound = G_SoundIndex("sound/weapons/detpacklatch.wav");
		shieldActivateSound = G_SoundIndex("sound/movers/forceup.wav");
		shieldDamageSound = G_SoundIndex("sound/ambience/spark5.wav");
		shieldMurderSound = G_SoundIndex("sound/world/electro.wav"); //RPG-X: - RedTechie Added shild ZAP! sound
		shieldItem = BG_FindItemForHoldable(HI_SHIELD);
	}

	// can we place this in front of us?
	AngleVectors (playerent->client->ps.viewangles, fwd, NULL, NULL);
	fwd[2] = 0;
	VectorMA(playerent->client->ps.origin, SHIELD_PLACEDIST, fwd, dest);
	trap_Trace (&tr, playerent->client->ps.origin, mins, maxs, dest, playerent->s.number, MASK_SHOT );
	if (tr.fraction > 0.9)
	{//room in front
		VectorCopy(tr.endpos, pos);
		// drop to floor
		VectorSet( dest, pos[0], pos[1], pos[2] - 4096 );
		trap_Trace( &tr, pos, mins, maxs, dest, playerent->s.number, MASK_SOLID );
		if ( !tr.startsolid && !tr.allsolid )
		{
			// got enough room so place the portable shield
			shield = G_Spawn();

			// Figure out what direction the shield is facing.
			if (fabs(fwd[0]) > fabs(fwd[1]))
			{	// shield is north/south, facing east.
				shield->s.angles[YAW] = 0;
			}
			else
			{	// shield is along the east/west axis, facing north
				shield->s.angles[YAW] = 90;
			}
			shield->think = CreateShield;
			shield->nextthink = level.time + 500;	// power up after .5 seconds
			shield->parent = playerent;

			// Set team number.
			shield->s.otherEntityNum2 = playerent->client->sess.sessionTeam;

			shield->s.eType = ET_USEABLE;
			shield->s.modelindex =  HI_SHIELD;	// this'll be used in CG_Useable() for rendering.
			shield->classname = shieldItem->classname;

			shield->r.contents = CONTENTS_TRIGGER;

			shield->touch = 0;
			// using an item causes it to respawn
			shield->use = 0; //Use_Item;

			// allow to ride movers
			shield->s.groundEntityNum = tr.entityNum;

			G_SetOrigin( shield, tr.endpos );

			shield->s.eFlags &= ~EF_NODRAW;
			shield->r.svFlags &= ~SVF_NOCLIENT;

			trap_LinkEntity (shield);

			// Play placing sound...
			G_AddEvent(shield, EV_GENERAL_SOUND, shieldAttachSound);

			return qtrue;
		}
	}
	// no room
	return qfalse;
}



//-------------------------------------------------------------- DECOY ACTIVITIES
void DecoyThink(gentity_t *ent)
{
	ent->s.apos =(ent->parent)->s.apos;					// Update Current Rotation
	ent->nextthink = level.time + irandom(2000, 6000);	// Next think between 2 & 8 seconds

	(ent->count) --;									// Count Down
	if (ent->count<0)			G_FreeEntity(ent);		// Time To Erase The Ent
}

qboolean PlaceDecoy(gentity_t *ent)
{
	gentity_t	*decoy = NULL;
	static gitem_t *decoyItem = NULL;
	float		detDistance = 80;	// Distance the object will be placed from player
	trace_t		tr;
	vec3_t		fwd, right, up, end, mins = {-16,-16,-24}, maxs = {16,16,16};

	if (decoyItem == NULL)
	{
		decoyItem = BG_FindItemForHoldable(HI_DECOY);
	}

	// can we place this in front of us?
	AngleVectors (ent->client->ps.viewangles, fwd, right, up);
	fwd[2] = 0;
	VectorMA(ent->client->ps.origin, detDistance, fwd, end);
	trap_Trace (&tr, ent->client->ps.origin, mins, maxs, end, ent->s.number, MASK_SHOT );
	if ( !tr.allsolid && !tr.startsolid && tr.fraction > 0.9 )
	{
		//--------------------------- SPAWN AND PLACE DECOY ON GROUND
		decoy = G_Spawn();
		G_SpawnItem(decoy, decoyItem);				// Generate it as an item, temporarly
		decoy->physicsBounce = 0.0f;//decoys are *not* bouncy
		VectorMA(ent->client->ps.origin, detDistance + mins[0], fwd, decoy->s.origin);
		decoy->r.mins[2] = mins[2];//keep it off the floor
		VectorNegate(fwd, fwd);					// ???  What does this do??
		vectoangles(fwd, decoy->s.angles);
		if ( !FinishSpawningDecoy(decoy, decoyItem - bg_itemlist) )
		{
			return qfalse;		// Drop to ground and trap to clients
		}
		decoy->s.clientNum = ent->client->ps.clientNum;

		//--------------------------- SPECIALIZED DECOY SETUP
		decoy->think = DecoyThink;
		decoy->count = 12;						// about 1 minute before dissapear
		decoy->nextthink = level.time + 4000;	// think after 4 seconds
		decoy->parent = ent;

		(decoy->s).eType = (ent->s).eType;		// set to type PLAYER
		(decoy->s).eFlags= (ent->s).eFlags;
		(decoy->s).eFlags |= EF_ITEMPLACEHOLDER;// set the HOLOGRAM FLAG to ON

		decoy->s.weapon = ent->s.weapon;		// get Player's Wepon Type
//		decoy->s.constantLight = 10 + (10 << 8) + (10 << 16) + (9 << 24);

		//decoy->s.pos.trBase[2] += 24;			// shift up to adjust origin of body
		decoy->s.apos = ent->s.apos;			// copy angle of player to decoy

		decoy->s.legsAnim = BOTH_STAND1;			// Just standing TORSO_STAND
		decoy->s.torsoAnim = BOTH_STAND1;

		//--------------------------- WEAPON ADJUST
		if (decoy->s.weapon==WP_PHASER || decoy->s.weapon==WP_DERMAL_REGEN)
			decoy->s.weapon = WP_COMPRESSION_RIFLE;

		//   The Phaser and Dreadnought (Arc Welder) weapons are rendered on the
		//   client side differently, and cannot be used by the decoy

		return qtrue;						// SUCCESS
	}
	else
	{
		return qfalse;						// FAILURE: no room
	}
}
//-------------------------------------------------------------- DECOY ACTIVITIES





void G_Rematerialize( gentity_t *ent )
{
	if ( ent->s.number == borgQueenClientNum )
	{
		ent->client->teleportTime = level.time + ( 60 * 1000 );
		ent->client->ps.stats[STAT_USEABLE_PLACED] = 60;
	}
	else
	{
		ent->client->teleportTime = level.time + ( 15 * 1000 );
		ent->client->ps.stats[STAT_USEABLE_PLACED] = 15;
	}
	ent->flags &= ~FL_NOTARGET;
	ent->takedamage = qtrue;
	ent->r.contents = MASK_PLAYERSOLID;
	ent->s.eFlags &= ~EF_NODRAW;
	ent->client->ps.eFlags &= ~EF_NODRAW;
	TeleportPlayer( ent, ent->client->ps.origin, ent->client->ps.viewangles, TP_BORG );
	//ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
	//take it away
	ent->client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
}

void G_GiveHoldable( gclient_t *client, holdable_t item )
{
	gitem_t	*holdable = BG_FindItemForHoldable( item );

	client->ps.stats[STAT_HOLDABLE_ITEM] = holdable - bg_itemlist;//teleport spots should be on other side of map
	RegisterItem( holdable );
}

/*
================
ClientEvents

Events will be passed on to the clients for presentation,
but any server game effects are handled here
================
*/
void ClientEvents( gentity_t *ent, int oldEventSequence ) {
	int		i;
	int		event;
	gclient_t *client;
	int		damage;

	client = ent->client;

	if ( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) {
		oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
	}
	for ( i = oldEventSequence ; i < client->ps.eventSequence ; i++ ) {
		event = client->ps.events[ i & (MAX_PS_EVENTS-1) ];

		switch ( event ) {
		case EV_FALL_MEDIUM:
		case EV_FALL_FAR:
			if ( ent->s.eType != ET_PLAYER ) {
				break;		// not in the player model
			}
			if ( g_dmflags.integer & DF_NO_FALLING ) {
				break;
			}
			if ( rpg_selfdamage.integer != 0 )
			{
				if ( event == EV_FALL_FAR )
				{
					damage = 110; //10 -TiM : Make the falling more realistc!
				}
				else
				{
					damage = 90; //5
				}
			}
			else
			{
				damage = 0;
			}
			ent->pain_debounce_time = level.time + 200;	// no normal pain sound
			G_Damage (ent, NULL, NULL, NULL, NULL, damage, DAMAGE_ARMOR_PIERCING, MOD_FALLING);

			break;

		case EV_FIRE_WEAPON:
			FireWeapon( ent, qfalse );
			break;

		case EV_ALT_FIRE:
			FireWeapon( ent, qtrue );
			break;

		case EV_FIRE_EMPTY_PHASER:
			FireWeapon( ent, qfalse );
			break;

		case EV_USE_ITEM1:		// transporter

			//------------------------------------------------------- DROP FLAGS
			if      ( ent->client->ps.powerups[PW_REDFLAG] ) {
				Team_ReturnFlag(TEAM_RED);
			}
			else if ( ent->client->ps.powerups[PW_BLUEFLAG] ) {
				Team_ReturnFlag(TEAM_BLUE);
			}
			ent->client->ps.powerups[PW_BLUEFLAG] = 0;
			ent->client->ps.powerups[PW_REDFLAG]  = 0;
			//------------------------------------------------------- DROP FLAGS
			if ( ent->client->sess.sessionClass == PC_BORG )
			{
				if ( !ent->client->ps.stats[STAT_USEABLE_PLACED] )
				{//go into free-roaming mode
					gentity_t	*tent;
					//FIXME: limit this to 10 seconds
					ent->flags |= FL_NOTARGET;
					ent->takedamage = qfalse;
					ent->r.contents = CONTENTS_PLAYERCLIP;
					ent->s.eFlags |= EF_NODRAW;
					ent->client->ps.eFlags |= EF_NODRAW;
					ent->client->ps.stats[STAT_USEABLE_PLACED] = 2;
					ent->client->teleportTime = level.time + 10000;
					tent = G_TempEntity( ent->client->ps.origin, EV_BORG_TELEPORT );
					tent->s.clientNum = ent->s.clientNum;
				}
				else
				{//come out of free-roaming mode, teleport to current position
					G_Rematerialize( ent );
				}
			}
			else// get rid of transporter and go to random spawnpoint
			{
				//vec3_t	origin, angles;
				//gentity_t	*tent;

				//SelectSpawnPoint( ent->client->ps.origin, origin, angles );

				//if(TransDat[ent->client->ps.clientNum].pUsed == qfalse)
				//TiM: Since we purge the vectors each cycle.  I'll save us some memory by using the vectors themselves as a check.
				if ( TransDat[ent->client->ps.clientNum].beamTime == 0 && 
					VectorCompare( vec3_origin, TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].origin ) &&
					 VectorCompare( vec3_origin, TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].angles ) )
				{
					VectorCopy( ent->client->ps.origin, TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].origin );
					VectorCopy( ent->client->ps.viewangles, TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].angles );
					//VectorCopy(ent->client->ps.origin, TransDat[ent->client->ps.clientNum].pTransCoord);
					//VectorCopy(ent->client->ps.viewangles, TransDat[ent->client->ps.clientNum].pTransCoordRot);
					//TransDat[ent->client->ps.clientNum].pUsed = qtrue;
					trap_SendServerCommand( ent-g_entities, va("chat \"Site to Site Transporter Location Confirmed.\nPress again to Energize.\"", Q_COLOR_ESCAPE));
					//trap_SendConsoleCommand( EXEC_APPEND, va("echo Site to Site Transporter Location Confirmed.\necho Press again to Energize.") );
					ent->client->ps.stats[STAT_HOLDABLE_ITEM] = BG_FindItemForHoldable( HI_TRANSPORTER ) - bg_itemlist;
					ent->client->ps.stats[STAT_USEABLE_PLACED] = 2; // = 1
					break;
				}

				//TeleportPlayer( ent, TransDat[ent->client->ps.clientNum].pTransCoord, TransDat[ent->client->ps.clientNum].pTransCoordRot, TP_NORMAL );
				if ( TransDat[ent->client->ps.clientNum].beamTime == 0 && level.time > ent->client->ps.powerups[PW_QUAD] ) {
					G_InitTransport( ent->client->ps.clientNum, TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].origin,
									TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE].angles );

					memset( &TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE], 0, sizeof( TransDat[ent->client->ps.clientNum].storedCoord[TPT_PORTABLE]) );
				}
				else {
					trap_SendServerCommand( ent-g_entities, va("chat \"Unable to comply. Already within transport cycle.\"", Q_COLOR_ESCAPE));
				}

				ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
				
				if ( ent->client->sess.sessionClass == PC_ALPHAOMEGA22 )
				{
					ent->client->teleportTime = level.time + ( 1 * 1000 ); // 15 * 1000
					ent->client->ps.stats[STAT_USEABLE_PLACED] = 1; // = 1
				}
					
			}
			break;

		case EV_USE_ITEM2:		// medkit
			// New set of rules.  You get either 100 health, or an extra 25, whichever is higher.
			// Give 1/4 health.
			ent->health += ent->client->ps.stats[STAT_MAX_HEALTH]*0.25;

			if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH])
			{	// If that doesn't bring us up to 100, make it go up to 100.
				ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
			}
			else if (ent->health > ent->client->ps.stats[STAT_MAX_HEALTH]*2)
			{	// Otherwise, 25 is all you get.  Just make sure we don't go above 200.
				ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]*2;
			}
			break;

		case EV_USE_ITEM3:		// detpack
			// if we haven't placed it yet, place it
			if (0 == ent->client->ps.stats[STAT_USEABLE_PLACED])
			{
				if ( PlaceDetpack(ent) )
				{
					ent->client->ps.stats[STAT_USEABLE_PLACED] = 1;
					trap_SendServerCommand( ent-g_entities, "cp \"CHARGE PLACED\"" );
				}
				else
				{//couldn't place it
					ent->client->ps.stats[STAT_HOLDABLE_ITEM] = (BG_FindItemForHoldable( HI_DETPACK ) - bg_itemlist);
					trap_SendServerCommand( ent-g_entities, "cp \"NO ROOM TO PLACE CHARGE\"" );
				}
			}
			else
			{
				//turn off invincibility since you are going to do massive damage
				//ent->client->ps.powerups[PW_GHOST] = 0;//NOTE: this means he can respawn and get a detpack 10 seconds later
				// ok, we placed it earlier. blow it up.
				ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
				DetonateDetpack(ent);
			}
			break;

		case EV_USE_ITEM4:		// portable shield
			if ( !PlaceShield(ent) )	// fixme if we fail, perhaps just spawn it as a pickup
			{//couldn't place it
				ent->client->ps.stats[STAT_HOLDABLE_ITEM] = (BG_FindItemForHoldable( HI_SHIELD ) - bg_itemlist);
				trap_SendServerCommand( ent-g_entities, "cp \"NO ROOM TO PLACE FORCE FIELD\"" );
			}
			else
			{
				trap_SendServerCommand( ent-g_entities, "cp \"FORCE FIELD PLACED\"" );
				//if ( ent->client->sess.sessionClass == PC_ADMIN )
			//	{
				//	ent->client->teleportTime = level.time + ( 30 * 1000 );
				//	ent->client->ps.stats[STAT_USEABLE_PLACED] = 30;
				//}
			}
			break;

		case EV_USE_ITEM5:		// decoy
			if ( !PlaceDecoy(ent) )
			{//couldn't place it
				ent->client->ps.stats[STAT_HOLDABLE_ITEM] = (BG_FindItemForHoldable( HI_DECOY ) - bg_itemlist);
				trap_SendServerCommand( ent-g_entities, "cp \"NO ROOM TO PLACE DECOY\"" );
			}
			else
			{
				trap_SendServerCommand( ent-g_entities, "cp \"DECOY PLACED\"" );
			}
			break;

		default:
			break;
		}
	}

}

void BotTestSolid(vec3_t origin);

/*
==============
SendPendingPredictableEvents
==============
*/
void SendPendingPredictableEvents( playerState_t *ps ) {
	gentity_t *t;
	int event, seq;
	int extEvent;

	// if there are still events pending
	if ( ps->entityEventSequence < ps->eventSequence ) {
		// create a temporary entity for this event which is sent to everyone
		// except the client generated the event
		seq = ps->entityEventSequence & (MAX_PS_EVENTS-1);
		event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
		// set external event to zero before calling BG_PlayerStateToEntityState
		extEvent = ps->externalEvent;
		ps->externalEvent = 0;
		// create temporary entity for event
		t = G_TempEntity( ps->origin, event );
		BG_PlayerStateToEntityState( ps, &t->s, qtrue );
		t->s.eType = ET_EVENTS + event;
		// send to everyone except the client who generated the event
		t->r.svFlags |= SVF_NOTSINGLECLIENT;
		t->r.singleClient = ps->clientNum;
		// set back external event
		ps->externalEvent = extEvent;
	}
}

/*
==============
ClientThink

This will be called once for each client frame, which will
usually be a couple times for each server frame on fast clients.

If "g_synchronousClients 1" is set, this will be called exactly
once for each server frame, which makes for smooth demo recording.
==============
*/
void ClientThink_real( gentity_t *ent ) {
	gclient_t	*client;
	pmove_t		pm;
	vec3_t		oldOrigin;
	int			oldEventSequence;
	int			msec;
	usercmd_t	*ucmd;

	client = ent->client;

	// don't think if the client is not yet connected (and thus not yet spawned in)
	if (client->pers.connected != CON_CONNECTED) {
		return;
	}
	// mark the time, so the connection sprite can be removed
	ucmd = &ent->client->pers.cmd;

	// sanity check the command time to prevent speedup cheating
	if ( ucmd->serverTime > level.time + 200 ) {
		ucmd->serverTime = level.time + 200;
//		G_Printf("serverTime <<<<<\n" );
	}
	if ( ucmd->serverTime < level.time - 1000 ) {
		ucmd->serverTime = level.time - 1000;
//		G_Printf("serverTime >>>>>\n" );
	}

	msec = ucmd->serverTime - client->ps.commandTime;
	// following others may result in bad times, but we still want
	// to check for follow toggles
	if ( msec < 1 && client->sess.spectatorState != SPECTATOR_FOLLOW ) {
		return;
	}
	if ( msec > 200 ) {
		msec = 200;
	}

	//
	// check for exiting intermission
	//
	if ( level.intermissiontime ) {
		ClientIntermissionThink( client );
		return;
	}

	// Don't move while under intro sequence.
	/*if (client->ps.introTime > level.time)
	{	// Don't be visible either.
		ent->s.eFlags |= EF_NODRAW;
		SetClientViewAngle( ent, ent->s.angles );
		ucmd->buttons = 0;
		ucmd->weapon = 0;
//		ucmd->angles[0] = ucmd->angles[1] = ucmd->angles[2] = 0;
		ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = 0;
//		return;
	}*/

	// spectators don't do much
	if ( client->sess.sessionTeam == TEAM_SPECTATOR || (client->ps.eFlags&EF_ELIMINATED) ) {
		if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
			return;
		}
		SpectatorThink( ent, ucmd );
		return;
	}

	// check for inactivity timer, but never drop the local client of a non-dedicated server
	if ( !ClientInactivityTimer( client ) ) {
		return;
	}

	//TiM - If we're null content... see what's up
	if ( ent->r.contents == CONTENTS_NONE ) {
		if ( !G_MoveBox( ent ) && client->ps.stats[STAT_HEALTH] >= 1 ) {
			ent->r.contents = CONTENTS_BODY;
		}
		else {
			ent->r.contents = CONTENTS_NONE ;
		}
	}

	// clear the rewards if time
	/*if ( level.time > client->rewardTime ) {
		client->ps.eFlags &= ~EF_AWARD_MASK;
	}*/
	
	//RPG-X: Checked to see if medics revive is on if so do as following
	if(rpg_medicsrevive.integer == 1){
		if ( client->noclip ) {
			client->ps.pm_type = PM_NOCLIP;
		} else if ( client->ps.stats[STAT_HEALTH] == 1 ) {
			client->ps.pm_type = PM_DEAD;
		} else {
			client->ps.pm_type = PM_NORMAL;
		}
	}else{
		if ( client->noclip ) {
			client->ps.pm_type = PM_NOCLIP;
		} else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
			client->ps.pm_type = PM_DEAD;
		} else {
			client->ps.pm_type = PM_NORMAL;
		}
	}
	
	//RPG-X: J2J & Phenix - For the gravity ent
	if(client->SpecialGrav != qtrue)
	{
		client->ps.gravity = g_gravity.value;
	}

	// set speed
	client->ps.speed = g_speed.value;

	if ( client->ps.powerups[PW_HASTE] )
	{
		client->ps.speed *= 1.5;
	}
	else if (client->ps.powerups[PW_FLIGHT] )
	{//flying around
		client->ps.speed *= 1.3;
	}
	else if ( client->ps.stats[STAT_HEALTH] <= 20 ) {
		client->ps.speed *= 0.55;
	}
	
	if (( client->ps.powerups[PW_EVOSUIT] ) && ( client->ps.gravity == 0 ))
	{//Evosuit time.. RPG-X | Phenix | 8/8/2004
		client->ps.speed *= 1.3;
	}
	
	//RPG-X: Redtechie - n00bie stay.....good boy!
	if ( client->sess.sessionClass == PC_N00B ){
     client->ps.speed = 0;
	}

	if ( client->sess.sessionClass == PC_HEAVY )
	{
		client->ps.speed *= 0.75;
	}

	if ( client->sess.sessionClass == PC_BORG )
	{
		if ( BG_BorgTransporting( &client->ps ) )
		{
			client->ps.speed *= 1.5;
		}
		else if ( !BG_BorgTransporting( &client->ps ) )
		{
			client->ps.speed *= 0.75;
		}
	}

	//TiM : SP Style Transporter. :)
	//first check to see if we should be beaming
	if ( level.time < TransDat[client->ps.clientNum].beamTime ) {
		//Hold players in place.  Do we need??
		//client->ps.speed = 0.0;

		//if we're past the mid point of each materialization cycle, make it
		//so bullets and other players will pass thru the transportee. :)
		if ( (level.time > TransDat[client->ps.clientNum].beamTime - 6000) &&
			( level.time < TransDat[client->ps.clientNum].beamTime - 2000 ) ) {
			if ( client->ps.stats[STAT_HEALTH] > 1 ) {
				ent->r.contents = CONTENTS_NONE;
			}
			//ent->r.contents = CONTENTS_CORPSE;
		}
		else {
			if ( client->ps.stats[STAT_HEALTH] > 1 ) {
				ent->r.contents = MASK_PLAYERSOLID;
				//ent->r.contents = CONTENTS_BODY;
			}
		}

		//If we're half-way thru the cycle, teleport the player now
		if ( level.time > TransDat[client->ps.clientNum].beamTime - 4000 && 
		!TransDat[client->ps.clientNum].beamed ) {
			TeleportPlayer( ent, TransDat[client->ps.clientNum].currentCoord.origin, 
									TransDat[client->ps.clientNum].currentCoord.angles, 
									TP_TRI_TP );

			TransDat[client->ps.clientNum].beamed = qtrue;
		}
	}
	else {
		//all done, let's reset :)
		if ( TransDat[client->ps.clientNum].beamTime > 0 ) {
			TransDat[client->ps.clientNum].beamTime = 0;
			client->ps.powerups[PW_BEAM_OUT] = 0;
			client->ps.powerups[PW_QUAD] = 0;
			TransDat[client->ps.clientNum].beamed = qfalse;

			memset( &TransDat[client->ps.clientNum].currentCoord, 0, 
					sizeof( TransDat[client->ps.clientNum].currentCoord.origin ) );
		}
	}

	//TiM : Freeze their movement if they're halfway through a transport cycle
	if ( level.time < TransDat[client->ps.clientNum].beamTime && 
		level.time > TransDat[client->ps.clientNum].beamTime - 4000 )
	{
		vec3_t	endPoint;
		trace_t	tr;
		VectorSet( endPoint, client->ps.origin[0], client->ps.origin[1], client->ps.origin[2] - 48 );
		//Do a trace down.  If we're near ground, just re-enable gravity.  Else we we get weird animations O_o
		trap_Trace( &tr, client->ps.origin, NULL, NULL, endPoint, client->ps.clientNum, CONTENTS_SOLID );

		if ( tr.fraction == 1.0 ) {
			client->ps.gravity = 0;
			client->ps.velocity[2] = 0;
		}

		client->ps.speed = 0;
		
		client->ps.velocity[0] = client->ps.velocity[1] = 0.0;
	}

	// set up for pmove
	oldEventSequence = client->ps.eventSequence;

	memset (&pm, 0, sizeof(pm));

	pm.ps = &client->ps;
	pm.cmd = *ucmd;
	if ( pm.ps->pm_type == PM_DEAD ) {
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
	}
	else {
		pm.tracemask = MASK_PLAYERSOLID;
	}
	pm.trace = trap_Trace;
	pm.pointcontents = trap_PointContents;
	pm.debugLevel = g_debugMove.integer;
	pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;
	pm.pModDisintegration = g_pModDisintegration.integer > 0;

	VectorCopy( client->ps.origin, oldOrigin );

	// perform a pmove
	Pmove (&pm);

	// save results of pmove
	if ( ent->client->ps.eventSequence != oldEventSequence ) {
		ent->eventTime = level.time;
	}
	BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );

	SendPendingPredictableEvents( &ent->client->ps );

	// use the snapped origin for linking so it matches client predicted versions
	VectorCopy( ent->s.pos.trBase, ent->r.currentOrigin );

	VectorCopy (pm.mins, ent->r.mins);
	VectorCopy (pm.maxs, ent->r.maxs);

	ent->waterlevel = pm.waterlevel;
	ent->watertype = pm.watertype;

	// execute client events
	ClientEvents( ent, oldEventSequence );

	if ( pm.useEvent )
	{		//TODO: Use
		TryUse( ent );
	}

	// link entity now, after any personal teleporters have been used
	trap_LinkEntity (ent);
//	if ( !ent->client->noclip ) {
		G_TouchTriggers( ent );
//	}

	// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
	VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );

	//test for solid areas in the AAS file
	BotTestSolid(ent->r.currentOrigin);

	// touch other objects
	ClientImpacts( ent, &pm );

	// save results of triggers and client events
	if (ent->client->ps.eventSequence != oldEventSequence) {
		ent->eventTime = level.time;
	}

	// swap and latch button actions
	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons |= client->buttons & ~client->oldbuttons;

	// check for respawning
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		// wait for the attack button to be pressed
		if ( level.time > client->respawnTime ) {
			// forcerespawn is to prevent users from waiting out powerups
			//RPG-X: RedTechie - No forcerespawn
			/*if ( g_forcerespawn.integer > 0 &&
				( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) {
				respawn( ent );
				return;
			}*/

			// pressing attack or use is the normal respawn method
			if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
				respawn( ent );
				return;
			}

			//in assimilation and elimination, always force respawn
			if ( level.time - client->respawnTime > 1300 && //NOTE: when killed, client->respawnTime = level.time + 1700, so this is 3000 ms
				( g_pModAssimilation.integer || g_pModElimination.integer ) )
			{
				respawn( ent );
				return;
			}
		}
		return;
	}

	// perform once-a-second actions
	ClientTimerActions( ent, msec );

	if ( ent->client->teleportTime > 0 && ent->client->teleportTime < level.time )
	{
		if ( ent->client->sess.sessionClass == PC_BORG )
		{
			if ( BG_BorgTransporting( &ent->client->ps ) )
			{
				G_Rematerialize( ent );
			}
			else
			{
				G_GiveHoldable( ent->client, HI_TRANSPORTER );
				ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
				ent->client->teleportTime = 0;
			}
		}
		else if ( ent->client->sess.sessionClass == PC_DEMO )
		{
			G_GiveHoldable( ent->client, HI_DETPACK );
			ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
			ent->client->teleportTime = 0;
		}
		else if ( ent->client->sess.sessionClass == PC_ALPHAOMEGA22 )
		{
			G_GiveHoldable( ent->client, HI_TRANSPORTER );
			ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
			ent->client->teleportTime = 0;
		}
		else if ( ent->client->sess.sessionClass == PC_ADMIN )
		{
			G_GiveHoldable( ent->client, HI_SHIELD );
			ent->client->ps.stats[STAT_USEABLE_PLACED] = 0;
			ent->client->teleportTime = 0;
		}
	}

}

/*
==================
ClientThink

A new command has arrived from the client
==================
*/
void ClientThink( int clientNum ) {
	gentity_t *ent;

	ent = g_entities + clientNum;
	trap_GetUsercmd( clientNum, &ent->client->pers.cmd );

	// mark the time we got info, so we can display the
	// phone jack if they don't get any for a while
	ent->client->lastCmdTime = level.time;

	if ( !g_synchronousClients.integer ) {
		ClientThink_real( ent );
	}
}


void G_RunClient( gentity_t *ent ) {
	if ( !g_synchronousClients.integer ) {
		return;
	}
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink_real( ent );
}


/*
==================
SpectatorClientEndFrame

==================
*/
void SpectatorClientEndFrame( gentity_t *ent ) {
	gclient_t	*cl;

	// if we are doing a chase cam or a remote view, grab the latest info
	if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) {
		int		clientNum;

		clientNum = ent->client->sess.spectatorClient;

		// team follow1 and team follow2 go to whatever clients are playing
		if ( clientNum == -1 ) {
			clientNum = level.follow1;
		} else if ( clientNum == -2 ) {
			clientNum = level.follow2;
		}
		if ( clientNum >= 0 ) {
			cl = &level.clients[ clientNum ];
			if ( cl->pers.connected == CON_CONNECTED && cl->sess.sessionTeam != TEAM_SPECTATOR ) {
				ent->client->ps = cl->ps;
				ent->client->ps.pm_flags |= PMF_FOLLOW;
				return;
			} else {
				// drop them to free spectators unless they are dedicated camera followers
				if ( ent->client->sess.spectatorClient >= 0 ) {
					ent->client->sess.spectatorState = SPECTATOR_FREE;
					ClientBegin( ent->client - level.clients, qfalse );
				}
			}
		}
	}

	if ( ent->client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
		ent->client->ps.pm_flags |= PMF_SCOREBOARD;
	} else {
		ent->client->ps.pm_flags &= ~PMF_SCOREBOARD;
	}
}

/*
==============
ClientEndFrame

Called at the end of each server frame for each connected client
A fast client will have multiple ClientThink for each ClientEdFrame,
while a slow client may have multiple ClientEndFrame between ClientThink.
==============
*/
void ClientEndFrame( gentity_t *ent ) {
	int			i;
	clientPersistant_t	*pers;

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || (ent->client->ps.eFlags&EF_ELIMINATED) ) {
		SpectatorClientEndFrame( ent );
		ent->client->noclip = qtrue;
		return;
	}

	pers = &ent->client->pers;

	// turn off any expired powerups
	for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {
		if ( ent->client->ps.powerups[ i ] < level.time ) {
			ent->client->ps.powerups[ i ] = 0;
		}
	}

	// save network bandwidth
#if 0
	if ( !g_synchronousClients->integer && ent->client->ps.pm_type == PM_NORMAL ) {
		// FIXME: this must change eventually for non-sync demo recording
		VectorClear( ent->client->ps.viewangles );
	}
#endif

	//
	// If the end of unit layout is displayed, don't give
	// the player any normal movement attributes
	//
	if ( level.intermissiontime ) {
		return;
	}

	// burn from lava, etc
	P_WorldEffects (ent);

	// apply all the damage taken this frame
	P_DamageFeedback (ent);

	// add the EF_CONNECTION flag if we haven't gotten commands recently
	if ( level.time - ent->client->lastCmdTime > 1000 ) {
		ent->s.eFlags |= EF_CONNECTION;
	} else {
		ent->s.eFlags &= ~EF_CONNECTION;
	}

	ent->client->ps.stats[STAT_HEALTH] = ent->health;	// FIXME: get rid of ent->health...

	G_SetClientSound (ent);

	// set the latest infor
	BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
	SendPendingPredictableEvents( &ent->client->ps );

}


