Пример #1
0
/*
==================
BotValidChatPosition
==================
*/
int BotValidChatPosition(bot_state_t *bs) {
	vec3_t point, start, end, mins, maxs;
	bsp_trace_t trace;

	//if the bot is dead all positions are valid
	if (BotIsDead(bs)) return qtrue;
	//never start chatting with a powerup
	if (bs->inventory[INVENTORY_QUAD] ||
		bs->inventory[INVENTORY_ENVIRONMENTSUIT] ||
		bs->inventory[INVENTORY_HASTE] ||
		bs->inventory[INVENTORY_INVISIBILITY] ||
		bs->inventory[INVENTORY_REGEN] ||
		bs->inventory[INVENTORY_FLIGHT]) return qfalse;
	//must be on the ground
	//if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse;
	//do not chat if in lava or slime
	VectorCopy(bs->origin, point);
	point[2] -= 24;
	if (trap_PointContents(point,bs->entitynum) & (CONTENTS_LAVA|CONTENTS_SLIME)) return qfalse;
	//do not chat if under water
	VectorCopy(bs->origin, point);
	point[2] += 32;
	if (trap_PointContents(point,bs->entitynum) & MASK_WATER) return qfalse;
	//must be standing on the world entity
	VectorCopy(bs->origin, start);
	VectorCopy(bs->origin, end);
	start[2] += 1;
	end[2] -= 10;
	trap_AAS_PresenceTypeBoundingBox(PRESENCE_CROUCH, mins, maxs);
	BotAI_Trace(&trace, start, mins, maxs, end, bs->playernum, MASK_SOLID);
	if (trace.entityNum != ENTITYNUM_WORLD) return qfalse;
	//the bot is in a position where it can chat
	return qtrue;
}
Пример #2
0
/*
==================
BotValidChatPosition
==================
*/
int BotValidChatPosition( bot_state_t *bs ) {
	vec3_t point, start, end, mins, maxs;
	bsp_trace_t trace;

	//if the bot is dead all positions are valid
	if ( BotIsDead( bs ) ) {
		return qtrue;
	}
	//must be on the ground
	//if (bs->cur_ps.groundEntityNum != ENTITYNUM_NONE) return qfalse;
	//do not chat if in lava or slime
	VectorCopy( bs->origin, point );
	point[2] -= 24;
	if ( trap_PointContents( point,bs->entitynum ) & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) {
		return qfalse;
	}
	//do not chat if under water
	VectorCopy( bs->origin, point );
	point[2] += 32;
	if ( trap_PointContents( point,bs->entitynum ) & MASK_WATER ) {
		return qfalse;
	}
	//must be standing on the world entity
	VectorCopy( bs->origin, start );
	VectorCopy( bs->origin, end );
	start[2] += 1;
	end[2] -= 10;
	trap_AAS_PresenceTypeBoundingBox( PRESENCE_CROUCH, mins, maxs );
	BotAI_Trace( &trace, start, mins, maxs, end, bs->client, MASK_SOLID );
	if ( trace.ent != ENTITYNUM_WORLD ) {
		return qfalse;
	}
	//the bot is in a position where it can chat
	return qtrue;
}
Пример #3
0
/*
================
G_RunItem

================
*/
void G_RunItem( gentity_t *ent ) {
	vec3_t		origin;
	trace_t		tr;
	int			contents;
	int			mask;

	// if groundentity has been set to -1, it may have been pushed off an edge
	if ( ent->s.groundEntityNum == -1 ) {
		if ( ent->s.pos.trType != TR_GRAVITY ) {
			ent->s.pos.trType = TR_GRAVITY;
			ent->s.pos.trTime = level.time;
		}
	}

	if ( ent->s.pos.trType == TR_STATIONARY ) {
		// check think function
		G_RunThink( ent );
		return;
	}

	// get current position
	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );

	// trace a line from the previous position to the current position
	if ( ent->clipmask ) {
		mask = ent->clipmask;
	} else {
		mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;//MASK_SOLID;
	}
	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin,
		ent->r.ownerNum, mask );

	VectorCopy( tr.endpos, ent->r.currentOrigin );

	if ( tr.startsolid ) {
		tr.fraction = 0;
	}

	trap_LinkEntity( ent );	// FIXME: avoid this for stationary?

	// check think function
	G_RunThink( ent );

	if ( tr.fraction == 1 ) {
		return;
	}

	// if it is in a nodrop volume, remove it
	contents = trap_PointContents( ent->r.currentOrigin, -1 );
	if ( contents & CONTENTS_NODROP ) {
		if (ent->item && ent->item->giType == IT_TEAM) {
			Team_FreeEntity(ent);
		} else {
			G_FreeEntity( ent );
		}
		return;
	}

	G_BounceItem( ent, &tr );
}
Пример #4
0
void SprotzThink(gentity_t *self){
	vec3_t newOrigin, oldorg;
	trace_t trace;
	int contents;
	VectorCopy(self->s.pos.trBase, oldorg);

	// calculate new position
	BG_EvaluateTrajectory( &self->s.pos, level.time, newOrigin );

	// trace a line from previous position to new position
	trap_Trace( &trace, oldorg, NULL, NULL, newOrigin, -1, CONTENTS_SOLID );
	contents = trap_PointContents(self->r.currentOrigin, self->r.ownerNum);

	if(contents & CONTENTS_WATER){
		G_FreeEntity( self);
		return;
	}

	// still in free fall
	if ( trace.fraction == 1.0 ){
		VectorCopy(newOrigin, self->r.currentOrigin);
	} else {
		SetWhiskeyPool(self->parent, trace.endpos, trace.plane.normal,
			self->s.apos.trDelta[0]);
		G_FreeEntity(self);
		return;
	}

	self->nextthink = level.time+50;
}
Пример #5
0
/* JPW NERVE
================
limbo
================
*/
void limbo(gentity_t *ent) {
	int i;
	int startclient = ent->client->ps.clientNum;

	if (ent->r.svFlags & SVF_POW) {
		return;
	}

	if (!(ent->client->ps.pm_flags & PMF_LIMBO)) {
		// DHM - Nerve :: First save off persistant info we'll need for respawn
		for (i = 0; i < MAX_PERSISTANT; i++) {
			ent->client->saved_persistant[i] = ent->client->ps.persistant[i];
		}

		ent->client->ps.pm_flags |= PMF_LIMBO;
		ent->client->ps.pm_flags |= PMF_FOLLOW;

		trap_UnlinkEntity(ent);

		// DHM - Nerve :: reset these values
		ent->client->ps.viewlocked        = 0;
		ent->client->ps.viewlocked_entNum = 0;

		ent->r.maxs[2]           = 0;
		ent->r.currentOrigin[2] += 8;

		trap_PointContents(ent->r.currentOrigin, -1);   // drop stuff

		ent->client->sess.spectatorClient = startclient;
	}
}
Пример #6
0
//----------------------------------------------------------------
static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir )
//----------------------------------------------------------------
{
	vec3_t		org;
	gentity_t	*bolt;

	if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) )
	{
		return;
	}

	VectorMA( start, -START_DIS, dir, org ); // dumb....
	G_PlayEffectID( ent->genericValue13, org, dir );

	bolt = G_Spawn();
	
	//use a custom shot effect
	bolt->s.otherEntityNum2 = ent->genericValue14;
	//use a custom impact effect
	bolt->s.emplacedOwner = ent->genericValue15;

	bolt->classname = "turret_proj";
	bolt->nextthink = level.time + 10000;
	bolt->think = G_FreeEntity;
	bolt->s.eType = ET_MISSILE;
	bolt->s.weapon = WP_EMPLACED_GUN;
	bolt->r.ownerNum = ent->s.number;
	bolt->damage = ent->damage;
	bolt->alliedTeam = ent->alliedTeam;
	bolt->teamnodmg = ent->teamnodmg;
	//bolt->dflags = DAMAGE_NO_KNOCKBACK;// | DAMAGE_HEAVY_WEAP_CLASS;		// Don't push them around, or else we are constantly re-aiming
	bolt->splashDamage = ent->damage;
	bolt->splashRadius = 100;
	bolt->methodOfDeath = MOD_TARGET_LASER;
	//[BugFix16]
	bolt->splashMethodOfDeath = MOD_TARGET_LASER;
	//[/BugFix16]
	bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
	//bolt->trigger_formation = qfalse;		// don't draw tail on first frame	

	VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 );
	VectorScale( bolt->r.maxs, -1, bolt->r.mins );
	bolt->s.pos.trType = TR_LINEAR;
	bolt->s.pos.trTime = level.time;
	VectorCopy( start, bolt->s.pos.trBase );
	VectorScale( dir, ent->mass, bolt->s.pos.trDelta );
	SnapVector( bolt->s.pos.trDelta );		// save net bandwidth
	VectorCopy( start, bolt->r.currentOrigin);

	bolt->parent = ent;
}
Пример #7
0
//----------------------------------------------------------------
static void turret_fire ( gentity_t *ent, vec3_t start, vec3_t dir )
//----------------------------------------------------------------
{
	vec3_t		org;
	gentity_t	*bolt;

	if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) )
	{
		return;
	}

	VectorMA( start, -START_DIS, dir, org ); // dumb....
	G_PlayEffectID( ent->genericValue13, org, dir );

	bolt = G_Spawn();
	
	//use a custom shot effect
	bolt->s.otherEntityNum2 = ent->genericValue14;
	//use a custom impact effect
	bolt->s.emplacedOwner = ent->genericValue15;

	bolt->classname = "turret_proj";
	bolt->nextthink = level.time + 10000;
	bolt->think = G_FreeEntity;
	bolt->s.eType = ET_MISSILE;
	bolt->s.weapon = WP_EMPLACED_GUN;
	bolt->r.ownerNum = ent->s.number;
	bolt->damage = ent->damage;
	bolt->alliedTeam = ent->alliedTeam;
	bolt->teamnodmg = ent->teamnodmg;
	bolt->splashDamage = ent->damage;
	bolt->splashRadius = 100;
	bolt->methodOfDeath = MOD_TARGET_LASER;
	bolt->splashMethodOfDeath = MOD_TARGET_LASER;
	bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;

	VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 );
	VectorScale( bolt->r.maxs, -1, bolt->r.mins );
	bolt->s.pos.trType = TR_LINEAR;
	bolt->s.pos.trTime = level.time;
	VectorCopy( start, bolt->s.pos.trBase );
	VectorScale( dir, ent->mass, bolt->s.pos.trDelta );
	SnapVector( bolt->s.pos.trDelta );		// save net bandwidth
	VectorCopy( start, bolt->r.currentOrigin);

	bolt->parent = ent;
}
Пример #8
0
void Rune_Phase_Use(gentity_t *ent)
{
	vec3_t oldmuzzle,muzzle,forward,right,up,end;
	trace_t trace, trace2;

	AngleVectors (ent->client->ps.viewangles, forward, right, up);
	CalcMuzzlePoint ( ent, forward, right, up, muzzle );

	if (ent->runetime<level.time)
	{
		VectorMA (muzzle, 250, forward, end);
		trap_Trace (&trace, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );

		// we didn't hit anything, so exit
		if ( trace.fraction == 1)
			return;

		SnapVectorTowards( trace.endpos, muzzle );

        // prepare for firing through the wall
        VectorCopy (muzzle, oldmuzzle);
        VectorCopy (trace.endpos, muzzle);
        VectorMA (muzzle, 96, forward, muzzle);                

        if ( !( trap_PointContents( muzzle, -1 ) & CONTENTS_SOLID ))
        {
			// the point isn't inside a wall, so check to see if we are outside the bounds of the level
			VectorCopy(muzzle,end);
			end[2]=-10000;
			trap_Trace (&trace2, muzzle, NULL, NULL, end, ent->s.number, MASK_SHOT );

			// if we didn't hit anything after that big a trace, we are outside the level
			if (trace2.fraction==1)
				return;

			TeleportPlayer(ent,muzzle,ent->client->ps.viewangles, qtrue, qtrue);
			ent->runetime = level.time + RUNE_PHASE_RECHARGE;
        }


	}
	else
	{
		//trap_SendServerCommand( ent->s.clientNum, va("print \"Phase is recharging\n\""));
	}
}
Пример #9
0
int luautil_pointcontents(lua_State *L) {
	vec3_t point;
	int pass = -1;
	int contents;

	//luaL_checkudata(L,1,"Vector");
	if(lua_type(L,1) != LUA_TUSERDATA) return 0;

	lua_tovector(L,1,point);

	if(lua_type(L,2) == LUA_TNUMBER) {
		pass = lua_tonumber(L,2);
	}
	contents = trap_PointContents(point,pass);

	lua_pushinteger(L,contents);
	return 1;
}
Пример #10
0
gentity_t *fire_speargun( gentity_t *self, vec3_t start, vec3_t dir ) {
	gentity_t   *bolt;

	VectorNormalize( dir );

	bolt = G_Spawn();
	bolt->classname = const_cast<char*>("spear");
	bolt->nextthink = level.time + 10000;
	bolt->think = G_ExplodeMissile;
	bolt->s.eType = ET_MISSILE;

	bolt->r.svFlags = SVF_USE_CURRENT_ORIGIN;

	bolt->s.weapon = WP_SPEARGUN;
	bolt->r.ownerNum = self->s.number;
	bolt->parent = self;
	bolt->damage = 15;      // (SA) spear damage here
	bolt->splashDamage = 0;
	bolt->methodOfDeath = MOD_SPEARGUN;
	bolt->clipmask = MASK_MISSILESHOT;

	bolt->s.pos.trTime = level.time - MISSILE_PRESTEP_TIME;     // move a bit on the very first frame
	VectorCopy( start, bolt->s.pos.trBase );

	// (SA) Kind of a cheap hack to make the speargun worthless out of the water
	//		This'll probably change to something better
	if ( ( trap_PointContents( start, -1 ) & CONTENTS_WATER ) ) {
		bolt->s.pos.trType = TR_LINEAR;
		VectorScale( dir, SPEAR_WATERSPEED, bolt->s.pos.trDelta );
	} else {
		bolt->s.pos.trType = TR_GRAVITY_LOW;
		VectorScale( dir, SPEAR_AIRSPEED, bolt->s.pos.trDelta );
	}

	SnapVector( bolt->s.pos.trDelta );          // save net bandwidth
	VectorCopy( start, bolt->r.currentOrigin );


	return bolt;
}
Пример #11
0
// Wandering code (based on old ACE movement code)
void ACEMV_Wander(gentity_t * self)
{
	vec3_t          tmp;

	// do not move
	if(self->bs.next_move_time > level.time)
		return;

	// Special check for elevators, stand still until the ride comes to a complete stop.
	/*
	 * FIXME
	 if(self->groundentity != NULL && self->groundentity->use == Use_Plat)
	 if(self->groundentity->moveinfo.state == STATE_UP || self->groundentity->moveinfo.state == STATE_DOWN) // only move when platform not
	 {
	 self->velocity[0] = 0;
	 self->velocity[1] = 0;
	 self->velocity[2] = 0;
	 self->next_move_time = level.time + 500;
	 return;
	 }

	 */

	// touched jumppad last Frame?
	if(self->s.groundEntityNum == ENTITYNUM_NONE)
	{
		if(VectorLength(self->client->ps.velocity) > 120)
		{
			VectorNormalize2(self->client->ps.velocity, tmp);

			if(AngleBetweenVectors(self->bs.moveVector, tmp) >= 120)
			{
				// we might have been knocked back by someone or something ..
				if(!self->bs.moveTarget)
				{
					VectorCopy(tmp, self->bs.moveVector);
					ACEMV_ChangeBotAngle(self);
				}
			}
		}

		//ACEMV_ChangeBotAngle(self);
		//self->client->ps.velocity[0] = self->bs.moveVector[0] * 360;
		//self->client->ps.velocity[1] = self->bs.moveVector[1] * 360;
		//return;
	}

	// is there a target to move to
	if(self->bs.moveTarget)
	{
		ACEMV_MoveToGoal(self);
	}

	// swimming?
	VectorCopy(self->client->ps.origin, tmp);
	tmp[2] += 24;

	if(trap_PointContents(tmp, self->s.number) & MASK_WATER)
	{
		// if drowning and no node, move up
		if(self->client->airOutTime > 0)
		{
			self->client->pers.cmd.upmove = 1;
			self->bs.viewAngles[PITCH] = -45;
		}
		else
			self->client->pers.cmd.upmove = 15;

		self->client->pers.cmd.forwardmove = 100;
	}
	else
	{
		//self->client->airOutTime = 0;    // probably shound not be messing with this, but
	}

	// lava?
	tmp[2] -= 48;
	if(trap_PointContents(tmp, self->s.number) & (CONTENTS_LAVA | CONTENTS_SLIME))
	{
		//  safe_bprintf(PRINT_MEDIUM,"lava jump\n");
		self->bs.viewAngles[YAW] += random() * 360 - 180;
		self->client->pers.cmd.forwardmove = 127;
		self->client->pers.cmd.upmove = 127;
		return;
	}

	// check for special movement if we have a normal move (have to test)
	if(VectorLength(self->client->ps.velocity) < 37)
	{
		//if(random() > 0.1 && ACEMV_SpecialMove(self))
		//  return; //removed this because when wandering, the last thing you want is bots jumping
		//over things and going off ledges.  It's better for them to just bounce around the map.

		self->bs.viewAngles[YAW] += random() * 180 - 90;

		if(ACEMV_CanMove(self, MOVE_FORWARD))
			self->client->pers.cmd.forwardmove = 127;
		else if(ACEMV_CanMove(self, MOVE_BACK))
			self->client->pers.cmd.forwardmove = -127;

		// if there is ground continue otherwise wait for next move
		if( /*!M_CheckBottom || */ self->s.groundEntityNum != ENTITYNUM_NONE)
		{
			if(ACEMV_CanMove(self, MOVE_FORWARD))
				self->client->pers.cmd.forwardmove = 127;
		}

		return;
	}

	if(ACEMV_CheckEyes(self))
		return;

	if(ACEMV_CanMove(self, MOVE_FORWARD))
		self->client->pers.cmd.forwardmove = 127;
}
Пример #12
0
// Main movement code. (following node path)
void ACEMV_Move(gentity_t * self)
{
	int             currentNodeType = -1;
	int             nextNodeType = -1;

	// get current and next node back from nav code.
	if(!ACEND_FollowPath(self))
	{
		self->bs.state = STATE_WANDER;
		self->bs.wander_timeout = level.time + 1000;

		// center view
		//self->bs.viewAngles[PITCH] = 0;   //-self->client->ps.delta_angles[PITCH];
		return;
	}

	currentNodeType = nodes[self->bs.currentNode].type;
	nextNodeType = nodes[self->bs.nextNode].type;

	// move to a selected goal, if any
	if(self->bs.moveTarget)
	{
		ACEMV_MoveToGoal(self);
	}

	// grapple
	/*
	   if(nextNodeType == NODE_GRAPPLE)
	   {
	   ACEMV_ChangeBotAngle(self);
	   ACEIT_ChangeWeapon(self, FindItem("grapple"));
	   self->client->pers.cmd.buttons = BUTTON_ATTACK;
	   return;
	   }
	   // Reset the grapple if hangin on a graple node
	   if(currentNodeType == NODE_GRAPPLE)
	   {
	   CTFPlayerResetGrapple(self);
	   return;
	   }
	 */

#if 0
	// check for platforms
	if(currentNodeType != NODE_PLATFORM && nextNodeType == NODE_PLATFORM)
	{
		// check to see if lift is down?
		for(i = 0; i < num_items; i++)
			if(item_table[i].node == self->bs.nextNode)
				if(item_table[i].ent->moverState != MOVER_POS1)
					return;		// Wait for elevator
	}
#endif

	if(currentNodeType == NODE_PLATFORM && nextNodeType == NODE_PLATFORM)
	{
		// move to the center
		self->bs.moveVector[2] = 0;	// kill z movement

		if(VectorLength(self->bs.moveVector) > 10)
			self->client->pers.cmd.forwardmove = 200;	// walk to center

		ACEMV_ChangeBotAngle(self);

		return;					// No move, riding elevator
	}

	// jumpto nodes
	if(nextNodeType == NODE_JUMP ||
	   (currentNodeType == NODE_JUMP && nextNodeType != NODE_ITEM &&
		nodes[self->bs.nextNode].origin[2] > self->client->ps.origin[2]))
	{
		// set up a jump move
		if(ACEMV_CanMove(self, MOVE_FORWARD))
			self->client->pers.cmd.forwardmove = 127;

		self->client->pers.cmd.upmove = 127;

		ACEMV_ChangeBotAngle(self);

		//VectorCopy(self->bs.moveVector, dist);
		//VectorScale(dist, 127, self->client->ps.velocity);
		return;
	}


	// ladder nodes
	/*
	   if(nextNodeType == NODE_LADDER && nodes[self->nextNode].origin[2] > self->s.origin[2])
	   {
	   // Otherwise move as fast as we can
	   self->client->pers.cmd.forwardmove = 400;
	   self->velocity[2] = 320;

	   ACEMV_ChangeBotAngle(self);

	   return;

	   }
	   // If getting off the ladder
	   if(currentNodeType == NODE_LADDER && nextNodeType != NODE_LADDER && nodes[self->nextNode].origin[2] > self->s.origin[2])
	   {
	   self->client->pers.cmd.forwardmove = 400;
	   self->client->pers.cmd.upmove = 200;
	   self->velocity[2] = 200;
	   ACEMV_ChangeBotAngle(self);
	   return;
	   }
	 */

	// water nodes
	if(currentNodeType == NODE_WATER)
	{
		// we need to be pointed up/down
		ACEMV_ChangeBotAngle(self);

		// ff the next node is not in the water, then move up to get out.
		if(nextNodeType != NODE_WATER && !(trap_PointContents(nodes[self->bs.nextNode].origin, self->s.number) & MASK_WATER))
		{
			// exit water
			self->client->pers.cmd.upmove = 127;
		}

		self->client->pers.cmd.forwardmove = 100;
		return;

	}

	// falling off ledge?
	if(self->s.groundEntityNum == ENTITYNUM_NONE)
	{
		ACEMV_ChangeBotAngle(self);

		//self->client->ps.velocity[0] = self->bs.moveVector[0] * 360;
		//self->client->ps.velocity[1] = self->bs.moveVector[1] * 360;
		return;
	}

	// check to see if stuck, and if so try to free us
	// also handles crouching
	if(VectorLength(self->client->ps.velocity) < 37)
	{
		// keep a random factor just in case....
		if(random() > 0.1 && ACEMV_SpecialMove(self))
			return;

		self->bs.viewAngles[YAW] += random() * 180 - 90;

		if(ACEMV_CanMove(self, MOVE_FORWARD))
			self->client->pers.cmd.forwardmove = 127;
		else if(ACEMV_CanMove(self, MOVE_BACK))
			self->client->pers.cmd.forwardmove = -127;
		return;
	}

	// otherwise move as fast as we can
	if(ACEMV_CanMove(self, MOVE_FORWARD))
		self->client->pers.cmd.forwardmove = 127;

	ACEMV_ChangeBotAngle(self);
}
Пример #13
0
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	gentity_t   *ent;
	// TTimo might be used uninitialized
	int contents = 0;
	int killer;
	int i;
	const char        *killerName, *obit;
	qboolean nogib = qtrue;
	gitem_t     *item = NULL; // JPW NERVE for flag drop
	vec3_t launchvel,launchspot;      // JPW NERVE
	gentity_t   *flag; // JPW NERVE

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

	if ( level.intermissiontime ) {
		return;
	}

	self->client->ps.pm_type = PM_DEAD;

	G_AddEvent( self, EV_STOPSTREAMINGSOUND, 0 );

	if ( attacker ) {
		killer = attacker->s.number;
		if ( attacker->client ) {
			killerName = attacker->client->pers.netname;
		} else {
			killerName = "<non-client>";
		}
	} else {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( killer < 0 || killer >= MAX_CLIENTS ) {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
		obit = "<bad obituary>";
	} else {
		obit = modNames[ meansOfDeath ];
	}

	G_LogPrintf( "Kill: %i %i %i: %s killed %s by %s\n",
				 killer, self->s.number, meansOfDeath, killerName,
				 self->client->pers.netname, obit );

	// broadcast the death event to everyone
	ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
	ent->s.eventParm = meansOfDeath;
	ent->s.otherEntityNum = self->s.number;
	ent->s.otherEntityNum2 = killer;
	ent->r.svFlags = SVF_BROADCAST; // send to everyone

	self->enemy = attacker;

	self->client->ps.persistant[PERS_KILLED]++;

// JPW NERVE -- if player is holding ticking grenade, drop it
	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		if ( ( self->client->ps.grenadeTimeLeft ) && ( self->s.weapon != WP_DYNAMITE ) ) {
			launchvel[0] = crandom();
			launchvel[1] = crandom();
			launchvel[2] = random();
			VectorScale( launchvel, 160, launchvel );
			VectorCopy( self->r.currentOrigin, launchspot );
			launchspot[2] += 40;
			fire_grenade( self, launchspot, launchvel, self->s.weapon );

		}
	}
// jpw

	if ( attacker && attacker->client ) {
		if ( attacker == self || OnSameTeam( self, attacker ) ) {

			// DHM - Nerve :: Complaint lodging
			if ( attacker != self && level.warmupTime <= 0 ) {
				if ( attacker->client->pers.localClient ) {
					trap_SendServerCommand( self - g_entities, "complaint -4" );
				} else {
					trap_SendServerCommand( self - g_entities, va( "complaint %i", attacker->s.number ) );
					self->client->pers.complaintClient = attacker->s.clientNum;
					self->client->pers.complaintEndTime = level.time + 20500;
				}
			}
			// dhm

			// JPW NERVE
			if ( g_gametype.integer >= GT_WOLF ) { // high penalty to offset medic heal
				AddScore( attacker, WOLF_FRIENDLY_PENALTY );
			} else {
				// jpw
				AddScore( attacker, -1 );
			}
		} else {
			// JPW NERVE -- mostly added as conveneience so we can tweak from the #defines all in one place
			if ( g_gametype.integer >= GT_WOLF ) {
				AddScore( attacker, WOLF_FRAG_BONUS );
			} else {
				// jpw
				AddScore( attacker, 1 );
			}

			attacker->client->lastKillTime = level.time;
		}
	} else {
		AddScore( self, -1 );
	}

	// Add team bonuses
	Team_FragBonuses( self, inflictor, attacker );

	// if client is in a nodrop area, don't drop anything
// JPW NERVE new drop behavior
	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {   // only drop here in single player; in multiplayer, drop @ limbo
		contents = trap_PointContents( self->r.currentOrigin, -1 );
		if ( !( contents & CONTENTS_NODROP ) ) {
			TossClientItems( self );
		}
	}

	// drop flag regardless
	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		if ( self->client->ps.powerups[PW_REDFLAG] ) {
			item = BG_FindItem( "Red Flag" );
			if ( !item ) {
				item = BG_FindItem( "Objective" );
			}

			self->client->ps.powerups[PW_REDFLAG] = 0;
		}
		if ( self->client->ps.powerups[PW_BLUEFLAG] ) {
			item = BG_FindItem( "Blue Flag" );
			if ( !item ) {
				item = BG_FindItem( "Objective" );
			}

			self->client->ps.powerups[PW_BLUEFLAG] = 0;
		}

		if ( item ) {
			launchvel[0] = crandom() * 20;
			launchvel[1] = crandom() * 20;
			launchvel[2] = 10 + random() * 10;

			flag = LaunchItem( item,self->r.currentOrigin,launchvel,self->s.number );
			flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here
			flag->message = self->message;  // DHM - Nerve :: also restore item name
			// Clear out player's temp copies
			self->s.otherEntityNum2 = 0;
			self->message = NULL;
		}

		// send a fancy "MEDIC!" scream.  Sissies, ain' they?
		if ( self->client != NULL ) {
			if ( self->health > GIB_HEALTH && meansOfDeath != MOD_SUICIDE ) {

				if ( self->client->sess.sessionTeam == TEAM_RED ) {
					if ( random() > 0.5 ) {
						G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic2.wav" ) );
					} else {
						G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/axis/g-medic3.wav" ) );
					}
				} else {
					if ( random() > 0.5 ) {
						G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic3.wav" ) );
					} else {
						G_AddEvent( self, EV_GENERAL_SOUND, G_SoundIndex( "sound/multiplayer/allies/a-medic2.wav" ) );
					}
				}
			}
		}
	}
// jpw

	Cmd_Score_f( self );        // show scores
	// send updated scores to any clients that are following this one,
	// or they would get stale scoreboards
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		gclient_t   *client;

		client = &level.clients[i];
		if ( client->pers.connected != CON_CONNECTED ) {
			continue;
		}
		if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
			continue;
		}
		if ( client->sess.spectatorClient == self->s.number ) {
			Cmd_Score_f( g_entities + i );
		}
	}

	self->takedamage = qtrue;   // can still be gibbed
	self->r.contents = CONTENTS_CORPSE;

	self->s.powerups = 0;
// JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate
	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
		self->s.weapon = WP_NONE;
		self->s.angles[0] = 0;
	} else {
		self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo
	}
// jpw
	self->s.angles[2] = 0;
	LookAtKiller( self, inflictor, attacker );

	VectorCopy( self->s.angles, self->client->ps.viewangles );
	self->s.loopSound = 0;

	trap_UnlinkEntity( self );
	self->r.maxs[2] = 0;
	self->client->ps.maxs[2] = 0;
	trap_LinkEntity( self );

	// don't allow respawn until the death anim is done
	// g_forcerespawn may force spawning at some later time
	self->client->respawnTime = level.time + 800;

	// remove powerups
	memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );

	// never gib in a nodrop
	if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) ) {
		GibEntity( self, killer );
		nogib = qfalse;
	}

	if ( nogib ) {
		// normal death
		// for the no-blood option, we need to prevent the health
		// from going to gib level
		if ( self->health <= GIB_HEALTH ) {
			self->health = GIB_HEALTH + 1;
		}

// JPW NERVE for medic
		self->client->medicHealAmt = 0;
// jpw

		// DHM - Play death animation, and set pm_time to delay 'fallen' animation
		self->client->ps.pm_time = BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue );

		G_AddEvent( self, EV_DEATH1 + 1, killer );

		// the body can still be gibbed
		self->die = body_die;
	}

	trap_LinkEntity( self );

	if ( g_gametype.integer >= GT_WOLF && meansOfDeath == MOD_SUICIDE ) {
		limbo( self, qtrue );
	}
}
Пример #14
0
/*
=============
SpawnCorpse

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
static void SpawnCorpse( gentity_t *ent )
{
	gentity_t *body;
	int       contents;
	vec3_t    origin, mins;

	VectorCopy( ent->r.currentOrigin, origin );

	trap_UnlinkEntity( ent );

	// if client is in a nodrop area, don't leave the body
	contents = trap_PointContents( origin, -1 );

	if ( contents & CONTENTS_NODROP )
	{
		return;
	}

	body = G_NewEntity();

	VectorCopy( ent->s.apos.trBase, body->s.angles );
	body->s.eFlags = EF_DEAD;
	body->s.eType = entityType_t::ET_CORPSE;
	body->timestamp = level.time;
	body->s.event = 0;
	body->r.contents = CONTENTS_CORPSE;
	body->clipmask = MASK_DEADSOLID;
	body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
	body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;

	if ( ent->client->pers.team == TEAM_HUMANS )
	{
		body->classname = "humanCorpse";
	}
	else
	{
		body->classname = "alienCorpse";
	}

	body->s.misc = MAX_CLIENTS;

	body->think = BodySink;
	body->nextthink = level.time + 20000;

	body->s.legsAnim = ent->s.legsAnim;

	if ( !body->nonSegModel )
	{
		switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT )
		{
			case BOTH_DEATH1:
			case BOTH_DEAD1:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
				break;

			case BOTH_DEATH2:
			case BOTH_DEAD2:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
				break;

			case BOTH_DEATH3:
			case BOTH_DEAD3:
			default:
				body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
				break;
		}
	}
	else
	{
		switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT )
		{
			case NSPA_DEATH1:
			case NSPA_DEAD1:
				body->s.legsAnim = NSPA_DEAD1;
				break;

			case NSPA_DEATH2:
			case NSPA_DEAD2:
				body->s.legsAnim = NSPA_DEAD2;
				break;

			case NSPA_DEATH3:
			case NSPA_DEAD3:
			default:
				body->s.legsAnim = NSPA_DEAD3;
				break;
		}
	}

	//change body dimensions
	BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], mins, nullptr, nullptr, body->r.mins, body->r.maxs );

	//drop down to match the *model* origins of ent and body
	origin[2] += mins[ 2 ] - body->r.mins[ 2 ];

	G_SetOrigin( body, origin );
	body->s.pos.trType = trType_t::TR_GRAVITY;
	body->s.pos.trTime = level.time;
	VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );

	trap_LinkEntity( body );
}
Пример #15
0
/*
=============
CopyToBodyQue

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
void CopyToBodyQue( gentity_t *ent ) {
#ifdef MISSIONPACK
	gentity_t	*e;
	int i;
#endif
	gentity_t		*body;
	int			contents;

	trap_UnlinkEntity (ent);

	// if client is in a nodrop area, don't leave the body
	contents = trap_PointContents( ent->s.origin, -1 );
	if ( contents & CONTENTS_NODROP ) {
		return;
	}

	// grab a body que and cycle to the next one
	body = level.bodyQue[ level.bodyQueIndex ];
	level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE;

	trap_UnlinkEntity (body);

	body->s = ent->s;
	body->s.eFlags = EF_DEAD;		// clear EF_TALK, etc
#ifdef MISSIONPACK
	if ( ent->s.eFlags & EF_KAMIKAZE ) {
		body->s.eFlags |= EF_KAMIKAZE;

		// check if there is a kamikaze timer around for this owner
		for (i = 0; i < MAX_GENTITIES; i++) {
			e = &g_entities[i];
			if (!e->inuse)
				continue;
			if (e->activator != ent)
				continue;
			if (strcmp(e->classname, "kamikaze timer"))
				continue;
			e->activator = body;
			break;
		}
	}
#endif
	body->s.powerups = 0;	// clear powerups
	body->s.loopSound = 0;	// clear lava burning
	body->s.number = body - g_entities;
	body->timestamp = level.time;
	body->physicsObject = qtrue;
	body->physicsBounce = 0;		// don't bounce
	if ( body->s.groundEntityNum == ENTITYNUM_NONE ) {
		body->s.pos.trType = TR_GRAVITY;
		body->s.pos.trTime = level.time;
		VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
	} else {
		body->s.pos.trType = TR_STATIONARY;
	}
	body->s.event = 0;

	// change the animation to the last-frame only, so the sequence
	// doesn't repeat anew for the body
	switch ( body->s.legsAnim & ~ANIM_TOGGLEBIT ) {
	case BOTH_DEATH1:
	case BOTH_DEAD1:
		body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
		break;
	case BOTH_DEATH2:
	case BOTH_DEAD2:
		body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
		break;
	case BOTH_DEATH3:
	case BOTH_DEAD3:
	default:
		body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
		break;
	}

	body->r.svFlags = ent->r.svFlags;
	VectorCopy (ent->r.mins, body->r.mins);
	VectorCopy (ent->r.maxs, body->r.maxs);
	VectorCopy (ent->r.absmin, body->r.absmin);
	VectorCopy (ent->r.absmax, body->r.absmax);

	body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	body->r.contents = CONTENTS_CORPSE;
	body->r.ownerNum = ent->s.number;

	body->nextthink = level.time + 5000;
	body->think = BodySink;

	body->die = body_die;

	// don't take more damage if already gibbed
	if ( ent->health <= GIB_HEALTH ) {
		body->takedamage = qfalse;
	} else {
		body->takedamage = qtrue;
	}


	VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
	trap_LinkEntity (body);
}
Пример #16
0
//----------------------------------------------------------------
static void turretG2_fire ( gentity_t *ent, vec3_t start, vec3_t dir )
//----------------------------------------------------------------
{
    vec3_t		org, ang;
    gentity_t	*bolt;

    if ( (trap_PointContents( start, ent->s.number )&MASK_SHOT) )
    {
        return;
    }

    VectorMA( start, -START_DIS, dir, org ); // dumb....

    if ( ent->random )
    {
        vectoangles( dir, ang );
        ang[PITCH] += flrand( -ent->random, ent->random );
        ang[YAW] += flrand( -ent->random, ent->random );
        AngleVectors( ang, dir, NULL, NULL );
    }

    vectoangles(dir, ang);

    if ( (ent->spawnflags&SPF_TURRETG2_TURBO) )
    {
        //muzzle flash
        G_PlayEffectID( ent->genericValue13, org, ang );
        WP_FireTurboLaserMissile( ent, start, dir );
        if ( ent->alt_fire )
        {
            TurboLaser_SetBoneAnim( ent, 2, 3 );
        }
        else
        {
            TurboLaser_SetBoneAnim( ent, 0, 1 );
        }
    }
    else
    {
        G_PlayEffectID( G_EffectIndex("blaster/muzzle_flash"), org, ang );
        bolt = G_Spawn();

        bolt->classname = "turret_proj";
        bolt->nextthink = level.time + 10000;
        bolt->think = G_FreeEntity;
        bolt->s.eType = ET_MISSILE;
        bolt->s.weapon = WP_BLASTER;
        bolt->r.ownerNum = ent->s.number;
        bolt->damage = ent->damage;
        bolt->alliedTeam = ent->alliedTeam;
        bolt->teamnodmg = ent->teamnodmg;
        bolt->dflags = (DAMAGE_NO_KNOCKBACK|DAMAGE_HEAVY_WEAP_CLASS);		// Don't push them around, or else we are constantly re-aiming
        bolt->splashDamage = ent->splashDamage;
        bolt->splashRadius = ent->splashDamage;
        bolt->methodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY;
        bolt->splashMethodOfDeath = MOD_TARGET_LASER;//MOD_ENERGY;
        bolt->clipmask = MASK_SHOT | CONTENTS_LIGHTSABER;
        //bolt->trigger_formation = qfalse;		// don't draw tail on first frame

        VectorSet( bolt->r.maxs, 1.5, 1.5, 1.5 );
        VectorScale( bolt->r.maxs, -1, bolt->r.mins );
        bolt->s.pos.trType = TR_LINEAR;
        bolt->s.pos.trTime = level.time;
        VectorCopy( start, bolt->s.pos.trBase );
        VectorScale( dir, ent->mass, bolt->s.pos.trDelta );
        SnapVector( bolt->s.pos.trDelta );		// save net bandwidth
        VectorCopy( start, bolt->r.currentOrigin);
    }
}
Пример #17
0
void Weapon_LightningFire( gentity_t *ent ) {
	trace_t		tr;
	vec3_t		end;
#ifdef MISSIONPACK
	vec3_t impactpoint, bouncedir;
#endif
	gentity_t	*traceEnt, *tent;
	int			damage, i, passent;

	damage = 8 * s_quadFactor;

	passent = ent->s.number;
	for (i = 0; i < 10; i++) {
		VectorMA( muzzle, LIGHTNING_RANGE, forward, end );

// eser - lightning discharge
	if (trap_PointContents (muzzle, -1) & MASK_WATER)
	{
		int zaps;
		gentity_t *tent;

		zaps = ent->client->ps.ammo[WP_LIGHTNING];	// determines size/power of discharge
		if (!zaps) return;	// prevents any subsequent frames causing second discharge + error
		zaps++;		// pmove does an ammo[gun]--, so we must compensate
		SnapVectorTowards (muzzle, ent->s.origin);	// save bandwidth

		tent = G_TempEntity (muzzle, EV_LIGHTNING_DISCHARGE);
		tent->s.eventParm = zaps;				// duration / size of explosion graphic

        ent->client->ps.ammo[WP_LIGHTNING] = 0;		// drain ent's lightning count
		if (G_RadiusDamage (muzzle, ent, damage * zaps, (damage * zaps) + 16, NULL, MOD_LIGHTNING_DISCHARGE, qtrue))
			ent->client->accuracy_hits++;

		return;
	}
// eser - lightning discharge


		trap_Trace( &tr, muzzle, NULL, NULL, end, passent, MASK_SHOT );

#ifdef MISSIONPACK
		// if not the first trace (the lightning bounced of an invulnerability sphere)
		if (i) {
			// add bounced off lightning bolt temp entity
			// the first lightning bolt is a cgame only visual
			//
			tent = G_TempEntity( muzzle, EV_LIGHTNINGBOLT );
			VectorCopy( tr.endpos, end );
			SnapVector( end );
			VectorCopy( end, tent->s.origin2 );
		}
#endif
		if ( tr.entityNum == ENTITYNUM_NONE ) {
			return;
		}

		traceEnt = &g_entities[ tr.entityNum ];

		if ( traceEnt->takedamage) {
#ifdef MISSIONPACK
			if ( traceEnt->client && traceEnt->client->invulnerabilityTime > level.time ) {
				if (G_InvulnerabilityEffect( traceEnt, forward, tr.endpos, impactpoint, bouncedir )) {
					G_BounceProjectile( muzzle, impactpoint, bouncedir, end );
					VectorCopy( impactpoint, muzzle );
					VectorSubtract( end, impactpoint, forward );
					VectorNormalize(forward);
					// the player can hit him/herself with the bounced lightning
					passent = ENTITYNUM_NONE;
				}
				else {
					VectorCopy( tr.endpos, muzzle );
					passent = traceEnt->s.number;
				}
				continue;
			}
#endif
			if( LogAccuracyHit( traceEnt, ent ) ) {
				ent->client->accuracy_hits++;
			}
			G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_LIGHTNING);
		}

		if ( traceEnt->takedamage && traceEnt->client ) {
			tent = G_TempEntity( tr.endpos, EV_MISSILE_HIT );
			tent->s.otherEntityNum = traceEnt->s.number;
			tent->s.eventParm = DirToByte( tr.plane.normal );
			tent->s.weapon = ent->s.weapon;
		} else if ( !( tr.surfaceFlags & SURF_NOIMPACT ) ) {
			tent = G_TempEntity( tr.endpos, EV_MISSILE_MISS );
			tent->s.eventParm = DirToByte( tr.plane.normal );
		}

		break;
	}
}
Пример #18
0
/*
================
G_RunItem

================
*/
void G_RunItem(gentity_t * ent)
{
	vec3_t origin;
	trace_t tr;
	int contents;
	int mask;

	// if its groundentity has been set to none, it may have been pushed off an edge
	if (ent->s.groundEntityNum == ENTITYNUM_NONE) {
		if (ent->s.pos.trType != TR_GRAVITY) {
			ent->s.pos.trType = TR_GRAVITY;
			ent->s.pos.trTime = level.time;
		}
	}
	if (ent->s.pos.trType == TR_STATIONARY) {
		// check think function
		G_RunThink(ent);
		return;
	}
	// get current position
	G_EvaluateTrajectory(&ent->s.pos, level.time, origin);

	// trace a line from the previous position to the current position
	if (ent->clipmask) {
		mask = ent->clipmask;
	} else {
		mask = MASK_PLAYERSOLID & ~CONTENTS_BODY;	//MASK_SOLID;
	}
	trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->r.ownerNum, mask);

	VectorCopy(tr.endpos, ent->r.currentOrigin);

	if (tr.startsolid) {
		tr.fraction = 0;
	}

	if (ent->flags & FL_DROPPED_ITEM && VectorLength(ent->s.pos.trDelta) != 0 &&
	    (ent->item->giType == IT_WEAPON || ent->item->giType == IT_HOLDABLE)) {
		// calculate spin -- should be identical to cg.autoAngles
		//cg.autoAnglesFast[1] = ( cg.time & 1023 ) * 360 / 1024.0f;
		ent->s.angles[1] = (level.time & 1023) * 360 / 1024.0f;
	}

	trap_LinkEntity(ent);	// FIXME: avoid this for stationary?

	// check think function
	G_RunThink(ent);

	if (tr.fraction == 1) {
		return;
	}
	//Elder: debug
	//if (ent->item && ent->item->giType == IT_WEAPON) {
	//G_Printf("item velocity: %s\n", vtos(ent->s.pos.trDelta));
	//}

	// if it is in a nodrop volume, remove it
	contents = trap_PointContents(ent->r.currentOrigin, -1);
	if (contents & CONTENTS_NODROP) {
		if (ent->item && ent->item->giType == IT_TEAM) {
			Team_FreeEntity(ent);
		} else if (ent->item && ent->item->giType == IT_WEAPON) {
			//Elder: force-call the weaponthink function
			RQ3_DroppedWeaponThink(ent);
		} else if (ent->item && ent->item->giType == IT_HOLDABLE) {
			RQ3_DroppedItemThink(ent);
		} else {
			G_FreeEntity(ent);
		}
		return;
	}

	G_BounceItem(ent, &tr);
}
Пример #19
0
/*
----------------------------------------
DeadThink
----------------------------------------
*/
static void DeadThink ( void ) 
{
	trace_t	trace;

	//HACKHACKHACKHACKHACK
	//We should really have a seperate G2 bounding box (seperate from the physics bbox) for G2 collisions only
	//FIXME: don't ever inflate back up?
	NPC->r.maxs[2] = NPC->client->renderInfo.eyePoint[2] - NPC->r.currentOrigin[2] + 4;
	if ( NPC->r.maxs[2] < -8 )
	{
		NPC->r.maxs[2] = -8;
	}
	if ( VectorCompare( NPC->client->ps.velocity, vec3_origin ) )
	{//not flying through the air
		if ( NPC->r.mins[0] > -32 )
		{
			NPC->r.mins[0] -= 1;
			trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
			if ( trace.allsolid )
			{
				NPC->r.mins[0] += 1;
			}
		}
		if ( NPC->r.maxs[0] < 32 )
		{
			NPC->r.maxs[0] += 1;
			trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
			if ( trace.allsolid )
			{
				NPC->r.maxs[0] -= 1;
			}
		}
		if ( NPC->r.mins[1] > -32 )
		{
			NPC->r.mins[1] -= 1;
			trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
			if ( trace.allsolid )
			{
				NPC->r.mins[1] += 1;
			}
		}
		if ( NPC->r.maxs[1] < 32 )
		{
			NPC->r.maxs[1] += 1;
			trap_Trace (&trace, NPC->r.currentOrigin, NPC->r.mins, NPC->r.maxs, NPC->r.currentOrigin, NPC->s.number, NPC->clipmask );
			if ( trace.allsolid )
			{
				NPC->r.maxs[1] -= 1;
			}
		}
	}
	//HACKHACKHACKHACKHACK
	{
		//death anim done (or were given a specific amount of time to wait before removal), wait the requisite amount of time them remove
		if ( level.time >= NPCInfo->timeOfDeath + BodyRemovalPadTime( NPC ) )
		{
			if ( NPC->client->ps.eFlags & EF_NODRAW )
			{
				if (!trap_ICARUS_IsRunning(NPC->s.number))
				{
					NPC->think = G_FreeEntity;
					NPC->nextthink = level.time + level.frameTime;
				}
			}
			else
			{
				class_t	npc_class;

				// Start the body effect first, then delay 400ms before ditching the corpse
				NPC_RemoveBodyEffect();

				//FIXME: keep it running through physics somehow?
				NPC->think = NPC_RemoveBody;
				NPC->nextthink = level.time + level.frameTime;
				npc_class = NPC->client->NPC_class;
				// check for droids
				if ( npc_class == CLASS_SEEKER || npc_class == CLASS_REMOTE || npc_class == CLASS_PROBE || npc_class == CLASS_MOUSE ||
					 npc_class == CLASS_GONK || npc_class == CLASS_R2D2 || npc_class == CLASS_R5D2 ||
					 npc_class == CLASS_MARK2 || npc_class == CLASS_SENTRY )
				{
					NPC->client->ps.eFlags |= EF_NODRAW;
					NPCInfo->timeOfDeath = level.time + level.frameTime * 8;
				}
				else
					NPCInfo->timeOfDeath = level.time + level.frameTime * 4;
			}
			return;
		}
	}

	// If the player is on the ground and the resting position contents haven't been set yet...(BounceCount tracks the contents)
	if ( NPC->bounceCount < 0 && NPC->s.groundEntityNum >= 0 )
	{
		// if client is in a nodrop area, make him/her nodraw
		int contents = NPC->bounceCount = trap_PointContents( NPC->r.currentOrigin, -1 );

		if ( ( contents & CONTENTS_NODROP ) ) 
		{
			NPC->client->ps.eFlags |= EF_NODRAW;
		}
	}

	CorpsePhysics( NPC );
}
Пример #20
0
/*
==============
AICast_VisibleFromPos
==============
*/
qboolean AICast_VisibleFromPos( vec3_t srcpos, int srcnum,
								vec3_t destpos, int destnum, qboolean updateVisPos ) {
	int i, contents_mask, passent, hitent;
	trace_t trace;
	vec3_t start, end, middle, eye;
	cast_state_t        *cs = NULL;
	int srcviewheight;
	vec3_t destmins, destmaxs;
	vec3_t right, vec;
	qboolean inPVS;

	if ( g_entities[destnum].flags & FL_NOTARGET ) {
		return qfalse;
	}

	if ( srcnum < aicast_maxclients ) {
		cs = AICast_GetCastState( srcnum );
	}
	//
	if ( cs && cs->bs ) {
		srcviewheight = cs->bs->cur_ps.viewheight;
	} else if ( g_entities[srcnum].client ) {
		srcviewheight = g_entities[srcnum].client->ps.viewheight;
	} else {
		srcviewheight = 0;
	}
	//
	VectorCopy( g_entities[destnum].r.mins, destmins );
	VectorCopy( g_entities[destnum].r.maxs, destmaxs );
	//
	//calculate middle of bounding box
	VectorAdd( destmins, destmaxs, middle );
	VectorScale( middle, 0.5, middle );
	VectorAdd( destpos, middle, middle );
	// calculate eye position
	VectorCopy( srcpos, eye );
	eye[2] += srcviewheight;
	//
	// set the right vector
	VectorSubtract( middle, eye, vec );
	VectorNormalize( vec );
	right[0] = vec[1];
	right[1] = vec[0];
	right[2] = 0;
	//
	inPVS = qfalse;
	//
	for ( i = 0; i < 5; i++ )
	{
		if ( cs && updateVisPos ) {   // if it's a grenade or something, PVS checks don't work very well
			//if the point is not in potential visible sight
			if ( i < 3 ) {    // don't do PVS check for left/right checks
				if ( !trap_InPVS( eye, middle ) ) {
					continue;
				} else {
					inPVS = qtrue;
				}
			} else if ( !inPVS ) {
				break;      // wasn't in potential view in either of the previous tests
			}               // so don't bother doing left/right
		}
		//
		contents_mask = MASK_AISIGHT; //(MASK_SHOT | CONTENTS_AI_NOSIGHT) & ~(CONTENTS_BODY);	// we can see anything that a bullet can pass through
		passent = srcnum;
		hitent = destnum;
		VectorCopy( eye, start );
		VectorCopy( middle, end );
		//if the entity is in water, lava or slime
		if ( trap_PointContents( middle, destnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			contents_mask |= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //if eye is in water, lava or slime
		if ( trap_PointContents( eye, srcnum ) & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			if ( !( contents_mask & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) ) {
				passent = destnum;
				hitent = srcnum;
				VectorCopy( middle, start );
				VectorCopy( eye, end );
			} //end if
			contents_mask ^= ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
		} //end if
		  //trace from start to end
		trap_Trace( &trace, start, NULL, NULL, end, ENTITYNUM_NONE /*passent*/, contents_mask );
		//if water was hit
		if ( trace.contents & ( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER ) ) {
			//if the water surface is translucent
//			if (trace.surface.flags & (SURF_TRANS33|SURF_TRANS66))
			{
				//trace through the water
				contents_mask &= ~( CONTENTS_LAVA | CONTENTS_SLIME | CONTENTS_WATER );
				trap_Trace( &trace, trace.endpos, NULL, NULL, end, passent, contents_mask );
			} //end if
		} //end if
		  //if a full trace or the hitent was hit
		if ( trace.fraction >= 1 || trace.entityNum == hitent ) {
			return qtrue;
		}
		//check bottom and top of bounding box as well
		if ( i == 0 ) {
			middle[2] -= ( destmaxs[2] - destmins[2] ) * 0.5;
		} else if ( i == 1 ) {
			middle[2] += destmaxs[2] - destmins[2];
		} else if ( i == 2 )                                                          { // right side
			middle[2] -= ( destmaxs[2] - destmins[2] ) / 2.0;
			VectorMA( eye, destmaxs[0] - 0.5, right, eye );
		} else if ( i == 3 ) {    // left side
			VectorMA( eye, -2.0 * ( destmaxs[0] - 0.5 ), right, eye );
		}
	} //end for

	return qfalse;
}
Пример #21
0
void DoImpact( gentity_t *self, gentity_t *other, qboolean damageSelf )
{
	float magnitude, my_mass;
	vec3_t	velocity;
	int cont;

	if( self->client )
	{
		VectorCopy( self->client->ps.velocity, velocity );
		my_mass = self->mass;
	}
	else 
	{
		VectorCopy( self->s.pos.trDelta, velocity );
		if ( self->s.pos.trType == TR_GRAVITY )
		{
			velocity[2] -= 0.25f * g_gravity.value;
		}
		if( !self->mass )
		{
			my_mass = 1;
		}
		else if ( self->mass <= 10 )
		{
			my_mass = 10;
		}
		else
		{
			my_mass = self->mass;///10;
		}
	}

	magnitude = VectorLength( velocity ) * my_mass / 10;

	/*
	if(pointcontents(self.absmax)==CONTENT_WATER)//FIXME: or other watertypes
		magnitude/=3;							//water absorbs 2/3 velocity

	if(self.classname=="barrel"&&self.aflag)//rolling barrels are made for impacts!
		magnitude*=3;

	if(self.frozen>0&&magnitude<300&&self.flags&FL_ONGROUND&&loser==world&&self.velocity_z<-20&&self.last_onground+0.3<time)
		magnitude=300;
	*/

	if ( !self->client || self->client->ps.lastOnGround+300<level.time || ( self->client->ps.lastOnGround+100 < level.time && other->material >= MAT_GLASS ) )
	{
		vec3_t dir1, dir2;
		float force = 0, dot;

		if ( other->material >= MAT_GLASS )
			magnitude *= 2;

		//damage them
		if ( magnitude >= 100 && other->s.number < ENTITYNUM_WORLD )
		{
			VectorCopy( velocity, dir1 );
			VectorNormalize( dir1 );
			if( VectorCompare( other->r.currentOrigin, vec3_origin ) )
			{//a brush with no origin
				VectorCopy ( dir1, dir2 );
			}
			else
			{
				VectorSubtract( other->r.currentOrigin, self->r.currentOrigin, dir2 );
				VectorNormalize( dir2 );
			}

			dot = DotProduct( dir1, dir2 );

			if ( dot >= 0.2 )
			{
				force = dot;
			}
			else
			{
				force = 0;
			}

			force *= (magnitude/50);

			cont = trap_PointContents( other->r.absmax, other->s.number );
			if( (cont&CONTENTS_WATER) )//|| (self.classname=="barrel"&&self.aflag))//FIXME: or other watertypes
			{
				force /= 3;							//water absorbs 2/3 velocity
			}

			/*
			if(self.frozen>0&&force>10)
				force=10;
			*/

			if( ( force >= 1 && other->s.number != 0 ) || force >= 10)
			{
	/*			
				dprint("Damage other (");
				dprint(loser.classname);
				dprint("): ");
				dprint(ftos(force));
				dprint("\n");
	*/
				if ( other->r.svFlags & SVF_GLASS_BRUSH )
				{
					other->splashRadius = (float)(self->r.maxs[0] - self->r.mins[0])/4.0f;
				}
				if ( other->takedamage )
				{
					G_Damage( other, self, self, velocity, self->r.currentOrigin, force, DAMAGE_NO_ARMOR, MOD_CRUSH);//FIXME: MOD_IMPACT
				}
				else
				{
					G_ApplyKnockback( other, dir2, force );
				}
			}
		}

		if ( damageSelf && self->takedamage )
		{
			//Now damage me
			//FIXME: more lenient falling damage, especially for when driving a vehicle
			if ( self->client && self->client->ps.fd.forceJumpZStart )
			{//we were force-jumping
				if ( self->r.currentOrigin[2] >= self->client->ps.fd.forceJumpZStart )
				{//we landed at same height or higher than we landed
					magnitude = 0;
				}
				else
				{//FIXME: take off some of it, at least?
					magnitude = (self->client->ps.fd.forceJumpZStart-self->r.currentOrigin[2])/3;
				}
			}
			//if(self.classname!="monster_mezzoman"&&self.netname!="spider")//Cats always land on their feet
				if( ( magnitude >= 100 + self->health && self->s.number != 0 && self->s.weapon != WP_SABER ) || ( magnitude >= 700 ) )//&& self.safe_time < level.time ))//health here is used to simulate structural integrity
				{
					if ( (self->s.weapon == WP_SABER || self->s.number == 0) && self->client && self->client->ps.groundEntityNum < ENTITYNUM_NONE && magnitude < 1000 )
					{//players and jedi take less impact damage
						//allow for some lenience on high falls
						magnitude /= 2;
						/*
						if ( self.absorb_time >= time )//crouching on impact absorbs 1/2 the damage
						{
							magnitude/=2;
						}
						*/
					}
					magnitude /= 40;
					magnitude = magnitude - force/2;//If damage other, subtract half of that damage off of own injury
					if ( magnitude >= 1 )
					{
		//FIXME: Put in a thingtype impact sound function
		/*					
						dprint("Damage self (");
						dprint(self.classname);
						dprint("): ");
						dprint(ftos(magnitude));
						dprint("\n");
		*/
						/*
						if ( self.classname=="player_sheep "&& self.flags&FL_ONGROUND && self.velocity_z > -50 )
							return;
						*/
						G_Damage( self, NULL, NULL, NULL, self->r.currentOrigin, magnitude/2, DAMAGE_NO_ARMOR, MOD_FALLING );//FIXME: MOD_IMPACT
					}
				}
		}

		//FIXME: slow my velocity some?

		// NOTENOTE We don't use lastimpact as of yet
//		self->lastImpact = level.time;

		/*
		if(self.flags&FL_ONGROUND)
			self.last_onground=time;
		*/
	}
}
Пример #22
0
/*
=============
CopyToBodyQue

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
void CopyToBodyQue( gentity_t *ent ) {
	gentity_t		*body;
	int			contents;

	if (level.intermissiontime)
	{
		return;
	}

	trap_UnlinkEntity (ent);

	// if client is in a nodrop area, don't leave the body
	contents = trap_PointContents( ent->s.origin, -1 );
	if ( contents & CONTENTS_NODROP ) {
		return;
	}

	if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION))
	{ //for now, just don't spawn a body if you got disint'd
		return;
	}

	// grab a body que and cycle to the next one
	body = level.bodyQue[ level.bodyQueIndex ];
	level.bodyQueIndex = (level.bodyQueIndex + 1) % BODY_QUEUE_SIZE;

	trap_UnlinkEntity (body);
	body->s = ent->s;

	//avoid oddly angled corpses floating around
	body->s.angles[PITCH] = body->s.angles[ROLL] = body->s.apos.trBase[PITCH] = body->s.apos.trBase[ROLL] = 0;

	body->s.g2radius = 100;

	body->s.eType = ET_BODY;
	body->s.eFlags = EF_DEAD;		// clear EF_TALK, etc

	if (ent->client && (ent->client->ps.eFlags & EF_DISINTEGRATION))
	{
		body->s.eFlags |= EF_DISINTEGRATION;
	}

	VectorCopy(ent->client->ps.lastHitLoc, body->s.origin2);

	body->s.powerups = 0;	// clear powerups
	body->s.loopSound = 0;	// clear lava burning
	body->s.number = body - g_entities;
	body->timestamp = level.time;
	body->physicsObject = qtrue;
	body->physicsBounce = 0;		// don't bounce
	if ( body->s.groundEntityNum == ENTITYNUM_NONE ) {
		body->s.pos.trType = TR_GRAVITY;
		body->s.pos.trTime = level.time;
		VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
	} else {
		body->s.pos.trType = TR_STATIONARY;
	}
	body->s.event = 0;

	body->s.weapon = ent->s.bolt2;

	if (body->s.weapon == WP_SABER && ent->client->ps.saberInFlight)
	{
		body->s.weapon = WP_BLASTER; //lie to keep from putting a saber on the corpse, because it was thrown at death
	}

	G_AddEvent(body, EV_BODY_QUEUE_COPY, ent->s.clientNum);

	body->r.svFlags = ent->r.svFlags | SVF_BROADCAST;
	VectorCopy (ent->r.mins, body->r.mins);
	VectorCopy (ent->r.maxs, body->r.maxs);
	VectorCopy (ent->r.absmin, body->r.absmin);
	VectorCopy (ent->r.absmax, body->r.absmax);

	body->s.torsoAnim = body->s.legsAnim = ent->client->ps.legsAnim & ~ANIM_TOGGLEBIT;

	body->clipmask = CONTENTS_SOLID | CONTENTS_PLAYERCLIP;
	body->r.contents = CONTENTS_CORPSE;
	body->r.ownerNum = ent->s.number;

	body->nextthink = level.time + BODY_SINK_TIME;
	body->think = BodySink;

	body->die = body_die;

	// don't take more damage if already gibbed
	if ( ent->health <= GIB_HEALTH ) {
		body->takedamage = qfalse;
	} else {
		body->takedamage = qtrue;
	}

	VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
	trap_LinkEntity (body);
}
Пример #23
0
/*
=============
SpawnCorpse

A player is respawning, so make an entity that looks
just like the existing corpse to leave behind.
=============
*/
static void SpawnCorpse( gentity_t *ent )
{
    gentity_t   *body;
    int         contents;
    vec3_t      origin, dest;
    trace_t     tr;
    float       vDiff;

    VectorCopy( ent->r.currentOrigin, origin );

    trap_UnlinkEntity( ent );

    // if client is in a nodrop area, don't leave the body
    contents = trap_PointContents( origin, -1 );
    if( contents & CONTENTS_NODROP )
        return;

    body = G_Spawn( );

    VectorCopy( ent->s.apos.trBase, body->s.angles );
    body->s.eFlags = EF_DEAD;
    body->s.eType = ET_CORPSE;
    body->s.number = body - g_entities;
    body->timestamp = level.time;
    body->s.event = 0;
    body->r.contents = CONTENTS_CORPSE;
    body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
    body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;

    if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
        body->classname = "humanCorpse";
    else
        body->classname = "alienCorpse";

    body->s.misc = MAX_CLIENTS;

    body->think = BodySink;
    body->nextthink = level.time + 20000;

    body->s.legsAnim = ent->s.legsAnim;

    if( !body->nonSegModel )
    {
        switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
        {
        case BOTH_DEATH1:
        case BOTH_DEAD1:
            body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
            break;
        case BOTH_DEATH2:
        case BOTH_DEAD2:
            body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
            break;
        case BOTH_DEATH3:
        case BOTH_DEAD3:
        default:
            body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
            break;
        }
    }
    else
    {
        switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
        {
        case NSPA_DEATH1:
        case NSPA_DEAD1:
            body->s.legsAnim = NSPA_DEAD1;
            break;
        case NSPA_DEATH2:
        case NSPA_DEAD2:
            body->s.legsAnim = NSPA_DEAD2;
            break;
        case NSPA_DEATH3:
        case NSPA_DEAD3:
        default:
            body->s.legsAnim = NSPA_DEAD3;
            break;
        }
    }

    body->takedamage = qfalse;

    body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
    ent->health = 0;

    //change body dimensions
    BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
    vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];

    //drop down to match the *model* origins of ent and body
    VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
    trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
    VectorCopy( tr.endpos, origin );

    G_SetOrigin( body, origin );
    VectorCopy( origin, body->s.origin );
    body->s.pos.trType = TR_GRAVITY;
    body->s.pos.trTime = level.time;
    VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );

    VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
    trap_LinkEntity( body );
}
Пример #24
0
/*
==================
player_die
==================
*/
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	gentity_t	*ent;
	int			anim;
	int			contents;
	int			killer;
	int			i;
	char		*killerName, *obit;

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

	if ( level.intermissiontime ) {
		return;
	}

	//if we're in SP mode and player killed a bot, award score for the kill
	if ( IsBot( self ) ) {
		if ( self->parent && self->parent->health && attacker->client ) {
			AddScore( attacker, self->r.currentOrigin, self->parent->health );
			self->s.time = level.time;
		}
	}	

	if (self->client && self->client->hook) {
		Weapon_HookFree(self->client->hook);
	}
	self->client->ps.pm_type = PM_DEAD;

	if ( attacker ) {
		killer = attacker->s.number;
		if ( attacker->client ) {
			killerName = attacker->client->pers.netname;
		} else {
			killerName = "<non-client>";
		}
	} else {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( killer < 0 || killer >= MAX_CLIENTS ) {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
		obit = "<bad obituary>";
	} else {
		obit = modNames[ meansOfDeath ];
	}

	G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", 
		killer, self->s.number, meansOfDeath, killerName, 
		self->client->pers.netname, obit );

	// broadcast the death event to everyone
	/*
	ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
	ent->s.eventParm = meansOfDeath;
	ent->s.otherEntityNum = self->s.number;
	ent->s.otherEntityNum2 = killer;
	ent->r.svFlags = SVF_BROADCAST;	// send to everyone
	*/

	self->enemy = attacker;

	self->client->ps.persistant[PERS_KILLED]++;

	// Add team bonuses
	//Team_FragBonuses(self, inflictor, attacker);

	// if client is in a nodrop area, don't drop anything (but return CTF flags!)
	contents = trap_PointContents( self->r.currentOrigin, -1 );
	TossClientItems( self );

	Cmd_Score_f( self );		// show scores
	// send updated scores to any clients that are following this one,
	// or they would get stale scoreboards
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		gclient_t	*client;

		client = &level.clients[i];
		if ( client->pers.connected != CON_CONNECTED ) {
			continue;
		}
		if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
			continue;
		}
		if ( client->sess.spectatorClient == self->s.number ) {
			Cmd_Score_f( g_entities + i );
		}
	}

	self->takedamage = qtrue;	// can still be gibbed

	self->s.weapon = WP_NONE;
	self->s.powerups = 0;
	self->r.contents = CONTENTS_CORPSE;

	self->s.angles[0] = 0;
	self->s.angles[2] = 0;
	LookAtKiller (self, inflictor, attacker);

	VectorCopy( self->s.angles, self->client->ps.viewangles );

	self->s.loopSound = 0;

	self->r.maxs[2] = -8;

	// don't allow respawn until the death anim is done
	// g_forcerespawn may force spawning at some later time
	if ( !IsBot(self) )
		self->client->respawnTime = level.time + 1700;
	else
		self->client->respawnTime = level.time + 5000;	//keep bot bodies around slightly longer

	// remove powerups
	memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );

	// never gib in a nodrop
	if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) {
		// gib death
		GibEntity( self, killer );
	} else {
		// normal death
		static int i;

		switch ( i ) {
		case 0:
			anim = BOTH_DEATH1;
			break;
		case 1:
			anim = BOTH_DEATH2;
			break;
		case 2:
		default:
			anim = BOTH_DEATH3;
			break;
		}

		// for the no-blood option, we need to prevent the health
		// from going to gib level
		if ( self->health <= GIB_HEALTH ) {
			self->health = GIB_HEALTH+1;
		}

		self->client->ps.legsAnim = 
			( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
		self->client->ps.torsoAnim = 
			( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;

		G_AddEvent( self, EV_DEATH1 + i, killer );

		// the body can still be gibbed
		self->die = body_die;

		// globally cycle through the different death animations
		i = ( i + 1 ) % 3;
	}

	trap_LinkEntity (self);

	// Fire trigger_death and trigger_frag target entities and the deathtarget for the related target_botspawn 
	G_UseTriggerFragAndDeathEntities ( self, attacker );
	if ( self->parent )
		G_UseDeathTargets( self->parent, self );

	if ( !IsBot( self ) )
		G_FadeOut( 1.0 );
}
Пример #25
0
/*
==================
player_die
==================
*/
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	gentity_t	*ent;
	int			anim;
	int			contents;
	int			killer;
	int			i;
	char		*killerName, *obit;

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

	if ( level.intermissiontime ) {
		return;
	}

	// check for an almost capture
	CheckAlmostCapture( self, attacker );
	// check for a player that almost brought in cubes
	CheckAlmostScored( self, attacker );

	if (self->client && self->client->hook) {
		Weapon_HookFree(self->client->hook);
	}
#ifdef MISSIONPACK
	if ((self->client->ps.eFlags & EF_TICKING) && self->activator) {
		self->client->ps.eFlags &= ~EF_TICKING;
		self->activator->think = G_FreeEntity;
		self->activator->nextthink = level.time;
	}
#endif
	self->client->ps.pm_type = PM_DEAD;

	if ( attacker ) {
		killer = attacker->s.number;
		if ( attacker->client ) {
			killerName = attacker->client->pers.netname;
		} else {
			killerName = "<non-client>";
		}
	} else {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( killer < 0 || killer >= MAX_CLIENTS ) {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( meansOfDeath < 0 || meansOfDeath >= ARRAY_LEN( modNames ) ) {
		obit = "<bad obituary>";
	} else {
		obit = modNames[meansOfDeath];
	}

	G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", 
		killer, self->s.number, meansOfDeath, killerName, 
		self->client->pers.netname, obit );

	// broadcast the death event to everyone
	ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
	ent->s.eventParm = meansOfDeath;
	ent->s.otherEntityNum = self->s.number;
	ent->s.otherEntityNum2 = killer;
	ent->r.svFlags = SVF_BROADCAST;	// send to everyone

	self->enemy = attacker;

	self->client->ps.persistant[PERS_KILLED]++;

	if (attacker && attacker->client) {
		attacker->client->lastkilled_client = self->s.number;

		if ( attacker == self || OnSameTeam (self, attacker ) ) {
			AddScore( attacker, self->r.currentOrigin, -1 );
		} else {
			AddScore( attacker, self->r.currentOrigin, 1 );

			if( meansOfDeath == MOD_GAUNTLET ) {
				
				// play humiliation on player
				attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;

				// add the sprite over the player's head
				attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
				attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET;
				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

				// also play humiliation on target
				self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD;
			}

			// check for two kills in a short amount of time
			// if this is close enough to the last kill, give a reward sound
			if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) {
				// play excellent on player
				attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;

				// add the sprite over the player's head
				attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
				attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT;
				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
			}
			attacker->client->lastKillTime = level.time;

		}
	} else {
		AddScore( self, self->r.currentOrigin, -1 );
	}

	// Add team bonuses
	Team_FragBonuses(self, inflictor, attacker);

	// if I committed suicide, the flag does not fall, it returns.
	if (meansOfDeath == MOD_SUICIDE) {
		if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
			Team_ReturnFlag( TEAM_FREE );
			self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
		}
		else if ( self->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
			Team_ReturnFlag( TEAM_RED );
			self->client->ps.powerups[PW_REDFLAG] = 0;
		}
		else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
			Team_ReturnFlag( TEAM_BLUE );
			self->client->ps.powerups[PW_BLUEFLAG] = 0;
		}
	}

	// if client is in a nodrop area, don't drop anything (but return CTF flags!)
	contents = trap_PointContents( self->r.currentOrigin, -1 );
	if ( !( contents & CONTENTS_NODROP )) {
		TossClientItems( self );
	}
	else {
		if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
			Team_ReturnFlag( TEAM_FREE );
		}
		else if ( self->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
			Team_ReturnFlag( TEAM_RED );
		}
		else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
			Team_ReturnFlag( TEAM_BLUE );
		}
	}
#ifdef MISSIONPACK
	TossClientPersistantPowerups( self );
	if( g_gametype.integer == GT_HARVESTER ) {
		TossClientCubes( self );
	}
#endif

	Cmd_Score_f( self );		// show scores
	// send updated scores to any clients that are following this one,
	// or they would get stale scoreboards
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		gclient_t	*client;

		client = &level.clients[i];
		if ( client->pers.connected != CON_CONNECTED ) {
			continue;
		}
		if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
			continue;
		}
		if ( client->sess.spectatorClient == self->s.number ) {
			Cmd_Score_f( g_entities + i );
		}
	}

	self->takedamage = qtrue;	// can still be gibbed

	self->s.weapon = WP_NONE;
	self->s.powerups = 0;
	self->r.contents = CONTENTS_CORPSE;

	self->s.angles[0] = 0;
	self->s.angles[2] = 0;
	LookAtKiller (self, inflictor, attacker);

	VectorCopy( self->s.angles, self->client->ps.viewangles );

	self->s.loopSound = 0;

	self->r.maxs[2] = -8;

	// don't allow respawn until the death anim is done
	// g_forcerespawn may force spawning at some later time
	self->client->respawnTime = level.time + 1700;

	// remove powerups
	memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );

	// never gib in a nodrop
	if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) {
		// gib death
		GibEntity( self, killer );
	} else {
		// normal death
		static int i;

		switch ( i ) {
		case 0:
			anim = BOTH_DEATH1;
			break;
		case 1:
			anim = BOTH_DEATH2;
			break;
		case 2:
		default:
			anim = BOTH_DEATH3;
			break;
		}

		// for the no-blood option, we need to prevent the health
		// from going to gib level
		if ( self->health <= GIB_HEALTH ) {
			self->health = GIB_HEALTH+1;
		}

		self->client->ps.legsAnim = 
			( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
		self->client->ps.torsoAnim = 
			( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;

		G_AddEvent( self, EV_DEATH1 + i, killer );

		// the body can still be gibbed
		self->die = body_die;

		// globally cycle through the different death animations
		i = ( i + 1 ) % 3;

#ifdef MISSIONPACK
		if (self->s.eFlags & EF_KAMIKAZE) {
			Kamikaze_DeathTimer( self );
		}
#endif
	}

	trap_LinkEntity (self);

}
Пример #26
0
/*
================
G_Physics

================
*/
void G_Physics( gentity_t *ent, int msec )
{
	vec3_t  origin;
	trace_t tr;
	int     contents;

	// if groundentity has been set to ENTITYNUM_NONE, it may have been pushed off an edge
	if ( ent->s.groundEntityNum == ENTITYNUM_NONE )
	{
		if ( ent->s.eType == ET_BUILDABLE )
		{
			if ( ent->s.pos.trType != BG_Buildable( ent->s.modelindex )->traj )
			{
				ent->s.pos.trType = BG_Buildable( ent->s.modelindex )->traj;
				ent->s.pos.trTime = level.time;
			}
		}
		else if ( ent->s.pos.trType != TR_GRAVITY )
		{
			ent->s.pos.trType = TR_GRAVITY;
			ent->s.pos.trTime = level.time;
		}
	}

	if ( ent->s.pos.trType == TR_STATIONARY )
	{
		// check think function
		G_RunThink( ent );

		//check floor infrequently
		if ( ent->nextPhysicsTime < level.time )
		{
			VectorCopy( ent->r.currentOrigin, origin );

			VectorMA( origin, -2.0f, ent->s.origin2, origin );

			trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask );

			if ( tr.fraction == 1.0f )
			{
				ent->s.groundEntityNum = ENTITYNUM_NONE;
			}

			ent->nextPhysicsTime = level.time + PHYSICS_TIME;
		}

		return;
	}

	// trace a line from the previous position to the current position

	// get current position
	BG_EvaluateTrajectory( &ent->s.pos, level.time, origin );

	trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, ent->s.number, ent->clipmask );

	VectorCopy( tr.endpos, ent->r.currentOrigin );

	if ( tr.startsolid )
	{
		tr.fraction = 0;
	}

	trap_LinkEntity( ent );  // FIXME: avoid this for stationary?

	// check think function
	G_RunThink( ent );

	if ( tr.fraction == 1.0f )
	{
		return;
	}

	// if it is in a nodrop volume, remove it
	contents = trap_PointContents( ent->r.currentOrigin, -1 );

	if ( contents & CONTENTS_NODROP )
	{
		G_FreeEntity( ent );
		return;
	}

	G_Bounce( ent, &tr );
}
Пример #27
0
/*
============
AICast_Die
============
*/
void AICast_Die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	int contents;
	int killer;
	cast_state_t    *cs;
	qboolean nogib = qtrue;

	// print debugging message
	if ( aicast_debug.integer == 2 && attacker->s.number == 0 ) {
		G_Printf( "killed %s\n", self->aiName );
	}

	cs = AICast_GetCastState( self->s.number );

	if ( attacker ) {
		killer = attacker->s.number;
	} else {
		killer = ENTITYNUM_WORLD;
	}

	// record the sighting (FIXME: silent weapons shouldn't do this, but the AI should react in some way)
	if ( attacker->client ) {
		AICast_UpdateVisibility( self, attacker, qtrue, qtrue );
	}

	// the zombie should show special effect instead of gibbing
	if ( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime ) {
		if ( cs->secondDeadTime > 1 ) {
			// we are already totally dead
			self->health += damage; // don't drop below gib_health if we weren't already below it
			return;
		}
/*
		if (!cs->rebirthTime)
		{
			self->health = -999;
			damage = 999;
		} else if ( self->health >= GIB_HEALTH ) {
			// while waiting for rebirth, we only "die" if we drop below gib health
			return;
		}
*/
		// always gib
		self->health = -999;
		damage = 999;
	}

	// Zombies are very fragile against highly explosives
	if ( self->aiCharacter == AICHAR_ZOMBIE && damage > 20 && inflictor != attacker ) {
		self->health = -999;
		damage = 999;
	}

	// process the event
	if ( self->client->ps.pm_type == PM_DEAD ) {
		// already dead
		if ( self->health < GIB_HEALTH ) {
			if ( self->aiCharacter == AICHAR_ZOMBIE ) {
				// RF, changed this so Zombies always gib now
				GibEntity( self, killer );
				nogib = qfalse;
/*
				// Zombie has special exploding cloud effect
				if (attacker != inflictor || attacker->s.weapon == WP_VENOM)
				{
					GibEntity( self, killer );
					nogib = qfalse;
				} else {
					// Zombie will decompose upon dying
					self->client->ps.eFlags |= EF_MONSTER_EFFECT2;
					self->s.effect2Time = level.time+200;
					self->health = -1;
				}
*/
				self->takedamage = qfalse;
				self->r.contents = 0;
				cs->secondDeadTime = 2;
				cs->rebirthTime = 0;
				cs->revivingTime = 0;
			} else {
				body_die( self, inflictor, attacker, damage, meansOfDeath );
				return;
			}
		}

	} else {    // this is our first death, so set everything up

		if ( level.intermissiontime ) {
			return;
		}

		self->client->ps.pm_type = PM_DEAD;

		self->enemy = attacker;

		// drop a weapon?
		// if client is in a nodrop area, don't drop anything
		contents = trap_PointContents( self->r.currentOrigin, -1 );
		if ( !( contents & CONTENTS_NODROP ) ) {
			TossClientItems( self );
		}

		// make sure the client doesn't forget about this entity until it's set to "dead" frame
		// otherwise it might replay it's death animation if it goes out and into client view
		self->r.svFlags |= SVF_BROADCAST;

		self->takedamage = qtrue;   // can still be gibbed

		self->s.weapon = WP_NONE;
		self->s.powerups = 0;
		self->r.contents = CONTENTS_CORPSE;

		self->s.angles[0] = 0;
		self->s.angles[1] = self->client->ps.viewangles[1];
		self->s.angles[2] = 0;

		VectorCopy( self->s.angles, self->client->ps.viewangles );

		self->s.loopSound = 0;

		self->r.maxs[2] = -8;
		self->client->ps.maxs[2] = self->r.maxs[2];

		// remove powerups
		memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );

		//cs->rebirthTime = 0;

		// never gib in a nodrop
		if ( self->health <= GIB_HEALTH ) {
			if ( self->aiCharacter == AICHAR_ZOMBIE ) {
				// RF, changed this so Zombies always gib now
				GibEntity( self, killer );
				nogib = qfalse;
/*
				// Zombie has special exploding cloud effect
				if (attacker != inflictor || attacker->s.weapon == WP_VENOM)
				{
					GibEntity( self, killer );
					nogib = qfalse;
					self->takedamage = qfalse;
					self->r.contents = 0;
					cs->secondDeadTime = 2;
				} else {
					self->client->ps.eFlags |= EF_MONSTER_EFFECT2;
					self->s.effect2Time = level.time+200;
					self->takedamage = qfalse;
					self->r.contents = 0;
					self->health = -1;
					cs->secondDeadTime = 2;
				}
*/
			} else if ( !( contents & CONTENTS_NODROP ) ) {
				body_die( self, inflictor, attacker, damage, meansOfDeath );
				//GibEntity( self, killer );
				nogib = qfalse;
			}
		}

		// if we are a zombie, and lying down during our first death, then we should just die
		if ( !( self->aiCharacter == AICHAR_ZOMBIE && cs->secondDeadTime && cs->rebirthTime ) ) {

			// set enemy weapon
			BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse );
			if ( attacker->client ) {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, inflictor->s.weapon, qtrue );
			} else {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_WEAPON, 0, qfalse );
			}

			// set enemy location
			BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, 0, qfalse );
			if ( infront( self, inflictor ) ) {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_INFRONT, qtrue );
			} else {
				BG_UpdateConditionValue( self->s.number, ANIM_COND_ENEMY_POSITION, POSITION_BEHIND, qtrue );
			}

			// play the animation
			BG_AnimScriptEvent( &self->client->ps, ANIM_ET_DEATH, qfalse, qtrue );

			// set this flag so no other anims override us
			self->client->ps.eFlags |= EF_DEAD;
			self->s.eFlags |= EF_DEAD;

		}
	}

	if ( nogib ) {
		// set for rebirth
		if ( self->aiCharacter == AICHAR_ZOMBIE ) {
			if ( !cs->secondDeadTime ) {
				cs->rebirthTime = level.time + 5000 + rand() % 2000;
				cs->secondDeadTime = qtrue;
				cs->revivingTime = 0;
			} else if ( cs->secondDeadTime > 1 ) {
				cs->rebirthTime = 0;
				cs->revivingTime = 0;
				cs->deathTime = level.time;
			}
		} else {
			// the body can still be gibbed
			self->die = body_die;
		}
	}

	trap_LinkEntity( self );

	// mark the time of death
	cs->deathTime = level.time;

	// dying ai's can trigger a target
	if ( !cs->rebirthTime ) {
		G_UseTargets( self, self );
		// really dead now, so call the script
		AICast_ScriptEvent( cs, "death", "" );
		// call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things
		if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) {
			cs->deathfunc( self, attacker, damage, meansOfDeath );   //----(SA)	added mod
		}
	} else {
		// really dead now, so call the script
		AICast_ScriptEvent( cs, "fakedeath", "" );
		// call the deathfunc for this cast, so we can play associated sounds, or do any character-specific things
		if ( !( cs->aiFlags & AIFL_DENYACTION ) && cs->deathfunc ) {
			cs->deathfunc( self, attacker, damage, meansOfDeath );   //----(SA)	added mod
		}
	}
}
Пример #28
0
/*
==================
player_die
==================
*/
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	gentity_t   *ent;
	int anim;
	int contents = 0;
	int killer;
	int i;
	char        *killerName, *obit;
	qboolean nogib = qtrue;
	gitem_t     *item = NULL; // JPW NERVE for flag drop
	vec3_t launchvel;      // JPW NERVE
	gentity_t   *flag; // JPW NERVE

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

	if ( level.intermissiontime ) {
		return;
	}

//----(SA) commented out as we have no hook
//	if (self->client && self->client->hook)
//		Weapon_HookFree(self->client->hook);

	self->client->ps.pm_type = PM_DEAD;

	if ( attacker ) {
		killer = attacker->s.number;
		if ( attacker->client ) {
			killerName = attacker->client->pers.netname;
		} else {
			killerName = "<non-client>";
		}
	} else {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( killer < 0 || killer >= MAX_CLIENTS ) {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
		obit = "<bad obituary>";
	} else {
		obit = modNames[ meansOfDeath ];
	}

	G_LogPrintf( "Kill: %i %i %i: %s killed %s by %s\n",
				 killer, self->s.number, meansOfDeath, killerName,
				 self->client->pers.netname, obit );

	// broadcast the death event to everyone
	ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
	ent->s.eventParm = meansOfDeath;
	ent->s.otherEntityNum = self->s.number;
	ent->s.otherEntityNum2 = killer;
	ent->r.svFlags = SVF_BROADCAST; // send to everyone

	self->enemy = attacker;

	self->client->ps.persistant[PERS_KILLED]++;

	if ( attacker && attacker->client ) {
		if ( attacker == self || OnSameTeam( self, attacker ) ) {
			AddScore( attacker, -1 );
		} else {
			AddScore( attacker, 1 );

			// Ridah, not in single player
			if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
				// done.
				if ( meansOfDeath == MOD_GAUNTLET ) {
					attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
					attacker->client->ps.persistant[PERS_REWARD] = REWARD_GAUNTLET;
					attacker->client->ps.persistant[PERS_REWARD_COUNT]++;

					// add the sprite over the player's head
//					attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT /*| EF_AWARD_GAUNTLET*/ );
					//attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET;
					attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

					// also play humiliation on target
					self->client->ps.persistant[PERS_REWARD] = REWARD_GAUNTLET;
					self->client->ps.persistant[PERS_REWARD_COUNT]++;
				}

				// check for two kills in a short amount of time
				// if this is close enough to the last kill, give a reward sound
				if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) {
					attacker->client->ps.persistant[PERS_REWARD_COUNT]++;
					attacker->client->ps.persistant[PERS_REWARD] = REWARD_EXCELLENT;
					attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;

					// add the sprite over the player's head
//					attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT /*| EF_AWARD_GAUNTLET*/ );
//					attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT;
					attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
				}
				// Ridah
			}
			// done.
			attacker->client->lastKillTime = level.time;
		}
	} else {
		AddScore( self, -1 );
	}

	// Add team bonuses
	Team_FragBonuses( self, inflictor, attacker );

	// if client is in a nodrop area, don't drop anything
// JPW NERVE new drop behavior
	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {   // only drop here in single player; in multiplayer, drop @ limbo
		contents = trap_PointContents( self->r.currentOrigin, -1 );
		if ( !( contents & CONTENTS_NODROP ) ) {
			TossClientItems( self );
		}
	}

	// drop flag regardless
	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		if ( self->client->ps.powerups[PW_REDFLAG] ) {
			item = BG_FindItem( "Red Flag" );
		}
		if ( self->client->ps.powerups[PW_BLUEFLAG] ) {
			item = BG_FindItem( "Blue Flag" );
		}
		launchvel[0] = crandom() * 20;
		launchvel[1] = crandom() * 20;
		launchvel[2] = 10 + random() * 10;
		if ( item ) {
			flag = LaunchItem( item,self->r.currentOrigin,launchvel );
			flag->s.modelindex2 = self->s.otherEntityNum2; // JPW NERVE FIXME set player->otherentitynum2 with old modelindex2 from flag and restore here
		}
	}
// jpw

	Cmd_Score_f( self );        // show scores
	// send updated scores to any clients that are following this one,
	// or they would get stale scoreboards
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		gclient_t   *client;

		client = &level.clients[i];
		if ( client->pers.connected != CON_CONNECTED ) {
			continue;
		}
		if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
			continue;
		}
		if ( client->sess.spectatorClient == self->s.number ) {
			Cmd_Score_f( g_entities + i );
		}
	}

	self->takedamage = qtrue;   // can still be gibbed

	self->s.powerups = 0;
// JPW NERVE -- only corpse in SP; in MP, need CONTENTS_BODY so medic can operate
	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
		self->r.contents = CONTENTS_CORPSE;
		self->s.weapon = WP_NONE;
	} else {
		self->client->limboDropWeapon = self->s.weapon; // store this so it can be dropped in limbo
	}
// jpw
	self->s.angles[0] = 0;
	self->s.angles[2] = 0;
	LookAtKiller( self, inflictor, attacker );

	VectorCopy( self->s.angles, self->client->ps.viewangles );

	self->s.loopSound = 0;

	self->r.maxs[2] = -8;

	// don't allow respawn until the death anim is done
	// g_forcerespawn may force spawning at some later time
	self->client->respawnTime = level.time + 1700;

	// remove powerups
	memset( self->client->ps.powerups, 0, sizeof( self->client->ps.powerups ) );

	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
		trap_SendServerCommand( -1, "mu_play sound/music/l_failed_1.wav 0\n" );
		trap_SetConfigstring( CS_MUSIC_QUEUE, "" );  // clear queue so it'll be quiet after hit
		trap_SendServerCommand( -1, "cp missionfail0" );
	}


	// never gib in a nodrop
	if ( self->health <= GIB_HEALTH && !( contents & CONTENTS_NODROP ) && g_blood.integer ) {
//		if(self->client->ps.eFlags & EF_HEADSHOT)
//		{
//			GibHead(self, killer);
//		}
//		else	// gib death
//		{
		GibEntity( self, killer );
		nogib = qfalse;
//		}
	}

	if ( nogib ) {
		// normal death
		static int i;

		switch ( i ) {
		case 0:
			anim = BOTH_DEATH1;
			break;
		case 1:
			anim = BOTH_DEATH2;
			break;
		case 2:
		default:
			anim = BOTH_DEATH3;
			break;
		}

		// for the no-blood option, we need to prevent the health
		// from going to gib level
		if ( self->health <= GIB_HEALTH ) {
			self->health = GIB_HEALTH + 1;
		}

// JPW NERVE for medic
		self->client->medicHealAmt = 0;
// jpw

		self->client->ps.legsAnim =
			( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
		self->client->ps.torsoAnim =
			( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;

		G_AddEvent( self, EV_DEATH1 + 1, killer );

		// the body can still be gibbed
		self->die = body_die;

		// globally cycle through the different death animations
		i = ( i + 1 ) % 3;
	}

	trap_LinkEntity( self );

	if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
		AICast_ScriptEvent( AICast_GetCastState( self->s.number ), "death", "" );
	}
}
Пример #29
0
/*
==================
player_die
==================
*/
void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath ) {
	gentity_t	*ent;
	int			anim;
	int			contents;
	int			killer;
	int			i,counter2;
	char		*killerName, *obit;

	if ( !(self->client) || (self->client->ps.pm_type == PM_DEAD) ) {
		return;
	}

	if ( level.intermissiontime ) {
		return;
	}

//unlagged - backward reconciliation #2
	// make sure the body shows up in the client's current position
	G_UnTimeShiftClient( self );
//unlagged - backward reconciliation #2
    //KK-OAX Here is where we run the streak logic. 
    G_RunStreakLogic( attacker, self );
    
	// check for an almost capture
	CheckAlmostCapture( self, attacker );
	// check for a player that almost brought in cubes
	CheckAlmostScored( self, attacker );

	if (self->client && self->client->hook) {
		Weapon_HookFree(self->client->hook);
	}
	if ((self->client->ps.eFlags & EF_TICKING) && self->activator) {
		self->client->ps.eFlags &= ~EF_TICKING;
		self->activator->think = G_FreeEntity;
		self->activator->nextthink = level.time;
	}
	self->client->ps.pm_type = PM_DEAD;

	if ( attacker ) {
		killer = attacker->s.number;
		if ( attacker->client ) {
			killerName = attacker->client->pers.netname;
		} else {
			killerName = "<non-client>";
		}
	} else {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( killer < 0 || killer >= MAX_CLIENTS ) {
		killer = ENTITYNUM_WORLD;
		killerName = "<world>";
	}

	if ( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) ) {
		obit = "<bad obituary>";
	} else {
		obit = modNames[meansOfDeath];
	}

	G_LogPrintf("Kill: %i %i %i: %s killed %s by %s\n", 
		killer, self->s.number, meansOfDeath, killerName, 
		self->client->pers.netname, obit );

	// broadcast the death event to everyone
	ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
	ent->s.eventParm = meansOfDeath;
	ent->s.otherEntityNum = self->s.number;
	ent->s.otherEntityNum2 = killer;
        //Sago: Hmmm... generic? Can I transmit anything I like? Like if it is a team kill? Let's try
        ent->s.generic1 = OnSameTeam (self, attacker);
        if( !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime) )
            ent->r.svFlags = SVF_BROADCAST;	// send to everyone (if not an elimination gametype during active warmup)
        else
            ent->r.svFlags = SVF_NOCLIENT;

	self->enemy = attacker;

	self->client->ps.persistant[PERS_KILLED]++;

	if (attacker && attacker->client) {
		attacker->client->lastkilled_client = self->s.number;

		if ( attacker == self || OnSameTeam (self, attacker ) ) {
			if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime))
                            if( (g_gametype.integer <GT_TEAM && g_ffa_gt!=1 && self->client->ps.persistant[PERS_SCORE]>0) || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide
                                AddScore( attacker, self->r.currentOrigin, -1 );
		} else {
			if(g_gametype.integer!=GT_LMS)
				AddScore( attacker, self->r.currentOrigin, 1 );

			if( meansOfDeath == MOD_GAUNTLET ) {
				
				// Attack gets a challenge complete:
				if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT))
					ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS);
		
				// play humiliation on player
				attacker->client->ps.persistant[PERS_GAUNTLET_FRAG_COUNT]++;
                                G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 0, attacker->client->pers.netname, "GAUNTLET" );

				// add the sprite over the player's head
				attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
				attacker->client->ps.eFlags |= EF_AWARD_GAUNTLET;
				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;

				// also play humiliation on target
				self->client->ps.persistant[PERS_PLAYEREVENTS] ^= PLAYEREVENT_GAUNTLETREWARD;
			}

                        //If neither attacker or taget is bots and not the same
                        if(!(attacker->r.svFlags & SVF_BOT) && !(self->r.svFlags & SVF_BOT) && self!=attacker)
                        {
                            switch(meansOfDeath)
                            {
                                case MOD_GAUNTLET:
                                    ChallengeMessage(attacker,WEAPON_GAUNTLET_KILLS);
                                    break;
                                case MOD_MACHINEGUN:
                                    ChallengeMessage(attacker,WEAPON_MACHINEGUN_KILLS);
                                    break;
                                case MOD_SHOTGUN:
                                    ChallengeMessage(attacker,WEAPON_SHOTGUN_KILLS);
                                    break;
                                case MOD_GRENADE:
                                case MOD_GRENADE_SPLASH:
                                    ChallengeMessage(attacker,WEAPON_GRANADE_KILLS);
                                    break;
                                case MOD_ROCKET:
                                case MOD_ROCKET_SPLASH:
                                    ChallengeMessage(attacker,WEAPON_ROCKET_KILLS);
                                    break;
                                case MOD_LIGHTNING:
                                    ChallengeMessage(attacker,WEAPON_LIGHTNING_KILLS);
                                    break;
                                case MOD_PLASMA:
                                case MOD_PLASMA_SPLASH:
                                    ChallengeMessage(attacker,WEAPON_PLASMA_KILLS);
                                    break;
                                case MOD_RAILGUN:
                                    if(g_instantgib.integer)
                                        ChallengeMessage(attacker,WEAPON_INSTANT_RAIL_KILLS);
                                    else
                                        ChallengeMessage(attacker,WEAPON_RAIL_KILLS);
                                    break;
                                case MOD_BFG:
                                case MOD_BFG_SPLASH:
                                    ChallengeMessage(attacker,WEAPON_BFG_KILLS);
                                    break;
                                case MOD_NAIL:
                                    ChallengeMessage(attacker,WEAPON_NAILGUN_KILLS);
                                    break;
                                case MOD_CHAINGUN:
                                    ChallengeMessage(attacker,WEAPON_CHAINGUN_KILLS);
                                    break;
                                case MOD_PROXIMITY_MINE:
                                    ChallengeMessage(attacker,WEAPON_MINE_KILLS);
                                    break;
                                case MOD_GRAPPLE:
                                    ChallengeMessage(attacker,WEAPON_GRAPPLE_KILLS);
                                    break;
                                case MOD_LAVA:
                                case MOD_SLIME:
                                case MOD_TRIGGER_HURT:
                                case MOD_FALLING:
                                    ChallengeMessage(attacker,WEAPON_PUSH_KILLS);
                                    break;
                                case MOD_CRUSH:
                                    ChallengeMessage(attacker,WEAPON_CRUSH_KILLS);
                                    break;
                                case MOD_TELEFRAG:
                                    ChallengeMessage(attacker,WEAPON_TELEFRAG_KILLS);
                                    break;
                            };
                            ChallengeMessage(attacker,GENERAL_TOTALKILLS);
                            ChallengeMessage(self,GENERAL_TOTALDEATHS);

                            //Lets count number of powerups:
                            i = 0;
                            counter2 = 0;

                            if(attacker->client->ps.powerups[PW_QUAD]) {
                                ChallengeMessage(attacker,POWERUP_QUAD_KILL);
                                counter2++;
                            }
                            if(self->client->ps.powerups[PW_QUAD]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_QUAD);
                                i++;
                            }

                            if(attacker->client->ps.powerups[PW_HASTE]) {
                                ChallengeMessage(attacker,POWERUP_SPEED_KILL);
                                counter2++;
                            }
                            if(self->client->ps.powerups[PW_HASTE]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_SPEED);
                                i++;
                            }

                            if(attacker->client->ps.powerups[PW_INVIS]) {
                                ChallengeMessage(attacker,POWERUP_INVIS_KILL);
                                counter2++;
                            }
                            if(self->client->ps.powerups[PW_INVIS]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_INVIS);
                                i++;
                            }

                            if(attacker->client->ps.powerups[PW_FLIGHT]) {
                                ChallengeMessage(attacker,POWERUP_FLIGHT_KILL);
                                counter2++;
                            }
                            if(self->client->ps.powerups[PW_FLIGHT]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_FLIGHT);
                                i++;
                            }

                            if(self->client->ps.powerups[PW_BATTLESUIT]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_ENVIR);
                                i++;
                            }

                            if(self->client->ps.powerups[PW_REGEN]) {
                                ChallengeMessage(attacker,POWERUP_COUNTER_REGEN);
                                i++;
                            }

                            if(i>1) //The target had more than one powerup
                                ChallengeMessage(attacker,POWERUP_COUNTER_MULTI);
                            if(counter2>1) //The attacker has more than one powerup
                                ChallengeMessage(attacker,POWERUP_MULTI_KILL);
                        }

			// check for two kills in a short amount of time
			// if this is close enough to the last kill, give a reward sound
			if ( level.time - attacker->client->lastKillTime < CARNAGE_REWARD_TIME ) {
				// KK-OAX
				// Check if Multikills are enabled
				if( g_altExcellent.integer ) {
				    attacker->client->pers.multiKillCount++;
				    G_checkForMultiKill( attacker );
				} // play excellent on player
				attacker->client->ps.persistant[PERS_EXCELLENT_COUNT]++;
                                G_LogPrintf( "Award: %i %i: %s gained the %s award!\n", attacker->client->ps.clientNum, 1, attacker->client->pers.netname, "EXCELLENT" );
                                if(!level.hadBots) //There has not been any bots
                                    ChallengeMessage(attacker,AWARD_EXCELLENT);
				// add the sprite over the player's head
				attacker->client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
				attacker->client->ps.eFlags |= EF_AWARD_EXCELLENT;
				attacker->client->rewardTime = level.time + REWARD_SPRITE_TIME;
			} else { 
			    //KK-OAX Clear multikill count
			    //Must be 1 so the correct number of kills are displayed to the clients. 
			    attacker->client->pers.multiKillCount = 1;
			}
			attacker->client->lastKillTime = level.time;
		}
	} else {
		if(g_gametype.integer!=GT_LMS && !((g_gametype.integer==GT_ELIMINATION || g_gametype.integer==GT_CTF_ELIMINATION) && level.time < level.roundStartTime))
                    if(self->client->ps.persistant[PERS_SCORE]>0 || level.numNonSpectatorClients<3) //Cannot get negative scores by suicide
			AddScore( self, self->r.currentOrigin, -1 );
	}

	// Add team bonuses
	Team_FragBonuses(self, inflictor, attacker);

	// if I committed suicide, the flag does not fall, it returns.
	if (meansOfDeath == MOD_SUICIDE) {
		if ( self->client->ps.powerups[PW_NEUTRALFLAG] ) {		// only happens in One Flag CTF
			Team_ReturnFlag( TEAM_FREE );
			self->client->ps.powerups[PW_NEUTRALFLAG] = 0;
		}
		else if ( self->client->ps.powerups[PW_REDFLAG] ) {		// only happens in standard CTF
			Team_ReturnFlag( TEAM_RED );
			self->client->ps.powerups[PW_REDFLAG] = 0;
		}
		else if ( self->client->ps.powerups[PW_BLUEFLAG] ) {	// only happens in standard CTF
			Team_ReturnFlag( TEAM_BLUE );
			self->client->ps.powerups[PW_BLUEFLAG] = 0;
		}
	}
        TossClientPersistantPowerups( self );
        if( g_gametype.integer == GT_HARVESTER ) {
                TossClientCubes( self );
        }
	// if client is in a nodrop area, don't drop anything (but return CTF flags!)
	TossClientItems( self );
//#endif

	Cmd_Score_f( self );		// show scores
	// send updated scores to any clients that are following this one,
	// or they would get stale scoreboards
	for ( i = 0 ; i < level.maxclients ; i++ ) {
		gclient_t	*client;

		client = &level.clients[i];
		if ( client->pers.connected != CON_CONNECTED ) {
			continue;
		}
		if ( client->sess.sessionTeam != TEAM_SPECTATOR ) {
			continue;
		}
		if ( client->sess.spectatorClient == self->s.number ) {
			Cmd_Score_f( g_entities + i );
		}
	}

	self->takedamage = qtrue;	// can still be gibbed

	self->s.weapon = WP_NONE;
	self->s.powerups = 0;
	self->r.contents = CONTENTS_CORPSE;

	self->s.angles[0] = 0;
	self->s.angles[2] = 0;
	LookAtKiller (self, inflictor, attacker);

	VectorCopy( self->s.angles, self->client->ps.viewangles );

	self->s.loopSound = 0;

	self->r.maxs[2] = -8;

	// don't allow respawn until the death anim is done
	// g_forcerespawn may force spawning at some later time
	self->client->respawnTime = level.time + 1700 +i;
        if(g_respawntime.integer>0) {
            for(i=0; self->client->respawnTime > i*g_respawntime.integer*1000;i++);

            self->client->respawnTime = i*g_respawntime.integer*1000;
        }
        //For testing:
        //G_Printf("Respawntime: %i\n",self->client->respawnTime);
	//However during warm up, we should respawn quicker!
	if(g_gametype.integer == GT_ELIMINATION || g_gametype.integer == GT_CTF_ELIMINATION || g_gametype.integer == GT_LMS)
		if(level.time<=level.roundStartTime && level.time>level.roundStartTime-1000*g_elimination_activewarmup.integer)
			self->client->respawnTime = level.time + rand()%800;

        RespawnTimeMessage(self,self->client->respawnTime);
		

	// remove powerups
	memset( self->client->ps.powerups, 0, sizeof(self->client->ps.powerups) );

	// never gib in a nodrop
	contents = trap_PointContents( self->r.currentOrigin, -1 );

	if ( (self->health <= GIB_HEALTH && !(contents & CONTENTS_NODROP) && g_blood.integer) || meansOfDeath == MOD_SUICIDE) {
		// gib death
		GibEntity( self, killer );
	} else {
		// normal death
		static int i;

		switch ( i ) {
		case 0:
			anim = BOTH_DEATH1;
			break;
		case 1:
			anim = BOTH_DEATH2;
			break;
		case 2:
		default:
			anim = BOTH_DEATH3;
			break;
		}

		// for the no-blood option, we need to prevent the health
		// from going to gib level
		if ( self->health <= GIB_HEALTH ) {
			self->health = GIB_HEALTH+1;
		}

		self->client->ps.legsAnim = 
			( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
		self->client->ps.torsoAnim = 
			( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;

		G_AddEvent( self, EV_DEATH1 + i, killer );

		// the body can still be gibbed
		self->die = body_die;

		// globally cycle through the different death animations
		i = ( i + 1 ) % 3;

		if (self->s.eFlags & EF_KAMIKAZE) {
			Kamikaze_DeathTimer( self );
		}
	}

	trap_LinkEntity (self);

}
Пример #30
0
/* LQ3A: Added bBroadcast parameter to allow the calling function to suppress the change. */
void SetTeam( gentity_t *ent, char *s, qboolean bBroadcast) {
	/* LQ3A */
	team_t				team, oldTeam;

	gclient_t			*client;
	int					clientNum;
	spectatorState_t	specState;
	int					specClient;
	int					teamLeader;

	//
	// see what change is requested
	//
	client = ent->client;

	clientNum = client - level.clients;
	specClient = 0;
	specState = SPECTATOR_NOT;
	if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" )  ) {
		team = TEAM_SPECTATOR;
		specState = SPECTATOR_SCOREBOARD;
	} else if ( !Q_stricmp( s, "follow1" ) ) {
		team = TEAM_SPECTATOR;
		specState = SPECTATOR_FOLLOW;
		specClient = -1;
	} else if ( !Q_stricmp( s, "follow2" ) ) {
		team = TEAM_SPECTATOR;
		specState = SPECTATOR_FOLLOW;
		specClient = -2;
	} else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) {
		team = TEAM_SPECTATOR;
		specState = SPECTATOR_FREE;
	} else if ( g_gametype.integer >= GT_TEAM ) {
		// if running a team game, assign player to one of the teams
		specState = SPECTATOR_NOT;
		if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) {
			team = TEAM_RED;
		} else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) {
			team = TEAM_BLUE;
		} else {
			// pick the team with the least number of players
			team = PickTeam( clientNum );
		}

		if ( g_teamForceBalance.integer  ) {
			int		counts[TEAM_NUM_TEAMS];

			counts[TEAM_BLUE] = TeamCount( ent->client->ps.clientNum, TEAM_BLUE );
			counts[TEAM_RED] = TeamCount( ent->client->ps.clientNum, TEAM_RED );

			// We allow a spread of two
			if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] > 1 ) {
				trap_SendServerCommand( ent->client->ps.clientNum, 
					"cp \"Red team has too many players.\n\"" );
				return; // ignore the request
			}
			if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] > 1 ) {
				trap_SendServerCommand( ent->client->ps.clientNum, 
					"cp \"Blue team has too many players.\n\"" );
				return; // ignore the request
			}

			// It's ok, the team we are switching to has less or same number of players
		}

	} else {
		// force them to spectators if there aren't any spots free
		/* LQ3A */
		team = ((client->sess.sessionTeam == TEAM_FREE) ||
			((client->sess.sessionTeam != TEAM_FREE) && LQ3A_GetVacantPlayerSlots())) ? TEAM_FREE : TEAM_SPECTATOR;
	}

	// override decision if limiting the players
	if ( (g_gametype.integer == GT_TOURNAMENT)
		&& level.numNonSpectatorClients >= 2 ) {
		team = TEAM_SPECTATOR;
	} else if ( g_maxGameClients.integer > 0 && 
		level.numNonSpectatorClients > g_maxGameClients.integer ) {
		team = TEAM_SPECTATOR;
	}

	//
	// decide if we will allow the change
	//
	oldTeam = client->sess.sessionTeam;
	/* LQ3A */
	if ( team == oldTeam /*&& team != TEAM_SPECTATOR*/ ) {
		return;
	}

	/* LQ3A: Ensure we're allowed to spectate. */
	if ((team == TEAM_SPECTATOR) && !LQ3A_CanClientSpectate(ent))
	{
		return;
	}

	//
	// execute the team change
	//

	// if the player was dead leave the body
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		CopyToBodyQue(ent);
	}

	// he starts at 'base'
	client->pers.teamState.state = TEAM_BEGIN;

	/* LQ3A*/
	if (oldTeam == TEAM_SPECTATOR)
	{
		ent->r.svFlags &= ~SVF_NOCLIENT;
		client->pers.enterTime += (level.time - client->sess.spectatorTime);

		/* Restore the players score. */
		client->ps.persistant[PERS_SCORE] = client->pers.iScore;
	}
	else if (ent->client->ps.stats[STAT_HEALTH] > 0)
	{
		// Kill him (makes sure he loses flags, etc)
		ent->flags &= ~FL_GODMODE;

		/* LQ3A: Kill the player if they have too little health. */
		if ((g_spectatorFreePass.integer > 0) && (ent->client->ps.stats[STAT_HEALTH] < g_spectatorFreePass.integer))
		{
			ent->client->ps.stats[STAT_HEALTH] = ent->health = 0;
			player_die (ent, ent, ent, 100000, MOD_SUICIDE);
		}
		else
		{
			/* LQ3A: Drop items and return flags when we're allowed
				to become a spectator without commiting suicide. */

			// if client is in a nodrop area, don't drop anything (but return CTF flags!)
			if (!(trap_PointContents(ent->r.currentOrigin, -1) & CONTENTS_NODROP))
			{
				TossClientItems(ent);
			}
			else 
			{
				if (ent->client->ps.powerups[PW_NEUTRALFLAG])
				{
					Team_ReturnFlag(TEAM_FREE);
				}
				else if (ent->client->ps.powerups[PW_REDFLAG])
				{
					Team_ReturnFlag(TEAM_RED);
				}
				else if (ent->client->ps.powerups[PW_BLUEFLAG])
				{
					Team_ReturnFlag(TEAM_BLUE);
				}
			}
#ifdef MISSIONPACK
			TossClientPersistantPowerups(ent);

			if(g_gametype.integer == GT_HARVESTER)
			{
				TossClientCubes(ent);
			}
#endif
		}

	}
	// they go to the end of the line for tournements
	if ( team == TEAM_SPECTATOR ) {

		/* LQ3A */
		LQ3A_CompleteClientMoveToSpectatorTeam(ent);
	}

	client->sess.sessionTeam = team;
	client->sess.spectatorState = specState;
	client->sess.spectatorClient = specClient;

	client->sess.teamLeader = qfalse;
	if ( team == TEAM_RED || team == TEAM_BLUE ) {
		teamLeader = TeamLeader( team );
		// if there is no team leader or the team leader is a bot and this client is not a bot
		if ( teamLeader == -1 || ( !(g_entities[clientNum].r.svFlags & SVF_BOT) && (g_entities[teamLeader].r.svFlags & SVF_BOT) ) ) {
			SetLeader( team, clientNum );
		}
	}
	// make sure there is a team leader on the team the player came from
	if ( oldTeam == TEAM_RED || oldTeam == TEAM_BLUE ) {
		CheckTeamLeader( oldTeam );
	}

	/* LQ3A: Broadcast the change when instructed to do so. */
	if (bBroadcast)
	{
		BroadcastTeamChange(client, oldTeam);
	}

	// get and distribute relevent paramters
	ClientUserinfoChanged( clientNum );

	/* LQ3A */
	if (team != TEAM_SPECTATOR)
	{
		/* Spawn the client into the game. */
		ClientBegin(clientNum);
	}
	else
	{
		/* Update the cached scores. */
		CalculateRanks();
	}
}