/*
======================
SV_StepDirection

Turns to the movement direction, and walks the current distance if
facing it.

======================
*/
qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
{
	vec3_t		move, oldorigin;
	float		delta;
	
	ent->ideal_yaw = yaw;
	M_ChangeYaw (ent);
	
	yaw = yaw*M_PI*2 / 360;
	move[0] = cos(yaw)*dist;
	move[1] = sin(yaw)*dist;
	move[2] = 0;

	VectorCopy (ent->s.origin, oldorigin);
	if (SV_movestep (ent, move, false))
	{
		delta = ent->s.angles[YAW] - ent->ideal_yaw;
		if (delta > 45 && delta < 315)
		{		// not turned far enough, so don't take the step
			VectorCopy (oldorigin, ent->s.origin);
		}
		gi.linkentity (ent);
		G_TouchTriggers (ent);
		return true;
	}
	gi.linkentity (ent);
	G_TouchTriggers (ent);
	return false;
}
Esempio n. 2
0
qboolean M_default_movestep (edict_t *self, usercmd_t *ucmd)
{
	vec3_t	movedir;
	vec3_t	neworigin;
	int		movetype;
	float	speed = 0;

	AngleVectors( tv( 0, self->s.angles[YAW], 0), movedir, NULL, NULL);

	if( ucmd->forwardmove > 200 )
		speed = 20;
	else if( ucmd->forwardmove > 10 )
		speed = 5;
	//else if( ucmd->forwardmove < -200 )
		//speed = -20;
	//else if( ucmd->forwardmove < -10 )
		//speed = -5;
	else {
		//speed is 0
		VectorCopy( self->s.origin, self->s.old_origin );
		//relink
		gi.linkentity(self);//trap_LinkEntity (self);
		G_TouchTriggers (self);
		return true;
	}


	VectorCopy( self->s.origin, self->s.old_origin );

	//VectorCopy( self->ai->move_vector, movedir );
	VectorNormalize( movedir );

	movetype = M_default_GravityBoxStep( self->s.origin, speed, movedir, neworigin, self->mins, self->maxs, MASK_AISOLID, self );
	if( movetype & LINK_INVALID )
	{
		return false;
	} else {
		VectorCopy( neworigin, self->s.origin );

		//store velocity for dmclass move code checks
		VectorSubtract(self->s.origin, self->s.old_origin, self->velocity);

		//relink
		gi.linkentity(self);//trap_LinkEntity (self);
		G_TouchTriggers (self);
		return true;
	}

	return false;
}
Esempio n. 3
0
/**
 * @brief The client lets the server spawn the actors for a given player by sending their information (models, inventory, etc..) over the network.
 * @param[in] player The player to spawn the actors for.
 * @sa GAME_SendCurrentTeamSpawningInfo
 * @sa clc_teaminfo
 */
void G_ClientTeamInfo (const player_t * player)
{
	const int length = gi.ReadByte(); /* Get the actor amount that the client sent. */
	int i;

	for (i = 0; i < length; i++) {
		const actorSizeEnum_t actorFieldSize = gi.ReadByte();
		/* Search for a spawn point for each entry the client sent */
		if (player->pers.team == TEAM_NO_ACTIVE || !G_ActorSpawnIsAllowed(i, player->pers.team))
			G_ClientSkipActorInfo();
		else {
			edict_t *ent = G_ClientGetFreeSpawnPointForActorSize(player, actorFieldSize);
			if (ent) {
				Com_DPrintf(DEBUG_GAME, "Player: %i - team %i - size: %i\n", player->num, ent->team, ent->fieldSize);

				G_ClientReadCharacter(ent);
				G_ClientReadInventory(ent);
				G_ClientAssignDefaultActorValues(ent);
				G_ActorGiveTimeUnits(ent);
				G_TouchTriggers(ent);
				ent->contentFlags = G_ActorGetContentFlags(ent->origin);
			} else {
				gi.DPrintf("Not enough spawn points for team %i (actorsize: %i)\n", player->pers.team, actorFieldSize);

				G_ClientSkipActorInfo();
			}
		}
	}

	Com_Printf("Used inventory slots client %s spawn: %i\n", player->pers.netname, game.i.GetUsedSlots(&game.i));
}
Esempio n. 4
0
static void G_RoundTouchTriggers (int team)
{
	Actor* actor = nullptr;

	while ((actor = G_EdictsGetNextLivingActorOfTeam(actor, team))) {
		G_TouchTriggers(actor);
	}
}
Esempio n. 5
0
/*
=================
SpectatorThink
=================
*/
void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) 
{
	gclient_t	*client = ent->client;

	if ( client->sess.spectatorState != SPECTATOR_FOLLOW ) 
	{
		client->ps.pm_type = PM_SPECTATOR;
		client->ps.speed = 400;	// faster than normal
		pmove_t		pm;
		// set up for pmove
		memset (&pm, 0, sizeof(pm));
		pm.ps = &client->ps;
		pm.cmd = *ucmd;
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;	// spectators can fly through bodies
		pm.trace = trap_Trace;
		pm.pointcontents = trap_PointContents;

		pm.animations = NULL;

		// perform a pmove
		Pmove (&pm);

		G_UpdatePlayerStateScores ( ent );

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

		G_TouchTriggers( ent );
		trap_UnlinkEntity( ent );
	}

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

	// attack button cycles through spectators
	if ( client->sess.spectatorState != SPECTATOR_FOLLOW && g_forceFollow.integer )
	{
		Cmd_FollowCycle_f( ent, 1 );
	}
	if ( ( client->buttons & BUTTON_ATTACK ) && ! ( client->oldbuttons & BUTTON_ATTACK ) ) 
	{
		Cmd_FollowCycle_f( ent, 1 );
	}
	else if ( ( client->buttons & BUTTON_ALT_ATTACK ) && ! ( client->oldbuttons & BUTTON_ALT_ATTACK ) ) 
	{
		Cmd_FollowCycle_f( ent, -1 );
	}
	else if ( !g_forceFollow.integer && ucmd->upmove > 0 && (client->ps.pm_flags & PMF_FOLLOW) )
	{
		G_StopFollowing( ent );
	}
}
Esempio n. 6
0
/*
======================
SV_StepDirection

Turns to the movement direction, and walks the current distance if
facing it.

======================
*/
qboolean SV_StepDirection (edict_t *ent, float yaw, float dist)
{
    vec3_t		move, oldorigin;
    float		delta;

    if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem

    ent->ideal_yaw = yaw;
    M_ChangeYaw (ent);

    yaw = yaw*M_PI*2 / 360;
    move[0] = cos(yaw)*dist;
    move[1] = sin(yaw)*dist;
    move[2] = 0;

    VectorCopy (ent->s.origin, oldorigin);
    if (SV_movestep (ent, move, false))
    {
        ent->monsterinfo.aiflags &= ~AI_BLOCKED;
        if(!ent->inuse)	return true;		// PGM g_touchtrigger free problem

        delta = ent->s.angles[YAW] - ent->ideal_yaw;
        if (strncmp(ent->classname, "monster_widow", 13))
        {
            if (delta > 45 && delta < 315)
            {   // not turned far enough, so don't take the step
                VectorCopy (oldorigin, ent->s.origin);
            }
        }
        gi.linkentity (ent);
        G_TouchTriggers (ent);
        return true;
    }
    gi.linkentity (ent);
    G_TouchTriggers (ent);
    return false;
}
Esempio n. 7
0
/*
 * Does not change the entities velocity at all
 */
trace_t
SV_PushEntity(edict_t *ent, vec3_t push)
{
	trace_t trace;
	vec3_t start;
	vec3_t end;
	int mask;

	VectorCopy(ent->s.origin, start);
	VectorAdd(start, push, end);

retry:

	if (ent->clipmask)
	{
		mask = ent->clipmask;
	}
	else
	{
		mask = MASK_SOLID;
	}

	trace = gi.trace(start, ent->mins, ent->maxs, end, ent, mask);

	VectorCopy(trace.endpos, ent->s.origin);
	gi.linkentity(ent);

	if (trace.fraction != 1.0)
	{
		SV_Impact(ent, &trace);

		/* if the pushed entity went away and the pusher is still there */
		if (!trace.ent->inuse && ent->inuse)
		{
			/* move the pusher back and try again */
			VectorCopy(start, ent->s.origin);
			gi.linkentity(ent);
			goto retry;
		}
	}

	if (ent->inuse)
	{
		G_TouchTriggers(ent);
	}

	return trace;
}
Esempio n. 8
0
/*
============
SV_PushEntity

Does not change the entities velocity at all
============
*/
trace_t SV_PushEntity (edict_t *ent, vec3_t push)
{
	trace_t	trace;
	vec3_t	start;
	vec3_t	end;
	int		mask;

	VectorCopy (ent->s.origin, start);
	VectorAdd (start, push, end);

retry:
	if (ent->clipmask)
		mask = ent->clipmask;
	else
		mask = MASK_SOLID;

	trace = gi.trace (start, ent->mins, ent->maxs, end, ent, mask);
	
	VectorCopy (trace.endpos, ent->s.origin);
	gi.linkentity (ent);

	if (trace.fraction != 1.0)
	{
		SV_Impact (ent, &trace);

		// if the pushed entity went away and the pusher is still there
		if (!trace.ent->inuse && ent->inuse)
		{
			// move the pusher back and try again
			VectorCopy (start, ent->s.origin);
			gi.linkentity (ent);
			goto retry;
		}
	}

// ================
// PGM
	// FIXME - is this needed?
	ent->gravity = 1.0;
// PGM
// ================

	if (ent->inuse)
		G_TouchTriggers (ent);

	return trace;
}					
Esempio n. 9
0
/**
 * @brief Make the actor use (as in open/close) a door edict
 * @note Will also check whether the door is still reachable (this might have
 * changed due to the rotation) after the usage
 * @param actor The actor that is using the door
 * @param door The door that should be opened/closed
 */
void G_ActorUseDoor (Edict* actor, Edict* door)
{
	/* check whether it's part of an edict group but not the master */
	if (door->flags & FL_GROUPSLAVE)
		door = door->groupMaster;

	if (!G_ClientUseEdict(actor->getPlayer(), actor, door))
		return;

	/* end this loop here, for the AI this is a) not interesting,
	 * and b) could result in endless loops */
	if (G_IsAI(actor))
		return;

	Edict* closeActor = nullptr;
	while ((closeActor = G_FindRadius(closeActor, door->origin, UNIT_SIZE * 3))) {
		/* check whether the door is still reachable (this might have
		 * changed due to the rotation) or whether an actor can reach it now */
		G_TouchTriggers(closeActor);
	}
}
Esempio n. 10
0
/*
 * @brief Does not change the entity's velocity at all
 */
c_trace_t G_PushEntity(g_edict_t *ent, vec3_t push) {
	c_trace_t trace;
	vec3_t start;
	vec3_t end;
	int32_t mask;

	VectorCopy(ent->s.origin, start);
	VectorAdd(start, push, end);

	retry: if (ent->clip_mask)
		mask = ent->clip_mask;
	else
		mask = MASK_SOLID;

	trace = gi.Trace(start, end, ent->mins, ent->maxs, ent, mask);

	VectorCopy(trace.end, ent->s.origin);
	gi.LinkEdict(ent);

	if (trace.fraction != 1.0) {
		G_Impact(ent, &trace);

		// if the pushed entity went away and the pusher is still there
		if (!trace.ent->in_use && ent->in_use) {
			// move the pusher back and try again
			VectorCopy(start, ent->s.origin);
			gi.LinkEdict(ent);
			goto retry;
		}
	}

	if (ent->in_use && ent->client && ent->locals.health > 0)
		G_TouchTriggers(ent);

	return trace;
}
Esempio n. 11
0
//FIXME since we need to test end position contents here, can we avoid doing
//it again later in catagorize position?
qboolean SV_movestep (edict_t *ent, vec3_t move, qboolean relink)
{
	float		dz;
	vec3_t		oldorg, neworg, end;
	trace_t		trace;
	int			i;
	float		stepsize;
	vec3_t		test;
	int			contents;

// BEGIN:	Xatrix/Ridah/Navigator/14-apr-1998
	int			mask = MASK_MONSTERSOLID;

	if (ent->cast_info.aiflags & AI_PLAYERCLIP)
		mask = MASK_PLAYERSOLID;
	else if (ent->flags & FL_RESPAWN)	// used for moveout command
		mask = MASK_SOLID;
// END:		Xatrix/Ridah/Navigator/14-apr-1998

// try the move	
	VectorCopy (ent->s.origin, oldorg);
	VectorAdd (ent->s.origin, move, neworg);

// flying monsters don't step up
	if ( ent->flags & (FL_SWIM | FL_FLY) )
	{
	// try one move with vertical motion, then one without
		for (i=0 ; i<2 ; i++)
		{
			VectorAdd (ent->s.origin, move, neworg);
			if (i == 0 && ent->enemy)
			{
				if (!ent->goalentity)
					ent->goalentity = ent->enemy;
				dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
				if (ent->goalentity->client)
				{
					if (dz > 40)
						neworg[2] -= 8;
					if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
						if (dz < 30)
							neworg[2] += 8;
				}
				else
				{
						if (dz > 8)
							neworg[2] -= 8;
						else if (dz > 0)
							neworg[2] -= dz;
						else if (dz < -8)
							neworg[2] += 8;
						else
							neworg[2] += dz;
					
				}
			}
			
// BEGIN:	Xatrix/Ridah/Navigator/14-apr-1998		(use modified mask for Grunts)
			trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, neworg, ent, mask);
// END:		Xatrix/Ridah/Navigator/14-apr-1998

			// fly monsters don't enter water voluntarily
			if (ent->flags & FL_FLY)
			{
				if (!ent->waterlevel)
				{
					test[0] = trace.endpos[0];
					test[1] = trace.endpos[1];
					test[2] = trace.endpos[2] + ent->mins[2] + 1;
					contents = gi.pointcontents(test);
					if (contents & MASK_WATER)
						return false;
				}
			}

			// swim monsters don't exit water voluntarily
			if (ent->flags & FL_SWIM)
			{
				if (ent->waterlevel < 2)
				{
					test[0] = trace.endpos[0];
					test[1] = trace.endpos[1];
					test[2] = trace.endpos[2] + ent->mins[2] + 1;
					contents = gi.pointcontents(test);
					if (!(contents & MASK_WATER))
						return false;
				}
			}

			if (trace.fraction == 1)
			{
				VectorCopy (trace.endpos, ent->s.origin);
				if (relink)
				{
					gi.linkentity (ent);
					G_TouchTriggers (ent);
				}
				return true;
			}
			
			if (!ent->enemy)
				break;
		}
		
		return false;
	}

// push down from a step height above the wished position
	if (!(ent->cast_info.aiflags & AI_NOSTEP))
		stepsize = STEPSIZE;
	else
		stepsize = 1;

// BEGIN: Xatrix/Ridah/Navigator/03-apr-1998
	if (ent->nav_data.goal_index && ((level.node_data->nodes[ent->nav_data.goal_index-1]->origin[2] - ent->s.origin[2]) > 0))
	{
		stepsize += 8;

		if (ent->waterlevel > 2)
			stepsize = 1;
	}
// END:   Xatrix/Ridah/Navigator/03-apr-1998

	neworg[2] += stepsize;
	VectorCopy (neworg, end);
	end[2] -= stepsize*2;

	trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, mask);

	// Ridah, tell this ent to avoid us if possible
	if ((trace.ent && trace.ent->svflags & SVF_MONSTER) && (ent->health > 0))	// Ridah, Fixes life after death bug
	{
		extern void AI_GetAvoidDirection( edict_t *self, edict_t *other );

		AI_GetAvoidDirection( trace.ent, ent );
		ent->cast_info.currentmove = ent->cast_info.move_avoid_walk;
	}

	if (trace.allsolid)
		return false;

	if (trace.startsolid)
	{
		neworg[2] -= stepsize;
		trace = gi.trace (neworg, ent->mins, ent->maxs, end, ent, mask);
		if (trace.allsolid || trace.startsolid || trace.fraction == 0)
		{
			return false;
		}
	}

// BEGIN:	Xatrix/Ridah/Navigator/03-apr-1998
	// don't let monsters walk onto other monsters
	if ((trace.fraction < 1) && (trace.ent->svflags & SVF_MONSTER))
	{
		return false;
	}
// END:		Xatrix/Ridah/Navigator/03-apr-1998

	// don't go in to lava/slime
	if (ent->waterlevel < 2)
	{
		test[0] = trace.endpos[0];
		test[1] = trace.endpos[1];
		test[2] = trace.endpos[2] + ent->mins[2] + 16;
		contents = gi.pointcontents(test);

		if (contents & (CONTENTS_LAVA|CONTENTS_SLIME))
			return false;
	}

	if (trace.fraction == 1)
	{
	// if monster had the ground pulled out, go ahead and fall
		if ( ent->flags & FL_PARTIALGROUND )
		{
			VectorAdd (ent->s.origin, move, ent->s.origin);
			if (relink)
			{
				gi.linkentity (ent);
				G_TouchTriggers (ent);
			}
			ent->groundentity = NULL;
			return true;
		}
// BEGIN: Xatrix/Ridah/Navigator/14-apr-1998
		else
		{
			float fall_dist=0;

			// never fall into water
			VectorCopy(trace.endpos, neworg);
			VectorCopy(neworg, end);
			end[2] -= 2048;

			trace = gi.trace( neworg, ent->mins, ent->maxs, end, ent, mask );

			test[0] = trace.endpos[0];
			test[1] = trace.endpos[1];
			test[2] = trace.endpos[2] + ent->mins[2] + 16;
			contents = gi.pointcontents(test);

			if (contents & MASK_WATER)
				return false;
			

/*
			// let them fall into water if they should be doing so
			if (	ent->waterlevel
				||	(ent->count && level.node_data->nodes[ent->count-1]->waterlevel)
				||	(ent->goalentity && ent->goalentity->waterlevel))
			{
				VectorAdd (ent->s.origin, move, ent->s.origin);
				VectorScale(move, 10, ent->velocity);
				ent->groundentity = NULL;

				return true;
			}
*/

			if (	(ent->nav_data.goal_index && ((fall_dist = level.node_data->nodes[ent->nav_data.goal_index-1]->origin[2] - ent->s.origin[2]) < 0)) 
				||	(ent->goalentity && (( fall_dist = ent->goalentity->s.origin[2] - ent->s.origin[2]) < 0)))
			{
//				VectorCopy(trace.endpos, neworg);
				VectorCopy(neworg, end);
				end[2] += (fall_dist - 48);

				trace = gi.trace( neworg, ent->mins, ent->maxs, end, ent, mask );

				if (trace.fraction < 1)
				{	// start to fall
					VectorAdd (ent->s.origin, move, ent->s.origin);
					VectorScale(move, 5, ent->velocity);
					ent->groundentity = NULL;

					return true;
				}
			}
		}
// END:   Xatrix/Ridah/Navigator/23-mar-1998

		// walked off an edge
		return false;
	}

	// check point traces down for dangling corners
	VectorCopy (trace.endpos, ent->s.origin);
	
	if (!M_CheckBottom (ent))
	{
		if ( ent->flags & FL_PARTIALGROUND )
		{	// entity had floor mostly pulled out from underneath it
			// and is trying to correct
			if (relink)
			{
				gi.linkentity (ent);
				G_TouchTriggers (ent);
			}
			return true;
		}
		
		// Joseph
		if (!ent->fallerflag)
		{	
			// Ridah, don't bother checking all corners
//			VectorCopy (oldorg, ent->s.origin);
//			return false;
		}
		else 
		{
			vec3_t	forward, right, up;
			int speed = 140;
			
			// JOSEPH 7-OCT-98
			// Stop any drag sound
			ent->s.sound = 0;
			
			// Object is falling
			ent->fallingflag = 1;
			// END JOSEPH
			
			// JOSEPH 17-MAY-99
			if (!ent->pullingflag)
			{
				AngleVectors (move, forward, right, up);

				VectorScale (move, speed, ent->velocity);
				VectorMA (ent->velocity, 100, up, ent->velocity);
				ent->movetype = MOVETYPE_TOSS;
			}
			// END JOSEPH

			// JOSEPH 17-MAY-99
			//VectorAdd (ent->s.origin, move, ent->s.origin);
			// END JOSEPH
			if (relink)
			{
				gi.linkentity (ent);
				G_TouchTriggers (ent);
			}
			ent->groundentity = NULL;
			return true;
		}
	}

	if ( ent->flags & FL_PARTIALGROUND )
	{
		ent->flags &= ~FL_PARTIALGROUND;
	}
	ent->groundentity = trace.ent;
	ent->groundentity_linkcount = trace.ent->linkcount;

// the move is ok
	if (relink)
	{
		gi.linkentity (ent);
		G_TouchTriggers (ent);
	}
	return true;
}
Esempio n. 12
0
File: p_client.c Progetto: ZwS/qudos
/*
==============
ClientThink

This will be called once for each client frame, which will
usually be a couple times for each server frame.
==============
*/
void ClientThink (edict_t *ent, usercmd_t *ucmd)
{
	gclient_t	*client;
	edict_t	*other;
	int		i, j;
	pmove_t	pm;

#if defined(_DEBUG) && defined(_Z_TESTMODE)

  if(testitemOriginMove && testItemDroped)
  {
    if(ucmd->forwardmove > 0)
    {
      testItemDroped->s.origin[2]++;
    }
    else if(ucmd->forwardmove < 0)
    {
      testItemDroped->s.origin[2]--;
    }

    return;
  }

#endif

	level.current_entity = ent;
	client = ent->client;
	
#ifdef GAME_MOD
	Blinky_BeginClientThink(ent, ucmd);
#endif

	if (level.intermissiontime)
	{
		client->ps.pmove.pm_type = PM_FREEZE;
		// can exit intermission after five seconds
		if (level.time > level.intermissiontime + 5.0 
			&& (ucmd->buttons & BUTTON_ANY) )
			level.exitintermission = true;
		return;
	}

	if(ent->movetype == MOVETYPE_FREEZE)
	{
		client->ps.pmove.pm_type = PM_FREEZE;
		return;
	}

	pm_passent = ent;

	// set up for pmove
	memset (&pm, 0, sizeof(pm));
	
	if (ent->movetype == MOVETYPE_NOCLIP)
		client->ps.pmove.pm_type = PM_SPECTATOR;
	else if (ent->s.modelindex != 255)
		client->ps.pmove.pm_type = PM_GIB;
	else if (ent->deadflag)
		client->ps.pmove.pm_type = PM_DEAD;
	else
		client->ps.pmove.pm_type = PM_NORMAL;

	client->ps.pmove.gravity = sv_gravity->value;
	pm.s = client->ps.pmove;

	for (i=0 ; i<3 ; i++)
	{
		pm.s.origin[i] = ent->s.origin[i]*8;
		pm.s.velocity[i] = ent->velocity[i]*8;
	}

	if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
	{
		pm.snapinitial = true;
//		gi.dprintf ("pmove changed!\n");
	}

	pm.cmd = *ucmd;

	pm.trace = PM_trace;	// adds default parms
	pm.pointcontents = gi.pointcontents;

	// perform a pmove
	gi.Pmove (&pm);

	// save results of pmove
	client->ps.pmove = pm.s;
	client->old_pmove = pm.s;

	for (i=0 ; i<3 ; i++)
	{
		ent->s.origin[i] = pm.s.origin[i]*0.125;
		ent->velocity[i] = pm.s.velocity[i]*0.125;
	}

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

	client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
	client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
	client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);

	if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
	{
		gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
		PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
	}

	ent->viewheight = pm.viewheight;
	ent->waterlevel = pm.waterlevel;
	ent->watertype = pm.watertype;
	ent->groundentity = pm.groundentity;
	if (pm.groundentity)
		ent->groundentity_linkcount = pm.groundentity->linkcount;

	if (ent->deadflag)
	{
		client->ps.viewangles[ROLL] = 40;
		client->ps.viewangles[PITCH] = -15;
		client->ps.viewangles[YAW] = client->killer_yaw;
	}
	else
	{
		VectorCopy (pm.viewangles, client->v_angle);
		VectorCopy (pm.viewangles, client->ps.viewangles);
	}


	gi.linkentity (ent);

	if (ent->movetype != MOVETYPE_NOCLIP)
		G_TouchTriggers (ent);

	// touch other objects
	for (i=0 ; i<pm.numtouch ; i++)
	{
		other = pm.touchents[i];
		for (j=0 ; j<i ; j++)
			if (pm.touchents[j] == other)
				break;
		if (j != i)
			continue;	// duplicated
		if (!other->touch)
			continue;
		other->touch (other, ent, NULL, NULL);
	}


	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons |= client->buttons & ~client->oldbuttons;

	// save light level the player is standing on for
	// monster sighting AI
	ent->light_level = ucmd->lightlevel;

	// fire weapon from final position if needed
	if (client->latched_buttons & BUTTON_ATTACK)
	{
		if (!client->weapon_thunk)
		{
			client->weapon_thunk = true;
			Think_Weapon (ent);
		}
	}
#ifdef WITH_ACEBOT
// ACEBOT_ADD
	if (!ent->is_bot && !ent->deadflag && !ent->client->resp.spectator)
		ACEND_PathMap(ent);
// ACEBOT_END
#endif
}
Esempio n. 13
0
/**
 * @brief Generates the client events that are send over the netchannel to move an actor
 * @param[in] player Player who is moving an actor
 * @param[in] visTeam The team to check the visibility for - if this is 0 we build the forbidden list
 * above all edicts - for the human controlled actors this would mean that clicking to a grid
 * position that is not reachable because an invisible actor is standing there would not result in
 * a single step - as the movement is aborted before. For AI movement this is in general @c 0 - but
 * not if they e.g. hide.
 * @param[in] ent Edict to move
 * @param[in] to The grid position to walk to
 * @sa CL_ActorStartMove
 * @sa PA_MOVE
 */
void G_ClientMove (const player_t * player, int visTeam, edict_t* ent, const pos3_t to)
{
    int status, initTU;
    dvec_t dvtab[MAX_DVTAB];
    int dir;
    byte numdv, length;
    pos3_t pos;
    float div;
    int oldState;
    int oldHP;
    bool autoCrouchRequired = false;
    byte crouchingState;

    if (VectorCompare(ent->pos, to))
        return;

    /* check if action is possible */
    if (!G_ActionCheckForCurrentTeam(player, ent, TU_MOVE_STRAIGHT))
        return;

    crouchingState = G_IsCrouched(ent) ? 1 : 0;
    oldState = oldHP = 0;

    /* calculate move table */
    G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
    length = gi.MoveLength(level.pathingMap, to, crouchingState, false);

    /* length of ROUTING_NOT_REACHABLE means not reachable */
    if (length && length >= ROUTING_NOT_REACHABLE)
        return;

    /* Autostand: check if the actor is crouched and player wants autostanding...*/
    if (crouchingState && player->autostand) {
        /* ...and if this is a long walk... */
        if (SHOULD_USE_AUTOSTAND(length)) {
            /* ...make them stand first. If the player really wants them to walk a long
             * way crouched, he can move the actor in several stages.
             * Uses the threshold at which standing, moving and crouching again takes
             * fewer TU than just crawling while crouched. */
            G_ClientStateChange(player, ent, STATE_CROUCHED, true); /* change to stand state */
            crouchingState = G_IsCrouched(ent) ? 1 : 0;
            if (!crouchingState) {
                G_MoveCalc(visTeam, ent, ent->pos, crouchingState, ent->TU);
                length = gi.MoveLength(level.pathingMap, to, crouchingState, false);
                autoCrouchRequired = true;
            }
        }
    }

    /* this let the footstep sounds play even over network */
    ent->think = G_PhysicsStep;
    ent->nextthink = level.time;

    /* assemble dvec-encoded move data */
    VectorCopy(to, pos);
    initTU = ent->TU;

    numdv = G_FillDirectionTable(dvtab, lengthof(dvtab), crouchingState, pos);

    /* make sure to end any other pending events - we rely on EV_ACTOR_MOVE not being active anymore */
    gi.EndEvents();

    /* everything ok, found valid route? */
    if (VectorCompare(pos, ent->pos)) {
        byte* stepAmount = NULL;
        int usedTUs = 0;
        /* no floor inventory at this point */
        FLOOR(ent) = NULL;

        while (numdv > 0) {
            /* A flag to see if we needed to change crouch state */
            int crouchFlag;
            const byte oldDir = ent->dir;
            int dvec;

            /* get next dvec */
            numdv--;
            dvec = dvtab[numdv];
            /* This is the direction to make the step into */
            dir = getDVdir(dvec);

            /* turn around first */
            status = G_ActorDoTurn(ent, dir);
            if (status & VIS_STOP) {
                autoCrouchRequired = false;
                if (ent->moveinfo.steps == 0)
                    usedTUs += TU_TURN;
                break;
            }

            if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv)) {
                /* don't autocrouch if new enemy becomes visible */
                autoCrouchRequired = false;
                /* if something appears on our route that didn't trigger a VIS_STOP, we have to
                 * send the turn event if this is our first step */
                if (oldDir != ent->dir && ent->moveinfo.steps == 0) {
                    G_EventActorTurn(ent);
                    usedTUs += TU_TURN;
                }
                break;
            }

            /* decrease TUs */
            div = gi.GetTUsForDirection(dir, G_IsCrouched(ent));
            if ((int) (usedTUs + div) > ent->TU)
                break;
            usedTUs += div;

            /* This is now a flag to indicate a change in crouching - we need this for
             * the stop in mid move call(s), because we need the updated entity position */
            crouchFlag = 0;
            /* Calculate the new position after the decrease in TUs, otherwise the game
             * remembers the false position if the time runs out */
            PosAddDV(ent->pos, crouchFlag, dvec);

            /* slower if crouched */
            if (G_IsCrouched(ent))
                ent->speed = ACTOR_SPEED_CROUCHED;
            else
                ent->speed = ACTOR_SPEED_NORMAL;
            ent->speed *= g_actorspeed->value;

            if (crouchFlag == 0) { /* No change in crouch */
                edict_t* clientAction;
                int contentFlags;
                vec3_t pointTrace;

                G_EdictCalcOrigin(ent);
                VectorCopy(ent->origin, pointTrace);
                pointTrace[2] += PLAYER_MIN;

                contentFlags = gi.PointContents(pointTrace);

                /* link it at new position - this must be done for every edict
                 * movement - to let the server know about it. */
                gi.LinkEdict(ent);

                /* Only the PHALANX team has these stats right now. */
                if (ent->chr.scoreMission) {
                    float truediv = gi.GetTUsForDirection(dir, 0);		/* regardless of crouching ! */
                    if (G_IsCrouched(ent))
                        ent->chr.scoreMission->movedCrouched += truediv;
                    else
                        ent->chr.scoreMission->movedNormal += truediv;
                }
                /* write the step to the net */
                G_WriteStep(ent, &stepAmount, dvec, contentFlags);

                /* check if player appears/perishes, seen from other teams */
                G_CheckVis(ent, true);

                /* check for anything appearing, seen by "the moving one" */
                status = G_CheckVisTeamAll(ent->team, false, ent);

                /* Set ent->TU because the reaction code relies on ent->TU being accurate. */
                G_ActorSetTU(ent, initTU - usedTUs);

                clientAction = ent->clientAction;
                oldState = ent->state;
                oldHP = ent->HP;
                /* check triggers at new position */
                if (G_TouchTriggers(ent)) {
                    if (!clientAction)
                        status |= VIS_STOP;
                }

                G_TouchSolids(ent, 10.0f);

                /* state has changed - maybe we walked on a trigger_hurt */
                if (oldState != ent->state)
                    status |= VIS_STOP;
                else if (oldHP != ent->HP)
                    status |= VIS_STOP;
            } else if (crouchFlag == 1) {
                /* Actor is standing */
                G_ClientStateChange(player, ent, STATE_CROUCHED, true);
            } else if (crouchFlag == -1) {
                /* Actor is crouching and should stand up */
                G_ClientStateChange(player, ent, STATE_CROUCHED, false);
            }

            /* check for reaction fire */
            if (G_ReactionFireOnMovement(ent)) {
                status |= VIS_STOP;

                autoCrouchRequired = false;
            }

            /* check for death */
            if (((oldHP != 0 && oldHP != ent->HP) || (oldState != ent->state)) && !G_IsDazed(ent)) {
                /** @todo Handle dazed via trigger_hurt */
                /* maybe this was due to rf - then the G_ActorDie was already called */
                if (!G_IsDead(ent)) {
                    G_CheckDeathOrKnockout(ent, NULL, NULL, oldHP - ent->HP);
                }
                return;
            }

            if (G_ActorShouldStopInMidMove(ent, status, dvtab, numdv - 1)) {
                /* don't autocrouch if new enemy becomes visible */
                autoCrouchRequired = false;
                break;
            }

            /* Restore ent->TU because the movement code relies on it not being modified! */
            G_ActorSetTU(ent, initTU);
        }

        /* submit the TUs / round down */
        if (g_notu != NULL && g_notu->integer)
            G_ActorSetTU(ent, initTU);
        else
            G_ActorSetTU(ent, initTU - usedTUs);

        G_SendStats(ent);

        /* end the move */
        G_GetFloorItems(ent);
        gi.EndEvents();
    }

    if (autoCrouchRequired) {
        /* toggle back to crouched state */
        G_ClientStateChange(player, ent, STATE_CROUCHED, true);
    }
}
Esempio n. 14
0
qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove)
{
	int			i, e;
	edict_t		*check, *block;
	vec3_t		mins, maxs;
	pushed_t	*p;
	vec3_t		org, org2, move2, forward, right, up;

	// clamp the move to 1/8 units, so the position will
	// be accurate for client side prediction
	for (i=0 ; i<3 ; i++)
	{
		float	temp;
		temp = move[i]*8.0;
		if (temp > 0.0)
			temp += 0.5;
		else
			temp -= 0.5;
		move[i] = 0.125 * (int)temp;
	}

	// find the bounding box
	for (i=0 ; i<3 ; i++)
	{
		mins[i] = pusher->absmin[i] + move[i];
		maxs[i] = pusher->absmax[i] + move[i];
	}

// we need this for pushing things later
	VectorSubtract (vec3_origin, amove, org);
	AngleVectors (org, forward, right, up);

// save the pusher's original position
	pushed_p->ent = pusher;
	VectorCopy (pusher->s.origin, pushed_p->origin);
	VectorCopy (pusher->s.angles, pushed_p->angles);
	if (pusher->client)
		pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];
	pushed_p++;

// move the pusher to it's final position
	VectorAdd (pusher->s.origin, move, pusher->s.origin);
	VectorAdd (pusher->s.angles, amove, pusher->s.angles);
	gi.linkentity (pusher);

// see if any solid entities are inside the final position
	check = g_edicts+1;
	for (e = 1; e < globals.num_edicts; e++, check++)
	{
		if (!check->inuse)
			continue;
		if (check->movetype == MOVETYPE_PUSH
		|| check->movetype == MOVETYPE_STOP
		|| check->movetype == MOVETYPE_NONE
		|| check->movetype == MOVETYPE_NOCLIP)
			continue;

		if (!check->area.prev)
			continue;		// not linked in anywhere

		// Ridah, dead monsters don't block door
		if (check->svflags & SVF_MONSTER && check->health <= 0)
		{
			//gi.dprintf( "SV_Push todo: Gib blocking characters\n");
			continue;
		}

		// Ridah, dead clients shouldn't block doors
		if (check->client && (check->health <= 0 || !check->solid))
			continue;

	// if the entity is standing on the pusher, it will definitely be moved
		if (check->groundentity != pusher)
		{
			// see if the ent needs to be tested
			if ( check->absmin[0] >= maxs[0]
			|| check->absmin[1] >= maxs[1]
			|| check->absmin[2] >= maxs[2]
			|| check->absmax[0] <= mins[0]
			|| check->absmax[1] <= mins[1]
			|| check->absmax[2] <= mins[2] )
				continue;

			// see if the ent's bbox is inside the pusher's final position
			if (!SV_TestEntityPosition (check))
				continue;
		}

		// Ridah, doors don't push
		if ((pusher->blocked == door_blocked) && !pusher->style && !(check->svflags & SVF_MONSTER))
		{
			// move it back
			VectorSubtract (pusher->s.origin, move, pusher->s.origin);
			VectorSubtract (pusher->s.angles, amove, pusher->s.angles);
			gi.linkentity (pusher);

			obstacle = check;
			return false;
		}

		if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher))
		{
			// move to a visible node
			if ((check->svflags & SVF_MONSTER) && !check->goal_ent)
			{
				extern void AI_FreeAndClearGoalEnt( edict_t *self );
				node_t *node;

				if (node = NAV_GetClosestNode( check, VIS_PARTIAL, false, false ))
				{
					check->goal_ent = G_Spawn();
					check->goal_ent->owner = check;

					VectorCopy( node->origin, check->goal_ent->s.origin );

					check->goal_ent->think = AI_FreeAndClearGoalEnt;
					check->goal_ent->nextthink = level.time + 3;
					check->goal_ent->dmg_radius = 0;		// get real close to it

					if (check->cast_info.move_runwalk)
						check->cast_info.currentmove = check->cast_info.move_runwalk;
					else
						check->cast_info.currentmove = check->cast_info.move_run;

				}
			}

			// move this entity
			pushed_p->ent = check;
			VectorCopy (check->s.origin, pushed_p->origin);
			VectorCopy (check->s.angles, pushed_p->angles);
			pushed_p++;

			// try moving the contacted entity 
			VectorAdd (check->s.origin, move, check->s.origin);
			if (check->client)
			{	// FIXME: doesn't rotate monsters?
				check->client->ps.pmove.delta_angles[YAW] += amove[YAW];
				// JOSEPH 5-APR-99
				if (pusher->touch)
					pusher->touch(pusher, check, 0, 0);
				// END JOSEPH
			}

			// figure movement due to the pusher's amove
			VectorSubtract (check->s.origin, pusher->s.origin, org);
			org2[0] = DotProduct (org, forward);
			org2[1] = -DotProduct (org, right);
			org2[2] = DotProduct (org, up);
			VectorSubtract (org2, org, move2);
			VectorAdd (check->s.origin, move2, check->s.origin);

			// may have pushed them off an edge
			if (check->groundentity != pusher)
				check->groundentity = NULL;

			block = SV_TestEntityPosition (check);
			if (!block)
			{	// pushed ok
				gi.linkentity (check);
				// impact?
				continue;
			}

			// if it is ok to leave in the old position, do it
			// this is only relevent for riding entities, not pushed
			// FIXME: this doesn't acount for rotation
			VectorSubtract (check->s.origin, move, check->s.origin);
			block = SV_TestEntityPosition (check);
			if (!block)
			{
				pushed_p--;
				continue;
			}
		}
		
		// save off the obstacle so we can call the block function
		obstacle = check;

		// move back any entities we already moved
		// go backwards, so if the same entity was pushed
		// twice, it goes back to the original position
		for (p=pushed_p-1 ; p>=pushed ; p--)
		{
			VectorCopy (p->origin, p->ent->s.origin);
			VectorCopy (p->angles, p->ent->s.angles);
			if (p->ent->client)
			{
				p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;
			}
			gi.linkentity (p->ent);
		}
		return false;
	}

//FIXME: is there a better way to handle this?
	// see if anything we moved has touched a trigger
	for (p=pushed_p-1 ; p>=pushed ; p--)
		G_TouchTriggers (p->ent);

	return true;
}
Esempio n. 15
0
/*
 * G_ClientThink
 *
 * This will be called once for each client frame, which will usually be a
 * couple times for each server frame.
 */
void G_ClientThink(g_edict_t *ent, user_cmd_t *ucmd) {
	g_client_t *client;
	g_edict_t *other;
	int i, j;

	g_level.current_entity = ent;
	client = ent->client;

	if (g_level.intermission_time) {
		client->ps.pmove.pm_type = PM_FREEZE;
		return;
	}

	if (client->chase_target) { // ensure chase is valid

		client->ps.pmove.pm_flags |= PMF_NO_PREDICTION;

		if (!client->chase_target->in_use
				|| client->chase_target->client->persistent.spectator) {

			other = client->chase_target;

			G_ChaseNext(ent);

			if (client->chase_target == other) { // no one to chase
				client->chase_target = NULL;
			}
		}
	}

	if (!client->chase_target) { // set up for pmove
		pm_move_t pm;

		client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;

		if (ent->move_type == MOVE_TYPE_NO_CLIP)
			client->ps.pmove.pm_type = PM_SPECTATOR;
		else if (ent->s.model1 != 255 || ent->dead)
			client->ps.pmove.pm_type = PM_DEAD;
		else
			client->ps.pmove.pm_type = PM_NORMAL;

		client->ps.pmove.gravity = g_level.gravity;

		memset(&pm, 0, sizeof(pm));
		pm.s = client->ps.pmove;

		for (i = 0; i < 3; i++) {
			pm.s.origin[i] = ent->s.origin[i] * 8.0;
			pm.s.velocity[i] = ent->velocity[i] * 8.0;
		}

		pm.cmd = *ucmd;

		pm.Trace = G_ClientMoveTrace; // adds default params
		pm.PointContents = gi.PointContents;

		// perform a pmove
		gi.Pmove(&pm);

		// save results of pmove
		client->ps.pmove = pm.s;

		for (i = 0; i < 3; i++) {
			ent->s.origin[i] = pm.s.origin[i] * 0.125;
			ent->velocity[i] = pm.s.velocity[i] * 0.125;
		}

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

		client->cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
		client->cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
		client->cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);

		// check for jump, play randomized sound
		if (ent->ground_entity && !pm.ground_entity && (pm.cmd.up >= 10)
				&& (pm.water_level == 0) && client->jump_time < g_level.time
				- 0.2) {

			vec3_t angles, forward, velocity;
			float speed;

			VectorSet(angles, 0.0, ent->s.angles[YAW], 0.0);
			AngleVectors(angles, forward, NULL, NULL);

			VectorCopy(ent->velocity, velocity);
			velocity[2] = 0.0;

			speed = VectorNormalize(velocity);

			if (DotProduct(velocity, forward) < 0.0 && speed > 200.0)
				G_SetAnimation(ent, ANIM_LEGS_JUMP2, true);
			else
				G_SetAnimation(ent, ANIM_LEGS_JUMP1, true);

			ent->s.event = EV_CLIENT_JUMP;
			client->jump_time = g_level.time;
		}

		ent->view_height = pm.view_height;
		ent->water_level = pm.water_level;
		ent->water_type = pm.water_type;
		ent->ground_entity = pm.ground_entity;

		if (ent->ground_entity)
			ent->ground_entity_link_count = ent->ground_entity->link_count;

		VectorCopy(pm.angles, client->angles);
		VectorCopy(pm.angles, client->ps.angles);

		gi.LinkEntity(ent);

		// touch jump pads, hurt brushes, etc..
		if (ent->move_type != MOVE_TYPE_NO_CLIP && ent->health > 0)
			G_TouchTriggers(ent);

		// touch other objects
		for (i = 0; i < pm.num_touch; i++) {

			other = pm.touch_ents[i];

			for (j = 0; j < i; j++)
				if (pm.touch_ents[j] == other)
					break;

			if (j != i)
				continue; // duplicated

			if (!other->touch)
				continue;

			other->touch(other, ent, NULL, NULL);
		}
	}

	client->old_buttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons |= client->buttons & ~client->old_buttons;

	// fire weapon if requested
	if (client->latched_buttons & BUTTON_ATTACK) {
		if (client->persistent.spectator) {

			client->latched_buttons = 0;

			if (client->chase_target) { // toggle chase camera
				client->chase_target = NULL;
				client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
			} else {
				G_ChaseTarget(ent);
			}
		} else if (client->weapon_think_time < g_level.time) {
			G_WeaponThink(ent);
		}
	}

	// update chase camera if being followed
	for (i = 1; i <= sv_max_clients->integer; i++) {

		other = g_game.edicts + i;

		if (other->in_use && other->client->chase_target == ent) {
			G_ChaseThink(other);
		}
	}

	G_ClientInventoryThink(ent);
}
Esempio n. 16
0
void SV_Physics_Step (edict_t *ent)
{
	qboolean	wasonground;
	qboolean	hitsound = false;
	float		*vel;
	float		speed, newspeed, control;
	float		friction;
	edict_t		*groundentity;
	int			mask;

	// airborn monsters should always check for ground
	if (!ent->groundentity)
		M_CheckGround (ent);

	groundentity = ent->groundentity;

	SV_CheckVelocity (ent);

	if (groundentity)
		wasonground = true;
	else
		wasonground = false;
		
	if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
		SV_AddRotationalFriction (ent);

	// add gravity except:
	//   flying monsters
	//   swimming monsters who are in the water
	if (! wasonground)
		if (!(ent->flags & FL_FLY))
			if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2)))
			{
				if (ent->velocity[2] < sv_gravity->value*-0.1)
					hitsound = true;
				if (ent->waterlevel == 0)
					SV_AddGravity (ent);
			}

	// friction for flying monsters that have been given vertical velocity
	if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
	{
		speed = fabs(ent->velocity[2]);
		control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
		friction = sv_friction/3;
		newspeed = speed - (FRAMETIME * control * friction);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		ent->velocity[2] *= newspeed;
	}

	// friction for flying monsters that have been given vertical velocity
	if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
	{
		speed = fabs(ent->velocity[2]);
		control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
		newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		ent->velocity[2] *= newspeed;
	}

	if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
	{
		// apply friction
		// let dead monsters who aren't completely onground slide
		if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY)))
			if (!(ent->health <= 0.0 && !M_CheckBottom(ent)))
			{
				vel = ent->velocity;
				speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
				if (speed)
				{
					friction = sv_friction;

					control = speed < sv_stopspeed->value ? sv_stopspeed->value : speed;
					newspeed = speed - FRAMETIME*control*friction;

					if (newspeed < 0)
						newspeed = 0;
					newspeed /= speed;

					vel[0] *= newspeed;
					vel[1] *= newspeed;
				}
			}

		if (ent->svflags & SVF_MONSTER)
			mask = MASK_MONSTERSOLID;
		else
			mask = MASK_SOLID;
		SV_FlyMove (ent, FRAMETIME, mask);

		gi.linkentity (ent);

// ========
// PGM - reset this every time they move. 
//       G_touchtriggers will set it back if appropriate
		ent->gravity = 1.0;
// ========
		
		G_TouchTriggers (ent);
		if (!ent->inuse)
			return;

		if (ent->groundentity)
			if (!wasonground)
				if (hitsound)
					gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0);
	}

	if(!ent->inuse)			// PGM g_touchtrigger free problem
		return;

// regular thinking
	SV_RunThink (ent);
}
Esempio n. 17
0
/*
==============
ClientEndFrame

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

	// Nico, flood protection
	if (level.time >= (ent->client->sess.nextReliableTime + 1000) &&
	    ent->client->sess.numReliableCmds) {
		ent->client->sess.numReliableCmds--;

		// Reset the threshold because they were good for a bit
		if (!ent->client->sess.numReliableCmds) {
			ent->client->sess.thresholdTime = 0;
		}
	}

	// Nico, update best speeds
	if (ent->client->sess.timerunActive) {
		float currentSpeed;

		currentSpeed = sqrt(ent->client->ps.velocity[0] * ent->client->ps.velocity[0] + ent->client->ps.velocity[1] * ent->client->ps.velocity[1]);

		// Nico, update overall max speed
		if (currentSpeed > ent->client->sess.overallMaxSpeed) {
			ent->client->sess.overallMaxSpeed = currentSpeed;
		}

		// Nico, update max speed of the current run
		if (currentSpeed > ent->client->sess.maxSpeed) {
			ent->client->sess.maxSpeed = currentSpeed;
		}
	}

	// used for informing of speclocked teams.
	// Zero out here and set only for certain specs
	ent->client->ps.powerups[PW_BLACKOUT] = 0;

	if ((ent->client->sess.sessionTeam == TEAM_SPECTATOR) || (ent->client->ps.pm_flags & PMF_LIMBO)) {       // JPW NERVE
		SpectatorClientEndFrame(ent);
		return;
	}

	// turn off any expired powerups
	// OSP -- range changed for MV
	for (i = 0 ; i < PW_NUM_POWERUPS ; ++i) {

		if (i == PW_FIRE ||                 // these aren't dependant on level.time
		    i == PW_ELECTRIC ||
		    i == PW_BREATHER ||
		    ent->client->ps.powerups[i] == 0            // OSP
		    || i == PW_OPS_CLASS_1
		    || i == PW_OPS_CLASS_2
		    || i == PW_OPS_CLASS_3

		    ) {

			continue;
		}
		// OSP -- If we're paused, update powerup timers accordingly.
		// Make sure we dont let stuff like CTF flags expire.
		if (ent->client->ps.powerups[i] < level.time) {
			ent->client->ps.powerups[i] = 0;
		}
	}

	//
	// If the end of unit layout is displayed, don't give
	// the player any normal movement attributes
	//

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

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

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

	ent->client->ps.stats[STAT_HEALTH] = ent->health;   // FIXME: get rid of ent->health...
	                                                    // Gordon: WHY? other ents use it.

	G_SetClientSound(ent);

	// set the latest infor

	// Ridah, fixes jittery zombie movement
	if (g_smoothClients.integer) {
		BG_PlayerStateToEntityStateExtraPolate(&ent->client->ps, &ent->s, level.time, qfalse);
	} else {
		BG_PlayerStateToEntityState(&ent->client->ps, &ent->s, qfalse);
	}

	// DHM - Nerve :: If it's been a couple frames since being revived, and props_frame_state
	//					wasn't reset, go ahead and reset it
	if (ent->props_frame_state >= 0 && ((level.time - ent->s.effect3Time) > 100)) {
		ent->props_frame_state = -1;
	}

	// DHM - Nerve :: Reset 'count2' for flamethrower
	if (!(ent->client->buttons & BUTTON_ATTACK)) {
		ent->count2 = 0;
	}
	// dhm

	// zinx - #280 - run touch functions here too, so movers don't have to wait
	// until the next ClientThink, which will be too late for some map
	// scripts (railgun)
	G_TouchTriggers(ent);

	// run entity scripting
	G_Script_ScriptRun(ent);

	// store the client's current position for antilag traces
	G_StoreClientPosition(ent);
}
Esempio n. 18
0
/*
================
G_RunMissile
================
*/
void G_RunMissile(gentity_t * ent)
{
	vec3_t          origin;
	trace_t         tr;
	int             passent;

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

	// if this missile bounced off an invulnerability sphere
	if(ent->target_ent)
	{
		passent = ent->target_ent->s.number;
	}
#ifdef MISSIONPACK
	// prox mines that left the owner bbox will attach to anything, even the owner
	else if(ent->s.weapon == WP_PROX_LAUNCHER && ent->count)
	{
		passent = ENTITYNUM_NONE;
	}
#endif
	else
	{
		// ignore interactions with the missile owner
		passent = ent->r.ownerNum;
	}

	// trace a line from the previous position to the current position
	trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, origin, passent, ent->clipmask);

	if(tr.startsolid || tr.allsolid)
	{
		// make sure the tr.entityNum is set to the entity we're stuck in
		trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, passent, ent->clipmask);
		tr.fraction = 0;
	}
	else
	{
		VectorCopy(tr.endpos, ent->r.currentOrigin);
	}

	trap_LinkEntity(ent);

	if(tr.fraction != 1)
	{
		// never explode or bounce on sky
		if(tr.surfaceFlags & SURF_NOIMPACT)
		{
			// If grapple, reset owner
			if(ent->parent && ent->parent->client && ent->parent->client->hook == ent)
			{
				ent->parent->client->hook = NULL;
			}
			G_FreeEntity(ent);
			return;
		}

		G_MissileImpact(ent, &tr);

		if(ent->s.eType != ET_PROJECTILE && ent->s.eType != ET_PROJECTILE2)
		{
			return;				// exploded
		}
	}
#ifdef MISSIONPACK
	// if the prox mine wasn't yet outside the player body
	if(ent->s.weapon == WP_PROX_LAUNCHER && !ent->count)
	{
		// check if the prox mine is outside the owner bbox
		trap_Trace(&tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ENTITYNUM_NONE, ent->clipmask);
		if(!tr.startsolid || tr.entityNum != ent->r.ownerNum)
		{
			ent->count = 1;
		}
	}
#endif

	// otty: added
	G_TouchTriggers(ent);

	// check think function after bouncing
	G_RunThink(ent);
}
Esempio n. 19
0
/*
==============
ClientThink

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

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

	client = ent->client;

	// don't think if the client is not yet connected (and thus not yet spawned in)
	if (client->pers.connected != CON_CONNECTED) 
	{
		return;
	}

	// mark the time, so the connection sprite can be removed
	ucmd = &ent->client->pers.cmd;

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

	if ( msec > 200 ) 
	{
		msec = 200;
	}

	if ( pmove_msec.integer < 8 ) 
	{
		trap_Cvar_Set("pmove_msec", "8");
	}
	else if (pmove_msec.integer > 33) 
	{
		trap_Cvar_Set("pmove_msec", "33");
	}

	if ( pmove_fixed.integer || client->pers.pmoveFixed ) 
	{
		ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
	}

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

	// spectators don't do much
	if ( G_IsClientSpectating ( client ) ) 
	{
		if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) 
		{
			return;
		}
		SpectatorThink( ent, ucmd );
		return;
	}

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

	if ( client->noclip ) 
	{
		client->ps.pm_type = PM_NOCLIP;
	} 
	else if ( client->ps.stats[STAT_HEALTH] <= 0 ) 
	{
		client->ps.pm_type = PM_DEAD;
	}
	else 
	{
		client->ps.pm_type = PM_NORMAL;
	}

	client->ps.gravity = g_gravity.value;

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

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

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

	pm.ps = &client->ps;
	pm.cmd = *ucmd;
	if ( pm.ps->pm_type == PM_DEAD ) 
	{
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
	}
	else if ( client->siameseTwin ) 
	{
		// Make sure we are still stuck, if so, clip through players.
		if ( G_IsClientSiameseTwin ( ent, client->siameseTwin ) )
		{
			pm.tracemask = MASK_PLAYERSOLID & ~(CONTENTS_BODY);
		}
		else
		{
			// Ok, we arent stuck anymore so we can clear the stuck flag.
			client->siameseTwin->client->siameseTwin = NULL;
			client->siameseTwin = NULL;

			pm.tracemask = MASK_PLAYERSOLID;
		}
	}
	else if ( ent->r.svFlags & SVF_BOT ) 
	{
		pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
	}
	else 
	{
		pm.tracemask = MASK_PLAYERSOLID;
	}
	pm.trace = trap_Trace;
	pm.pointcontents = trap_PointContents;
	pm.debugLevel = g_debugMove.integer;
	pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;

	pm.pmove_fixed = (bool)pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_msec = pmove_msec.integer;

	pm.animations = NULL;

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

	Pmove (&pm);

	G_UpdatePlayerStateScores ( ent );

	// save results of pmove
	if ( ent->client->ps.eventSequence != oldEventSequence ) 
	{
		ent->eventTime = level.time;
	}
	
	// See if the invulnerable flag should be removed for this client
	if ( ent->client->ps.eFlags & EF_INVULNERABLE )
	{
		if ( level.time - ent->client->invulnerableTime >= g_respawnInvulnerability.integer * 1000 )
		{
			ent->client->ps.eFlags &= ~EF_INVULNERABLE;
		}
	}

	if (g_smoothClients.integer)
	{
		BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, true );
	}
	else 
	{
		BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, true );
	}

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

	if ( !( ent->client->ps.eFlags & EF_FIRING ) ) 
	{
		client->fireHeld = false;		// for grapple
	}

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

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

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

	// Need to cache off the firemodes to the persitant data segment so they
	// are maintained across spectating and respawning
	memcpy ( ent->client->pers.firemode, ent->client->ps.firemode, sizeof(ent->client->ps.firemode ) );

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

	// Update the client animation info
	G_UpdateClientAnimations ( ent );

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

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

	// Update the clients anti-lag history
	G_UpdateClientAntiLag ( ent );

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

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

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

	// check for respawning
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) 
	{			
		// wait for the attack button to be pressed
		if ( level.time > client->respawnTime ) 
		{
			if ( g_forcerespawn.integer > 0 && 
				( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) 
			{
				respawn( ent );
				return;
			}

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

		return;
	}

	// perform once-a-second actions
	ClientTimerActions( ent, msec );
}
Esempio n. 20
0
/*
=================
SpectatorThink
NiceAss: Heavy modifications will be here for AQ2-like spectator mode and zcam!?
=================
*/
void SpectatorThink(gentity_t * ent, usercmd_t * ucmd)
{
	pmove_t pm;
	gclient_t *client;
	int clientNum;

	client = ent->client;
	clientNum = client - level.clients;

	if (client->sess.spectatorState == SPECTATOR_ZCAM) {
		client->ps.commandTime = ucmd->serverTime;
		camera_think(ent);
	}

	if (client->sess.spectatorState == SPECTATOR_FREE) {
		if (g_gametype.integer == GT_CTF && level.team_round_going &&
			(client->sess.savedTeam == TEAM_RED || client->sess.savedTeam == TEAM_BLUE))
			client->ps.pm_type = PM_FREEZE;
		else
			client->ps.pm_type = PM_SPECTATOR;
		client->ps.speed = 400;	// faster than normal

		// set up for pmove
		memset(&pm, 0, sizeof(pm));
		pm.ps = &client->ps;
		pm.cmd = *ucmd;
		pm.tracemask = 0;	// spectators can fly through bodies
		pm.trace = trap_Trace;
		pm.pointcontents = trap_PointContents;
		pm.predict = qtrue;
		// perform a pmove
		Pmove(&pm);
		// save results of pmove
		VectorCopy(client->ps.origin, ent->s.origin);

		G_TouchTriggers(ent);
		trap_UnlinkEntity(ent);
	}
// JBravo: Lets not allow bots to use any specmode other than FREE
	if (ent->r.svFlags & SVF_BOT)
		return;

	//Slicer - Changing this for aq2 way
	// Jump button cycles throught spectators
	if (client->sess.spectatorState == SPECTATOR_FOLLOW || client->sess.spectatorState == SPECTATOR_ZCAM) {
		if (ucmd->upmove >= 10) {
			if (!(client->ps.pm_flags & PMF_JUMP_HELD)) {
				client->ps.pm_flags |= PMF_JUMP_HELD;
				if (client->sess.spectatorState == SPECTATOR_ZCAM) {
					if (client->camera->mode == CAMERA_MODE_SWING)
						CameraSwingCycle(ent, 1);
				} else
					Cmd_FollowCycle_f(ent, 1);
			}
		} else
			client->ps.pm_flags &= ~PMF_JUMP_HELD;
	}

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

	if (g_gametype.integer == GT_CTF && client->sess.spectatorState == SPECTATOR_FREE &&
		(client->sess.savedTeam == TEAM_RED || client->sess.savedTeam == TEAM_BLUE))
		return;

	// Attack Button cycles throught free view, follow or zcam
	if ((ucmd->buttons & BUTTON_ATTACK) && !(client->oldbuttons & BUTTON_ATTACK)) {
		if (g_gametype.integer == GT_TEAMPLAY && g_RQ3_limchasecam.integer != 0 && client->sess.referee == 0 ) {
			if (!OKtoFollow(clientNum))
				return;
			if (client->sess.spectatorState != SPECTATOR_FOLLOW) {
				client->sess.spectatorState = SPECTATOR_FOLLOW;
				client->specMode = SPECTATOR_FOLLOW;
				client->ps.pm_flags |= PMF_FOLLOW;
				client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM;
			}
			return;
		} else if (client->sess.spectatorState == SPECTATOR_FREE && OKtoFollow(clientNum)) {
			client->sess.spectatorState = SPECTATOR_ZCAM;
			client->specMode = SPECTATOR_ZCAM;
			client->camera->mode = CAMERA_MODE_SWING;
			client->ps.stats[STAT_RQ3] |= RQ3_ZCAM;
			client->ps.pm_flags &= ~PMF_FOLLOW;
			CameraSwingCycle(ent, 1);
			RQ3_SpectatorMode(ent);
		} else if (client->sess.spectatorState == SPECTATOR_ZCAM && client->camera->mode == CAMERA_MODE_SWING
			   && OKtoFollow(clientNum)) {
			client->sess.spectatorState = SPECTATOR_ZCAM;
			client->specMode = SPECTATOR_ZCAM;
			client->camera->mode = CAMERA_MODE_FLIC;
			client->ps.stats[STAT_RQ3] |= RQ3_ZCAM;
			client->ps.pm_flags &= ~PMF_FOLLOW;
			CameraFlicBegin(ent);
			RQ3_SpectatorMode(ent);
		} else if (client->sess.spectatorState == SPECTATOR_ZCAM && OKtoFollow(clientNum)) {
			client->sess.spectatorState = SPECTATOR_FOLLOW;
			client->specMode = SPECTATOR_FOLLOW;
			client->ps.pm_flags |= PMF_FOLLOW;
			client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM;
			Cmd_FollowCycle_f(ent, 1);
			RQ3_SpectatorMode(ent);
		} else if (client->sess.spectatorState == SPECTATOR_FOLLOW) {
			client->sess.spectatorState = SPECTATOR_FREE;
			client->specMode = SPECTATOR_FREE;
			client->ps.pm_flags &= ~PMF_FOLLOW;
			client->ps.stats[STAT_RQ3] &= ~RQ3_ZCAM;
			StopFollowing(ent);
			RQ3_SpectatorMode(ent);
		}
	}
}
Esempio n. 21
0
/*
==============
ClientThink

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

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

	client = ent->client;

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

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

	client->lastUpdateFrame = level.framenum;

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

	if (pmove_msec.integer < 8) {
		trap_Cvar_Set("pmove_msec", "8");
	} else if (pmove_msec.integer > 33) {
		trap_Cvar_Set("pmove_msec", "33");
	}

	if (pmove_fixed.integer || client->pers.pmoveFixed) {
		ucmd->serverTime =
		    ((ucmd->serverTime + pmove_msec.integer - 1) / pmove_msec.integer) * pmove_msec.integer;
	}
	//
	// check for exiting intermission
	//
	if (level.intermissiontime) {
		ClientIntermissionThink(client);
		return;
	}
	// spectators don't do much
	if (client->sess.sessionTeam == TEAM_SPECTATOR) {
		if (client->sess.spectatorState == SPECTATOR_SCOREBOARD) {
			return;
		}
		SpectatorThink(ent, ucmd);
		return;
	}
	// check for inactivity timer, but never drop the local client of a non-dedicated server
	if (!ClientInactivityTimer(client)) {
		return;
	}
	// clear the rewards if time
	if (level.time > client->rewardTime) {
		client->ps.eFlags &=
		    ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND |
		      EF_AWARD_CAP);
	}

	if (client->noclip) {
		client->ps.pm_type = PM_NOCLIP;
	} else if (client->ps.stats[STAT_HEALTH] <= 0) {
		client->ps.pm_type = PM_DEAD;
	} else {
		client->ps.pm_type = PM_NORMAL;
	}

	client->ps.gravity = g_gravity.value;

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

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

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

	if (ent->flags & FL_FORCE_GESTURE) {
		ent->flags &= ~FL_FORCE_GESTURE;
		ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
	}
	//Elder: 3rb Code moved to bg_pmove.c (resides in PM_Weapon)

	pm.ps = &client->ps;
	pm.cmd = *ucmd;

	if (pm.ps->pm_type == PM_DEAD) {
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
	} else if (ent->r.svFlags & SVF_BOT) {
		pm.tracemask = MASK_PLAYERSOLID | CONTENTS_BOTCLIP;
	} else {
		pm.tracemask = MASK_PLAYERSOLID;
	}

// JBravo: fixing telefragging and shit during spawnig.  (Thanks NiceAss! :)
	if ((g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_TEAM) &&
	    ((ent->client->ps.stats[STAT_RQ3] & RQ3_PLAYERSOLID) != RQ3_PLAYERSOLID) && !level.lights_camera_action) {
		UnstickPlayer(ent);
	}
	if ((g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_TEAM) &&
	    ((ent->client->ps.stats[STAT_RQ3] & RQ3_PLAYERSOLID) != RQ3_PLAYERSOLID)) {
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
	}

	pm.trace = trap_Trace;
	pm.pointcontents = trap_PointContents;
	pm.debugLevel = g_debugMove.integer;
	pm.noFootsteps = (g_dmflags.integer & DF_NO_FOOTSTEPS) > 0;

	pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_msec = pmove_msec.integer;

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

// JBravo: setting lca in pm if appropriate
	if (g_RQ3_lca.integer == 1)
		pm.lca = qtrue;
	else
		pm.lca = qfalse;

	pm.predict = qtrue;

	Pmove(&pm);

	if ((pm.cmd.upmove > 10) &&
	    (pm.waterlevel == 0) &&
	    ent->s.groundEntityNum != ENTITYNUM_NONE && pm.ps->groundEntityNum == ENTITYNUM_NONE)
		bJumping = 1;

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

	BG_PlayerStateToEntityState(&ent->client->ps, &ent->s, qtrue);
	SendPendingPredictableEvents(&ent->client->ps);

	if (!(ent->client->ps.eFlags & EF_FIRING)) {
		client->fireHeld = qfalse;	// for grapple
	}
	// use the snapped origin for linking so it matches client predicted versions
	VectorCopy(ent->s.pos.trBase, ent->r.currentOrigin);

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

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

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

	// link entity now, after any personal teleporters have been used
	// JBravo: this call reactivates gibbed players.
	if (!ent->client->gibbed)
		trap_LinkEntity(ent);
	if (!ent->client->noclip) {
		G_TouchTriggers(ent);
	}
	// NOTE: now copy the exact origin over otherwise clients can be snapped into solid
	VectorCopy(ent->client->ps.origin, ent->r.currentOrigin);

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

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

	//Elder: someone added
	if (bJumping)
		JumpKick(ent);

	// save results of triggers and client events
	if (ent->client->ps.eventSequence != oldEventSequence) {
		ent->eventTime = level.time;
	}
	// swap and latch button actions
	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons |= client->buttons & ~client->oldbuttons;

	// check for respawning
	// JBravo: Lets make dead players into spectators.
	if (client->ps.stats[STAT_HEALTH] <= 0) {
		// wait for the attack button to be pressed
		if (level.time > client->respawnTime) {
			// forcerespawn is to prevent users from waiting out powerups
			if (g_forcerespawn.integer > 0 &&
			    (level.time - client->respawnTime) > g_forcerespawn.integer * 1000 &&
			    g_gametype.integer != GT_TEAMPLAY && g_gametype.integer != GT_CTF) {
				respawn(ent);
				return;
			}
			if ((g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_CTF) && level.time > client->respawnTime) {
				MakeSpectator(ent);
			}
			// pressing attack or use is the normal respawn method
			// JBravo: make'em spactate
			if (ucmd->buttons & (BUTTON_ATTACK | BUTTON_USE_HOLDABLE)) {
				if (g_gametype.integer == GT_TEAMPLAY || g_gametype.integer == GT_CTF) {
					MakeSpectator(ent);
				} else {
					respawn(ent);
				}
			}
		}
		return;
	}
// JBravo: Idle sounds
	if (g_RQ3_ppl_idletime.integer) {
		if (ucmd->forwardmove == 0 && ucmd->rightmove == 0) {
			if (client->idletime) {
				if (level.time >= client->idletime + (g_RQ3_ppl_idletime.integer * 1000)) {
					if (g_gametype.integer >= GT_TEAM && g_RQ3_idleaction.integer == 1 &&
						(ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
						trap_SendServerCommand( -1, va("print \"Removing %s^7 from his team for excessive Idling\n\"", 
								ent->client->pers.netname));
						trap_SendServerCommand(ent - g_entities, "stuff team none\n");
					} else if (g_gametype.integer >= GT_TEAM && g_RQ3_idleaction.integer == 2 &&
						(ent->client->sess.sessionTeam == TEAM_RED || ent->client->sess.sessionTeam == TEAM_BLUE)) {
						trap_SendServerCommand( -1, va("print \"Kicking %s^7 for excessive Idling\n\"", 
								ent->client->pers.netname));
						trap_DropClient(ent - g_entities, "Dropped due to excessive Idling");
					} else
						G_TempEntity(ent->r.currentOrigin, EV_INSANESOUND);
					client->idletime = 0;
				}
			} else {
				client->idletime = level.time;
			}
		} else {
			client->idletime = 0;
		}
	}
	// perform once-a-second actions
	ClientTimerActions(ent, msec);
}
Esempio n. 22
0
//FIXME since we need to test end position contents here, can we avoid doing
//it again later in catagorize position?
bool SV_movestep(edict_t *ent, vec3_t move, bool relink)
{
    float       dz;
    vec3_t      oldorg, neworg, end;
    trace_t     trace;
    int         i;
    float       stepsize;
    vec3_t      test;
    int         contents;

// try the move
    VectorCopy(ent->s.origin, oldorg);
    VectorAdd(ent->s.origin, move, neworg);

// flying monsters don't step up
    if (ent->flags & (FL_SWIM | FL_FLY)) {
        // try one move with vertical motion, then one without
        for (i = 0 ; i < 2 ; i++) {
            VectorAdd(ent->s.origin, move, neworg);
            if (i == 0 && ent->enemy) {
                if (!ent->goalentity)
                    ent->goalentity = ent->enemy;
                dz = ent->s.origin[2] - ent->goalentity->s.origin[2];
                if (ent->goalentity->client) {
                    if (dz > 40)
                        neworg[2] -= 8;
                    if (!((ent->flags & FL_SWIM) && (ent->waterlevel < 2)))
                        if (dz < 30)
                            neworg[2] += 8;
                } else {
                    if (dz > 8)
                        neworg[2] -= 8;
                    else if (dz > 0)
                        neworg[2] -= dz;
                    else if (dz < -8)
                        neworg[2] += 8;
                    else
                        neworg[2] += dz;
                }
            }
            trace = gi.trace(ent->s.origin, ent->mins, ent->maxs, neworg, ent, MASK_MONSTERSOLID);

            // fly monsters don't enter water voluntarily
            if (ent->flags & FL_FLY) {
                if (!ent->waterlevel) {
                    test[0] = trace.endpos[0];
                    test[1] = trace.endpos[1];
                    test[2] = trace.endpos[2] + ent->mins[2] + 1;
                    contents = gi.pointcontents(test);
                    if (contents & MASK_WATER)
                        return false;
                }
            }

            // swim monsters don't exit water voluntarily
            if (ent->flags & FL_SWIM) {
                if (ent->waterlevel < 2) {
                    test[0] = trace.endpos[0];
                    test[1] = trace.endpos[1];
                    test[2] = trace.endpos[2] + ent->mins[2] + 1;
                    contents = gi.pointcontents(test);
                    if (!(contents & MASK_WATER))
                        return false;
                }
            }

            if (trace.fraction == 1) {
                VectorCopy(trace.endpos, ent->s.origin);
                if (relink) {
                    gi.linkentity(ent);
                    G_TouchTriggers(ent);
                }
                return true;
            }

            if (!ent->enemy)
                break;
        }

        return false;
    }

// push down from a step height above the wished position
    if (!(ent->monsterinfo.aiflags & AI_NOSTEP))
        stepsize = STEPSIZE;
    else
        stepsize = 1;

    neworg[2] += stepsize;
    VectorCopy(neworg, end);
    end[2] -= stepsize * 2;

    trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);

    if (trace.allsolid)
        return false;

    if (trace.startsolid) {
        neworg[2] -= stepsize;
        trace = gi.trace(neworg, ent->mins, ent->maxs, end, ent, MASK_MONSTERSOLID);
        if (trace.allsolid || trace.startsolid)
            return false;
    }


    // don't go in to water
    if (ent->waterlevel == 0) {
        test[0] = trace.endpos[0];
        test[1] = trace.endpos[1];
        test[2] = trace.endpos[2] + ent->mins[2] + 1;
        contents = gi.pointcontents(test);

        if (contents & MASK_WATER)
            return false;
    }

    if (trace.fraction == 1) {
        // if monster had the ground pulled out, go ahead and fall
        if (ent->flags & FL_PARTIALGROUND) {
            VectorAdd(ent->s.origin, move, ent->s.origin);
            if (relink) {
                gi.linkentity(ent);
                G_TouchTriggers(ent);
            }
            ent->groundentity = NULL;
            return true;
        }

        return false;       // walked off an edge
    }

// check point traces down for dangling corners
    VectorCopy(trace.endpos, ent->s.origin);

    if (!M_CheckBottom(ent)) {
        if (ent->flags & FL_PARTIALGROUND) {
            // entity had floor mostly pulled out from underneath it
            // and is trying to correct
            if (relink) {
                gi.linkentity(ent);
                G_TouchTriggers(ent);
            }
            return true;
        }
        VectorCopy(oldorg, ent->s.origin);
        return false;
    }

    if (ent->flags & FL_PARTIALGROUND) {
        ent->flags &= ~FL_PARTIALGROUND;
    }
    ent->groundentity = trace.ent;
    ent->groundentity_linkcount = trace.ent->linkcount;

// the move is ok
    if (relink) {
        gi.linkentity(ent);
        G_TouchTriggers(ent);
    }
    return true;
}
Esempio n. 23
0
/*
==============
ClientThink

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

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

	client = ent->client;

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

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

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

	if ( pmove_msec.integer < 8 ) {
		trap_Cvar_Set("pmove_msec", "8");
	}
	else if (pmove_msec.integer > 33) {
		trap_Cvar_Set("pmove_msec", "33");
	}

	if ( pmove_fixed.integer || client->pers.pmoveFixed ) {
		ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer-1) / pmove_msec.integer) * pmove_msec.integer;
		//if (ucmd->serverTime - client->ps.commandTime <= 0)
		//	return;
	}

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

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

	if (ent && ent->client && (ent->client->ps.eFlags & EF_INVULNERABLE))
	{
		if (ent->client->invulnerableTimer <= level.time)
		{
			ent->client->ps.eFlags &= ~EF_INVULNERABLE;
		}
	}

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

	// clear the rewards if time
	if ( level.time > client->rewardTime ) {
		client->ps.eFlags &= ~(EF_AWARD_IMPRESSIVE | EF_AWARD_EXCELLENT | EF_AWARD_GAUNTLET | EF_AWARD_ASSIST | EF_AWARD_DEFEND | EF_AWARD_CAP );
	}

	if ( client->noclip ) {
		client->ps.pm_type = PM_NOCLIP;
	} else if ( client->ps.eFlags & EF_DISINTEGRATION ) {
		client->ps.pm_type = PM_NOCLIP;
	} else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		client->ps.pm_type = PM_DEAD;
	} else {
		if (client->ps.forceGripChangeMovetype)
		{
			client->ps.pm_type = client->ps.forceGripChangeMovetype;
		}
		else
		{
			client->ps.pm_type = PM_NORMAL;
		}
	}

	client->ps.gravity = g_gravity.value;

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

	if (ent->client->ps.duelInProgress)
	{
		gentity_t *duelAgainst = &g_entities[ent->client->ps.duelIndex];

		//Keep the time updated, so once this duel ends this player can't engage in a duel for another
		//10 seconds. This will give other people a chance to engage in duels in case this player wants
		//to engage again right after he's done fighting and someone else is waiting.
		ent->client->ps.fd.privateDuelTime = level.time + 10000;

		if (ent->client->ps.duelTime < level.time)
		{
			//Bring out the sabers
			if (ent->client->ps.weapon == WP_SABER && ent->client->ps.saberHolstered &&
				ent->client->ps.duelTime)
			{
				if (!saberOffSound || !saberOnSound)
				{
					saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav");
					saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav");
				}

				ent->client->ps.saberHolstered = qfalse;
				G_Sound(ent, CHAN_AUTO, saberOnSound);

				G_AddEvent(ent, EV_PRIVATE_DUEL, 2);

				ent->client->ps.duelTime = 0;
			}

			if (duelAgainst && duelAgainst->client && duelAgainst->inuse &&
				duelAgainst->client->ps.weapon == WP_SABER && duelAgainst->client->ps.saberHolstered &&
				duelAgainst->client->ps.duelTime)
			{
				if (!saberOffSound || !saberOnSound)
				{
					saberOffSound = G_SoundIndex("sound/weapons/saber/saberoffquick.wav");
					saberOnSound = G_SoundIndex("sound/weapons/saber/saberon.wav");
				}

				duelAgainst->client->ps.saberHolstered = qfalse;
				G_Sound(duelAgainst, CHAN_AUTO, saberOnSound);

				G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 2);

				duelAgainst->client->ps.duelTime = 0;
			}
		}
		else
		{
			client->ps.speed = 0;
			client->ps.basespeed = 0;
			ucmd->forwardmove = 0;
			ucmd->rightmove = 0;
			ucmd->upmove = 0;
		}

		if (!duelAgainst || !duelAgainst->client || !duelAgainst->inuse ||
			duelAgainst->client->ps.duelIndex != ent->s.number)
		{
			ent->client->ps.duelInProgress = 0;
			G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
		}
		else if (duelAgainst->health < 1 || duelAgainst->client->ps.stats[STAT_HEALTH] < 1)
		{
			ent->client->ps.duelInProgress = 0;
			duelAgainst->client->ps.duelInProgress = 0;

			G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
			G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0);

			//Winner gets full health.. providing he's still alive
			if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0)
			{
				if (ent->health < ent->client->ps.stats[STAT_MAX_HEALTH])
				{
					ent->client->ps.stats[STAT_HEALTH] = ent->health = ent->client->ps.stats[STAT_MAX_HEALTH];
				}

				if (g_spawnInvulnerability.integer)
				{
					ent->client->ps.eFlags |= EF_INVULNERABLE;
					ent->client->invulnerableTimer = level.time + g_spawnInvulnerability.integer;
				}
			}

			/*
			trap_SendServerCommand( ent-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) );
			trap_SendServerCommand( duelAgainst-g_entities, va("print \"%s %s\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER")) );
			*/
			//Private duel announcements are now made globally because we only want one duel at a time.
			if (ent->health > 0 && ent->client->ps.stats[STAT_HEALTH] > 0)
			{
				trap_SendServerCommand( -1, va("cp \"%s %s %s!\n\"", ent->client->pers.netname, G_GetStripEdString("SVINGAME", "PLDUELWINNER"), duelAgainst->client->pers.netname) );
			}
			else
			{ //it was a draw, because we both managed to die in the same frame
				trap_SendServerCommand( -1, va("cp \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELTIE")) );
			}
		}
		else
		{
			vec3_t vSub;
			float subLen = 0;

			VectorSubtract(ent->client->ps.origin, duelAgainst->client->ps.origin, vSub);
			subLen = VectorLength(vSub);

			if (subLen >= 1024)
			{
				ent->client->ps.duelInProgress = 0;
				duelAgainst->client->ps.duelInProgress = 0;

				G_AddEvent(ent, EV_PRIVATE_DUEL, 0);
				G_AddEvent(duelAgainst, EV_PRIVATE_DUEL, 0);

				trap_SendServerCommand( -1, va("print \"%s\n\"", G_GetStripEdString("SVINGAME", "PLDUELSTOP")) );
			}
		}
	}

	/*
	if ( client->ps.powerups[PW_HASTE] ) {
		client->ps.speed *= 1.3;
	}
	*/

	if (client->ps.usingATST && ent->health > 0)
	{ //we have special shot clip boxes as an ATST
		ent->r.contents |= CONTENTS_NOSHOT;
		ATST_ManageDamageBoxes(ent);
	}
	else
	{
		ent->r.contents &= ~CONTENTS_NOSHOT;
		client->damageBoxHandle_Head = 0;
		client->damageBoxHandle_RLeg = 0;
		client->damageBoxHandle_LLeg = 0;
	}

	//rww - moved this stuff into the pmove code so that it's predicted properly
	//BG_AdjustClientSpeed(&client->ps, &client->pers.cmd, level.time);

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

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

	if ( ent->flags & FL_FORCE_GESTURE ) {
		ent->flags &= ~FL_FORCE_GESTURE;
		ent->client->pers.cmd.buttons |= BUTTON_GESTURE;
	}

	if (ent->client && ent->client->ps.fallingToDeath &&
		(level.time - FALL_FADE_TIME) > ent->client->ps.fallingToDeath)
	{ //die!
		player_die(ent, ent, ent, 100000, MOD_FALLING);
		respawn(ent);
		ent->client->ps.fallingToDeath = 0;

		G_MuteSound(ent->s.number, CHAN_VOICE); //stop screaming, because you are dead!
	}

	if (ent->client->ps.otherKillerTime > level.time &&
		ent->client->ps.groundEntityNum != ENTITYNUM_NONE &&
		ent->client->ps.otherKillerDebounceTime < level.time)
	{
		ent->client->ps.otherKillerTime = 0;
		ent->client->ps.otherKiller = ENTITYNUM_NONE;
	}
	else if (ent->client->ps.otherKillerTime > level.time &&
		ent->client->ps.groundEntityNum == ENTITYNUM_NONE)
	{
		if (ent->client->ps.otherKillerDebounceTime < (level.time + 100))
		{
			ent->client->ps.otherKillerDebounceTime = level.time + 100;
		}
	}

//	WP_ForcePowersUpdate( ent, msec, ucmd); //update any active force powers
//	WP_SaberPositionUpdate(ent, ucmd); //check the server-side saber point, do apprioriate server-side actions (effects are cs-only)

	if ((ent->client->pers.cmd.buttons & BUTTON_USE) && ent->client->ps.useDelay < level.time)
	{
		TryUse(ent);
		ent->client->ps.useDelay = level.time + 100;
	}

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

	pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_msec = pmove_msec.integer;

	pm.animations = bgGlobalAnimations;//NULL;

	pm.gametype = g_gametype.integer;

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

	if (level.intermissionQueued != 0 && g_singlePlayer.integer) {
		if ( level.time - level.intermissionQueued >= 1000  ) {
			pm.cmd.buttons = 0;
			pm.cmd.forwardmove = 0;
			pm.cmd.rightmove = 0;
			pm.cmd.upmove = 0;
			if ( level.time - level.intermissionQueued >= 2000 && level.time - level.intermissionQueued <= 2500 ) {
				trap_SendConsoleCommand( EXEC_APPEND, "centerview\n");
			}
			ent->client->ps.pm_type = PM_SPINTERMISSION;
		}
	}

	for ( i = 0 ; i < MAX_CLIENTS ; i++ )
	{
		if (g_entities[i].inuse && g_entities[i].client)
		{
			pm.bgClients[i] = &g_entities[i].client->ps;
		}
	}

	if (ent->client->ps.saberLockTime > level.time)
	{
		gentity_t *blockOpp = &g_entities[ent->client->ps.saberLockEnemy];

		if (blockOpp && blockOpp->inuse && blockOpp->client)
		{
			vec3_t lockDir, lockAng;

			//VectorClear( ent->client->ps.velocity );
			VectorSubtract( blockOpp->r.currentOrigin, ent->r.currentOrigin, lockDir );
			//lockAng[YAW] = vectoyaw( defDir );
			vectoangles(lockDir, lockAng);
			SetClientViewAngle( ent, lockAng );
		}

		if ( ( ent->client->buttons & BUTTON_ATTACK ) && ! ( ent->client->oldbuttons & BUTTON_ATTACK ) )
		{
			ent->client->ps.saberLockHits++;
		}
		if (ent->client->ps.saberLockHits > 2)
		{
			if (!ent->client->ps.saberLockAdvance)
			{
				ent->client->ps.saberLockHits -= 3;
			}
			ent->client->ps.saberLockAdvance = qtrue;
		}
	}
	else
	{
		ent->client->ps.saberLockFrame = 0;
		//check for taunt
		if ( (pm.cmd.generic_cmd == GENCMD_ENGAGE_DUEL) && (g_gametype.integer == GT_TOURNAMENT) )
		{//already in a duel, make it a taunt command
			pm.cmd.buttons |= BUTTON_GESTURE;
		}
	}

	Pmove (&pm);

	if (pm.checkDuelLoss)
	{
		if (pm.checkDuelLoss > 0 && pm.checkDuelLoss <= MAX_CLIENTS)
		{
			gentity_t *clientLost = &g_entities[pm.checkDuelLoss-1];

			if (clientLost && clientLost->inuse && clientLost->client && Q_irand(0, 40) > clientLost->health)
			{
				vec3_t attDir;
				VectorSubtract(ent->client->ps.origin, clientLost->client->ps.origin, attDir);
				VectorNormalize(attDir);

				VectorClear(clientLost->client->ps.velocity);
				clientLost->client->ps.forceHandExtend = HANDEXTEND_NONE;
				clientLost->client->ps.forceHandExtendTime = 0;

				gGAvoidDismember = 1;
				G_Damage(clientLost, ent, ent, attDir, clientLost->client->ps.origin, 9999, DAMAGE_NO_PROTECTION, MOD_SABER);

				if (clientLost->health < 1)
				{
					gGAvoidDismember = 2;
					G_CheckForDismemberment(clientLost, clientLost->client->ps.origin, 999, (clientLost->client->ps.legsAnim&~ANIM_TOGGLEBIT));
				}

				gGAvoidDismember = 0;
			}
		}

		pm.checkDuelLoss = 0;
	}

	switch(pm.cmd.generic_cmd)
	{
	case 0:
		break;
	case GENCMD_SABERSWITCH:
		Cmd_ToggleSaber_f(ent);
		break;
	case GENCMD_ENGAGE_DUEL:
		if ( g_gametype.integer == GT_TOURNAMENT )
		{//already in a duel, made it a taunt command
		}
		else
		{
			Cmd_EngageDuel_f(ent);
		}
		break;
	case GENCMD_FORCE_HEAL:
		ForceHeal(ent);
		break;
	case GENCMD_FORCE_SPEED:
		ForceSpeed(ent, 0);
		break;
	case GENCMD_FORCE_THROW:
		ForceThrow(ent, qfalse);
		break;
	case GENCMD_FORCE_PULL:
		ForceThrow(ent, qtrue);
		break;
	case GENCMD_FORCE_DISTRACT:
		ForceTelepathy(ent);
		break;
	case GENCMD_FORCE_RAGE:
		ForceRage(ent);
		break;
	case GENCMD_FORCE_PROTECT:
		ForceProtect(ent);
		break;
	case GENCMD_FORCE_ABSORB:
		ForceAbsorb(ent);
		break;
	case GENCMD_FORCE_HEALOTHER:
		ForceTeamHeal(ent);
		break;
	case GENCMD_FORCE_FORCEPOWEROTHER:
		ForceTeamForceReplenish(ent);
		break;
	case GENCMD_FORCE_SEEING:
		ForceSeeing(ent);
		break;
	case GENCMD_USE_SEEKER:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SEEKER)) &&
			G_ItemUsable(&ent->client->ps, HI_SEEKER) )
		{
			ItemUse_Seeker(ent);
			G_AddEvent(ent, EV_USE_ITEM0+HI_SEEKER, 0);
			ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SEEKER);
		}
		break;
	case GENCMD_USE_FIELD:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SHIELD)) &&
			G_ItemUsable(&ent->client->ps, HI_SHIELD) )
		{
			ItemUse_Shield(ent);
			G_AddEvent(ent, EV_USE_ITEM0+HI_SHIELD, 0);
			ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SHIELD);
		}
		break;
	case GENCMD_USE_BACTA:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_MEDPAC)) &&
			G_ItemUsable(&ent->client->ps, HI_MEDPAC) )
		{
			ItemUse_MedPack(ent);
			G_AddEvent(ent, EV_USE_ITEM0+HI_MEDPAC, 0);
			ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_MEDPAC);
		}
		break;
	case GENCMD_USE_ELECTROBINOCULARS:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) &&
			G_ItemUsable(&ent->client->ps, HI_BINOCULARS) )
		{
			ItemUse_Binoculars(ent);
			if (ent->client->ps.zoomMode == 0)
			{
				G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1);
			}
			else
			{
				G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2);
			}
		}
		break;
	case GENCMD_ZOOM:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_BINOCULARS)) &&
			G_ItemUsable(&ent->client->ps, HI_BINOCULARS) )
		{
			ItemUse_Binoculars(ent);
			if (ent->client->ps.zoomMode == 0)
			{
				G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 1);
			}
			else
			{
				G_AddEvent(ent, EV_USE_ITEM0+HI_BINOCULARS, 2);
			}
		}
		break;
	case GENCMD_USE_SENTRY:
		if ( (ent->client->ps.stats[STAT_HOLDABLE_ITEMS] & (1 << HI_SENTRY_GUN)) &&
			G_ItemUsable(&ent->client->ps, HI_SENTRY_GUN) )
		{
			ItemUse_Sentry(ent);
			G_AddEvent(ent, EV_USE_ITEM0+HI_SENTRY_GUN, 0);
			ent->client->ps.stats[STAT_HOLDABLE_ITEMS] &= ~(1 << HI_SENTRY_GUN);
		}
		break;
	case GENCMD_SABERATTACKCYCLE:
		Cmd_SaberAttackCycle_f(ent);
		break;
	default:
		break;
	}

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

	if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
		client->fireHeld = qfalse;		// for grapple
	}

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

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

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

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

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

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

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

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

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

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

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

	// Did we kick someone in our pmove sequence?
	if (client->ps.forceKickFlip)
	{
		gentity_t *faceKicked = &g_entities[client->ps.forceKickFlip-1];

		if (faceKicked && faceKicked->client && (!OnSameTeam(ent, faceKicked) || g_friendlyFire.integer) &&
			(!faceKicked->client->ps.duelInProgress || faceKicked->client->ps.duelIndex == ent->s.number) &&
			(!ent->client->ps.duelInProgress || ent->client->ps.duelIndex == faceKicked->s.number))
		{
			if ( faceKicked && faceKicked->client && faceKicked->health && faceKicked->takedamage )
			{//push them away and do pain
				vec3_t oppDir;
				int strength = (int)VectorNormalize2( client->ps.velocity, oppDir );

				strength *= 0.05;

				VectorScale( oppDir, -1, oppDir );

				G_Damage( faceKicked, ent, ent, oppDir, client->ps.origin, strength, DAMAGE_NO_ARMOR, MOD_MELEE );

				if ( faceKicked->client->ps.weapon != WP_SABER ||
					 faceKicked->client->ps.fd.saberAnimLevel < FORCE_LEVEL_3 ||
					 (!BG_SaberInAttack(faceKicked->client->ps.saberMove) && !PM_SaberInStart(faceKicked->client->ps.saberMove) && !PM_SaberInReturn(faceKicked->client->ps.saberMove) && !PM_SaberInTransition(faceKicked->client->ps.saberMove)) )
				{
					if (faceKicked->health > 0 &&
						faceKicked->client->ps.stats[STAT_HEALTH] > 0 &&
						faceKicked->client->ps.forceHandExtend != HANDEXTEND_KNOCKDOWN)
					{
						if (Q_irand(1, 10) <= 3)
						{ //only actually knock over sometimes, but always do velocity hit
							faceKicked->client->ps.forceHandExtend = HANDEXTEND_KNOCKDOWN;
							faceKicked->client->ps.forceHandExtendTime = level.time + 1100;
							faceKicked->client->ps.forceDodgeAnim = 0; //this toggles between 1 and 0, when it's 1 we should play the get up anim
						}

						faceKicked->client->ps.otherKiller = ent->s.number;
						faceKicked->client->ps.otherKillerTime = level.time + 5000;
						faceKicked->client->ps.otherKillerDebounceTime = level.time + 100;

						faceKicked->client->ps.velocity[0] = oppDir[0]*(strength*40);
						faceKicked->client->ps.velocity[1] = oppDir[1]*(strength*40);
						faceKicked->client->ps.velocity[2] = 200;
					}
				}

				G_Sound( faceKicked, CHAN_AUTO, G_SoundIndex( va("sound/weapons/melee/punch%d", Q_irand(1, 4)) ) );
			}
		}

		client->ps.forceKickFlip = 0;
	}

	// check for respawning
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		// wait for the attack button to be pressed
		if ( level.time > client->respawnTime && !gDoSlowMoDuel ) {
			// forcerespawn is to prevent users from waiting out powerups
			if ( g_forcerespawn.integer > 0 && 
				( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 ) {
				respawn( ent );
				return;
			}
		
			// pressing attack or use is the normal respawn method
			if ( ucmd->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) ) {
				respawn( ent );
			}
		}
		else if (gDoSlowMoDuel)
		{
			client->respawnTime = level.time + 1000;
		}
		return;
	}

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

	G_UpdateClientBroadcasts ( ent );
}
Esempio n. 24
0
/*
=================
SpectatorThink
=================
*/
void SpectatorThink(gentity_t *ent, usercmd_t *ucmd) {
	pmove_t   pm;
	gclient_t *client;

	client = ent->client;

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

		if (client->ps.sprintExertTime) {
			client->ps.speed *= 3;  // (SA) allow sprint in free-cam mode
		}

		// OSP - dead players are frozen too, in a timeout
		if (client->ps.pm_flags & PMF_LIMBO) {
			client->ps.pm_type = PM_FREEZE;
		} else if (client->noclip) {
			client->ps.pm_type = PM_NOCLIP;
		}

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

		Pmove(&pm);   // JPW NERVE

		// Rafael - Activate
		// Ridah, made it a latched event (occurs on keydown only)
		if (client->latched_buttons & BUTTON_ACTIVATE) {
			Cmd_Activate_f(ent);
		}

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

		G_TouchTriggers(ent);
		trap_UnlinkEntity(ent);
	}

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

//----(SA)	added
	client->oldwbuttons = client->wbuttons;
	client->wbuttons    = ucmd->wbuttons;

	// attack button cycles through spectators
	if ((client->buttons & BUTTON_ATTACK) && !(client->oldbuttons & BUTTON_ATTACK)) {
		Cmd_FollowCycle_f(ent, 1);
	} else if (
		(client->sess.sessionTeam == TEAM_SPECTATOR) &&   // don't let dead team players do free fly
		(client->sess.spectatorState == SPECTATOR_FOLLOW) &&
		(((client->buttons & BUTTON_ACTIVATE) &&
		  !(client->oldbuttons & BUTTON_ACTIVATE)) || ucmd->upmove > 0)) {
		// code moved to StopFollowing
		StopFollowing(ent);
	}
}
Esempio n. 25
0
/*
=============
SV_Physics_NewToss

Toss, bounce, and fly movement. When on ground and no velocity, do nothing. With velocity,
slide.
=============
*/
void SV_Physics_NewToss (edict_t *ent)
{
	trace_t		trace;
	vec3_t		move;
//	float		backoff;
	edict_t		*slave;
	qboolean	wasinwater;
	qboolean	isinwater;
	qboolean	wasonground;
	float		speed, newspeed;
	vec3_t		old_origin;
//	float		firstmove;
//	int			mask;

	// regular thinking
	SV_RunThink (ent);

	// if not a team captain, so movement will be handled elsewhere
	if ( ent->flags & FL_TEAMSLAVE)
		return;

	if (ent->groundentity)
		wasonground = true;
	else
		wasonground = false;
	
	wasinwater = ent->waterlevel;

	// find out what we're sitting on.
	VectorCopy (ent->s.origin, move);
	move[2] -= 0.25;
	trace = gi.trace (ent->s.origin, ent->mins, ent->maxs, move, ent, ent->clipmask);
	if(ent->groundentity && ent->groundentity->inuse)
		ent->groundentity = trace.ent;
	else
		ent->groundentity = NULL;

	// if we're sitting on something flat and have no velocity of our own, return.
	if (ent->groundentity && (trace.plane.normal[2] == 1.0) && 
		!ent->velocity[0] && !ent->velocity[1] && !ent->velocity[2])
	{
		return;
	}

	// store the old origin
	VectorCopy (ent->s.origin, old_origin);

	SV_CheckVelocity (ent);

	// add gravity
	SV_AddGravity (ent);

	if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
		SV_AddRotationalFriction (ent);

	// add friction
	speed = VectorLength(ent->velocity);
	if(ent->waterlevel)				// friction for water movement
	{
		newspeed = speed - (sv_waterfriction * 6 * ent->waterlevel);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		VectorScale (ent->velocity, newspeed, ent->velocity);
	}
	else if (!ent->groundentity)	// friction for air movement
	{
		newspeed = speed - ((sv_friction));
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		VectorScale (ent->velocity, newspeed, ent->velocity);
	}
	else	// use ground friction
	{
		newspeed = speed - (sv_friction * 6);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		VectorScale (ent->velocity, newspeed, ent->velocity);
	}

	SV_FlyMove (ent, FRAMETIME, ent->clipmask);
	gi.linkentity (ent);

	G_TouchTriggers (ent);

// check for water transition
	wasinwater = (ent->watertype & MASK_WATER);
	ent->watertype = gi.pointcontents (ent->s.origin);
	isinwater = ent->watertype & MASK_WATER;

	if (isinwater)
		ent->waterlevel = 1;
	else
		ent->waterlevel = 0;

	if (!wasinwater && isinwater)
		gi.positioned_sound (old_origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);
	else if (wasinwater && !isinwater)
		gi.positioned_sound (ent->s.origin, g_edicts, CHAN_AUTO, gi.soundindex("misc/h2ohit1.wav"), 1, 1, 0);

// move teamslaves
	for (slave = ent->teamchain; slave; slave = slave->teamchain)
	{
		VectorCopy (ent->s.origin, slave->s.origin);
		gi.linkentity (slave);
	}
}
Esempio n. 26
0
/*
==============
ClientThink

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

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

	// don't think if the client is not yet connected (and thus not yet spawned in)
	if (client->pers.connected != CON_CONNECTED) {
		return;
	}

	if (ent->s.eFlags & EF_MOUNTEDTANK) {
		client->pmext.centerangles[YAW]   = ent->tagParent->r.currentAngles[YAW];
		client->pmext.centerangles[PITCH] = ent->tagParent->r.currentAngles[PITCH];
	}

	// mark the time, so the connection sprite can be removed
	ucmd = &ent->client->pers.cmd;

	ent->client->ps.identifyClient = ucmd->identClient;     // NERVE - SMF

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

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

	// Nico, pmove_fixed
	if (client->pers.pmoveFixed) {
		ucmd->serverTime = ((ucmd->serverTime + pmove_msec.integer - 1) / pmove_msec.integer) * pmove_msec.integer;
	}

	if (client->wantsscore) {
		G_SendScore(ent);
		client->wantsscore = qfalse;
	}

	// check for inactivity timer, but never drop the local client of a non-dedicated server
	// OSP - moved here to allow for spec inactivity checks as well
	if (!ClientInactivityTimer(client)) {
		return;
	}

	if (!(ucmd->flags & 0x01) || ucmd->forwardmove || ucmd->rightmove || ucmd->upmove || ucmd->wbuttons || ucmd->doubleTap) {
		ent->r.svFlags &= ~(SVF_SELF_PORTAL_EXCLUSIVE | SVF_SELF_PORTAL);
	}

	// spectators don't do much
	// DHM - Nerve :: In limbo use SpectatorThink
	// suburb, check for noclip not to override client->ps.speed
	if ((client->sess.sessionTeam == TEAM_SPECTATOR || client->ps.pm_flags & PMF_LIMBO) && !client->noclip) {
		SpectatorThink(ent, ucmd);
		return;
	}

	if (client->ps.eFlags & EF_VIEWING_CAMERA) {
		ucmd->buttons     = 0;
		ucmd->forwardmove = 0;
		ucmd->rightmove   = 0;
		ucmd->upmove      = 0;
		ucmd->wbuttons    = 0;
		ucmd->doubleTap   = 0;

		// freeze player
		client->ps.pm_type = PM_FREEZE;
	} else if (client->noclip) {
		client->ps.pm_type = PM_NOCLIP;
	} else if (client->ps.stats[STAT_HEALTH] <= 0) {
		client->ps.pm_type = PM_DEAD;
	} else {
		client->ps.pm_type = PM_NORMAL;
	}

	client->ps.aiState = AISTATE_COMBAT;
	client->ps.gravity = DEFAULT_GRAVITY;
	client->ps.speed   = DEFAULT_SPEED;

	if (client->speedScale) {                // Goalitem speed scale
		client->ps.speed *= (client->speedScale * 0.01);
	}

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

	client->currentAimSpreadScale = (float)client->ps.aimSpreadScale / 255.0;

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

	pm.ps        = &client->ps;
	pm.pmext     = &client->pmext;
	pm.character = client->pers.character;
	pm.cmd       = *ucmd;
	pm.oldcmd    = client->pers.oldcmd;
	// MrE: always use capsule for AI and player
	pm.trace = trap_TraceCapsule;

	// Nico, ghost players
	pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
	if (pm.ps->pm_type == PM_DEAD) {
		pm.ps->eFlags |= EF_DEAD;
	} else if (pm.ps->pm_type == PM_SPECTATOR) {
		pm.trace = trap_TraceCapsuleNoEnts;
	}
	// Nico, end of ghost players

	//DHM - Nerve :: We've gone back to using normal bbox traces
	pm.pointcontents = trap_PointContents;
	pm.debugLevel    = g_debugMove.integer;
	pm.noFootsteps   = qfalse;

	// Nico, pmove_fixed
	// pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_fixed = client->pers.pmoveFixed;
	pm.pmove_msec  = pmove_msec.integer;

	// suburb, Noclip speed scale
	pm.noclipSpeed = client->pers.noclipSpeed;

	// Nico, game physics
	pm.physics = physics.integer;

	pm.isTimerun        = isTimerun.integer;
	pm.timerunActive    = client->sess.timerunActive;
	pm.timerunStartTime = client->sess.timerunStartTime + 500;

	// Nico, store logins status in pmove
	if (client->sess.logged) {
		pm.isLogged = 1;
	} else {
		pm.isLogged = 0;
	}

	pm.noWeapClips = qfalse;

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

	// NERVE - SMF
	pm.ltChargeTime       = level.lieutenantChargeTime[client->sess.sessionTeam - 1];
	pm.soldierChargeTime  = level.soldierChargeTime[client->sess.sessionTeam - 1];
	pm.engineerChargeTime = level.engineerChargeTime[client->sess.sessionTeam - 1];
	pm.medicChargeTime    = level.medicChargeTime[client->sess.sessionTeam - 1];
	// -NERVE - SMF

	client->pmext.airleft = ent->client->airOutTime - level.time;

	pm.covertopsChargeTime = level.covertopsChargeTime[client->sess.sessionTeam - 1];

	// Gordon: bit hacky, stop the slight lag from client -> server even on locahost, switching back to the weapon you were holding
	//			and then back to what weapon you should have, became VERY noticible for the kar98/carbine + gpg40, esp now i've added the
	//			animation locking
	if (level.time - client->pers.lastSpawnTime < 1000) {
		pm.cmd.weapon = client->ps.weapon;
	}

	Pmove(&pm);

	// Gordon: thx to bani for this
	// ikkyo - fix leaning players bug
	VectorCopy(client->ps.velocity, ent->s.pos.trDelta);
	SnapVector(ent->s.pos.trDelta);
	// end

	// server cursor hints
	if (ent->lastHintCheckTime < level.time) {
		G_CheckForCursorHints(ent);

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

	// DHM - Nerve :: Set animMovetype to 1 if ducking
	if (ent->client->ps.pm_flags & PMF_DUCKED) {
		ent->s.animMovetype = 1;
	} else {
		ent->s.animMovetype = 0;
	}

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

	// Ridah, fixes jittery zombie movement
	if (g_smoothClients.integer) {
		BG_PlayerStateToEntityStateExtraPolate(&ent->client->ps, &ent->s, level.time, qfalse);
	} else {
		BG_PlayerStateToEntityState(&ent->client->ps, &ent->s, qfalse);
	}

	if (!(ent->client->ps.eFlags & EF_FIRING)) {
		client->fireHeld = qfalse;      // for grapple
	}

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

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

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

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

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

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

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

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

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

	//----(SA)	added
	client->oldwbuttons      = client->wbuttons;
	client->wbuttons         = ucmd->wbuttons;
	client->latched_wbuttons = client->wbuttons & ~client->oldwbuttons;

	// suburb, return here for noclippers in spec to avoid following checks
	if (client->sess.sessionTeam == TEAM_SPECTATOR && client->noclip) {
		return;
	}

	// Rafael - Activate
	// Ridah, made it a latched event (occurs on keydown only)
	if (client->latched_buttons & BUTTON_ACTIVATE) {
		Cmd_Activate_f(ent);
	}

	if (g_entities[ent->client->ps.identifyClient].team != ent->team ||
	    !g_entities[ent->client->ps.identifyClient].client) {
		ent->client->ps.identifyClient = -1;
	}

	// check for respawning
	if (client->ps.stats[STAT_HEALTH] <= 0) {
		// Nico, forcing respawn
		limbo(ent);

		return;
	}

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

	// Nico, check ping
	if (client->ps.ping > MAX_PLAYER_PING) {
		if (!client->pers.loadKillNeeded) {
			CP(va("cpm \"%s^w: ^1Too high ping detected, load or kill required.\n\"", GAME_VERSION_COLORED));
			// suburb, prevent trigger bug
			client->pers.loadKillNeeded = qtrue;
		}
	}

	// Nico, pmove_fixed
	if (!client->pers.pmoveFixed) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you can not use pmove_fixed 0.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "pmoveon");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, check rate
	if (client->pers.rate < MIN_PLAYER_RATE_VALUE || client->pers.rate > MAX_PLAYER_RATE_VALUE) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= rate <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_RATE_VALUE, MAX_PLAYER_RATE_VALUE));
		trap_SendServerCommand(ent - g_entities, "resetRate");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, check snaps (unsigned int)
	if (client->pers.snaps > MAX_PLAYER_SNAPS_VALUE) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= snaps <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_SNAPS_VALUE, MAX_PLAYER_SNAPS_VALUE));
		trap_SendServerCommand(ent - g_entities, "resetSnaps");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, check timenudge
	if (client->pers.clientTimeNudge != FORCED_PLAYER_TIMENUDGE_VALUE) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_timenudge %d.\n\"", GAME_VERSION_COLORED, FORCED_PLAYER_TIMENUDGE_VALUE));
		trap_SendServerCommand(ent - g_entities, "resetTimeNudge");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, check maxpackets
	if (client->pers.clientMaxPackets < MIN_PLAYER_MAX_PACKETS_VALUE || client->pers.clientMaxPackets > MAX_PLAYER_MAX_PACKETS_VALUE) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= cl_maxpackets <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_MAX_PACKETS_VALUE, MAX_PLAYER_MAX_PACKETS_VALUE));
		trap_SendServerCommand(ent - g_entities, "resetMaxPackets");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, check max FPS
	if (client->pers.maxFPS < MIN_PLAYER_FPS_VALUE || client->pers.maxFPS > MAX_PLAYER_FPS_VALUE) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use %d <= com_maxfps <= %d.\n\"", GAME_VERSION_COLORED, MIN_PLAYER_FPS_VALUE, MAX_PLAYER_FPS_VALUE));
		trap_SendServerCommand(ent - g_entities, "resetMaxFPS");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// suburb, force yawspeed 0
	if (client->pers.yawspeed != 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_yawspeed 0.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "resetYawspeed");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// suburb, force pitchspeed 0
	if (client->pers.pitchspeed != 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cl_pitchspeed 0.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "resetPitchspeed");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, force auto demo record in cup mode
	if (g_cupMode.integer != 0 && client->pers.autoDemo == 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_autoDemo 1.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "autoDemoOn");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// Nico, force hide me in cup mode
	if (g_cupMode.integer != 0 && client->pers.hideme == 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_hideMe 1.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "hideMeOn");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// suburb, force CGaz if disabled on server
	if (g_disableCGaz.integer != 0 && client->pers.cgaz != 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_drawCGaz 0.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "CGazOff");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// suburb, force snapping hud if disabled on server or logged in
	if ((g_disableSnappingHUD.integer != 0 || (client->sess.logged && (client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES))) && client->pers.snapping != 0) {
		CP(va("cpm \"%s^w: ^1You were removed from teams because you must use cg_drawVelocitySnapping 0.\n\"", GAME_VERSION_COLORED));
		trap_SendServerCommand(ent - g_entities, "SnappingOff");
		SetTeam(ent, "s", -1, -1, qfalse);
	}

	// suburb, prevent pronebug & wallbug
	counter = 0;

	for (i = 0; i < 3; ++i) {
		if (client->pers.oldPosition[i] == (int) pm.ps->origin[i]) {
			counter++;
		}
	}

	Zaccel = (int) pm.ps->velocity[2] - client->pers.oldZvelocity;
	speed  = sqrt(pm.ps->velocity[0] * pm.ps->velocity[0] + pm.ps->velocity[1] * pm.ps->velocity[1]);

	if (client->sess.logged && ((!client->sess.timerunActive && speed > BUGGING_MAX_SPEED && counter == 3) || // prevent accelerating in brushes
	                            (pm.ps->eFlags & EF_PRONE && Zaccel > -6 && Zaccel < 0 && client->ps.groundEntityNum == ENTITYNUM_NONE && !client->pers.isTouchingJumppad))) { // prevent accelerating on steep slopes
		if (!client->pers.buggedLastFrame) { // only do something the second frame not to break jumppads
			client->pers.buggedLastFrame = qtrue;
			return;
		}
		if (!client->pers.loadKillNeeded) {
			CP(va("cpm \"%s^w: ^1Bugging detected, load or kill required.\n\"", GAME_VERSION_COLORED));
			client->pers.loadKillNeeded = qtrue;
		}
	}

	// checking acceleration in brushes every frame would break corner skimming
	if (level.time - client->pers.lastBuggingCheck > BUGGING_CHECK_FREQUENCY) {
		for (i = 0; i < 3; ++i) {
			client->pers.oldPosition[i]   = (int) pm.ps->origin[i];
			client->pers.lastBuggingCheck = level.time;
		}
	}

	client->pers.oldZvelocity    = (int) pm.ps->velocity[2];
	client->pers.buggedLastFrame = qfalse;
}
Esempio n. 27
0
/*
============
SV_Push

Objects need to be moved back on a failed push,
otherwise riders would continue to slide.
============
*/
qboolean SV_Push (edict_t *pusher, vec3_t move, vec3_t amove)
{
	int			i, e;
	edict_t		*check, *block;
	vec3_t		mins, maxs;
	pushed_t	*p;
	vec3_t		org, org2, move2, forward, right, up;

	// clamp the move to 1/8 units, so the position will
	// be accurate for client side prediction
	for (i=0 ; i<3 ; i++)
	{
		float	temp;
		temp = move[i]*8.0;
		if (temp > 0.0)
			temp += 0.5;
		else
			temp -= 0.5;
		move[i] = 0.125 * (int)temp;
	}

	// find the bounding box
	for (i=0 ; i<3 ; i++)
	{
		mins[i] = pusher->absmin[i] + move[i];
		maxs[i] = pusher->absmax[i] + move[i];
	}

// we need this for pushing things later
	VectorSubtract (vec3_origin, amove, org);
	AngleVectors (org, forward, right, up);

// save the pusher's original position
	pushed_p->ent = pusher;
	VectorCopy (pusher->s.origin, pushed_p->origin);
	VectorCopy (pusher->s.angles, pushed_p->angles);
	if (pusher->client)
		pushed_p->deltayaw = pusher->client->ps.pmove.delta_angles[YAW];
	pushed_p++;

// move the pusher to it's final position
	VectorAdd (pusher->s.origin, move, pusher->s.origin);
	VectorAdd (pusher->s.angles, amove, pusher->s.angles);
	gi.linkentity (pusher);

// see if any solid entities are inside the final position
	check = g_edicts+1;
	for (e = 1; e < globals.num_edicts; e++, check++)
	{
		if (!check->inuse)
			continue;
		if (check->movetype == MOVETYPE_PUSH
		|| check->movetype == MOVETYPE_STOP
		|| check->movetype == MOVETYPE_NONE
		|| check->movetype == MOVETYPE_NOCLIP)
			continue;

		if (!check->area.prev)
			continue;		// not linked in anywhere

	// if the entity is standing on the pusher, it will definitely be moved
		if (check->groundentity != pusher)
		{
			// see if the ent needs to be tested
			if ( check->absmin[0] >= maxs[0]
			|| check->absmin[1] >= maxs[1]
			|| check->absmin[2] >= maxs[2]
			|| check->absmax[0] <= mins[0]
			|| check->absmax[1] <= mins[1]
			|| check->absmax[2] <= mins[2] )
				continue;

			// see if the ent's bbox is inside the pusher's final position
			if (!SV_TestEntityPosition (check))
				continue;
		}

		if ((pusher->movetype == MOVETYPE_PUSH) || (check->groundentity == pusher))
		{
			// move this entity
			pushed_p->ent = check;
			VectorCopy (check->s.origin, pushed_p->origin);
			VectorCopy (check->s.angles, pushed_p->angles);
			pushed_p++;

			// try moving the contacted entity 
			VectorAdd (check->s.origin, move, check->s.origin);
			if (check->client)
			{	// FIXME: doesn't rotate monsters?
				check->client->ps.pmove.delta_angles[YAW] += amove[YAW];
			}

			// figure movement due to the pusher's amove
			VectorSubtract (check->s.origin, pusher->s.origin, org);
			org2[0] = DotProduct (org, forward);
			org2[1] = -DotProduct (org, right);
			org2[2] = DotProduct (org, up);
			VectorSubtract (org2, org, move2);
			VectorAdd (check->s.origin, move2, check->s.origin);

			// may have pushed them off an edge
			if (check->groundentity != pusher)
				check->groundentity = NULL;

			block = SV_TestEntityPosition (check);
			if (!block)
			{	// pushed ok
				gi.linkentity (check);
				// impact?
				continue;
			}

			// if it is ok to leave in the old position, do it
			// this is only relevent for riding entities, not pushed
			// FIXME: this doesn't acount for rotation
			VectorSubtract (check->s.origin, move, check->s.origin);
			block = SV_TestEntityPosition (check);
			if (!block)
			{
				pushed_p--;
				continue;
			}
		}
		
		// save off the obstacle so we can call the block function
		obstacle = check;

		// move back any entities we already moved
		// go backwards, so if the same entity was pushed
		// twice, it goes back to the original position
		for (p=pushed_p-1 ; p>=pushed ; p--)
		{
			VectorCopy (p->origin, p->ent->s.origin);
			VectorCopy (p->angles, p->ent->s.angles);
			if (p->ent->client)
			{
				p->ent->client->ps.pmove.delta_angles[YAW] = p->deltayaw;
			}
			gi.linkentity (p->ent);
		}
		return false;
	}

//FIXME: is there a better way to handle this?
	// see if anything we moved has touched a trigger
	for (p=pushed_p-1 ; p>=pushed ; p--)
		G_TouchTriggers (p->ent);

	return true;
}
Esempio n. 28
0
void SV_Physics_Step (edict_t *ent)
{
	qboolean	wasonground;
	qboolean	hitsound = false;
	float		*vel;
	float		speed, newspeed, control;
	float		friction;
	edict_t		*groundentity;
	int			mask;
	int			retval;
	vec3_t		oldpos;

	// BEGIN:	Xatrix/Ridah
	vec3_t	old_vel;
	// END:		Xatrix/Ridah

	// Joseph
	if (ent->fallerflag)
	{
		// Fix if sitting off center
		think_checkedges(ent);
	}
	
	// airborn monsters should always check for ground
	if (!ent->groundentity)
		M_CheckGround (ent);

	groundentity = ent->groundentity;

	SV_CheckVelocity (ent);

	if (groundentity)
		wasonground = true;
	else
		wasonground = false;
		
	if (ent->avelocity[0] || ent->avelocity[1] || ent->avelocity[2])
		SV_AddRotationalFriction (ent);

	// add gravity except:
	//   flying monsters
	//   swimming monsters who are in the water
	if (! wasonground)
		if (!(ent->flags & FL_FLY))
			if (!((ent->flags & FL_SWIM) && (ent->waterlevel > 2)))
			{
				if (ent->velocity[2] < sv_gravity->value*-0.1)
					hitsound = true;
// Ridah, 1-may-99, disabled this to prevent guys getting stuck in water
//				if (ent->waterlevel == 0)
					SV_AddGravity (ent);
			}
/*
	// friction for flying monsters that have been given vertical velocity
	if ((ent->flags & FL_FLY) && (ent->velocity[2] != 0))
	{
		speed = fabs(ent->velocity[2]);
		control = speed < sv_stopspeed ? sv_stopspeed : speed;
		friction = sv_friction/3;
		newspeed = speed - (FRAMETIME * control * friction);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		ent->velocity[2] *= newspeed;
	}
*/
	// friction for flying monsters that have been given vertical velocity
	if ((ent->flags & FL_SWIM) && (ent->velocity[2] != 0))
	{
		speed = fabs(ent->velocity[2]);
		control = speed < sv_stopspeed ? sv_stopspeed : speed;
		newspeed = speed - (FRAMETIME * control * sv_waterfriction * ent->waterlevel);
		if (newspeed < 0)
			newspeed = 0;
		newspeed /= speed;
		ent->velocity[2] *= newspeed;
	}

	if (ent->velocity[2] || ent->velocity[1] || ent->velocity[0])
	{
		// apply friction
		// let dead monsters who aren't completely onground slide
		if ((wasonground) || (ent->flags & (FL_SWIM|FL_FLY)))
			if (!(ent->health <= 0.0 && !M_CheckBottom(ent)))
			{
				vel = ent->velocity;
				speed = sqrt(vel[0]*vel[0] +vel[1]*vel[1]);
				if (speed)
				{
					friction = sv_friction;

					control = speed < sv_stopspeed ? sv_stopspeed : speed;
					newspeed = speed - FRAMETIME*control*friction;

					if (newspeed < 0)
						newspeed = 0;
					newspeed /= speed;

					vel[0] *= newspeed;
					vel[1] *= newspeed;
				}
			}

		// BEGIN:	Xatrix/Ridah
		// JOSEPH 26-APR-99
		if ((ent->svflags & SVF_MONSTER) || (ent->monsterprop))
		// END JOSEPH
		{
//			if (ent->cast_info.aiflags & AI_PLAYERCLIP)
				mask = MASK_PLAYERSOLID | CONTENTS_MONSTERCLIP;
//			else
//				mask = MASK_MONSTERSOLID;
		}
		else
			mask = MASK_SOLID;

		VectorCopy (ent->velocity, old_vel);
		// END:		Xatrix/Ridah

		VectorCopy (ent->s.origin, oldpos );

		retval = SV_FlyMove (ent, FRAMETIME, mask);

#if 0	// leave this here for now.

		// Ridah, HACK... sometimes they get stuck, we should debug this properly when we get the time
		if (!ValidBoxAtLoc( ent->s.origin, ent->mins, ent->maxs, ent, MASK_SOLID ))
		{	// move back to old position and clear velocity
			int iter=0;

			VectorCopy (oldpos, ent->s.origin);
			VectorClear (ent->velocity);

			// find a good position
			while (!ValidBoxAtLoc( ent->s.origin, ent->mins, ent->maxs, ent, MASK_SOLID ))
			{
				VectorAdd( ent->s.origin, tv((random()-0.5) * 64, (random()-0.5) * 64, (random()-0.5) * 64), ent->s.origin );

				if (++iter > 10)
					break;
			}

//			if (iter <= 4)
//			{	// make sure they're on the ground
//				M_droptofloor( ent );
//			}

			goto exit_vel_check;		// get out of here?
		}
#endif

		// BEGIN:	Xatrix/Ridah
		if (!ent->groundentity || (ent->flags & FL_FLY))
		{
			node_t	*land_node;

			// Ridah, prevent guys getting stuck trying to jump
			if (VectorDistance( ent->s.origin, oldpos ) < 1 && !ent->groundentity && (ent->last_onground < (level.time - 2)))
			{
				ent->velocity[0] = crandom() * 300;
				ent->velocity[1] = crandom() * 300;

				if (ent->velocity[2] < -200)
					ent->velocity[2] = -200;

				ent->velocity[2] += random() * 350;

				ent->nav_data.goal_index = 0;
				ent->last_onground = level.time;
			}

			if (ent->velocity[2] > 80 && retval != 20)
			{	// while rising, maintain XY velocity
				ent->velocity[0] = old_vel[0];
				ent->velocity[1] = old_vel[1];
			}

			// see if we've gone passed the landing position
			if (	!(ent->flags & FL_FLY)
				&&	(retval == -1) && (ent->nav_data.goal_index)
				&&	(land_node = level.node_data->nodes[ent->nav_data.goal_index-1]))
//				&&	(land_node->node_type & NODE_LANDING))
			{
				vec3_t	unit_vel, goal_dir, goal_vec;
				float	vel_scale, dist;

				VectorSubtract( land_node->origin, ent->s.origin, goal_vec );
				goal_vec[2] = 0;
				dist = VectorNormalize2( goal_vec, goal_dir );

				if (dist > 16)
				{

					VectorCopy( ent->velocity, unit_vel );
					unit_vel[2] = 0;
					vel_scale = VectorNormalize( unit_vel );

					if (DotProduct( unit_vel, goal_dir ) < 0.8)
					{	// we've either gone passed, or need some correction
						vec3_t new_pos;
						float	old_z;

						if (VectorLength( goal_vec ) < 40)
						{
							new_pos[0] = land_node->origin[0];
							new_pos[1] = land_node->origin[1];
							new_pos[2] = ent->s.origin[2];

							if (ValidBoxAtLoc( new_pos, ent->mins, ent->maxs, ent, MASK_PLAYERSOLID | MASK_MONSTERSOLID ))
							{	// move there, it's safe, and clear velocity
								VectorCopy( new_pos, ent->s.origin );
								ent->velocity[0] = ent->velocity[1] = 0;

								goto exit_vel_check;
							}
						}

						// we need to adjust our velocity
						if (land_node->origin[2] < (ent->s.origin[2] - 64))
						{
							old_z = ent->velocity[2];
							VectorScale( goal_dir, vel_scale, ent->velocity );
							ent->velocity[2] = old_z;
						}
						
					}

				}
			}	

			if (	(ent->flags & FL_FLY)
				&&	((land_node = level.node_data->nodes[ent->nav_data.goal_index-1]) || ((ent->flags &= ~FL_FLY) && false))
				/*&&	(land_node->node_type & NODE_LANDING)*/)
			{	// if climbing ladder, and we're reached the landing position, stop

				// Ridah, 8-jun-99, make sure dog's don't climb ladders
				if (!ent->gender)
				{
					goto abort_climb;
				}

				if (ent->s.origin[2] > land_node->origin[2])
				{
//gi.dprintf( "-> end of climb\n" );
//					VectorSubtract( land_node->origin, ent->s.origin, ent->velocity );
					AngleVectors( ent->s.angles, ent->velocity, NULL, NULL );
					ent->velocity[2] = 0;
					VectorNormalize( ent->velocity );
					VectorScale( ent->velocity, 96, ent->velocity );

					ent->velocity[2] = 200;

					ent->flags &= ~FL_FLY;
					ent->nav_data.goal_index = 0;		// look for a new node
					ent->nav_data.cache_node = -1;

					if (ent->cast_info.move_end_climb)
						ent->cast_info.currentmove = ent->cast_info.move_end_climb;
				}
				else
				{
					trace_t tr;
					vec3_t	end, goal_vec;

					VectorSubtract( land_node->origin, ent->s.origin, goal_vec );

					ent->velocity[0] = goal_vec[0];
					ent->velocity[1] = goal_vec[1];
					ent->velocity[2] = 120;

					// if another character is above us, abort
					VectorCopy( ent->s.origin, end );
					end[2] += 128;
					tr = gi.trace( ent->s.origin, ent->mins, ent->maxs, end, ent, MASK_PLAYERSOLID | MASK_MONSTERSOLID );
					
					if ((tr.fraction < 1) && (tr.ent->svflags & SVF_MONSTER))
					{

abort_climb:

						AngleVectors( ent->s.angles, goal_vec, NULL, NULL );
						VectorScale( goal_vec, -64, ent->velocity );
						ent->flags &= ~FL_FLY;
						ent->nav_data.goal_index = 0;

						if (ent->cast_info.move_end_climb)
						{
							ent->cast_info.currentmove = ent->cast_info.move_end_climb;
						}
						else if (ent->cast_info.move_jump)
						{
							ent->cast_info.currentmove = ent->cast_info.move_jump;
						}
					}
					else if (ent->s.origin[2] > (land_node->origin[2] - 48))
					{	// we're near the top, stopping climbing anim
//gi.dprintf( "near end of climb\n" );
						if (ent->cast_info.move_end_climb)
							ent->cast_info.currentmove = ent->cast_info.move_end_climb;

						// add some forward momentum
						AngleVectors( ent->s.angles, goal_vec, NULL, NULL );
						VectorMA( ent->velocity, 64, goal_vec, ent->velocity );
					}

				}

			}
		}

exit_vel_check:

		// END:		Xatrix/Ridah

		gi.linkentity (ent);
		G_TouchTriggers (ent);

// Note to Ryan: we can't use this because we are playing specific sounds elsewhere
/*
		if (ent->groundentity)
			if (!wasonground)
				if (hitsound)
					// BEGIN:	Xatrix/Ridah/Navigator/03-apr-1998
					if (!(ent->cast_info.move_run))
					// END:		Xatrix/Ridah/Navigator/03-apr-1998
						gi.sound (ent, 0, gi.soundindex("world/land.wav"), 1, 1, 0);
*/
	}

// regular thinking
	SV_RunThink (ent);
}
Esempio n. 29
0
/*
==============
ClientThink

This will be called once for each client frame, which will
usually be a couple times for each server frame.
==============
*/
void ClientThink (edict_t *ent, usercmd_t *ucmd)
{
	gclient_t	*client;
	edict_t	*other;
	int		i, j;
	pmove_t	pm;

	level.current_entity = ent;
	client = ent->client;

	if (level.intermissiontime)
	{
		client->ps.pmove.pm_type = PM_FREEZE;
		// can exit intermission after five seconds
		if (level.time > level.intermissiontime + 5.0 
			&& (ucmd->buttons & BUTTON_ANY) )
			level.exitintermission = true;
		return;
	}

	pm_passent = ent;

	if (ent->client->chase_target) {

		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);

	} else {

		// set up for pmove
		memset (&pm, 0, sizeof(pm));

		if (ent->movetype == MOVETYPE_NOCLIP)
			client->ps.pmove.pm_type = PM_SPECTATOR;
		else if (ent->s.modelindex != 255)
			client->ps.pmove.pm_type = PM_GIB;
		else if (ent->deadflag)
			client->ps.pmove.pm_type = PM_DEAD;
		else
			client->ps.pmove.pm_type = PM_NORMAL;

		client->ps.pmove.gravity = sv_gravity->value;
		pm.s = client->ps.pmove;

		for (i=0 ; i<3 ; i++)
		{
			pm.s.origin[i] = ent->s.origin[i]*8;
			pm.s.velocity[i] = ent->velocity[i]*8;
		}

		if (memcmp(&client->old_pmove, &pm.s, sizeof(pm.s)))
		{
			pm.snapinitial = true;
	//		gi.dprintf ("pmove changed!\n");
		}

		pm.cmd = *ucmd;

		pm.trace = PM_trace;	// adds default parms
		pm.pointcontents = gi.pointcontents;

		// perform a pmove
		gi.Pmove (&pm);

		// save results of pmove
		client->ps.pmove = pm.s;
		client->old_pmove = pm.s;

		for (i=0 ; i<3 ; i++)
		{
			ent->s.origin[i] = pm.s.origin[i]*0.125;
			ent->velocity[i] = pm.s.velocity[i]*0.125;
		}

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

		client->resp.cmd_angles[0] = SHORT2ANGLE(ucmd->angles[0]);
		client->resp.cmd_angles[1] = SHORT2ANGLE(ucmd->angles[1]);
		client->resp.cmd_angles[2] = SHORT2ANGLE(ucmd->angles[2]);

		if (ent->groundentity && !pm.groundentity && (pm.cmd.upmove >= 10) && (pm.waterlevel == 0))
		{
			gi.sound(ent, CHAN_VOICE, gi.soundindex("*jump1.wav"), 1, ATTN_NORM, 0);
			PlayerNoise(ent, ent->s.origin, PNOISE_SELF);
		}

		ent->viewheight = pm.viewheight;
		ent->waterlevel = pm.waterlevel;
		ent->watertype = pm.watertype;
		ent->groundentity = pm.groundentity;
		if (pm.groundentity)
			ent->groundentity_linkcount = pm.groundentity->linkcount;

		if (ent->deadflag)
		{
			client->ps.viewangles[ROLL] = 40;
			client->ps.viewangles[PITCH] = -15;
			client->ps.viewangles[YAW] = client->killer_yaw;
		}
		else
		{
			VectorCopy (pm.viewangles, client->v_angle);
			VectorCopy (pm.viewangles, client->ps.viewangles);
		}

		gi.linkentity (ent);

		if (ent->movetype != MOVETYPE_NOCLIP)
			G_TouchTriggers (ent);

		// touch other objects
		for (i=0 ; i<pm.numtouch ; i++)
		{
			other = pm.touchents[i];
			for (j=0 ; j<i ; j++)
				if (pm.touchents[j] == other)
					break;
			if (j != i)
				continue;	// duplicated
			if (!other->touch)
				continue;
			other->touch (other, ent, NULL, NULL);
		}

	}

	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons |= client->buttons & ~client->oldbuttons;

	// save light level the player is standing on for
	// monster sighting AI
	ent->light_level = ucmd->lightlevel;

	// fire weapon from final position if needed
	if (client->latched_buttons & BUTTON_ATTACK)
	{
		if (client->resp.spectator) {

			client->latched_buttons = 0;

			if (client->chase_target) {
				client->chase_target = NULL;
				client->ps.pmove.pm_flags &= ~PMF_NO_PREDICTION;
			} else
				GetChaseTarget(ent);

		} else if (!client->weapon_thunk) {
			client->weapon_thunk = true;
			Think_Weapon (ent);
		}
	}

	if (client->resp.spectator) {
		if (ucmd->upmove >= 10) {
			if (!(client->ps.pmove.pm_flags & PMF_JUMP_HELD)) {
				client->ps.pmove.pm_flags |= PMF_JUMP_HELD;
				if (client->chase_target)
					ChaseNext(ent);
				else
					GetChaseTarget(ent);
			}
		} else
			client->ps.pmove.pm_flags &= ~PMF_JUMP_HELD;
	}

	// update chase cam if being followed
	for (i = 1; i <= maxclients->value; i++) {
		other = g_edicts + i;
		if (other->inuse && other->client->chase_target == ent)
			UpdateChaseCam(other);
	}
}
Esempio n. 30
0
/*
==============
ClientThink

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

If "g_synchronousClients 1" is set, this will be called exactly
once for each server frame, which makes for smooth demo recording.
==============
*/
void ClientThink_real( gentity_t *ent ) {
	gclient_t   *client;
	pmove_t pm;
//	vec3_t		oldOrigin;
	int oldEventSequence;
	int msec;
	usercmd_t   *ucmd;
	int monsterslick = 0;
// JPW NERVE
	int i;
	vec3_t muzzlebounce;
	gitem_t *item;
	gentity_t *ent2;
	vec3_t velocity, org, offset;
	vec3_t angles,mins,maxs;
	int weapon;
	trace_t tr;
// jpw

	// Rafael wolfkick
	//int			validkick;
	//static int	wolfkicktimer = 0;

	client = ent->client;

	// don't think if the client is not yet connected (and thus not yet spawned in)
	if ( client->pers.connected != CON_CONNECTED ) {
		return;
	}

	if ( client->cameraPortal ) {
		G_SetOrigin( client->cameraPortal, client->ps.origin );
		trap_LinkEntity( client->cameraPortal );
		VectorCopy( client->cameraOrigin, client->cameraPortal->s.origin2 );
	}

	// mark the time, so the connection sprite can be removed
	ucmd = &ent->client->pers.cmd;

	ent->client->ps.identifyClient = ucmd->identClient;     // NERVE - SMF

// JPW NERVE -- update counter for capture & hold display
	if ( g_gametype.integer == GT_WOLF_CPH ) {
		client->ps.stats[STAT_CAPTUREHOLD_RED] = level.capturetimes[TEAM_RED];
		client->ps.stats[STAT_CAPTUREHOLD_BLUE] = level.capturetimes[TEAM_BLUE];
	}
// jpw

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

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

	if ( pmove_msec.integer < 8 ) {
		trap_Cvar_Set( "pmove_msec", "8" );
	} else if ( pmove_msec.integer > 33 )     {
		trap_Cvar_Set( "pmove_msec", "33" );
	}

	if ( pmove_fixed.integer || client->pers.pmoveFixed ) {
		ucmd->serverTime = ( ( ucmd->serverTime + pmove_msec.integer - 1 ) / pmove_msec.integer ) * pmove_msec.integer;
		//if (ucmd->serverTime - client->ps.commandTime <= 0)
		//	return;
	}

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

	// spectators don't do much
	// DHM - Nerve :: In limbo use SpectatorThink
	if ( client->sess.sessionTeam == TEAM_SPECTATOR || client->ps.pm_flags & PMF_LIMBO ) {
		if ( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) {
			return;
		}
		SpectatorThink( ent, ucmd );
		return;
	}

	// JPW NERVE do some time-based muzzle flip -- this never gets touched in single player (see g_weapon.c)
	// #define RIFLE_SHAKE_TIME 150 // JPW NERVE this one goes with the commented out old damped "realistic" behavior below
	#define RIFLE_SHAKE_TIME 300 // per Id request, longer recoil time
	if ( client->sniperRifleFiredTime ) {
		if ( level.time - client->sniperRifleFiredTime > RIFLE_SHAKE_TIME ) {
			client->sniperRifleFiredTime = 0;
		} else {
			VectorCopy( client->ps.viewangles,muzzlebounce );

			// JPW per Id request, longer recoil time
			muzzlebounce[PITCH] -= 2 * cos( 2.5 * ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			muzzlebounce[YAW] += 0.5*client->sniperRifleMuzzleYaw*cos( 1.0 - ( level.time - client->sniperRifleFiredTime ) * 3 / RIFLE_SHAKE_TIME );
			muzzlebounce[PITCH] -= 0.25 * random() * ( 1.0f - ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			muzzlebounce[YAW] += 0.5 * crandom() * ( 1.0f - ( level.time - client->sniperRifleFiredTime ) / RIFLE_SHAKE_TIME );
			SetClientViewAngle( ent,muzzlebounce );
		}
	}
	if ( client->ps.stats[STAT_PLAYER_CLASS] == PC_MEDIC ) {
		if ( level.time > client->ps.powerups[PW_REGEN] + 5000 ) {
			client->ps.powerups[PW_REGEN] = level.time;
		}
	}
	// also update weapon recharge time

	// JPW drop button drops secondary weapon so new one can be picked up
	// TTimo explicit braces to avoid ambiguous 'else'
	if ( g_gametype.integer != GT_SINGLE_PLAYER ) {
		if ( ucmd->wbuttons & WBUTTON_DROP ) {
			if ( !client->dropWeaponTime ) {
				client->dropWeaponTime = 1; // just latch it for now
				if ( ( client->ps.stats[STAT_PLAYER_CLASS] == PC_SOLDIER ) || ( client->ps.stats[STAT_PLAYER_CLASS] == PC_LT ) ) {
					for ( i = 0; i < MAX_WEAPS_IN_BANK_MP; i++ ) {
						weapon = weapBanksMultiPlayer[3][i];
						if ( COM_BitCheck( client->ps.weapons,weapon ) ) {

							item = BG_FindItemForWeapon( weapon );
							VectorCopy( client->ps.viewangles, angles );

							// clamp pitch
							if ( angles[PITCH] < -30 ) {
								angles[PITCH] = -30;
							} else if ( angles[PITCH] > 30 ) {
								angles[PITCH] = 30;
							}

							AngleVectors( angles, velocity, NULL, NULL );
							VectorScale( velocity, 64, offset );
							offset[2] += client->ps.viewheight / 2;
							VectorScale( velocity, 75, velocity );
							velocity[2] += 50 + random() * 35;

							VectorAdd( client->ps.origin,offset,org );

							VectorSet( mins, -ITEM_RADIUS, -ITEM_RADIUS, 0 );
							VectorSet( maxs, ITEM_RADIUS, ITEM_RADIUS, 2 * ITEM_RADIUS );

							trap_Trace( &tr, client->ps.origin, mins, maxs, org, ent->s.number, MASK_SOLID );
							VectorCopy( tr.endpos, org );

							ent2 = LaunchItem( item, org, velocity, client->ps.clientNum );
							COM_BitClear( client->ps.weapons,weapon );

							if ( weapon == WP_MAUSER ) {
								COM_BitClear( client->ps.weapons,WP_SNIPERRIFLE );
							}

							// Clear out empty weapon, change to next best weapon
							G_AddEvent( ent, EV_NOAMMO, 0 );

							i = MAX_WEAPS_IN_BANK_MP;
							// show_bug.cgi?id=568
							if ( client->ps.weapon == weapon ) {
								client->ps.weapon = 0;
							}
							ent2->count = client->ps.ammoclip[BG_FindClipForWeapon( weapon )];
							ent2->item->quantity = client->ps.ammoclip[BG_FindClipForWeapon( weapon )];
							client->ps.ammoclip[BG_FindClipForWeapon( weapon )] = 0;
						}
					}
				}
			}
		} else {
			client->dropWeaponTime = 0;
		}
	}
// jpw

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

	if ( reloading || client->cameraPortal ) {
		ucmd->buttons = 0;
		ucmd->forwardmove = 0;
		ucmd->rightmove = 0;
		ucmd->upmove = 0;
		ucmd->wbuttons = 0;
		ucmd->wolfkick = 0;
		if ( client->cameraPortal ) {
			client->ps.pm_type = PM_FREEZE;
		}
	} else if ( client->noclip ) {
		client->ps.pm_type = PM_NOCLIP;
	} else if ( client->ps.stats[STAT_HEALTH] <= 0 ) {
		client->ps.pm_type = PM_DEAD;
	} else {
		client->ps.pm_type = PM_NORMAL;
	}

	// set parachute anim condition flag
	BG_UpdateConditionValue( ent->s.number, ANIM_COND_PARACHUTE, ( ent->flags & FL_PARACHUTE ) != 0, qfalse );

	// all playing clients are assumed to be in combat mode
	if ( !client->ps.aiChar ) {
		client->ps.aiState = AISTATE_COMBAT;
	}

	client->ps.gravity = g_gravity.value;

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

	if ( client->ps.powerups[PW_HASTE] ) {
		client->ps.speed *= 1.3;
	}

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

	client->currentAimSpreadScale = (float)client->ps.aimSpreadScale / 255.0;

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

	pm.ps = &client->ps;
	pm.pmext = &client->pmext;
	pm.cmd = *ucmd;
	pm.oldcmd = client->pers.oldcmd;
	if ( pm.ps->pm_type == PM_DEAD ) {
		pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
		// DHM-Nerve added:: EF_DEAD is checked for in Pmove functions, but wasn't being set
		//              until after Pmove
		pm.ps->eFlags |= EF_DEAD;
		// dhm-Nerve end
	} else {
		pm.tracemask = MASK_PLAYERSOLID;
	}
	// MrE: always use capsule for AI and player
	//pm.trace = trap_TraceCapsule;//trap_Trace;
	//DHM - Nerve :: We've gone back to using normal bbox traces
	pm.trace = trap_Trace;
	pm.pointcontents = trap_PointContents;
	pm.debugLevel = g_debugMove.integer;
	pm.noFootsteps = ( g_dmflags.integer & DF_NO_FOOTSTEPS ) > 0;

	pm.pmove_fixed = pmove_fixed.integer | client->pers.pmoveFixed;
	pm.pmove_msec = pmove_msec.integer;

	pm.noWeapClips = ( g_dmflags.integer & DF_NO_WEAPRELOAD ) > 0;
	if ( ent->aiCharacter && AICast_NoReload( ent->s.number ) ) {
		pm.noWeapClips = qtrue; // ensure AI characters don't use clips if they're not supposed to.

	}
	// Ridah
//	if (ent->r.svFlags & SVF_NOFOOTSTEPS)
//		pm.noFootsteps = qtrue;

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

	// NERVE - SMF
	pm.gametype = g_gametype.integer;
	pm.ltChargeTime = g_LTChargeTime.integer;
	pm.soldierChargeTime = g_soldierChargeTime.integer;
	pm.engineerChargeTime = g_engineerChargeTime.integer;
	pm.medicChargeTime = g_medicChargeTime.integer;
	// -NERVE - SMF

	monsterslick = Pmove( &pm );

	if ( monsterslick && !( ent->flags & FL_NO_MONSTERSLICK ) ) {
		//vec3_t	dir;
		//vec3_t	kvel;
		//vec3_t	forward;
		// TTimo gcc: might be used unitialized in this function
		float angle = 0.0f;
		qboolean bogus = qfalse;

		// NE
		if ( ( monsterslick & SURF_MONSLICK_N ) && ( monsterslick & SURF_MONSLICK_E ) ) {
			angle = 45;
		}
		// NW
		else if ( ( monsterslick & SURF_MONSLICK_N ) && ( monsterslick & SURF_MONSLICK_W ) ) {
			angle = 135;
		}
		// N
		else if ( monsterslick & SURF_MONSLICK_N ) {
			angle = 90;
		}
		// SE
		else if ( ( monsterslick & SURF_MONSLICK_S ) && ( monsterslick & SURF_MONSLICK_E ) ) {
			angle = 315;
		}
		// SW
		else if ( ( monsterslick & SURF_MONSLICK_S ) && ( monsterslick & SURF_MONSLICK_W ) ) {
			angle = 225;
		}
		// S
		else if ( monsterslick & SURF_MONSLICK_S ) {
			angle = 270;
		}
		// E
		else if ( monsterslick & SURF_MONSLICK_E ) {
			angle = 0;
		}
		// W
		else if ( monsterslick & SURF_MONSLICK_W ) {
			angle = 180;
		} else
		{
			bogus = qtrue;
		}
	}

	// server cursor hints
	if ( ent->lastHintCheckTime < level.time ) {
		G_CheckForCursorHints( ent );

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

	// DHM - Nerve :: Set animMovetype to 1 if ducking
	if ( ent->client->ps.pm_flags & PMF_DUCKED ) {
		ent->s.animMovetype = 1;
	} else {
		ent->s.animMovetype = 0;
	}

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

	// Ridah, fixes jittery zombie movement
	if ( g_smoothClients.integer ) {
		BG_PlayerStateToEntityStateExtraPolate( &ent->client->ps, &ent->s, ent->client->ps.commandTime, qtrue );
	} else {
		BG_PlayerStateToEntityState( &ent->client->ps, &ent->s, qtrue );
	}

	if ( !( ent->client->ps.eFlags & EF_FIRING ) ) {
		client->fireHeld = qfalse;      // for grapple
	}

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

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

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

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

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

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

	// store the client's current position for antilag traces
	G_StoreClientPosition( ent );

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

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

	// swap and latch button actions
	client->oldbuttons = client->buttons;
	client->buttons = ucmd->buttons;
	client->latched_buttons = client->buttons & ~client->oldbuttons;
//	client->latched_buttons |= client->buttons & ~client->oldbuttons;	// FIXME:? (SA) MP method (causes problems for us.  activate 'sticks')

	//----(SA)	added
	client->oldwbuttons = client->wbuttons;
	client->wbuttons = ucmd->wbuttons;
	client->latched_wbuttons = client->wbuttons & ~client->oldwbuttons;
//	client->latched_wbuttons |= client->wbuttons & ~client->oldwbuttons;	// FIXME:? (SA) MP method

	// Rafael - Activate
	// Ridah, made it a latched event (occurs on keydown only)
	if ( client->latched_buttons & BUTTON_ACTIVATE ) {
		Cmd_Activate_f( ent );
	}

	if ( ent->flags & FL_NOFATIGUE ) {
		ent->client->ps.sprintTime = 20000;
	}


	// check for respawning
	if ( client->ps.stats[STAT_HEALTH] <= 0 ) {

		// DHM - Nerve
		if ( g_gametype.integer >= GT_WOLF ) {
			WolfFindMedic( ent );
		}
		// dhm - end

		// wait for the attack button to be pressed
		if ( level.time > client->respawnTime ) {
			// forcerespawn is to prevent users from waiting out powerups
			if ( ( g_gametype.integer != GT_SINGLE_PLAYER ) &&
				 ( g_forcerespawn.integer > 0 ) &&
				 ( ( level.time - client->respawnTime ) > g_forcerespawn.integer * 1000 )  &&
				 ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE
				// JPW NERVE
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qtrue );
				} else {
					respawn( ent );
				}
				// jpw
				return;
			}

			// DHM - Nerve :: Single player game respawns immediately as before,
			//				  but in multiplayer, require button press before respawn
			if ( g_gametype.integer == GT_SINGLE_PLAYER ) {
				respawn( ent );
			}
			// NERVE - SMF - we want to only respawn on jump button now
			else if ( ( ucmd->upmove > 0 ) &&
					  ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE
				// JPW NERVE
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qtrue );
				} else {
					respawn( ent );
				}
				// jpw
			}
			// dhm - Nerve :: end
			// NERVE - SMF - we want to immediately go to limbo mode if gibbed
			else if ( client->ps.stats[STAT_HEALTH] <= GIB_HEALTH && !( ent->client->ps.pm_flags & PMF_LIMBO ) ) {
				if ( g_gametype.integer >= GT_WOLF ) {
					limbo( ent, qfalse );
				} else {
					respawn( ent );
				}
			}
			// -NERVE - SMF
		}
		return;
	}

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