Ejemplo n.º 1
0
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles )
{
	// unlink to make sure it can't possibly interfere with G_KillBox
	trap_UnlinkEntity( player );

	VectorCopy( origin, player->client->ps.origin );
	player->client->ps.origin[ 2 ] += 1;

	// spit the player out
	AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
	VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
	player->client->ps.pm_time = 160; // hold time
	player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;

	// toggle the teleport bit so the client knows to not lerp
	player->client->ps.eFlags ^= EF_TELEPORT_BIT;
	G_UnlaggedClear( player );

	// cut all relevant zap beams
	G_ClearPlayerZapEffects( player );

	// set angles
	G_SetClientViewAngle( player, angles );

	// save results of pmove
	BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );

	// use the precise origin for linking
	VectorCopy( player->client->ps.origin, player->r.currentOrigin );

	if ( player->client->sess.spectatorState == SPECTATOR_NOT )
	{
		// kill anything at the destination
		G_KillBox( player );

		trap_LinkEntity( player );
	}
}
Ejemplo n.º 2
0
void TeleportPlayer( gentity_t *player, vec3_t origin, vec3_t angles ) {
	gentity_t	*tent;

	// use temp events at source and destination to prevent the effect
	// from getting dropped by a second player event
#ifndef SMOKINGUNS
	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
#else
	if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) {
#endif
		tent = G_TempEntity( player->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
		tent->s.clientNum = player->s.clientNum;

		tent = G_TempEntity( origin, EV_PLAYER_TELEPORT_IN );
		tent->s.clientNum = player->s.clientNum;
	}

	// unlink to make sure it can't possibly interfere with G_KillBox
	trap_UnlinkEntity (player);

	VectorCopy ( origin, player->client->ps.origin );
	player->client->ps.origin[2] += 1;

	// spit the player out
	AngleVectors( angles, player->client->ps.velocity, NULL, NULL );
	VectorScale( player->client->ps.velocity, 400, player->client->ps.velocity );
	player->client->ps.pm_time = 160;		// hold time
	player->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;

	// toggle the teleport bit so the client knows to not lerp
	player->client->ps.eFlags ^= EF_TELEPORT_BIT;

#ifdef SMOKINGUNS
//unlagged - backward reconciliation #3
	// we don't want players being backward-reconciled back through teleporters
	G_ResetHistory( player );
//unlagged - backward reconciliation #3
#endif

	// set angles
	SetClientViewAngle( player, angles );

	// kill anything at the destination
#ifndef SMOKINGUNS
	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
#else
	if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) {
#endif
		G_KillBox (player);
#ifdef SMOKINGUNS
		// Tequila comment: G_KillBox will set dontTelefrag as needed
		if (player->client->dontTelefrag) {
#ifdef DEBUG_TELEFRAG_CASE
			G_Printf(S_COLOR_MAGENTA "TeleportPlayer: Telefrag case delayed at respawn for %s...\n",player->client->pers.netname);
#endif
			trap_SendServerCommand( player->s.clientNum, va("print \"Go away %s\n\"",player->client->pers.netname) );
			// So we will link the player entity later with normal content
			player->r.contents = 0;
		}
#endif
	}

	// save results of pmove
	BG_PlayerStateToEntityState( &player->client->ps, &player->s, qtrue );

	// use the precise origin for linking
	VectorCopy( player->client->ps.origin, player->r.currentOrigin );

#ifndef SMOKINGUNS
	if ( player->client->sess.sessionTeam != TEAM_SPECTATOR ) {
#else
	if ( player->client->sess.sessionTeam < TEAM_SPECTATOR ) {
#endif
		trap_LinkEntity (player);
	}
}


/*QUAKED misc_teleporter_dest (1 0 0) (-32 -32 -24) (32 32 -16)
Point teleporters at these.
Now that we don't have teleport destination pads, this is just
an info_notnull
*/
void SP_misc_teleporter_dest( gentity_t *ent ) {
}


#ifdef SMOKINGUNS
// Imported from WoP OpenSource project
//===========================================================

/*QUAKED misc_externalmodel (1 0 0) (-16 -16 -16) (16 16 16)
"model"		arbitrary .md3 file to display
"wait"		time in seconds before the animation begins
*/
#define ANIMATION_THINKTIME	50

static void Think_AnimationExternalmodel( gentity_t *ent ) {

	if(ent->animationEnd>ent->animationStart) {
		ent->s.frame = (int)((float)level.time*0.001f*ent->animationFPS)%(ent->animationEnd-ent->animationStart);
		ent->s.frame += ent->animationStart;

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

void SP_misc_externalmodel( gentity_t *ent )
{
	ent->s.modelindex = G_ModelIndex( ent->model );
//	VectorSet (ent->mins, -16, -16, -16);
//	VectorSet (ent->maxs, 16, 16, 16);
	trap_LinkEntity (ent);

	G_SetOrigin( ent, ent->s.origin );
	VectorCopy( ent->s.angles, ent->s.apos.trBase );

	if(ent->animationEnd>ent->animationStart && ent->animationFPS>0.0f) {
		ent->think = Think_AnimationExternalmodel;

		ent->nextthink = level.time + ANIMATION_THINKTIME;

		// Tequila: Support for new entity features
		if (ent->wait>0.0f)
			ent->nextthink += (int)(ent->wait*1000);
	}
}
#endif
//===========================================================

/*QUAKED misc_model (1 0 0) (-16 -16 -16) (16 16 16)
"model"		arbitrary .md3 file to display
*/
void SP_misc_model( gentity_t *ent ) {

#if 0
	ent->s.modelindex = G_ModelIndex( ent->model );
	VectorSet (ent->mins, -16, -16, -16);
	VectorSet (ent->maxs, 16, 16, 16);
	trap_LinkEntity (ent);

	G_SetOrigin( ent, ent->s.origin );
	VectorCopy( ent->s.angles, ent->s.apos.trBase );
#else
	G_FreeEntity( ent );
#endif
}

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

void locateCamera( gentity_t *ent ) {
	vec3_t		dir;
	gentity_t	*target;
	gentity_t	*owner;

	owner = G_PickTarget( ent->target );
	if ( !owner ) {
		G_Printf( "Couldn't find target for misc_portal_surface\n" );
		G_FreeEntity( ent );
		return;
	}
	ent->r.ownerNum = owner->s.number;

	// frame holds the rotate speed
	if ( owner->spawnflags & 1 ) {
		ent->s.frame = 25;
	} else if ( owner->spawnflags & 2 ) {
		ent->s.frame = 75;
	}

#ifndef SMOKINGUNS
	// swing camera ?
	if ( owner->spawnflags & 4 ) {
		// set to 0 for no rotation at all
		ent->s.powerups = 0;
	}
	else {
		ent->s.powerups = 1;
	}
#else
	// set to 0 for no rotation at all
	ent->s.powerups = 1;
#endif

	// clientNum holds the rotate offset
	ent->s.clientNum = owner->s.clientNum;

	VectorCopy( owner->s.origin, ent->s.origin2 );

	// see if the portal_camera has a target
	target = G_PickTarget( owner->target );
	if ( target ) {
		VectorSubtract( target->s.origin, owner->s.origin, dir );
		VectorNormalize( dir );
	} else {
		G_SetMovedir( owner->s.angles, dir );
	}

	ent->s.eventParm = DirToByte( dir );
}
Ejemplo n.º 3
0
/*
===================
CG_TransitionSnapshot

The transition point from snap to nextSnap has passed
===================
*/
static void CG_TransitionSnapshot( void ) {
	centity_t			*cent;
	snapshot_t			*oldFrame;
	int					i, id;

	if ( !cg.snap ) {
		CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
	}
	if ( !cg.nextSnap ) {
		CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
	}

	// execute any server string commands before transitioning entities
	CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );

	// if we had a map_restart, set everthing with initial
	
	if ( !(cg.snap) || !(cg.nextSnap) ) {
		return;
	}

	// rain - I hate doing things like this for enums.  Oh well.
	memset(&oldValid, 0, sizeof(oldValid));

	// clear the currentValid flag for all entities in the existing snapshot
	for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
		cent = &cg_entities[ cg.snap->entities[ i ].number ];
		cent->currentValid = qfalse;
		oldValid[cg.snap->entities[i].number] = qtrue;
	}

	// OSP -- check for MV updates from new snapshot info
#ifdef MV_SUPPORT
	if(cg.snap->ps.powerups[PW_MVCLIENTLIST] != cg.mvClientList) {
		CG_mvProcessClientList();
	}
#endif

	// move nextSnap to snap and do the transitions
	oldFrame = cg.snap;
	cg.snap = cg.nextSnap;

	if( cg.snap->ps.clientNum == cg.clientNum ) {
		if( cg.xp < cg.snap->ps.stats[STAT_XP] ) {
			cg.xpChangeTime = cg.time;
		}
		cg.xp = cg.snap->ps.stats[STAT_XP];
	}

	BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
	cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;

	for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
		id = cg.snap->entities[ i ].number;
		CG_TransitionEntity( &cg_entities[ id ] );

		// rain - #374 - ent doesn't exist in this frame, reset it.
		// this is to fix the silent landmines bug, which is caused
		// by a stale miscTime in the cent
		if (cg_entities[id].currentValid == qfalse && oldValid[id] == qtrue) {
			CG_ResetEntity(&cg_entities[id]);
		}

		if(cg.mvTotalClients > 0 && CG_mvMergedClientLocate(id)) {
			CG_mvUpdateClientInfo(id);
		}
	}

	if(cg.mvTotalClients > 0) {
		CG_mvTransitionPlayerState(&cg.snap->ps);
	}

	cg.nextSnap = NULL;

	// check for playerstate transition events
	if ( oldFrame ) {
		playerState_t	*ops, *ps;

		ops = &oldFrame->ps;
		ps = &cg.snap->ps;
		// teleporting checks are irrespective of prediction
		if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT ) {
			cg.thisFrameTeleport = qtrue;	// will be cleared by prediction code
		}

		// if we are not doing client side movement prediction for any
		// reason, then the client events and view changes will be issued now
		if ( cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
			|| cg_nopredict.integer 
#ifdef ALLOW_GSYNC
			|| cg_synchronousClients.integer 
#endif // ALLOW_GSYNC
		) {
			CG_TransitionPlayerState( ps, ops );
		}
	}

}
Ejemplo n.º 4
0
void jp_portal_touch( gentity_t *self, gentity_t *other, trace_t *trace ) {
	//Remove the touch function for a while to avoid flooding
	self->genericValue1 = level.time;
	self->touch = NULL;

	if ( self->s.health != -1 ) {
		vector3 tpPos;
		vector3 sourceVelocity, destVelocity;
		vector3 sourceNormal, destNormal;

		//Fetch normals
		VectorCopy( &self->s.boneAngles1, &sourceNormal );
		VectorCopy( &g_entities[self->s.health].s.boneAngles1, &destNormal );

		//Set position to teleport to
		VectorCopy( &g_entities[self->s.health].s.origin, &tpPos );
		VectorMA( &tpPos, 32.0f, &destNormal, &tpPos );

		//BEGIN position
		if ( other->client ) {
			qboolean isNPC = (other->s.eType == ET_NPC);
			uint32_t eFlags = other->client->ps.eFlags ^ EF_TELEPORT_BIT;
			VectorCopy( &tpPos, &other->client->ps.origin );
			other->client->ps.origin.z += 1.0f;
			SetClientViewAngle( other, &other->client->ps.viewangles );
			other->client->ps.eFlags ^= EF_TELEPORT_BIT;
			BG_PlayerStateToEntityState( &other->client->ps, &other->s, qtrue );
			if ( isNPC )
				other->s.eType = ET_NPC;
			other->s.eFlags = eFlags;
			VectorCopy( &other->client->ps.origin, &other->r.currentOrigin );
		}
		else {
			VectorCopy( &tpPos, &other->s.pos.trBase );
		}
		//END position

		//BEGIN velocity
		if ( other->client ) {//Players and NPCs
			float	reflectFactor;
			vector3	exitVector;
			vector3	velNormal;
			float	length;
			vector3	debugLine;

			VectorNormalize2( &other->client->ps.velocity, &velNormal );
			reflectFactor = 2 * DotProduct( &sourceNormal, &velNormal );

			VectorCopy( &other->client->ps.velocity, &sourceVelocity );
			length = VectorLength( &sourceVelocity );

			exitVector.x = velNormal.x - reflectFactor * sourceNormal.x;
			exitVector.y = velNormal.y - reflectFactor * sourceNormal.y;
			exitVector.z = velNormal.z - reflectFactor * sourceNormal.z;

			VectorMA( &self->s.origin, -128.0f, &velNormal, &debugLine );
			G_TestLine( &debugLine, &self->s.origin, 0xF0, 7500 );

			MatrixMultiply3f( self->portal_matrix, &exitVector, &destVelocity );

			VectorMA( &g_entities[self->s.health].s.origin, 128.0f, &destVelocity, &debugLine );
			G_TestLine( &g_entities[self->s.health].s.origin, &debugLine, 0x0F, 7500 );

			VectorScale( &destVelocity, length, &destVelocity );
		}
		else {//Projectiles
			byteAlias_t ba;
			float velocity;
			ba.i = other->genericValue1; // stored during missile creation
			velocity = ba.f;
			//	VectorScale( destNormal, velocity, other->s.pos.trDelta );
			VectorMA( &other->s.pos.trBase, velocity, &destNormal, &other->s.pos.trDelta );
			other->s.pos.trTime = level.time;
			other->nextthink = level.time + other->genericValue2;
		}

		//VectorSet( destVelocity, destNormal.x*speed, destNormal.y*speed, destNormal.z*speed );
		if ( other->client ) {//Players and NPCs
			VectorCopy( &destVelocity, &other->client->ps.velocity );
			other->client->ps.pm_flags |= PMF_DUCKED;
			other->s.solid = 0;
		}
		else {//Projectiles
			VectorCopy( &destVelocity, &other->s.pos.trDelta );
			VectorCopy( &tpPos, &other->r.currentOrigin );
		}
		//END velocity

		g_entities[self->s.health].genericValue1 = level.time;
		g_entities[self->s.health].touch = NULL;
	}
	return;
}
Ejemplo n.º 5
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);
}
Ejemplo n.º 6
0
/*
==============
ClientEndFrame

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

	if (ent->client->sess.sessionTeam == TEAM_SPECTATOR) {
		SpectatorClientEndFrame(ent);
		return;
	}

//	pers = &ent->client->pers;

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

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

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

	if (ent->client->bleeding)
		CheckBleeding(ent);	// perform once-a-second actions

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

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

	// Update the clips Amount in weapon for the client
	ent->client->ps.stats[STAT_CLIPS] = ent->client->numClips[ent->client->ps.weapon];
	ent->client->ps.stats[STAT_HEALTH] = ent->health;	// FIXME: get rid of ent->health...

	//Elder: bleeding notification
	if (ent->client->bleeding || (ent->client->ps.stats[STAT_RQ3] & RQ3_LEGDAMAGE) == RQ3_LEGDAMAGE) {
		ent->client->ps.stats[STAT_RQ3] |= RQ3_BANDAGE_NEED;
	} else {
		ent->client->ps.stats[STAT_RQ3] &= ~RQ3_BANDAGE_NEED;
	}

	//Moved to pmove.c
	//Elder: M4 ride-up/kick -- condition for non-burst and ammo only

	if (ent->client->consecutiveShots &&
	    (ent->client->ps.ammo[WP_M4] <= 0 || ent->client->ps.weaponstate != WEAPON_FIRING)) {
		//Restore view after shots if not firing
		ent->client->ps.delta_angles[0] =
		    ANGLE2SHORT(SHORT2ANGLE(ent->client->ps.delta_angles[0]) - ent->client->consecutiveShots * -0.8);//-0.7f);
		ent->client->consecutiveShots = 0;
	}
	// Check to reset our openDoor boolean
	if (ent->client->openDoorTime && level.time - ent->client->openDoorTime > MAXDOORTIME) {
		ent->client->openDoor = qfalse;
		ent->client->openDoorTime = 0;
	}

	// JBravo: multiple items
	if (ent->client->ps.stats[STAT_HOLDABLE_ITEM] & (1 << HI_LASER)) {
		//Try to turn the laser on if it's off
		if (ent->client->lasersight == NULL)
			Laser_Gen(ent, qtrue);
	}
	//Slicer
	if (ent->client->weapon_attempts > 0)
		Cmd_Weapon(ent);

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

	// JBravo: unlagged
	ent->client->ps.eFlags &= ~EF_CONNECTION;
	frames = level.framenum - ent->client->lastUpdateFrame - 1;
	if (frames > 2) {
		frames = 2;
		ent->client->ps.eFlags |= EF_CONNECTION;
		ent->s.eFlags |= EF_CONNECTION;
	}
	if (frames > 0 && g_smoothClients.integer) {
		G_PredictPlayerMove(ent, (float)frames / sv_fps.integer);
		SnapVector(ent->s.pos.trBase);
	}
}
Ejemplo n.º 7
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn and evolve
Initializes all non-persistent parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, const vec3_t origin, const vec3_t angles )
{
	int                index;
	vec3_t             spawn_origin, spawn_angles;
	gclient_t          *client;
	int                i;
	clientPersistant_t saved;
	clientSession_t    savedSess;
	bool           savedNoclip, savedCliprcontents;
	int                persistant[ MAX_PERSISTANT ];
	gentity_t          *spawnPoint = nullptr;
	int                flags;
	int                savedPing;
	int                teamLocal;
	int                eventSequence;
	char               userinfo[ MAX_INFO_STRING ];
	vec3_t             up = { 0.0f, 0.0f, 1.0f };
	int                maxAmmo, maxClips;
	weapon_t           weapon;

	ClientSpawnCBSE(ent, ent == spawn);

	index = ent - g_entities;
	client = ent->client;

	teamLocal = client->pers.team;

	//if client is dead and following teammate, stop following before spawning
	if ( client->sess.spectatorClient != -1 )
	{
		client->sess.spectatorClient = -1;
		client->sess.spectatorState = SPECTATOR_FREE;
	}

	// only start client if chosen a class and joined a team
	if ( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
	{
		client->sess.spectatorState = SPECTATOR_FREE;
	}
	else if ( client->pers.classSelection == PCL_NONE )
	{
		client->sess.spectatorState = SPECTATOR_LOCKED;
	}

	// if client is dead and following teammate, stop following before spawning
	if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
	{
		G_StopFollowing( ent );
	}

	if ( origin != nullptr )
	{
		VectorCopy( origin, spawn_origin );
	}

	if ( angles != nullptr )
	{
		VectorCopy( angles, spawn_angles );
	}

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.spectatorState != SPECTATOR_NOT )
	{
		if ( teamLocal == TEAM_NONE )
		{
			spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
		}
		else if ( teamLocal == TEAM_ALIENS )
		{
			spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
		}
		else if ( teamLocal == TEAM_HUMANS )
		{
			spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
		}
	}
	else
	{
		if ( spawn == nullptr )
		{
			Com_Error(errorParm_t::ERR_DROP,  "ClientSpawn: spawn is NULL" );
		}

		spawnPoint = spawn;

		if ( spawnPoint->s.eType == entityType_t::ET_BUILDABLE )
		{
			G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, true );

			if ( spawnPoint->buildableTeam == TEAM_ALIENS )
			{
				spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
			}
			else if ( spawnPoint->buildableTeam == TEAM_HUMANS )
			{
				spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
			}
		}
	}

	// toggle the teleport bit so the client knows to not lerp
	flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
	G_UnlaggedClear( ent );

	// clear everything but the persistent data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
	savedNoclip = client->noclip;
	savedCliprcontents = client->cliprcontents;

	for ( i = 0; i < MAX_PERSISTANT; i++ )
	{
		persistant[ i ] = client->ps.persistant[ i ];
	}

	eventSequence = client->ps.eventSequence;
	memset( client, 0, sizeof( *client ) );

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
	client->noclip = savedNoclip;
	client->cliprcontents = savedCliprcontents;

	for ( i = 0; i < MAX_PERSISTANT; i++ )
	{
		client->ps.persistant[ i ] = persistant[ i ];
	}

	client->ps.eventSequence = eventSequence;

	// increment the spawncount so the client will detect the respawn
	client->ps.persistant[ PERS_SPAWN_COUNT ]++;
	client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;

	client->airOutTime = level.time + 12000;

	trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
	client->ps.eFlags = flags;

	//Log::Notice( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[ index ];
	ent->classname = S_PLAYER_CLASSNAME;
	if ( client->noclip )
	{
		client->cliprcontents = CONTENTS_BODY;
	}
	else
	{
		ent->r.contents = CONTENTS_BODY;
	}
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = G_PlayerDie;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags &= FL_GODMODE | FL_NOTARGET;

	// calculate each client's acceleration
	ent->evaluateAcceleration = true;

	client->ps.stats[ STAT_MISC ] = 0;

	client->ps.eFlags = flags;
	client->ps.clientNum = index;

	BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, nullptr, nullptr, nullptr );

	// clear entity values
	if ( ent->client->pers.classSelection == PCL_HUMAN_NAKED )
	{
		BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
		weapon = client->pers.humanItemSelection;
	}
	else if ( client->sess.spectatorState == SPECTATOR_NOT )
	{
		weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
	}
	else
	{
		weapon = WP_NONE;
	}

	maxAmmo = BG_Weapon( weapon )->maxAmmo;
	maxClips = BG_Weapon( weapon )->maxClips;
	client->ps.stats[ STAT_WEAPON ] = weapon;
	client->ps.ammo = maxAmmo;
	client->ps.clips = maxClips;

	// We just spawned, not changing weapons
	client->ps.persistant[ PERS_NEWWEAPON ] = 0;

	client->ps.persistant[ PERS_TEAM ] = client->pers.team;

	// TODO: Check whether stats can be cleared at once instead of per field
	client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
	client->ps.stats[ STAT_FUEL ]    = JETPACK_FUEL_MAX;
	client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
	client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
	client->ps.stats[ STAT_PREDICTION ] = 0;
	client->ps.stats[ STAT_STATE ] = 0;

	VectorSet( client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

	//clear the credits array
	// TODO: Handle in HealthComponent or ClientComponent.
	for ( i = 0; i < MAX_CLIENTS; i++ )
	{
		ent->credits[ i ].value = 0.0f;
		ent->credits[ i ].time = 0;
		ent->credits[ i ].team = TEAM_NONE;
	}

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	//give aliens some spawn velocity
	if ( client->sess.spectatorState == SPECTATOR_NOT &&
	     client->pers.team == TEAM_ALIENS )
	{
		if ( ent == spawn )
		{
			//evolution particle system
			G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
		}
		else
		{
			spawn_angles[ YAW ] += 180.0f;
			AngleNormalize360( spawn_angles[ YAW ] );

			if ( spawnPoint->s.origin2[ 2 ] > 0.0f )
			{
				vec3_t forward, dir;

				AngleVectors( spawn_angles, forward, nullptr, nullptr );
				VectorAdd( spawnPoint->s.origin2, forward, dir );
				VectorNormalize( dir );
				VectorScale( dir, BG_Class( ent->client->pers.classSelection )->jumpMagnitude,
				             client->ps.velocity );
			}

			G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
		}
	}
	else if ( client->sess.spectatorState == SPECTATOR_NOT &&
	          client->pers.team == TEAM_HUMANS )
	{
		spawn_angles[ YAW ] += 180.0f;
		AngleNormalize360( spawn_angles[ YAW ] );
	}

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	G_SetClientViewAngle( ent, spawn_angles );

	if ( client->sess.spectatorState == SPECTATOR_NOT )
	{
		trap_LinkEntity( ent );

		// force the base weapon up
		if ( client->pers.team == TEAM_HUMANS )
		{
			G_ForceWeaponChange( ent, weapon );
		}

		client->ps.weaponstate = WEAPON_READY;
	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	ent->nextRegenTime = level.time;

	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	usercmdClearButtons( client->latched_buttons );

	// set default animations
	client->ps.torsoAnim = TORSO_STAND;
	client->ps.legsAnim = LEGS_IDLE;

	if ( level.intermissiontime )
	{
		MoveClientToIntermission( ent );
	}
	else
	{
		// fire the targets of the spawn point
		if ( !spawn && spawnPoint )
		{
			G_EventFireEntity( spawnPoint, ent, ON_SPAWN );
		}

		// select the highest weapon number available, after any
		// spawn given items have fired
		client->ps.weapon = 1;

		for ( i = WP_NUM_WEAPONS - 1; i > 0; i-- )
		{
			if ( BG_InventoryContainsWeapon( i, client->ps.stats ) )
			{
				client->ps.weapon = i;
				break;
			}
		}
	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent - g_entities );

	// positively link the client, even if the command times are weird
	if ( client->sess.spectatorState == SPECTATOR_NOT )
	{
		BG_PlayerStateToEntityState( &client->ps, &ent->s, true );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

	// must do this here so the number of active clients is calculated
	CalculateRanks();

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, true );

	client->pers.infoChangeTime = level.time;

	// (re)tag the client for its team
	Beacon::DeleteTags( ent );
	Beacon::Tag( ent, (team_t)ent->client->ps.persistant[ PERS_TEAM ], true );
}
Ejemplo n.º 8
0
/*
==================
CG_SetInitialSnapshot

This will only happen on the very first snapshot, or
on tourney restarts.  All other times will use
CG_TransitionSnapshot instead.

FIXME: Also called by map_restart?
==================
*/
void CG_SetInitialSnapshot(snapshot_t *snap)
{
	int           i;
	centity_t     *cent;
	entityState_t *state;
	char          buff[16];

	cg.snap = snap;

	//  trap_S_ClearSounds( qtrue );

	BG_PlayerStateToEntityState(&snap->ps, &cg_entities[snap->ps.clientNum].currentState, cg.time, qfalse);

	// sort out solid entities
	CG_BuildSolidList();

	CG_ExecuteNewServerCommands(snap->serverCommandSequence);

	// set our local weapon selection pointer to
	// what the server has indicated the current weapon is
	CG_Respawn(qfalse);

	for (i = 0 ; i < cg.snap->numEntities ; i++)
	{
		state = &cg.snap->entities[i];
		cent  = &cg_entities[state->number];

		memcpy(&cent->currentState, state, sizeof(entityState_t));
		//cent->currentState = *state;
		cent->interpolate  = qfalse;
		cent->currentValid = qtrue;

		CG_ResetEntity(cent);

		// check for events
		CG_CheckEvents(cent);
	}

	trap_Cvar_VariableStringBuffer("r_oldMode", buff, sizeof(buff));
	if (atoi(buff))
	{
		// confirmation screen
		trap_UI_Popup(UIMENU_INGAME);
	}
	else if (cg.demoPlayback)
	{
		ccInitial = qtrue;
	}
	else
	{
		static char prevmap[64] = { 0 };
		char        curmap[64];

		trap_Cvar_VariableStringBuffer("mapname", curmap, 64);

		if (Q_stricmp(curmap, prevmap))
		{
			strcpy(prevmap, curmap);
			if (cgs.campaignInfoLoaded)
			{
				if (!cg.showGameView)
				{
					CG_LimboMenu_f();
				}
				/*          } else {
				                ccInitial = qtrue;

				                // Start the Initial Camera if specified
				                CG_StartInitialCamera(); */
			}
		}
	}

	// remove motd window
	if (cg.motdWindow != NULL)
	{
		CG_windowFree(cg.motdWindow);
		cg.motdWindow = NULL;
	}

	// Activate alternate input handler during demo playback
	if (cg.demoPlayback)
	{
		CG_keyOn_f();
		if (demo_infoWindow.integer > 0)
		{
			CG_ShowHelp_On(&cg.demohelpWindow);
		}
	}

	// update client XP for spectator frames
	if (cg.snap->ps.clientNum == cg.clientNum)    // sanity check
	{
		int cXP = (32768 * cg.snap->ps.stats[STAT_XP_OVERFLOW]) + cg.snap->ps.stats[STAT_XP];

		if (cg.xp < cXP)
		{
			cg.xpChangeTime = cg.time;
		}
		cg.xp = cXP;
	}
}
Ejemplo n.º 9
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 );
}
Ejemplo n.º 10
0
/*
==============
ClientEndFrame

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

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {
		SpectatorClientEndFrame( ent );
		return;
	}

	pers = &ent->client->pers;

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

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

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

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

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

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

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

	G_SetClientSound (ent);

	// set the latest infor
	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 );

	// set the bit for the reachability area the client is currently in
//	i = trap_AAS_PointReachabilityAreaIndex( ent->client->ps.origin );
//	ent->client->areabits[i >> 3] |= 1 << (i & 7);
}
Ejemplo n.º 11
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;
//    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;
  }

  G_UpdatePTRConnection( client );

  // 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 if( client->ps.stats[ STAT_STATE ] & SS_INFESTING ||
           client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    client->ps.pm_type = PM_FREEZE;
  else if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ||
           client->ps.stats[ STAT_STATE ] & SS_GRABBED )
    client->ps.pm_type = PM_GRABBED;
  else if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
    client->ps.pm_type = PM_JETPACK;
  else
    client->ps.pm_type = PM_NORMAL;

  if( client->ps.stats[ STAT_STATE ] & SS_GRABBED &&
      client->grabExpiryTime < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_GRABBED;

  if( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED &&
      client->lastLockTime + 5000 < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED;

  if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED &&
      client->lastLockTime + ABUILDER_BLOB_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;

  client->ps.stats[ STAT_BOOSTTIME ] = level.time - client->lastBoostedTime;

  if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED &&
      client->lastBoostedTime + BOOST_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED &&
      client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED;

  if( client->ps.stats[ STAT_STATE ] & SS_POISONED &&
      client->lastPoisonTime + ALIEN_POISON_TIME < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;

  client->ps.gravity = g_gravity.value;

  if( BG_InventoryContainsUpgrade( UP_MEDKIT, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_MEDKIT, client->ps.stats ) )
  {
    //if currently using a medkit or have no need for a medkit now
    if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ||
        ( client->ps.stats[ STAT_HEALTH ] == client->ps.stats[ STAT_MAX_HEALTH ] &&
          !( client->ps.stats[ STAT_STATE ] & SS_POISONED ) ) )
    {
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
    }
    else if( client->ps.stats[ STAT_HEALTH ] > 0 )
    {
      //remove anti toxin
      BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats );
      BG_RemoveUpgradeFromInventory( UP_MEDKIT, client->ps.stats );

      client->ps.stats[ STAT_STATE ] &= ~SS_POISONED;
      client->poisonImmunityTime = level.time + MEDKIT_POISON_IMMUNITY_TIME;

      client->ps.stats[ STAT_STATE ] |= SS_MEDKIT_ACTIVE;
      client->lastMedKitTime = level.time;
      client->medKitHealthToRestore =
        client->ps.stats[ STAT_MAX_HEALTH ] - client->ps.stats[ STAT_HEALTH ];
      client->medKitIncrementTime = level.time +
        ( MEDKIT_STARTUP_TIME / MEDKIT_STARTUP_SPEED );

      G_AddEvent( ent, EV_MEDKIT_USED, 0 );
    }
  }

  if( BG_InventoryContainsUpgrade( UP_GRENADE, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_GRENADE, client->ps.stats ) )
  {
    int lastWeapon = ent->s.weapon;

    //remove grenade
    BG_DeactivateUpgrade( UP_GRENADE, client->ps.stats );
    BG_RemoveUpgradeFromInventory( UP_GRENADE, client->ps.stats );

    //M-M-M-M-MONSTER HACK
    ent->s.weapon = WP_GRENADE;
    FireWeapon( ent );
    ent->s.weapon = lastWeapon;
  }

  // set speed
  client->ps.speed = g_speed.value * BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] );

  if( client->lastCreepSlowTime + CREEP_TIMEOUT < level.time )
    client->ps.stats[ STAT_STATE ] &= ~SS_CREEPSLOWED;

  //randomly disable the jet pack if damaged
  if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) &&
      BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) )
  {
    if( ent->lastDamageTime + JETPACK_DISABLE_TIME > level.time )
    {
      if( random( ) > JETPACK_DISABLE_CHANCE )
        client->ps.pm_type = PM_NORMAL;
    }

    //switch jetpack off if no reactor
    if( !level.reactorPresent )
      BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
  }

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

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

  if( !( ucmd->buttons & BUTTON_TALK ) ) //&& client->ps.weaponTime <= 0 ) //TA: erk more server load
  {
    switch( client->ps.weapon )
    {
      case WP_ALEVEL0:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckVenomAttack( ent );
        break;

      case WP_ALEVEL1:
      case WP_ALEVEL1_UPG:
        CheckGrabAttack( ent );
        break;

      case WP_ALEVEL3:
      case WP_ALEVEL3_UPG:
        if( client->ps.weaponTime <= 0 )
          pm.autoWeaponHit[ client->ps.weapon ] = CheckPounceAttack( ent );
        break;

      default:
        break;
    }
  }

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

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

  if( pm.ps->pm_type == PM_DEAD )
    pm.tracemask = MASK_PLAYERSOLID; // & ~CONTENTS_BODY;

  if( pm.ps->stats[ STAT_STATE ] & SS_INFESTING ||
      pm.ps->stats[ STAT_STATE ] & SS_HOVELING )
    pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY;
  else
    pm.tracemask = MASK_PLAYERSOLID;

  pm.trace = trap_Trace;
  pm.pointcontents = trap_PointContents;
  pm.debugLevel = g_debugMove.integer;
  pm.noFootsteps = (qboolean)0;

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

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

  // moved from after Pmove -- potentially the cause of
  // future triggering bugs
  if( !ent->client->noclip )
    G_TouchTriggers( ent );

  Pmove( &pm );

  // 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
  if( !( ent->client->ps.eFlags & EF_FIRING2 ) )
    client->fire2Held = qfalse;

  // 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 );

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

  // 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;

  if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) &&
       client->ps.stats[ STAT_HEALTH ] > 0 )
  {
    trace_t   trace;
    vec3_t    view, point;
    gentity_t *traceEnt;

    if( client->ps.stats[ STAT_STATE ] & SS_HOVELING )
    {
      gentity_t *hovel = client->hovel;

      //only let the player out if there is room
      if( !AHovel_Blocked( hovel, ent, qtrue ) )
      {
        //prevent lerping
        client->ps.eFlags ^= EF_TELEPORT_BIT;
        client->ps.eFlags &= ~EF_NODRAW;

        //client leaves hovel
        client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING;

        //hovel is empty
        G_setBuildableAnim( hovel, BANIM_ATTACK2, qfalse );
        hovel->active = qfalse;
      }
      else
      {
        //exit is blocked
        G_TriggerMenu( ent->client->ps.clientNum, MN_A_HOVEL_BLOCKED );
      }
    }
    else
    {
#define USE_OBJECT_RANGE 64

      int       entityList[ MAX_GENTITIES ];
      vec3_t    range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE };
      vec3_t    mins, maxs;
      int       i, num;

      //TA: look for object infront of player
      AngleVectors( client->ps.viewangles, view, NULL, NULL );
      VectorMA( client->ps.origin, USE_OBJECT_RANGE, view, point );
      trap_Trace( &trace, client->ps.origin, NULL, NULL, point, ent->s.number, MASK_SHOT );

      traceEnt = &g_entities[ trace.entityNum ];

      if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
        traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
      else
      {
        //no entity in front of player - do a small area search

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

        num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
        for( i = 0; i < num; i++ )
        {
          traceEnt = &g_entities[ entityList[ i ] ];

          if( traceEnt && traceEnt->biteam == client->ps.stats[ STAT_PTEAM ] && traceEnt->use )
          {
            traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
            break;
          }
        }

        if( i == num && client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
        {
          if( BG_UpgradeClassAvailable( &client->ps ) )
          {
            //no nearby objects and alien - show class menu
            G_TriggerMenu( ent->client->ps.clientNum, MN_A_INFEST );
          }
          else
          {
            //flash frags
            G_AddEvent( ent, EV_ALIEN_EVOLVE_FAILED, 0 );
          }
        }
      }
    }
  }

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

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

  if( level.framenum > client->retriggerArmouryMenu && client->retriggerArmouryMenu )
  {
    G_TriggerMenu( client->ps.clientNum, MN_H_ARMOURY );

    client->retriggerArmouryMenu = 0;
  }

  // Give clients some credit periodically
  if( ent->client->lastKillTime + FREEKILL_PERIOD < level.time )
  {
    if( g_suddenDeathTime.integer &&
        ( level.time - level.startTime >= g_suddenDeathTime.integer * 60000 ) )
    {
      //gotta love logic like this eh?
    }
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS )
      G_AddCreditToClient( ent->client, FREEKILL_ALIEN, qtrue );
    else if( ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS )
      G_AddCreditToClient( ent->client, FREEKILL_HUMAN, qtrue );

    ent->client->lastKillTime = level.time;
  }

  // perform once-a-second actions
  ClientTimerActions( ent, msec );
  
  if( ent->suicideTime > 0 && ent->suicideTime < level.time )
  {
    ent->flags &= ~FL_GODMODE;
    ent->client->ps.stats[ STAT_HEALTH ] = ent->health = 0;
    player_die( ent, ent, ent, 100000, MOD_SUICIDE );

    ent->suicideTime = 0;
  }
}
Ejemplo n.º 12
0
void CG_DemosDrawActiveFrame( int serverTime, stereoFrame_t stereoView ) {
	int deltaTime;
	qboolean hadSkip;
	qboolean captureFrame;
	float captureFPS;
	float frameSpeed;
	int blurTotal, blurIndex;
	float blurFraction;
	float stereoSep = CG_Cvar_Get( "r_stereoSeparation" );

	int inwater, entityNum;

	if (!demo.initDone) {
		if ( !cg.snap ) {
			demoProcessSnapShots( qtrue );
		}
		if ( !cg.snap ) {
			CG_Error( "No Initial demo snapshot found" );
		}
		demoPlaybackInit();
	}

	cg.demoPlayback = 2;
	
	// update cvars
	CG_UpdateCvars();

	// if we are only updating the screen as a loading
	// pacifier, don't even try to read snapshots
	if ( cg.loading ) {
		CG_DrawInformation();
		return;
	}

	captureFrame = demo.capture.active && !demo.play.paused;
	if ( captureFrame ) {
		trap_MME_BlurInfo( &blurTotal, &blurIndex );
		captureFPS = mov_captureFPS.value;
		if ( blurTotal > 0) {
			captureFPS *= blurTotal;
			blurFraction = blurIndex / (float)blurTotal;
		} else {
			blurFraction = 0;
		}
	} else {
	}

	/* Forward the demo */
	deltaTime = serverTime - demo.serverTime;
	if (deltaTime > 50)
		deltaTime = 50;
	demo.serverTime = serverTime;
	demo.serverDeltaTime = 0.001 * deltaTime;
	cg.oldTime = cg.time;
	cg.oldTimeFraction = cg.timeFraction;

	if (demo.play.time < 0) {
		demo.play.time = demo.play.fraction = 0;
	}

	demo.play.oldTime = demo.play.time;


	/* Handle the music */
	if ( demo.play.paused ) {
		if ( lastMusicStart >= 0)
			demoSynchMusic( -1, 0 ); 
	} else {
		int musicStart = (demo.play.time - mov_musicStart.value * 1000 );
		if ( musicStart <= 0 ) {
			if (lastMusicStart >= 0 )
				demoSynchMusic( -1, 0 );
		} else {
			if ( demo.play.time != demo.play.lastTime || lastMusicStart < 0)
				demoSynchMusic( musicStart, 0 );
		}
	}
	/* forward the time a bit till the moment of capture */
	if ( captureFrame && demo.capture.locked && demo.play.time < demo.capture.start ) {
		int left = demo.capture.start - demo.play.time;
		if ( left > 2000) {
			left -= 1000;
			captureFrame = qfalse;
		} else if (left > 5) {
			captureFrame = qfalse;
			left = 5;
		}
		demo.play.time += left;
	} else if ( captureFrame && demo.loop.total && blurTotal ) {
		float loopFraction = demo.loop.index / (float)demo.loop.total;
		demo.play.time = demo.loop.start;
		demo.play.fraction = demo.loop.range * loopFraction;
		demo.play.time += (int)demo.play.fraction;
		demo.play.fraction -= (int)demo.play.fraction;
	} else if (captureFrame) {
		float frameDelay = 1000.0f / captureFPS;
		demo.play.fraction += frameDelay * demo.play.speed;
		demo.play.time += (int)demo.play.fraction;
		demo.play.fraction -= (int)demo.play.fraction;
	} else if ( demo.find ) {
		demo.play.time = demo.play.oldTime + 20;
		demo.play.fraction = 0;
		if ( demo.play.paused )
			demo.find = findNone;
	} else if (!demo.play.paused) {
		float delta = demo.play.fraction + deltaTime * demo.play.speed;
		demo.play.time += (int)delta;
		demo.play.fraction = delta - (int)delta;
	}

	demo.play.lastTime = demo.play.time;

	if ( demo.loop.total && captureFrame && blurTotal ) {
		//Delay till we hit the right part at the start
		int time;
		float timeFraction;
		if ( demo.loop.lineDelay && !blurIndex ) {
			time = demo.loop.start - demo.loop.lineDelay;
			timeFraction = 0;
			if ( demo.loop.lineDelay > 8 )
				demo.loop.lineDelay -= 8;
			else
				demo.loop.lineDelay = 0;
			captureFrame = qfalse;
		} else {
			if ( blurIndex == blurTotal - 1 ) {
				//We'll restart back to the start again
				demo.loop.lineDelay = 2000;
				if ( ++demo.loop.index >= demo.loop.total ) {
					demo.loop.total = 0;
				}
			}
			time = demo.loop.start;
			timeFraction = demo.loop.range * blurFraction;
		}
		time += (int)timeFraction;
		timeFraction -= (int)timeFraction;
		lineAt( time, timeFraction, &demo.line.time, &cg.timeFraction, &frameSpeed );
	} else {
		lineAt( demo.play.time, demo.play.fraction, &demo.line.time, &cg.timeFraction, &frameSpeed );
	}
	/* Set the correct time */
	cg.time = trap_MME_SeekTime( demo.line.time );
	/* cg.time is shifted ahead a bit to correct some issues.. */
	frameSpeed *= demo.play.speed;

	cg.frametime = (cg.time - cg.oldTime) + (cg.timeFraction - cg.oldTimeFraction);
	if (cg.frametime < 0) {
		int i;
		cg.frametime = 0;
		hadSkip = qtrue;
		cg.oldTime = cg.time;
		cg.oldTimeFraction = cg.timeFraction;
		CG_InitLocalEntities();
		CG_InitMarkPolys();
		CG_ClearParticles ();
		trap_FX_Reset( );
		trap_R_DecalReset();

		cg.centerPrintTime = 0;
        cg.damageTime = 0;
		cg.powerupTime = 0;
		cg.rewardTime = 0;
		cg.scoreFadeTime = 0;
		cg.lastKillTime = 0;
		cg.attackerTime = 0;
		cg.soundTime = 0;
		cg.itemPickupTime = 0;
		cg.itemPickupBlendTime = 0;
		cg.weaponSelectTime = 0;
		cg.headEndTime = 0;
		cg.headStartTime = 0;
		cg.v_dmg_time = 0;

		cg.rewardCount[0] = 0;
		cg.rewardStack = 0;
		cg.rewardTime = 0;

		trap_S_ClearLoopingSounds(qtrue);
		
		for (i = 0; i < MAX_CHATBOX_ITEMS; i++)
			cg.chatItems[i].time = 0;
	} else if (cg.frametime > 100) {
		hadSkip = qtrue;
	} else {
		hadSkip = qfalse;
	}
	/* Make sure the random seed is the same each time we hit this frame */
	srand( (cg.time % 10000000) + cg.timeFraction * 1000);
	/* Prepare to render the screen */		
	trap_S_ClearLoopingSounds(qfalse);
	trap_R_ClearScene();
	/* Update demo related information */
	trap_SetUserCmdValue( cg.weaponSelect, 1 );
	demoProcessSnapShots( hadSkip );
	if ( !cg.snap ) {
		CG_DrawInformation();
		return;
	}
	CG_PreparePacketEntities( );
	CG_DemosUpdatePlayer( );
	chaseUpdate( demo.play.time, demo.play.fraction );
	cameraUpdate( demo.play.time, demo.play.fraction );
	dofUpdate( demo.play.time, demo.play.fraction );
	demoEffectUpdate( demo.play.time, demo.play.fraction );
	cg.clientFrame++;
	// update cg.predictedPlayerState
	CG_InterpolatePlayerState( qfalse );
	BG_PlayerStateToEntityState( &cg.predictedPlayerState, &cg.predictedPlayerEntity.currentState, qfalse );
	if ( cg.cpma.detected ) {
		if ( cg.predictedPlayerState.pm_type >= 4 )
			cg.predictedPlayerEntity.currentState.eType = ET_INVISIBLE;
	}
	cg.predictedPlayerEntity.currentValid = qtrue;
	VectorCopy( cg.predictedPlayerEntity.currentState.pos.trBase, cg.predictedPlayerEntity.lerpOrigin );
	VectorCopy( cg.predictedPlayerEntity.currentState.apos.trBase, cg.predictedPlayerEntity.lerpAngles );

	inwater = demoSetupView();

	CG_TileClear();
	trap_FX_Begin( cg.time, cg.timeFraction );

	scriptRun( hadSkip );
	CG_AddPacketEntities();
	CG_AddMarks();
	CG_AddParticles ();
	CG_AddLocalEntities();
	
	if ( cg.playerCent == &cg.predictedPlayerEntity ) {
		// warning sounds when powerup is wearing off
		CG_PowerupTimerSounds();
		CG_AddViewWeapon( &cg.predictedPlayerState  );
	} else if ( cg.playerCent && cg.playerCent->currentState.number < MAX_CLIENTS )  {
		CG_AddViewWeaponDirect( cg.playerCent );
	}
	trap_S_UpdateEntityPosition(ENTITYNUM_NONE, cg.refdef.vieworg);
	CG_PlayBufferedSounds();
	CG_PlayBufferedVoiceChats();
		
	cg.refdef.time = cg.time;
	memcpy( cg.refdef.areamask, cg.snap->areamask, sizeof( cg.refdef.areamask ) );
	/* Render some extra demo related stuff */
	if (!captureFrame) {
		switch (demo.editType) {
		case editCamera:
			cameraDraw( demo.play.time, demo.play.fraction );
			break;
		case editChase:
			chaseDraw( demo.play.time, demo.play.fraction );
			break;
		case editDof:
			dofDraw( demo.play.time, demo.play.fraction );
			break;
		case editEffect:
			demoEffectDraw( demo.play.time, demo.play.fraction );
			break;
		}
		/* Add bounding boxes for easy aiming */
		if ( demo.editType && ( demo.cmd.buttons & BUTTON_ATTACK) && ( demo.cmd.buttons & BUTTON_AFFIRMATIVE)  ) {
			int i;
			centity_t *targetCent;
			for (i = 0;i<MAX_GENTITIES;i++) {
	            targetCent = demoTargetEntity( i );
				if (targetCent) {
					vec3_t container, traceStart, traceImpact, forward;
					const float *color;

					demoCentityBoxSize( targetCent, container );
					VectorSubtract( demo.viewOrigin, targetCent->lerpOrigin, traceStart );
					AngleVectors( demo.viewAngles, forward, 0, 0 );
					if (BoxTraceImpact( traceStart, forward, container, traceImpact )) {
						color = colorRed;
					} else {
						color = colorYellow;
					}
					demoDrawBox( targetCent->lerpOrigin, container, color );
				}
			}

		}
		if ( mov_gridStep.value > 0 && mov_gridRange.value > 0) {
			vec4_t color;
			vec3_t offset;
			qhandle_t shader = trap_R_RegisterShader( "mme/gridline" );
			color[0] = color[1] = color[2] = 1;
			color[3] = 0;
			offset[0] = offset[1] = offset[2] = 0;
			Q_parseColor( mov_gridColor.string, ospColors, color );
			demoDrawGrid( demo.viewOrigin, color, offset, mov_gridWidth.value, mov_gridStep.value, mov_gridRange.value, shader );
		}
	}
	
	if (frameSpeed > 5)
		frameSpeed = 5;

	trap_S_UpdateScale( frameSpeed );
	if (cg.playerCent && cg.predictedPlayerState.pm_type == PM_INTERMISSION) {
		entityNum = cg.snap->ps.clientNum;
	} else if (cg.playerCent) {
		entityNum = cg.playerCent->currentState.number;
	} else {
		entityNum = ENTITYNUM_NONE;
	}
	trap_S_Respatialize( entityNum, cg.refdef.vieworg, cg.refdef.viewaxis, inwater);

	trap_FX_End();
	if (captureFrame && stereoSep > 0.0f)
		trap_Cvar_Set("r_stereoSeparation", va("%f", -stereoSep));
	trap_MME_TimeFraction(cg.timeFraction);
	trap_R_RenderScene( &cg.refdef );

	if ( demo.viewType == viewChase && cg.playerCent && ( cg.playerCent->currentState.number < MAX_CLIENTS ) )
		CG_Draw2D();
	
//	CG_DrawSmallString( 0, 0, va( "height %d", cg.playerCent->pe.viewHeight ), 1 );

	if (captureFrame) {
		char fileName[MAX_OSPATH];
		Com_sprintf( fileName, sizeof( fileName ), "capture/%s/%s", mme_demoFileName.string, mov_captureName.string );
		trap_MME_Capture( fileName, captureFPS, demo.viewFocus, demo.viewRadius );
		if ( mov_captureCamera.integer )
			demoAddViewPos( fileName, demo.viewOrigin, demo.viewAngles, demo.viewFov );
	} else {
		if (demo.editType && !cg.playerCent)
			demoDrawCrosshair();
		hudDraw();
		if (demo.editType) {
			demoDrawProgress(trap_MME_ProgressTime());
		}
	}
//checkCaptureEnd:
	if ( demo.capture.active && demo.capture.locked && demo.play.time > demo.capture.end  ) {
		Com_Printf( "Capturing ended\n" );
		if (demo.autoLoad) {
			trap_SendConsoleCommand( "disconnect\n" );
		} 
		demo.capture.active = qfalse;
	}
}
Ejemplo n.º 13
0
void demoProcessSnapShots( qboolean hadSkip ) {
	int i;
	snapshot_t		*snap;

	// see what the latest snapshot the client system has is
	trap_GetCurrentSnapshotNumber( &cg.latestSnapshotNum, &cg.latestSnapshotTime );
	if (hadSkip || !cg.snap) {
		cgs.processedSnapshotNum = max(cg.latestSnapshotNum - 3, -1);
		if (cg.nextSnap)
			cgs.serverCommandSequence = cg.nextSnap->serverCommandSequence;
		else if (cg.snap)
			cgs.serverCommandSequence = cg.snap->serverCommandSequence;
		cg.snap = 0;
		cg.nextSnap = 0;
		cg.nextNextSnap = 0;

		for (i=-1;i<MAX_GENTITIES;i++) {
			centity_t *cent = i < 0 ? &cg.predictedPlayerEntity : &cg_entities[i];
			cent->trailTime = cg.time;
			cent->snapShotTime = cg.time;
			cent->currentValid = qfalse;
			cent->interpolate = qfalse;
			cent->muzzleFlashTime = cg.time - MUZZLE_FLASH_TIME - 1;
			cent->previousEvent = 0;
			if (cent->currentState.eType == ET_PLAYER) {
				memset( &cent->pe, 0, sizeof( cent->pe ) );
				cent->pe.legs.yawAngle = cent->lerpAngles[YAW];
				cent->pe.torso.yawAngle = cent->lerpAngles[YAW];
				cent->pe.torso.pitchAngle = cent->lerpAngles[PITCH];
			}
		}
	}

	/* Check if we have some transition between snapsnots */
	if (!cg.snap) {
		entityState_t pes;
		snap = CG_ReadNextSnapshot();
		if (!snap)
			return;
		cg.snap = snap;
		BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );
		CG_BuildSolidList();
		CG_ExecuteNewServerCommands( snap->serverCommandSequence );
		CG_UpdateTps( snap, qtrue );
		BG_PlayerStateToEntityStateExtraPolate( &snap->ps, &pes, snap->ps.commandTime, qfalse );
		CG_AddToHistory( snap->serverTime, &pes, &cg_entities[snap->ps.clientNum] );
		for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
			entityState_t *state = &cg.snap->entities[ i ];
			centity_t *cent = &cg_entities[ state->number ];
			memcpy(&cent->currentState, state, sizeof(entityState_t));
			cent->interpolate = qfalse;
			cent->currentValid = qtrue;
			CG_AddToHistory( snap->serverTime, state, cent );
			if (cent->currentState.eType > ET_EVENTS)
				cent->previousEvent = 1;
			else 
				cent->previousEvent = cent->currentState.event;
		}
	}
	do {
		if (!cg.nextSnap) {
			snap = CG_ReadNextSnapshot();
			if (!snap)
				break;
			CG_SetNextSnap( snap );
		}
		if ( !cg.nextNextSnap ) {
			snap = CG_ReadNextSnapshot();
			if ( !snap )
				break;
			CG_SetNextNextSnap( snap );
		}
		if ( cg.timeFraction >= cg.snap->serverTime - cg.time && cg.timeFraction < cg.nextSnap->serverTime - cg.time )
			break;
		//Todo our own transition checking if we wanna hear certain sounds
		CG_TransitionSnapshot();
	} while (1);
}
Ejemplo n.º 14
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	int		i;
	clientPersistant_t	saved;
	clientSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
//	char	userinfo[MAX_INFO_STRING];
	forcedata_t			savedForce;
	void		*ghoul2save;
	int		saveSaberNum = ENTITYNUM_NONE;
	int		wDisable = 0;

	index = ent - g_entities;
	client = ent->client;

	if (client->ps.fd.forceDoInit)
	{ //force a reread of force powers
		WP_InitForcePowers( ent );
		client->ps.fd.forceDoInit = 0;
	}
	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer == GT_CTF || g_gametype.integer == GT_CTY) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	}
	else if (g_gametype.integer == GT_SAGA)
	{
		spawnPoint = SelectSagaSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	}
	else {
		do {
			// the first spawn should be at a good looking spot
			if ( !client->pers.initialSpawn && client->pers.localClient ) {
				client->pers.initialSpawn = qtrue;
				spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles );
			} else {
				// don't spawn near existing origin if possible
				spawnPoint = SelectSpawnPoint ( 
					client->ps.origin, 
					spawn_origin, spawn_angles);
			}

			// Tim needs to prevent bots from spawning at the initial point
			// on q3dm0...
			if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}
			// just to be symetric, we have a nohumans option...
			if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}

			break;

		} while ( 1 );
	}
	client->pers.teamState.state = TEAM_ACTIVE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
//	savedAreaBits = client->areabits;
	accuracy_hits = client->accuracy_hits;
	accuracy_shots = client->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = client->ps.persistant[i];
	}
	eventSequence = client->ps.eventSequence;

	savedForce = client->ps.fd;

	ghoul2save = client->ghoul2;

	saveSaberNum = client->ps.saberEntityNum;

	memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?

	//rww - Don't wipe the ghoul2 instance or the animation data
	client->ghoul2 = ghoul2save;

	//or the saber ent num
	client->ps.saberEntityNum = saveSaberNum;

	client->ps.fd = savedForce;

	client->ps.duelIndex = ENTITYNUM_NONE;

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
//	client->areabits = savedAreaBits;
	client->accuracy_hits = accuracy_hits;
	client->accuracy_shots = accuracy_shots;
	client->lastkilled_client = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		client->ps.persistant[i] = persistant[i];
	}
	client->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	client->ps.persistant[PERS_SPAWN_COUNT]++;
	client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;

	client->airOutTime = level.time + 12000;

//	trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
	// set max health
	client->pers.maxHealth = 100;//atoi( Info_ValueForKey( userinfo, "handicap" ) );
	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
		client->pers.maxHealth = 100;
	}
	// clear entity values
	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
	client->ps.eFlags = flags;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->r.contents = CONTENTS_BODY;
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	
	VectorCopy (playerMins, ent->r.mins);
	VectorCopy (playerMaxs, ent->r.maxs);

	client->ps.clientNum = index;
	//give default weapons
	client->ps.stats[STAT_WEAPONS] = ( 1 << WP_NONE );

	if (g_gametype.integer == GT_HOLOCRON)
	{
		//always get free saber level 1 in holocron
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );	//these are precached in g_items, ClearRegisteredItems()
	}
	else
	{
		if (client->ps.fd.forcePowerLevel[FP_SABERATTACK])
		{
			client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_SABER );	//these are precached in g_items, ClearRegisteredItems()
		}
		else
		{ //if you don't have saber attack rank then you don't get a saber
			client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
		}
	}

	if (g_gametype.integer == GT_TOURNAMENT)
	{
		wDisable = g_duelWeaponDisable.integer;
	}
	else
	{
		wDisable = g_weaponDisable.integer;
	}

	if (!wDisable || !(wDisable & (1 << WP_BRYAR_PISTOL)))
	{
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
	}
	else if (g_gametype.integer == GT_JEDIMASTER)
	{
		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_BRYAR_PISTOL );
	}

	if (g_gametype.integer == GT_JEDIMASTER)
	{
		client->ps.stats[STAT_WEAPONS] &= ~(1 << WP_SABER);
		client->ps.stats[STAT_WEAPONS] |= (1 << WP_STUN_BATON);
	}

	if (client->ps.stats[STAT_WEAPONS] & (1 << WP_BRYAR_PISTOL))
	{
		client->ps.weapon = WP_BRYAR_PISTOL;
	}
	else if (client->ps.stats[STAT_WEAPONS] & (1 << WP_SABER))
	{
		client->ps.weapon = WP_SABER;
	}
	else
	{
		client->ps.weapon = WP_STUN_BATON;
	}

	/*
	client->ps.stats[STAT_HOLDABLE_ITEMS] |= ( 1 << HI_BINOCULARS );
	client->ps.stats[STAT_HOLDABLE_ITEM] = BG_GetItemIndexByTag(HI_BINOCULARS, IT_HOLDABLE);
	*/

	client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
	client->ps.stats[STAT_HOLDABLE_ITEM] = 0;

	if ( client->sess.sessionTeam == TEAM_SPECTATOR )
	{
		client->ps.stats[STAT_WEAPONS] = 0;
		client->ps.stats[STAT_HOLDABLE_ITEMS] = 0;
		client->ps.stats[STAT_HOLDABLE_ITEM] = 0;
	}

	client->ps.ammo[AMMO_BLASTER] = 100; //ammoData[AMMO_BLASTER].max; //100 seems fair.
//	client->ps.ammo[AMMO_POWERCELL] = ammoData[AMMO_POWERCELL].max;
//	client->ps.ammo[AMMO_FORCE] = ammoData[AMMO_FORCE].max;
//	client->ps.ammo[AMMO_METAL_BOLTS] = ammoData[AMMO_METAL_BOLTS].max;
//	client->ps.ammo[AMMO_ROCKETS] = ammoData[AMMO_ROCKETS].max;
/*
	client->ps.stats[STAT_WEAPONS] = ( 1 << WP_BRYAR_PISTOL);
	if ( g_gametype.integer == GT_TEAM ) {
		client->ps.ammo[WP_BRYAR_PISTOL] = 50;
	} else {
		client->ps.ammo[WP_BRYAR_PISTOL] = 100;
	}
*/
	client->ps.rocketLockIndex = MAX_CLIENTS;
	client->ps.rocketLockTime = 0;

	//rww - Set here to initialize the circling seeker drone to off.
	//A quick note about this so I don't forget how it works again:
	//ps.genericEnemyIndex is kept in sync between the server and client.
	//When it gets set then an entitystate value of the same name gets
	//set along with an entitystate flag in the shared bg code. Which
	//is why a value needs to be both on the player state and entity state.
	//(it doesn't seem to just carry over the entitystate value automatically
	//because entity state value is derived from player state data or some
	//such)
	client->ps.genericEnemyIndex = -1;

	client->ps.isJediMaster = qfalse;

	client->ps.fallingToDeath = 0;

	//Do per-spawn force power initialization
	WP_SpawnInitForcePowers( ent );

	// health will count down towards max_health
	ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH];// * 1.25;		Boot - no extra 25 hp on start.

	// Start with a small amount of armor as well.
	client->ps.stats[STAT_ARMOR] = client->ps.stats[STAT_MAX_HEALTH] * 0.25;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	SetClientViewAngle( ent, spawn_angles );

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {

	} else {
		G_KillBox( ent );
		trap_LinkEntity (ent);

		// force the base weapon up
		client->ps.weapon = WP_BRYAR_PISTOL;
		client->ps.weaponstate = FIRST_WEAPON;

	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	client->latched_buttons = 0;

	// set default animations
	client->ps.torsoAnim = WeaponReadyAnim[client->ps.weapon];
	client->ps.legsAnim = WeaponReadyAnim[client->ps.weapon];

	if ( level.intermissiontime ) {
		MoveClientToIntermission( ent );
	} else {
		// fire the targets of the spawn point
		G_UseTargets( spawnPoint, ent );

		// select the highest weapon number available, after any
		// spawn given items have fired
		client->ps.weapon = 1;
		for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
			if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
				client->ps.weapon = i;
				break;
			}
		}
	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent-g_entities );

	// positively link the client, even if the command times are weird
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

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

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Ejemplo n.º 15
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	int		i;
	clientPersistant_t	saved;
	clientSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
	char	userinfo[MAX_INFO_STRING];

	index = ent - g_entities;
	client = ent->client;

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else {
		do {
			// the first spawn should be at a good looking spot
			if ( !client->pers.initialSpawn && client->pers.localClient ) {
				client->pers.initialSpawn = qtrue;
				spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles );
			} else {
				// don't spawn near existing origin if possible
				spawnPoint = SelectSpawnPoint ( 
					client->ps.origin, 
					spawn_origin, spawn_angles);
			}

			// Tim needs to prevent bots from spawning at the initial point
			// on q3dm0...
			if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}
			// just to be symetric, we have a nohumans option...
			if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}

			break;

		} while ( 1 );
	}
	client->pers.teamState.state = TEAM_ACTIVE;

	// always clear the kamikaze flag
//	ent->s.eFlags &= ~EF_KAMIKAZE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->s.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
//	savedAreaBits = client->areabits;
	accuracy_hits = client->accuracy_hits;
	accuracy_shots = client->accuracy_shots;

	Com_Memset (client, 0, sizeof(*client));

	client->ps.stats[STAT_ATTACKERCLIENT] = -1;
	client->ps.stats[STAT_INFOCLIENT] = -1;

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
//	client->areabits = savedAreaBits;
	client->accuracy_hits = accuracy_hits;
	client->accuracy_shots = accuracy_shots;
	client->lastkilled_client = -1;

	client->airOutTime = level.time + 12000;

	trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
	// set max health
	client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) );
	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
		client->pers.maxHealth = 100;
	}
	// clear entity values
	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
	ent->s.eFlags = flags;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->r.contents = CONTENTS_BODY;
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	
	VectorCopy (playerMins, ent->r.mins);
	VectorCopy (playerMaxs, ent->r.maxs);

	client->ps.clientNum = index;



	// health will count down towards max_health
	ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	SetClientViewAngle( ent, spawn_angles );

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {

	} else {
		G_KillBox( ent );
		trap_LinkEntity (ent);

	

	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	client->latched_buttons = 0;

	if ( level.intermissiontime ) {
		MoveClientToIntermission( ent );
	} else {
		// fire the targets of the spawn point
		G_UseTargets( spawnPoint, ent );

	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent-g_entities );

	// positively link the client, even if the command times are weird
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );

	ent->s.modelindex = G_ModelIndex("models/human/coxswain.tik");
}
Ejemplo n.º 16
0
/*
==================
CG_SetInitialSnapshot

This will only happen on the very first snapshot, or
on tourney restarts.  All other times will use
CG_TransitionSnapshot instead.
==================
*/
void CG_SetInitialSnapshot(snapshot_t *snap) {
	int       i;
	centity_t *cent;
	char      buff[16];

	cg.snap = snap;

	BG_PlayerStateToEntityState(&snap->ps, &cg_entities[snap->ps.clientNum].currentState, qfalse);

	// sort out solid entities
	CG_BuildSolidList();

	CG_ExecuteNewServerCommands(snap->serverCommandSequence);

	// set our local weapon selection pointer to
	// what the server has indicated the current weapon is
	CG_Respawn();

	for (i = 0 ; i < cg.snap->numEntities ; ++i) {
		entityState_t *state = &cg.snap->entities[i];
		cent = &cg_entities[state->number];

		memcpy(&cent->currentState, state, sizeof (entityState_t));
		cent->interpolate  = qfalse;
		cent->currentValid = qtrue;

		CG_ResetEntity(cent);

		// check for events
		CG_CheckEvents(cent);
	}

	cg_fxflags = 0;

	trap_Cvar_VariableStringBuffer("r_oldMode", buff, sizeof (buff));
	if (atoi(buff)) {
		// Arnout: confirmation screen
		trap_UI_Popup(UIMENU_INGAME);
	} else if (!cg.demoPlayback) {
		static char prevmap[64] = { 0 };
		char        curmap[64];

		trap_Cvar_VariableStringBuffer("mapname", curmap, 64);

		if (Q_stricmp(curmap, prevmap)) {
			strcpy(prevmap, curmap);
			if (cgs.campaignInfoLoaded) {
				if (!cg.showGameView) {
					CG_LimboMenu_f();
				}
			}
		}
	}

	// OSP - remove motd window
	if (cg.motdWindow != NULL) {
		CG_windowFree(cg.motdWindow);
		cg.motdWindow = NULL;
	}

	// Activate alternate input handler during demo playback
	if (cg.demoPlayback) {
		CG_keyOn_f();
		if (demo_infoWindow.integer > 0) {
			CG_ShowHelp_On(&cg.demohelpWindow);
		}
	}
}
Ejemplo n.º 17
0
/*
===================
CG_TransitionSnapshot

The transition point from snap to nextSnap has passed
===================
*/
static void CG_TransitionSnapshot(void) {
	snapshot_t *oldFrame;
	int        i;

	if (!cg.snap) {
		CG_Error("CG_TransitionSnapshot: NULL cg.snap");
	}
	if (!cg.nextSnap) {
		CG_Error("CG_TransitionSnapshot: NULL cg.nextSnap");
	}

	// execute any server string commands before transitioning entities
	CG_ExecuteNewServerCommands(cg.nextSnap->serverCommandSequence);

	// if we had a map_restart, set everthing with initial

	if (!cg.snap || !cg.nextSnap) {
		return;
	}

	// rain - I hate doing things like this for enums.  Oh well.
	memset(&oldValid, 0, sizeof (oldValid));

	// clear the currentValid flag for all entities in the existing snapshot
	for (i = 0 ; i < cg.snap->numEntities ; ++i) {
		centity_t *cent = &cg_entities[cg.snap->entities[i].number];
		cent->currentValid                    = qfalse;
		oldValid[cg.snap->entities[i].number] = qtrue;
	}

	// move nextSnap to snap and do the transitions
	oldFrame = cg.snap;
	cg.snap  = cg.nextSnap;

	BG_PlayerStateToEntityState(&cg.snap->ps, &cg_entities[cg.snap->ps.clientNum].currentState, qfalse);
	cg_entities[cg.snap->ps.clientNum].interpolate = qfalse;

	for (i = 0 ; i < cg.snap->numEntities ; ++i) {
		int id = cg.snap->entities[i].number;
		CG_TransitionEntity(&cg_entities[id]);

		// rain - #374 - ent doesn't exist in this frame, reset it.
		// this is to fix the silent landmines bug, which is caused
		// by a stale miscTime in the cent
		if (cg_entities[id].currentValid == qfalse && oldValid[id] == qtrue) {
			CG_ResetEntity(&cg_entities[id]);
		}
	}

	cg.nextSnap = NULL;

	// check for playerstate transition events
	if (oldFrame) {
		playerState_t *ops, *ps;

		ops = &oldFrame->ps;
		ps  = &cg.snap->ps;
		// teleporting checks are irrespective of prediction
		if ((ps->eFlags ^ ops->eFlags) & EF_TELEPORT_BIT) {
			cg.thisFrameTeleport = qtrue;   // will be cleared by prediction code
		}

		// if we are not doing client side movement prediction for any
		// reason, then the client events and view changes will be issued now
		if (cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW)
		    || cg_nopredict.integer) {
			CG_TransitionPlayerState(ps, ops);
		}
	}
}
Ejemplo n.º 18
0
/*
===========
PlayerSpawn

Called every time a player is placed fresh in the world:
after the first PlayerBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void PlayerSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gplayer_t	*player;
	int		i;
	playerPersistant_t	saved;
	playerSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	gentity_t *tent;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
	int 	startWeapon, startAmmo;

	index = ent - g_entities;
	player = ent->player;

	VectorClear(spawn_origin);

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this player
	if ( player->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer >= GT_CTF ) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						player->sess.sessionTeam, 
						player->pers.teamState.state, 
						spawn_origin, spawn_angles,
						!!(ent->r.svFlags & SVF_BOT));
	}
	else
	{
		// the first spawn should be at a good looking spot
		if ( player->pers.initialSpawn && player->pers.localClient )
		{
			spawnPoint = SelectInitialSpawnPoint(spawn_origin, spawn_angles,
							     !!(ent->r.svFlags & SVF_BOT));
		}
		else
		{
			// don't spawn near existing origin if possible
			spawnPoint = SelectSpawnPoint ( 
				player->ps.origin, 
				spawn_origin, spawn_angles, !!(ent->r.svFlags & SVF_BOT));
		}
	}
	player->pers.teamState.state = TEAM_ACTIVE;

	// always clear the kamikaze flag
	ent->s.eFlags &= ~EF_KAMIKAZE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->player->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = player->pers;
	savedSess = player->sess;
	savedPing = player->ps.ping;
//	savedAreaBits = player->areabits;
	accuracy_hits = player->accuracy_hits;
	accuracy_shots = player->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = player->ps.persistant[i];
	}
	eventSequence = player->ps.eventSequence;

	Com_Memset (player, 0, sizeof(*player));

	player->pers = saved;
	player->sess = savedSess;
	player->ps.ping = savedPing;
//	player->areabits = savedAreaBits;
	player->accuracy_hits = accuracy_hits;
	player->accuracy_shots = accuracy_shots;
	player->lastkilled_player = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		player->ps.persistant[i] = persistant[i];
	}
	player->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	player->ps.persistant[PERS_SPAWN_COUNT]++;
	player->ps.persistant[PERS_TEAM] = player->sess.sessionTeam;

	player->airOutTime = level.time + 12000;

	// set max health
	player->pers.maxHealth = PlayerHandicap( player );
	// clear entity values
	player->ps.stats[STAT_MAX_HEALTH] = player->pers.maxHealth;
	player->ps.eFlags = flags;
	player->ps.contents = CONTENTS_BODY;
	player->ps.collisionType = ( g_playerCapsule.integer == 1 ) ? CT_CAPSULE : CT_AABB;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->player = &level.players[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	ent->player->headless = qfalse;
	
	VectorCopy (playerMins, player->ps.mins);
	VectorCopy (playerMaxs, player->ps.maxs);

	player->ps.playerNum = index;

	if ( g_instaGib.integer == 1 ) {
		startWeapon = WP_RAILGUN;
		startAmmo = 50;
	} else if ( g_instaGib.integer == 2 ) {
		startWeapon = WP_ROCKET_LAUNCHER;
		startAmmo = 50;
	} else {
		startWeapon = WP_MACHINEGUN;
		startAmmo = 100;
	}

	player->ps.stats[STAT_WEAPONS] = ( 1 << startWeapon );

	player->ps.ammo[startWeapon] = startAmmo;

	player->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
	player->ps.ammo[WP_GAUNTLET] = -1;
	player->ps.ammo[WP_GRAPPLING_HOOK] = -1;

	// health will count down towards max_health
	ent->health = player->ps.stats[STAT_HEALTH] = player->ps.stats[STAT_MAX_HEALTH] = MAX_HEALTH;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, player->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	player->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( player - level.players, &ent->player->pers.cmd );
	SetPlayerViewAngle( ent, spawn_angles );
	// don't allow full run speed for a bit
	player->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	player->ps.pm_time = 100;

	player->respawnTime = level.time;
	player->inactivityTime = level.time + g_inactivity.integer * 1000;
	player->latched_buttons = 0;

	// set default animations
	player->ps.torsoAnim = TORSO_STAND;
	player->ps.legsAnim = LEGS_IDLE;

	if (!level.intermissiontime) {
		if (ent->player->sess.sessionTeam != TEAM_SPECTATOR) {
			G_KillBox(ent);
			// force the base weapon up
			player->ps.weapon = startWeapon;
			player->ps.weaponstate = WEAPON_READY;
			// fire the targets of the spawn point
			G_UseTargets(spawnPoint, ent);
			// select the highest weapon number available, after any spawn given items have fired
			player->ps.weapon = 1;

			for (i = WP_NUM_WEAPONS - 1 ; i > 0 ; i--) {
				if (player->ps.stats[STAT_WEAPONS] & (1 << i)) {
					player->ps.weapon = i;
					break;
				}
			}
			// positively link the player, even if the command times are weird
			VectorCopy(ent->player->ps.origin, ent->r.currentOrigin);

			tent = G_TempEntity(ent->player->ps.origin, EV_PLAYER_TELEPORT_IN);
			tent->s.playerNum = ent->s.playerNum;

			trap_LinkEntity (ent);
		}
	} else {
		// move players to intermission
		MovePlayerToIntermission(ent);
	}
	// run a player frame to drop exactly to the floor,
	// initialize animations and other things
	player->ps.commandTime = level.time - 100;
	ent->player->pers.cmd.serverTime = level.time;
	PlayerThink( ent-g_entities );
	// run the presend to set anything else, follow spectators wait
	// until all players have been reconnected after map_restart
	if ( ent->player->sess.spectatorState != SPECTATOR_FOLLOW ) {
		PlayerEndFrame( ent );
	}

	// clear entity state values
	BG_PlayerStateToEntityState( &player->ps, &ent->s, qtrue );

	// we don't want players being backward-reconciled to the place they died
	G_ResetHistory( ent );
}
Ejemplo n.º 19
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn(gentity_t *ent) {
	int		index;
	vec3_t	spawn_origin, spawn_angles;
	gclient_t	*client;
	int		i;
	clientPersistant_t	saved;
	clientSession_t		savedSess;
	int		persistant[MAX_PERSISTANT];
	gentity_t	*spawnPoint;
	int		flags;
	int		savedPing;
//	char	*savedAreaBits;
	int		accuracy_hits, accuracy_shots;
	int		eventSequence;
	char	userinfo[MAX_INFO_STRING];
	int		weapon;

	index = ent - g_entities;
	client = ent->client;

	// find a spawn point
	// do it before setting health back up, so farthest
	// ranging doesn't count this client
	if ( client->sess.sessionTeam == TEAM_SPECTATOR ) {
		spawnPoint = SelectSpectatorSpawnPoint ( 
						spawn_origin, spawn_angles);
	} else if (g_gametype.integer >= GT_CTF ) {
		// all base oriented team games use the CTF spawn points
		spawnPoint = SelectCTFSpawnPoint ( 
						client->sess.sessionTeam, 
						client->pers.teamState.state, 
						spawn_origin, spawn_angles);
	} else {
		do {
			// the first spawn should be at a good looking spot
			if ( !client->pers.initialSpawn && client->pers.localClient ) {
				client->pers.initialSpawn = qtrue;
				spawnPoint = SelectInitialSpawnPoint( spawn_origin, spawn_angles );
			} else {
				// don't spawn near existing origin if possible
				spawnPoint = SelectSpawnPoint ( 
					client->ps.origin, 
					spawn_origin, spawn_angles);
			}

			// Tim needs to prevent bots from spawning at the initial point
			// on q3dm0...
			if ( ( spawnPoint->flags & FL_NO_BOTS ) && ( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}
			// just to be symetric, we have a nohumans option...
			if ( ( spawnPoint->flags & FL_NO_HUMANS ) && !( ent->r.svFlags & SVF_BOT ) ) {
				continue;	// try again
			}

			break;

		} while ( 1 );
	}
	client->pers.teamState.state = TEAM_ACTIVE;

	// always clear the kamikaze flag
	ent->s.eFlags &= ~EF_KAMIKAZE;

	// toggle the teleport bit so the client knows to not lerp
	// and never clear the voted flag
	flags = ent->client->ps.eFlags & (EF_TELEPORT_BIT | EF_VOTED | EF_TEAMVOTED);
	flags ^= EF_TELEPORT_BIT;

	// clear everything but the persistant data

	saved = client->pers;
	savedSess = client->sess;
	savedPing = client->ps.ping;
//	savedAreaBits = client->areabits;
	accuracy_hits = client->accuracy_hits;
	accuracy_shots = client->accuracy_shots;
	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		persistant[i] = client->ps.persistant[i];
	}
	eventSequence = client->ps.eventSequence;

	memset (client, 0, sizeof(*client)); // bk FIXME: Com_Memset?

	client->pers = saved;
	client->sess = savedSess;
	client->ps.ping = savedPing;
//	client->areabits = savedAreaBits;
	client->accuracy_hits = accuracy_hits;
	client->accuracy_shots = accuracy_shots;
	client->lastkilled_client = -1;

	for ( i = 0 ; i < MAX_PERSISTANT ; i++ ) {
		client->ps.persistant[i] = persistant[i];
	}
	client->ps.eventSequence = eventSequence;
	// increment the spawncount so the client will detect the respawn
	client->ps.persistant[PERS_SPAWN_COUNT]++;
	client->ps.persistant[PERS_TEAM] = client->sess.sessionTeam;

	client->airOutTime = level.time + 12000;

	trap_GetUserinfo( index, userinfo, sizeof(userinfo) );
	// set max health
	client->pers.maxHealth = atoi( Info_ValueForKey( userinfo, "handicap" ) );
	if ( client->pers.maxHealth < 1 || client->pers.maxHealth > 100 ) {
		client->pers.maxHealth = 100;
	}
	// clear entity values
	client->ps.stats[STAT_MAX_HEALTH] = client->pers.maxHealth;
	client->ps.eFlags = flags;

	ent->s.groundEntityNum = ENTITYNUM_NONE;
	ent->client = &level.clients[index];
	ent->takedamage = qtrue;
	ent->inuse = qtrue;
	ent->classname = "player";
	ent->r.contents = CONTENTS_BODY;
	ent->clipmask = MASK_PLAYERSOLID;
	ent->die = player_die;
	ent->waterlevel = 0;
	ent->watertype = 0;
	ent->flags = 0;
	
	VectorCopy (playerMins, ent->r.mins);
	VectorCopy (playerMaxs, ent->r.maxs);

	client->ps.clientNum = index;


	if (IsBot(client->ps.clientNum)) {
		weapon = WP_LIGHTNING;
		client->ps.stats[STAT_WEAPONS] = ( 1 << weapon );
		client->ps.ammo[weapon] = -1;
	} else {
		weapon = WP_MACHINEGUN;
		client->ps.stats[STAT_WEAPONS] = ( 1 << weapon );
		client->ps.ammo[weapon] = 100;

		client->ps.stats[STAT_WEAPONS] |= ( 1 << WP_GAUNTLET );
		client->ps.ammo[WP_GAUNTLET] = -1;
		client->ps.ammo[WP_GRAPPLING_HOOK] = -1;
	}

	// health will count down towards max_health
	ent->health = client->ps.stats[STAT_HEALTH] = client->ps.stats[STAT_MAX_HEALTH] + 25;

	G_SetOrigin( ent, spawn_origin );
	VectorCopy( spawn_origin, client->ps.origin );

	// the respawned flag will be cleared after the attack and jump keys come up
	client->ps.pm_flags |= PMF_RESPAWNED;

	trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
	SetClientViewAngle( ent, spawn_angles );

	if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) {

	} else {
		G_KillBox( ent );
		trap_LinkEntity (ent);

		// force the base weapon up
		client->ps.weapon = weapon;
		client->ps.weaponstate = WEAPON_READY;

	}

	// don't allow full run speed for a bit
	client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
	client->ps.pm_time = 100;

	client->respawnTime = level.time;
	client->inactivityTime = level.time + g_inactivity.integer * 1000;
	client->latched_buttons = 0;

	// set default animations
	client->ps.torsoAnim = TORSO_STAND;
	client->ps.legsAnim = LEGS_IDLE;

	if ( level.intermissiontime ) {
		MoveClientToIntermission( ent );
	} else {
		// fire the targets of the spawn point
		G_UseTargets( spawnPoint, ent );

		// select the highest weapon number available, after any
		// spawn given items have fired
		client->ps.weapon = weapon;
		for ( i = WP_NUM_WEAPONS - 1 ; i > 0 ; i-- ) {
			if ( client->ps.stats[STAT_WEAPONS] & ( 1 << i ) ) {
				client->ps.weapon = i;
				break;
			}
		}
	}

	// run a client frame to drop exactly to the floor,
	// initialize animations and other things
	client->ps.commandTime = level.time - 100;
	ent->client->pers.cmd.serverTime = level.time;
	ClientThink( ent-g_entities );

	// positively link the client, even if the command times are weird
	if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) {
		BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
		VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
		trap_LinkEntity( ent );
	}

	// run the presend to set anything else
	ClientEndFrame( ent );

	// clear entity state values
	BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
}
Ejemplo n.º 20
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 );
}
Ejemplo 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);
}
Ejemplo n.º 22
0
/*
==================
CG_SetInitialSnapshot

This will only happen on the very first snapshot, or
on tourney restarts.  All other times will use
CG_TransitionSnapshot instead.

FIXME: Also called by map_restart?
==================
*/
void CG_SetInitialSnapshot( snapshot_t *snap ) {
	char buf[64];
	int i;
	centity_t       *cent;
	entityState_t   *state;

	cg.snap = snap;

	BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );

	// sort out solid entities
	CG_BuildSolidList();

	CG_ExecuteNewServerCommands( snap->serverCommandSequence );

	trap_SendClientCommand( "fogswitch 0" );   // clear it out so the set below will take

	trap_Cvar_VariableStringBuffer( "r_savegameFogColor", buf, sizeof( buf ) );
	trap_Cvar_Set( "r_savegameFogColor", "0" );
	if ( strlen( buf ) > 1 ) {
		if ( !Q_stricmp( buf, "none" ) ) {
			trap_SendClientCommand( "fogswitch 0" );   // 'off'
		} else {
			trap_SendClientCommand( va( "fogswitch %s", buf ) );
		}
	} else {
		trap_Cvar_VariableStringBuffer( "r_mapFogColor", buf, sizeof( buf ) );
		trap_SendClientCommand( va( "fogswitch %s", buf ) );
	}

	// set our local weapon selection pointer to
	// what the server has indicated the current weapon is
	CG_Respawn();

	for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
		state = &cg.snap->entities[ i ];
		cent = &cg_entities[ state->number ];

		memcpy( &cent->currentState, state, sizeof( entityState_t ) );
		//cent->currentState = *state;
		cent->interpolate = qfalse;
		cent->currentValid = qtrue;

		CG_ResetEntity( cent );

		// check for events
		CG_CheckEvents( cent );
	}

	// DHM - Nerve :: Set cg.clientNum so that it may be used elsewhere
	cg.clientNum = snap->ps.clientNum;

	// NERVE - SMF
	{
		static char prevmap[64] = { 0 };
		char curmap[64];

		trap_Cvar_VariableStringBuffer( "mapname", curmap, 64 );

		if ( cgs.gametype == GT_WOLF && Q_stricmp( curmap, prevmap ) ) {
			strcpy( prevmap, curmap );
			trap_SendConsoleCommand( "openLimboMenu\n" );
		}
	}
	// -NERVE - SMF
}
Ejemplo n.º 23
0
/*
===========
ClientSpawn

Called every time a client is placed fresh in the world:
after the first ClientBegin, and after each respawn
Initializes all non-persistant parts of playerState
============
*/
void ClientSpawn( gentity_t *ent, gentity_t *spawn, vec3_t origin, vec3_t angles )
{
    int                 index;
    vec3_t              spawn_origin, spawn_angles;
    gclient_t           *client;
    int                 i;
    clientPersistant_t  saved;
    clientSession_t     savedSess;
    int                 persistant[ MAX_PERSISTANT ];
    gentity_t           *spawnPoint = NULL;
    int                 flags;
    int                 savedPing;
    int                 teamLocal;
    int                 eventSequence;
    char                userinfo[ MAX_INFO_STRING ];
    vec3_t              up = { 0.0f, 0.0f, 1.0f };
    int                 maxAmmo, maxClips;
    weapon_t            weapon;

    index = ent - g_entities;
    client = ent->client;

    teamLocal = client->pers.teamSelection;

    //if client is dead and following teammate, stop following before spawning
    if( client->sess.spectatorClient != -1 )
    {
        client->sess.spectatorClient = -1;
        client->sess.spectatorState = SPECTATOR_FREE;
    }

    // only start client if chosen a class and joined a team
    if( client->pers.classSelection == PCL_NONE && teamLocal == TEAM_NONE )
        client->sess.spectatorState = SPECTATOR_FREE;
    else if( client->pers.classSelection == PCL_NONE )
        client->sess.spectatorState = SPECTATOR_LOCKED;

    // if client is dead and following teammate, stop following before spawning
    if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
        G_StopFollowing( ent );

    if( origin != NULL )
        VectorCopy( origin, spawn_origin );

    if( angles != NULL )
        VectorCopy( angles, spawn_angles );

    // find a spawn point
    // do it before setting health back up, so farthest
    // ranging doesn't count this client
    if( client->sess.spectatorState != SPECTATOR_NOT )
    {
        if( teamLocal == TEAM_NONE )
            spawnPoint = G_SelectSpectatorSpawnPoint( spawn_origin, spawn_angles );
        else if( teamLocal == TEAM_ALIENS )
            spawnPoint = G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
        else if( teamLocal == TEAM_HUMANS )
            spawnPoint = G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
    }
    else
    {
        if( spawn == NULL )
        {
            G_Error( "ClientSpawn: spawn is NULL\n" );
            return;
        }

        spawnPoint = spawn;

        if( ent != spawn )
        {
            //start spawn animation on spawnPoint
            G_SetBuildableAnim( spawnPoint, BANIM_SPAWN1, qtrue );

            if( spawnPoint->buildableTeam == TEAM_ALIENS )
                spawnPoint->clientSpawnTime = ALIEN_SPAWN_REPEAT_TIME;
            else if( spawnPoint->buildableTeam == TEAM_HUMANS )
                spawnPoint->clientSpawnTime = HUMAN_SPAWN_REPEAT_TIME;
        }
    }

    // toggle the teleport bit so the client knows to not lerp
    flags = ( ent->client->ps.eFlags & EF_TELEPORT_BIT ) ^ EF_TELEPORT_BIT;
    G_UnlaggedClear( ent );

    // clear everything but the persistant data

    saved = client->pers;
    savedSess = client->sess;
    savedPing = client->ps.ping;

    for( i = 0; i < MAX_PERSISTANT; i++ )
        persistant[ i ] = client->ps.persistant[ i ];

    eventSequence = client->ps.eventSequence;
    memset( client, 0, sizeof( *client ) );

    client->pers = saved;
    client->sess = savedSess;
    client->ps.ping = savedPing;
    client->lastkilled_client = -1;

    for( i = 0; i < MAX_PERSISTANT; i++ )
        client->ps.persistant[ i ] = persistant[ i ];

    client->ps.eventSequence = eventSequence;

    // increment the spawncount so the client will detect the respawn
    client->ps.persistant[ PERS_SPAWN_COUNT ]++;
    client->ps.persistant[ PERS_SPECSTATE ] = client->sess.spectatorState;

    client->airOutTime = level.time + 12000;

    trap_GetUserinfo( index, userinfo, sizeof( userinfo ) );
    client->ps.eFlags = flags;

    //Com_Printf( "ent->client->pers->pclass = %i\n", ent->client->pers.classSelection );

    ent->s.groundEntityNum = ENTITYNUM_NONE;
    ent->client = &level.clients[ index ];
    ent->takedamage = qtrue;
    ent->inuse = qtrue;
    ent->classname = "player";
    ent->r.contents = CONTENTS_BODY;
    ent->clipmask = MASK_PLAYERSOLID;
    ent->die = player_die;
    ent->waterlevel = 0;
    ent->watertype = 0;
    ent->flags = 0;

    // calculate each client's acceleration
    ent->evaluateAcceleration = qtrue;

    client->ps.stats[ STAT_MISC ] = 0;

    client->ps.eFlags = flags;
    client->ps.clientNum = index;

    BG_ClassBoundingBox( ent->client->pers.classSelection, ent->r.mins, ent->r.maxs, NULL, NULL, NULL );

    if( client->sess.spectatorState == SPECTATOR_NOT )
        client->ps.stats[ STAT_MAX_HEALTH ] =
            BG_Class( ent->client->pers.classSelection )->health;
    else
        client->ps.stats[ STAT_MAX_HEALTH ] = 100;

    // clear entity values
    if( ent->client->pers.classSelection == PCL_HUMAN )
    {
        BG_AddUpgradeToInventory( UP_MEDKIT, client->ps.stats );
        weapon = client->pers.humanItemSelection;
    }
    else if( client->sess.spectatorState == SPECTATOR_NOT )
        weapon = BG_Class( ent->client->pers.classSelection )->startWeapon;
    else
        weapon = WP_NONE;

    maxAmmo = BG_Weapon( weapon )->maxAmmo;
    maxClips = BG_Weapon( weapon )->maxClips;
    client->ps.stats[ STAT_WEAPON ] = weapon;
    client->ps.ammo = maxAmmo;
    client->ps.clips = maxClips;

    // We just spawned, not changing weapons
    client->ps.persistant[ PERS_NEWWEAPON ] = 0;

    ent->client->ps.stats[ STAT_CLASS ] = ent->client->pers.classSelection;
    ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;

    ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
    ent->client->ps.stats[ STAT_STATE ] = 0;
    VectorSet( ent->client->ps.grapplePoint, 0.0f, 0.0f, 1.0f );

    // health will count down towards max_health
    ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; //* 1.25;

    //if evolving scale health
    if( ent == spawn )
    {
        ent->health *= ent->client->pers.evolveHealthFraction;
        client->ps.stats[ STAT_HEALTH ] *= ent->client->pers.evolveHealthFraction;
    }

    //clear the credits array
    for( i = 0; i < MAX_CLIENTS; i++ )
        ent->credits[ i ] = 0;

    client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;

    G_SetOrigin( ent, spawn_origin );
    VectorCopy( spawn_origin, client->ps.origin );

#define UP_VEL  150.0f
#define F_VEL   50.0f

    //give aliens some spawn velocity
    if( client->sess.spectatorState == SPECTATOR_NOT &&
            client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
    {
        if( ent == spawn )
        {
            //evolution particle system
            G_AddPredictableEvent( ent, EV_ALIEN_EVOLVE, DirToByte( up ) );
        }
        else
        {
            spawn_angles[ YAW ] += 180.0f;
            AngleNormalize360( spawn_angles[ YAW ] );

            if( spawnPoint->s.origin2[ 2 ] > 0.0f )
            {
                vec3_t  forward, dir;

                AngleVectors( spawn_angles, forward, NULL, NULL );
                VectorScale( forward, F_VEL, forward );
                VectorAdd( spawnPoint->s.origin2, forward, dir );
                VectorNormalize( dir );

                VectorScale( dir, UP_VEL, client->ps.velocity );
            }

            G_AddPredictableEvent( ent, EV_PLAYER_RESPAWN, 0 );
        }
    }
    else if( client->sess.spectatorState == SPECTATOR_NOT &&
             client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
    {
        spawn_angles[ YAW ] += 180.0f;
        AngleNormalize360( spawn_angles[ YAW ] );
    }

    // the respawned flag will be cleared after the attack and jump keys come up
    client->ps.pm_flags |= PMF_RESPAWNED;

    trap_GetUsercmd( client - level.clients, &ent->client->pers.cmd );
    G_SetClientViewAngle( ent, spawn_angles );

    if( client->sess.spectatorState == SPECTATOR_NOT )
    {
        trap_LinkEntity( ent );

        // force the base weapon up
        if( client->pers.teamSelection == TEAM_HUMANS )
            G_ForceWeaponChange( ent, weapon );

        client->ps.weaponstate = WEAPON_READY;
    }

    // don't allow full run speed for a bit
    client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
    client->ps.pm_time = 100;

    client->respawnTime = level.time;
    ent->nextRegenTime = level.time;

    client->inactivityTime = level.time + g_inactivity.integer * 1000;
    client->latched_buttons = 0;

    // set default animations
    client->ps.torsoAnim = TORSO_STAND;
    client->ps.legsAnim = LEGS_IDLE;

    if( level.intermissiontime )
        MoveClientToIntermission( ent );
    else
    {
        // fire the targets of the spawn point
        if( !spawn )
            G_UseTargets( spawnPoint, ent );

        client->ps.weapon = client->ps.stats[ STAT_WEAPON ];
    }

    // run a client frame to drop exactly to the floor,
    // initialize animations and other things
    client->ps.commandTime = level.time - 100;
    ent->client->pers.cmd.serverTime = level.time;
    ClientThink( ent-g_entities );

    // positively link the client, even if the command times are weird
    if( client->sess.spectatorState == SPECTATOR_NOT )
    {
        BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );
        VectorCopy( ent->client->ps.origin, ent->r.currentOrigin );
        trap_LinkEntity( ent );
    }

    // must do this here so the number of active clients is calculated
    CalculateRanks( );

    // run the presend to set anything else
    ClientEndFrame( ent );

    // clear entity state values
    BG_PlayerStateToEntityState( &client->ps, &ent->s, qtrue );

    client->pers.infoChangeTime = level.time;
}
Ejemplo n.º 24
0
/*
===================
CG_TransitionSnapshot

The transition point from snap to nextSnap has passed
===================
*/
static void CG_TransitionSnapshot()
{
	centity_t  *cent;
	snapshot_t *oldFrame;
	int        oldWeapon;

	if ( !cg.snap )
	{
		Sys::Drop( "CG_TransitionSnapshot: NULL cg.snap" );
	}

	if ( !cg.nextSnap )
	{
		Sys::Drop( "CG_TransitionSnapshot: NULL cg.nextSnap" );
	}

	// execute any server string commands before transitioning entities
	CG_ExecuteServerCommands( cg.nextSnap );

	// clear the currentValid flag for all entities in the existing snapshot
	for ( unsigned i = 0; i < cg.snap->entities.size(); i++ )
	{
		cent = &cg_entities[ cg.snap->entities[ i ].number ];
		cent->currentValid = false;
	}

	// move nextSnap to snap and do the transitions
	oldFrame = cg.snap;
	cg.snap = cg.nextSnap;

	// Need to store the previous weapon because BG_PlayerStateToEntityState might change it
	// so the CG_OnPlayerWeaponChange callback is never called
	oldWeapon = oldFrame->ps.weapon;

	BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, false );
	cg_entities[ cg.snap->ps.clientNum ].interpolate = false;

	for ( unsigned i = 0; i < cg.snap->entities.size(); i++ )
	{
		cent = &cg_entities[ cg.snap->entities[ i ].number ];
		CG_TransitionEntity( cent );

		// remember time of snapshot this entity was last updated in
		cent->snapShotTime = cg.snap->serverTime;
	}

	cg.nextSnap = nullptr;

	// check for playerstate transition events
	playerState_t *ops = &oldFrame->ps, *ps = &cg.snap->ps;

	// teleporting checks are irrespective of prediction
	if ((ps->eFlags ^ ops->eFlags) & EF_TELEPORT_BIT) {
		cg.thisFrameTeleport = true; // will be cleared by prediction code
	}

	// if we are not doing client side movement prediction for any
	// reason, then the client events and view changes will be issued now
	if (cg.demoPlayback || (cg.snap->ps.pm_flags & PMF_FOLLOW) ||
			cg_nopredict.integer || cg.pmoveParams.synchronous) {
		CG_TransitionPlayerState(ps, ops);
	}

	// Callbacks for changes in playerState like weapon/class/team
	if (oldWeapon != ps->weapon) {
		CG_OnPlayerWeaponChange();
	}

	if (ops->stats[STAT_ITEMS] != ps->stats[STAT_ITEMS]) {
		CG_OnPlayerUpgradeChange();
	}
}
Ejemplo n.º 25
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;
}
Ejemplo n.º 26
0
qboolean G_xpsave_load(gentity_t *ent)
{
	int i, j;
	qboolean found = qfalse, XPSMuted = qfalse;
	int clientNum;
	g_xpsave_t *x = g_xpsaves[0];
	time_t t;
	char agestr[MAX_STRING_CHARS];
	//char desc[64];
	// josh: Increased this
	// josh: TODO: tjw? What is this desc thing for?
	char desc[115];
	int age;
	int eff_XPSaveMaxAge_xp = G_getXPSaveMaxAge_xp();
	int eff_XPSaveMaxAge = G_getXPSaveMaxAge();
	float startxptotal = 0.0f;

	if(!ent || !ent->client)
		return qfalse;
	if(!(g_XPSave.integer & XPSF_ENABLE))
		return qfalse;	
	if(!time(&t))
		return qfalse;

	desc[0] = '\0';
	clientNum = ent - g_entities;
	
	for(i=0; g_xpsaves[i]; i++) {
		if(!Q_stricmp(g_xpsaves[i]->guid, 
			ent->client->sess.guid)) {
			found = qtrue;
			x = g_xpsaves[i];
			break;
		}
	}
	if(!found)
		return qfalse;

	age = t - x->time;
	if(age > eff_XPSaveMaxAge) {
		return qfalse;
	}

	if(age <= eff_XPSaveMaxAge_xp) {
		for(i=0; i<SK_NUM_SKILLS; i++) {
			ent->client->sess.skillpoints[i] = x->skill[i];
			// pheno: fix for session startxptotal value
			startxptotal += x->skill[i];
		}
		ent->client->sess.startxptotal = startxptotal;
		ent->client->ps.stats[STAT_XP] = 
			(int)ent->client->sess.startxptotal;
		Q_strcat(desc, sizeof(desc), "XP/");
		if((g_XPDecay.integer & XPDF_ENABLE) &&
			!(g_XPDecay.integer & XPDF_NO_DISCONNECT_DECAY)) {
			G_XPDecay(ent, age, qtrue);
		}
	}

	ent->client->sess.overall_killrating = x->kill_rating;
	ent->client->sess.overall_killvariance = x->kill_variance;
	//ent->client->sess.playerrating = x->playerrating;
	ent->client->sess.rating = x->rating;
	ent->client->sess.rating_variance = x->rating_variance;

	for(i=0; i<SK_NUM_SKILLS; i++) {
		for(j=0; j<NUM_SKILL_LEVELS; j++) {
			ent->client->sess.pr_skill_updates[i][j] =
				x->pr_skill_updates[i][j];
			ent->client->sess.pr_skill[i][j] =
				x->pr_skill[i][j];
		}
	}

	Q_strcat(desc, sizeof(desc), "ratings/");
	
	if(x->mutetime != 0) {
		if(x->mutetime < 0){
			ent->client->sess.auto_unmute_time = -1;
			XPSMuted = qtrue;
		}else if(x->mutetime > t){
			ent->client->sess.auto_unmute_time = (level.time + 1000*(x->mutetime - t));
			XPSMuted = qtrue;;
		}

		if(XPSMuted == qtrue){
			CP("print \"^5You've been muted by XPSave\n\"" );
			Q_strcat(desc, sizeof(desc), "mute/");
		}
	}

	ent->client->sess.hits = x->hits;
	ent->client->sess.team_hits = x->team_hits;
	G_CalcRank(ent->client);
	BG_PlayerStateToEntityState(&ent->client->ps,
			&ent->s,
			level.time,
			qtrue);
	// tjw: trim last / from desc
	if(strlen(desc))
		desc[strlen(desc)-1] = '\0';
	G_shrubbot_duration(age, agestr, sizeof(agestr)); 
	CP(va(
		"print \"^3server: loaded stored ^7%s^3 state from %s ago\n\"",
		desc,
		agestr));

	// josh: check and update if disconnect found
	// cs: not for bots since it is pointless for them, plus we never want SetTeam failing for them
	if (!(ent->r.svFlags & SVF_BOT)) {
		Reconnect(x,ent);
	}
	return qtrue;
}
Ejemplo n.º 27
0
/*
==================
CG_SetInitialSnapshot

This will only happen on the very first snapshot, or
on tourney restarts.  All other times will use 
CG_TransitionSnapshot instead.

FIXME: Also called by map_restart?
==================
*/
void CG_SetInitialSnapshot( snapshot_t *snap ) {
	int				i;
	centity_t		*cent;
	entityState_t	*state;
	char			buff[16];

	cg.snap = snap;

//	trap_S_ClearSounds( qtrue );

	BG_PlayerStateToEntityState( &snap->ps, &cg_entities[ snap->ps.clientNum ].currentState, qfalse );

	// sort out solid entities
	CG_BuildSolidList();

	CG_ExecuteNewServerCommands( snap->serverCommandSequence );

	// set our local weapon selection pointer to
	// what the server has indicated the current weapon is
	CG_Respawn( qfalse );

	for ( i = 0 ; i < cg.snap->numEntities ; i++ ) {
		state = &cg.snap->entities[ i ];
		cent = &cg_entities[ state->number ];

		memcpy(&cent->currentState, state, sizeof(entityState_t));
		//cent->currentState = *state;
		cent->interpolate = qfalse;
		cent->currentValid = qtrue;

		CG_ResetEntity( cent );

		// check for events
		CG_CheckEvents( cent );
	}

	cg_fxflags = 0;


	trap_Cvar_VariableStringBuffer( "r_oldMode", buff, sizeof(buff) );
	if( atoi(buff) ) {
		// Arnout: confirmation screen
		trap_UI_Popup( UIMENU_INGAME );
	} else if(cg.demoPlayback) {
		ccInitial = qtrue;
	} else {
		static char prevmap[64] = { 0 };
		char curmap[64];

		trap_Cvar_VariableStringBuffer( "mapname", curmap, 64 );

		if ( Q_stricmp( curmap, prevmap ) ) {
			strcpy( prevmap, curmap );
			if(cgs.campaignInfoLoaded) {
				if( !cg.showGameView ) {
					CG_LimboMenu_f();
				}
/*			} else {
				ccInitial = qtrue;

				// Start the Initial Camera if specified
				CG_StartInitialCamera(); */
			}
		}
	}

	// OSP - remove motd window
	if(cg.motdWindow != NULL) {
		CG_windowFree(cg.motdWindow);
		cg.motdWindow = NULL;
	}

	// Activate alternate input handler during demo playback
	if(cg.demoPlayback) {
		CG_keyOn_f();
		if(demo_infoWindow.integer > 0) {
			CG_ShowHelp_On(&cg.demohelpWindow);
		}
	}
	// OSP

#if __MACOS__
#ifdef GAMERANGER
	// LBO 12/13/04. Add support for GameRanger team voice IDs
	GRSetMyTeamID(cg.snap->ps.persistant[PERS_TEAM]);
#endif
#endif
}
Ejemplo n.º 28
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;

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

	if ( !ent->aiCharacter ) {
		// turn off any expired powerups
		for ( i = 0 ; i < MAX_POWERUPS ; i++ ) {

			if ( i == PW_FIRE ||             // these aren't dependant on level.time
				 i == PW_ELECTRIC ||
				 i == PW_BREATHER ||
				 i == PW_NOFATIGUE ) {

				continue;
			}

			if ( ent->client->ps.powerups[ i ] < level.time ) {
				ent->client->ps.powerups[ i ] = 0;
			}
		}
	}

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

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

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

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

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

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

	G_SetClientSound( ent );

	// set the latest infor

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

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

	// 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;
	}

	if ( ent->health > 0 && StuckInClient( ent ) ) {
		G_DPrintf( "%s is stuck in a client.\n", ent->client->pers.netname );
		ent->r.contents = CONTENTS_CORPSE;
	}

	if ( g_gametype.integer >= GT_WOLF && ent->health > 0 && ent->r.contents == CONTENTS_CORPSE ) {
		WolfReviveBbox( ent );
	}

	// DHM - Nerve :: Reset 'count2' for flamethrower
	if ( !( ent->client->buttons & BUTTON_ATTACK ) ) {
		ent->count2 = 0;
	}
	// dhm
}
Ejemplo n.º 29
0
/*
===================
CG_TransitionSnapshot

The transition point from snap to nextSnap has passed
===================
*/
static void CG_TransitionSnapshot( void )
{
	centity_t  *cent;
	snapshot_t *oldFrame;
	int        i;

	if ( !cg.snap )
	{
		CG_Error( "CG_TransitionSnapshot: NULL cg.snap" );
	}

	if ( !cg.nextSnap )
	{
		CG_Error( "CG_TransitionSnapshot: NULL cg.nextSnap" );
	}

	// execute any server string commands before transitioning entities
	CG_ExecuteNewServerCommands( cg.nextSnap->serverCommandSequence );

	// clear the currentValid flag for all entities in the existing snapshot
	for ( i = 0; i < cg.snap->numEntities; i++ )
	{
		cent = &cg_entities[ cg.snap->entities[ i ].number ];
		cent->currentValid = qfalse;
	}

	// move nextSnap to snap and do the transitions
	oldFrame = cg.snap;
	cg.snap = cg.nextSnap;

	BG_PlayerStateToEntityState( &cg.snap->ps, &cg_entities[ cg.snap->ps.clientNum ].currentState, qfalse );
	cg_entities[ cg.snap->ps.clientNum ].interpolate = qfalse;

	for ( i = 0; i < cg.snap->numEntities; i++ )
	{
		cent = &cg_entities[ cg.snap->entities[ i ].number ];
		CG_TransitionEntity( cent );

		// remember time of snapshot this entity was last updated in
		cent->snapShotTime = cg.snap->serverTime;
	}

	cg.nextSnap = NULL;

	// check for playerstate transition events
	if ( oldFrame )
	{
		playerState_t *ops, *ps;

		ops = &oldFrame->ps;
		ps = &cg.snap->ps;

		// teleporting checks are irrespective of prediction
		if ( ( ps->eFlags ^ ops->eFlags ) & EF_TELEPORT_BIT )
		{
			cg.thisFrameTeleport = qtrue; // will be cleared by prediction code
		}

		// if we are not doing client side movement prediction for any
		// reason, then the client events and view changes will be issued now
		if ( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ||
		     cg_nopredict.integer || cg_synchronousClients.integer )
		{
			CG_TransitionPlayerState( ps, ops );
		}
	}
}
Ejemplo 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 );
}