From 425decdf7e9284d15aa726e3ae96b9942fb0e3ea Mon Sep 17 00:00:00 2001 From: IronClawTrem Date: Sun, 16 Feb 2020 03:40:06 +0000 Subject: create tremded branch --- src/game/g_active.c | 1329 ++++++++++++++++++++++----------------------------- 1 file changed, 565 insertions(+), 764 deletions(-) (limited to 'src/game/g_active.c') diff --git a/src/game/g_active.c b/src/game/g_active.c index b5df273..d978a4a 100644 --- a/src/game/g_active.c +++ b/src/game/g_active.c @@ -1,13 +1,14 @@ /* =========================================================================== Copyright (C) 1999-2005 Id Software, Inc. -Copyright (C) 2000-2006 Tim Angus +Copyright (C) 2000-2013 Darklegion Development +Copyright (C) 2015-2019 GrangerHub This file is part of Tremulous. Tremulous is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as -published by the Free Software Foundation; either version 2 of the License, +published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version. Tremulous is distributed in the hope that it will be @@ -16,8 +17,8 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License -along with Tremulous; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA +along with Tremulous; if not, see + =========================================================================== */ @@ -40,7 +41,7 @@ void P_DamageFeedback( gentity_t *player ) vec3_t angles; client = player->client; - if( client->ps.pm_type == PM_DEAD ) + if( !PM_Alive( client->ps.pm_type ) ) return; // total points of damage shot at the player this frame @@ -129,7 +130,7 @@ void P_WorldEffects( gentity_t *ent ) // play a gurp sound instead of a normal pain sound if( ent->health <= ent->damage ) G_Sound( ent, CHAN_VOICE, G_SoundIndex( "*drown.wav" ) ); - else if( rand( ) & 1 ) + else if( rand( ) < RAND_MAX / 2 + 1 ) G_Sound( ent, CHAN_VOICE, G_SoundIndex( "sound/player/gurp1.wav" ) ); else G_Sound( ent, CHAN_VOICE, G_SoundIndex( "sound/player/gurp2.wav" ) ); @@ -191,59 +192,74 @@ void G_SetClientSound( gentity_t *ent ) //============================================================== -static void G_ClientShove( gentity_t *ent, gentity_t *victim ) +/* +============== +ClientShove +============== +*/ +static int GetClientMass( gentity_t *ent ) { - vec3_t dir, push; - int entMass = 200, vicMass = 200; - - // shoving enemies changes gameplay too much - if( !OnSameTeam( ent, victim ) ) - return; + int entMass = 100; - if ( ( victim->client->ps.weapon >= WP_ABUILD ) && - ( victim->client->ps.weapon <= WP_HBUILD ) && - ( victim->client->ps.stats[ STAT_BUILDABLE ] != BA_NONE ) ) - return; - - // alien mass is directly related to their health points - // human mass is 200, double for bsuit - if( ent->client->pers.teamSelection == PTE_ALIENS ) - { - entMass = BG_FindHealthForClass( ent->client->pers.classSelection ); - } - else if( ent->client->pers.teamSelection == PTE_HUMANS ) + if( ent->client->pers.teamSelection == TEAM_ALIENS ) + entMass = BG_Class( ent->client->pers.classSelection )->health; + else if( ent->client->pers.teamSelection == TEAM_HUMANS ) { if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, ent->client->ps.stats ) ) entMass *= 2; } else + return 0; + return entMass; +} + +static void ClientShove( gentity_t *ent, gentity_t *victim ) +{ + vec3_t dir, push; + float force; + int entMass, vicMass; + + // Don't push if the entity is not trying to move + if( !ent->client->pers.cmd.rightmove && !ent->client->pers.cmd.forwardmove && + !ent->client->pers.cmd.upmove ) return; - if( victim->client->pers.teamSelection == PTE_ALIENS ) - { - vicMass = BG_FindHealthForClass( victim->client->pers.classSelection ); - } - else if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, - victim->client->ps.stats ) ) - { - vicMass *= 2; - } + // Cannot push enemy players unless they are walking on the player + if( !OnSameTeam( ent, victim ) && + victim->client->ps.groundEntityNum != ent - g_entities ) + return; + // Shove force is scaled by relative mass + entMass = GetClientMass( ent ); + vicMass = GetClientMass( victim ); if( vicMass <= 0 || entMass <= 0 ) return; + force = g_shove.value * entMass / vicMass; + if( force < 0 ) + force = 0; + if( force > 150 ) + force = 150; + // Give the victim some shove velocity VectorSubtract( victim->r.currentOrigin, ent->r.currentOrigin, dir ); VectorNormalizeFast( dir ); - - // don't break the dretch elevator - if( fabs( dir[ 2 ] ) > fabs( dir[ 0 ] ) && fabs( dir[ 2 ] ) > fabs( dir[ 1 ] ) ) - return; - - VectorScale( dir, - ( g_shove.value * ( ( float )entMass / ( float )vicMass ) ), push ); - VectorAdd( victim->client->ps.velocity, push, - victim->client->ps.velocity ); - + VectorScale( dir, force, push ); + VectorAdd( victim->client->ps.velocity, push, victim->client->ps.velocity ); + + // Set the pmove timer so that the other client can't cancel + // out the movement immediately + if( !victim->client->ps.pm_time ) + { + int time; + + time = force * 2 + 0.5f; + if( time < 50 ) + time = 50; + if( time > 200 ) + time = 200; + victim->client->ps.pm_time = time; + victim->client->ps.pm_flags |= PMF_TIME_KNOCKBACK; + } } /* @@ -253,45 +269,38 @@ ClientImpacts */ void ClientImpacts( gentity_t *ent, pmove_t *pm ) { - int i, j; + int i; trace_t trace; gentity_t *other; + // clear a fake trace struct for touch function memset( &trace, 0, sizeof( trace ) ); for( i = 0; i < pm->numtouch; i++ ) { - for( j = 0; j < i; j++ ) - { - if( pm->touchents[ j ] == pm->touchents[ i ] ) - break; - } - - if( j != i ) - continue; // duplicated - other = &g_entities[ pm->touchents[ i ] ]; // see G_UnlaggedDetectCollisions(), this is the inverse of that. // if our movement is blocked by another player's real position, - // don't use the unlagged position for them because they are + // don't use the unlagged position for them because they are // blocking or server-side Pmove() from reaching it if( other->client && other->client->unlaggedCalc.used ) other->client->unlaggedCalc.used = qfalse; - //charge attack - if( ent->client->ps.weapon == WP_ALEVEL4 && - ent->client->ps.stats[ STAT_MISC ] > 0 && - ent->client->charging ) - ChargeAttack( ent, other ); + // tyrant impact attacks + if( ent->client->ps.weapon == WP_ALEVEL4 ) + { + G_ChargeAttack( ent, other ); + G_CrushAttack( ent, other ); + } + // shove players if( ent->client && other->client ) - G_ClientShove( ent, other ); - - if( !other->touch ) - continue; + ClientShove( ent, other ); - other->touch( other, ent, &trace ); + // touch triggers + if( other->touch ) + other->touch( other, ent, &trace ); } } @@ -316,11 +325,15 @@ void G_TouchTriggers( gentity_t *ent ) if( !ent->client ) return; + // noclipping clients don't activate triggers! + if( ent->client->noclip ) + return; + // dead clients don't activate triggers! if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 ) return; - BG_FindBBoxForClass( ent->client->ps.stats[ STAT_PCLASS ], + BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], pmins, pmaxs, NULL, NULL, NULL ); VectorAdd( ent->client->ps.origin, pmins, mins ); @@ -346,9 +359,7 @@ void G_TouchTriggers( gentity_t *ent ) continue; // ignore most entities if a spectator - if( ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) || - ( ent->client->ps.stats[ STAT_STATE ] & SS_INFESTING ) || - ( ent->client->ps.stats[ STAT_STATE ] & SS_HOVELING ) ) + if( ent->client->sess.spectatorState != SPECTATOR_NOT ) { if( hit->s.eType != ET_TELEPORT_TRIGGER && // this is ugly but adding a new ET_? type will @@ -380,136 +391,123 @@ void SpectatorThink( gentity_t *ent, usercmd_t *ucmd ) { pmove_t pm; gclient_t *client; - qboolean attack1, attack3; - qboolean doPmove = qtrue; + int clientNum; + qboolean attack1, following, queued; client = ent->client; client->oldbuttons = client->buttons; client->buttons = ucmd->buttons; - attack1 = ( ( client->buttons & BUTTON_ATTACK ) && - !( client->oldbuttons & BUTTON_ATTACK ) ); - attack3 = ( ( client->buttons & BUTTON_USE_HOLDABLE ) && - !( client->oldbuttons & BUTTON_USE_HOLDABLE ) ); - - if( level.mapRotationVoteTime ) + attack1 = ( client->buttons & BUTTON_ATTACK ) && + !( client->oldbuttons & BUTTON_ATTACK ); + + // We are in following mode only if we are following a non-spectating client + following = client->sess.spectatorState == SPECTATOR_FOLLOW; + if( following ) { - if( attack1 ) - { - G_IntermissionMapVoteCommand( ent, qtrue, qfalse ); - attack1 = qfalse; - } - if( ( client->buttons & BUTTON_ATTACK2 ) && !( client->oldbuttons & BUTTON_ATTACK2 ) ) - G_IntermissionMapVoteCommand( ent, qfalse, qfalse ); + clientNum = client->sess.spectatorClient; + if( clientNum < 0 || clientNum > level.maxclients || + !g_entities[ clientNum ].client || + g_entities[ clientNum ].client->sess.spectatorState != SPECTATOR_NOT ) + following = qfalse; } - - if( client->sess.spectatorState == SPECTATOR_LOCKED || client->sess.spectatorState == SPECTATOR_FOLLOW ) - client->ps.pm_type = PM_FREEZE; + + // Check to see if we are in the spawn queue + if( client->pers.teamSelection == TEAM_ALIENS ) + queued = G_SearchSpawnQueue( &level.alienSpawnQueue, ent - g_entities ); + else if( client->pers.teamSelection == TEAM_HUMANS ) + queued = G_SearchSpawnQueue( &level.humanSpawnQueue, ent - g_entities ); else - client->ps.pm_type = PM_SPECTATOR; + queued = qfalse; - if ( client->sess.spectatorState == SPECTATOR_FOLLOW ) + // Wants to get out of spawn queue + if( attack1 && queued ) { - gclient_t *cl; - if ( client->sess.spectatorClient >= 0 ) - { - cl = &level.clients[ client->sess.spectatorClient ]; - if ( cl->sess.sessionTeam != TEAM_SPECTATOR ) - doPmove = qfalse; - } + if( client->sess.spectatorState == SPECTATOR_FOLLOW ) + G_StopFollowing( ent ); + if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum ); + else if( client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) + G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum ); + client->pers.classSelection = PCL_NONE; + client->pers.humanItemSelection = WP_NONE; + client->ps.stats[ STAT_CLASS ] = PCL_NONE; + client->ps.pm_flags &= ~PMF_QUEUED; + queued = qfalse; + } + else if( attack1 ) + { + // Wants to get into spawn queue + if( client->sess.spectatorState == SPECTATOR_FOLLOW ) + G_StopFollowing( ent ); + if( client->pers.teamSelection == TEAM_NONE ) + G_TriggerMenu( client->ps.clientNum, MN_TEAM ); + else if( client->pers.teamSelection == TEAM_ALIENS ) + G_TriggerMenu( client->ps.clientNum, MN_A_CLASS ); + else if( client->pers.teamSelection == TEAM_HUMANS ) + G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN ); } - if (doPmove) + // We are either not following anyone or following a spectator + if( !following ) { - client->ps.speed = BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] ); - - // in case the client entered the queue while following a teammate - if( ( client->pers.teamSelection == PTE_ALIENS && - G_SearchSpawnQueue( &level.alienSpawnQueue, ent-g_entities ) ) || - ( client->pers.teamSelection == PTE_HUMANS && - G_SearchSpawnQueue( &level.humanSpawnQueue, ent-g_entities ) ) ) - { - client->ps.pm_flags |= PMF_QUEUED; - } - + if( client->sess.spectatorState == SPECTATOR_LOCKED || + client->sess.spectatorState == SPECTATOR_FOLLOW ) + client->ps.pm_type = PM_FREEZE; + else if( client->noclip ) + client->ps.pm_type = PM_NOCLIP; + else + client->ps.pm_type = PM_SPECTATOR; + + if( queued ) + client->ps.pm_flags |= PMF_QUEUED; + client->ps.speed = client->pers.flySpeed; client->ps.stats[ STAT_STAMINA ] = 0; client->ps.stats[ STAT_MISC ] = 0; - client->ps.stats[ STAT_BUILDABLE ] = 0; - client->ps.stats[ STAT_PCLASS ] = PCL_NONE; + client->ps.stats[ STAT_BUILDABLE ] = BA_NONE; + client->ps.stats[ STAT_CLASS ] = PCL_NONE; client->ps.weapon = WP_NONE; - // set up for pmove + // Set up for pmove memset( &pm, 0, sizeof( pm ) ); pm.ps = &client->ps; + pm.pmext = &client->pmext; pm.cmd = *ucmd; - pm.tracemask = MASK_PLAYERSOLID & ~CONTENTS_BODY; // spectators can fly through bodies + pm.tracemask = ent->clipmask; pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; - // perform a pmove + // Perform a pmove Pmove( &pm ); - // save results of pmove - VectorCopy( client->ps.origin, ent->s.origin ); + // Save results of pmove + VectorCopy( client->ps.origin, ent->s.pos.trBase ); + VectorCopy( client->ps.origin, ent->r.currentOrigin ); + VectorCopy( client->ps.viewangles, ent->r.currentAngles ); + VectorCopy( client->ps.viewangles, ent->s.pos.trBase ); G_TouchTriggers( ent ); trap_UnlinkEntity( ent ); - if( ( attack1 ) && ( client->ps.pm_flags & PMF_QUEUED ) ) - { - if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - G_RemoveFromSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum ); - else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - G_RemoveFromSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum ); - - client->pers.classSelection = PCL_NONE; - client->ps.stats[ STAT_PCLASS ] = PCL_NONE; - } - - if( attack1 && client->pers.classSelection == PCL_NONE ) - { - if( client->pers.teamSelection == PTE_NONE ) - G_TriggerMenu( client->ps.clientNum, MN_TEAM ); - else if( client->pers.teamSelection == PTE_ALIENS ) - G_TriggerMenu( client->ps.clientNum, MN_A_CLASS ); - else if( client->pers.teamSelection == PTE_HUMANS ) - G_TriggerMenu( client->ps.clientNum, MN_H_SPAWN ); - } - - //set the queue position for the client side + // Set the queue position and spawn count for the client side if( client->ps.pm_flags & PMF_QUEUED ) { - if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) { client->ps.persistant[ PERS_QUEUEPOS ] = G_GetPosInSpawnQueue( &level.alienSpawnQueue, client->ps.clientNum ); + client->ps.persistant[ PERS_SPAWNS ] = level.numAlienSpawns; } - else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + else if( client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) { client->ps.persistant[ PERS_QUEUEPOS ] = G_GetPosInSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum ); + client->ps.persistant[ PERS_SPAWNS ] = level.numHumanSpawns; } } } - - else if( attack1 && ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) - { - G_StopFollowing( ent ); - client->pers.classSelection = PCL_NONE; - if( client->pers.teamSelection == PTE_NONE ) - G_TriggerMenu( ent-g_entities, MN_TEAM ); - else if( client->pers.teamSelection == PTE_ALIENS ) - G_TriggerMenu( ent-g_entities, MN_A_CLASS ); - else if( client->pers.teamSelection == PTE_HUMANS ) - G_TriggerMenu( ent-g_entities, MN_H_SPAWN ); - } - - if( attack3 ) - { - G_ToggleFollow( ent ); - } } @@ -521,8 +519,10 @@ ClientInactivityTimer Returns qfalse if the client is dropped ================= */ -qboolean ClientInactivityTimer( gclient_t *client ) +qboolean ClientInactivityTimer( gentity_t *ent ) { + gclient_t *client = ent->client; + if( ! g_inactivity.integer ) { // give everyone some time, so if the operator sets g_inactivity during @@ -540,13 +540,16 @@ qboolean ClientInactivityTimer( gclient_t *client ) } else if( !client->pers.localClient ) { - if( level.time > client->inactivityTime ) + if( level.time > client->inactivityTime && + !G_admin_permission( ent, ADMF_ACTIVITY ) ) { trap_DropClient( client - level.clients, "Dropped due to inactivity" ); return qfalse; } - if( level.time > client->inactivityTime - 10000 && !client->inactivityWarning ) + if( level.time > client->inactivityTime - 10000 && + !client->inactivityWarning && + !G_admin_permission( ent, ADMF_ACTIVITY ) ) { client->inactivityWarning = qtrue; trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" ); @@ -569,203 +572,100 @@ void ClientTimerActions( gentity_t *ent, int msec ) usercmd_t *ucmd; int aForward, aRight; qboolean walking = qfalse, stopped = qfalse, - crouched = qfalse, jumping = qfalse, - strafing = qfalse; + crouched = qfalse; + int i; ucmd = &ent->client->pers.cmd; aForward = abs( ucmd->forwardmove ); aRight = abs( ucmd->rightmove ); - client = ent->client; - client->time100 += msec; - client->time1000 += msec; - client->time10000 += msec; - if( aForward == 0 && aRight == 0 ) stopped = qtrue; else if( aForward <= 64 && aRight <= 64 ) walking = qtrue; - if( aRight > 0 ) - strafing = qtrue; - - if( ucmd->upmove > 0 ) - jumping = qtrue; - else if( ent->client->ps.pm_flags & PMF_DUCKED ) + if( ucmd->upmove <= 0 && ent->client->ps.pm_flags & PMF_DUCKED ) crouched = qtrue; + client = ent->client; + client->time100 += msec; + client->time1000 += msec; + client->time10000 += msec; + while ( client->time100 >= 100 ) { + weapon_t weapon = BG_GetPlayerWeapon( &client->ps ); + client->time100 -= 100; - //if not trying to run then not trying to sprint - if( walking || stopped ) - client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; - - if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) - client->ps.stats[ STAT_STATE ] &= ~SS_SPEEDBOOST; - - if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && !crouched ) - { - //subtract stamina - if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) - client->ps.stats[ STAT_STAMINA ] -= STAMINA_LARMOUR_TAKE; - else - client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE; - - if( client->ps.stats[ STAT_STAMINA ] < -MAX_STAMINA ) - client->ps.stats[ STAT_STAMINA ] = -MAX_STAMINA; - } - - if( walking || crouched ) - { - //restore stamina - client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE; - - if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) - client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; - } - else if( stopped ) - { - //restore stamina faster + // Restore or subtract stamina + if( stopped || client->ps.pm_type == PM_JETPACK ) client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE; - - if( client->ps.stats[ STAT_STAMINA ] > MAX_STAMINA ) - client->ps.stats[ STAT_STAMINA ] = MAX_STAMINA; - } - - //client is charging up for a pounce - if( client->ps.weapon == WP_ALEVEL3 || client->ps.weapon == WP_ALEVEL3_UPG ) - { - int pounceSpeed = 0; - - if( client->ps.weapon == WP_ALEVEL3 ) - pounceSpeed = LEVEL3_POUNCE_SPEED; - else if( client->ps.weapon == WP_ALEVEL3_UPG ) - pounceSpeed = LEVEL3_POUNCE_UPG_SPEED; - - if( client->ps.stats[ STAT_MISC ] < pounceSpeed && ucmd->buttons & BUTTON_ATTACK2 ) - client->ps.stats[ STAT_MISC ] += ( 100.0f / (float)LEVEL3_POUNCE_CHARGE_TIME ) * pounceSpeed; - - if( !( ucmd->buttons & BUTTON_ATTACK2 ) ) - { - if( client->pmext.pouncePayload > 0 ) - client->allowedToPounce = qtrue; - } - - if( client->ps.stats[ STAT_MISC ] > pounceSpeed ) - client->ps.stats[ STAT_MISC ] = pounceSpeed; - } - - //client is charging up for a... charge - if( client->ps.weapon == WP_ALEVEL4 ) + else if( ( client->ps.stats[ STAT_STATE ] & SS_SPEEDBOOST ) && + !( client->buttons & BUTTON_WALKING ) ) // walk overrides sprint + client->ps.stats[ STAT_STAMINA ] -= STAMINA_SPRINT_TAKE; + else if( walking || crouched ) + client->ps.stats[ STAT_STAMINA ] += STAMINA_WALK_RESTORE; + + // Check stamina limits + if( client->ps.stats[ STAT_STAMINA ] > STAMINA_MAX ) + client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX; + else if( client->ps.stats[ STAT_STAMINA ] < -STAMINA_MAX ) + client->ps.stats[ STAT_STAMINA ] = -STAMINA_MAX; + + if( weapon == WP_ABUILD || weapon == WP_ABUILD2 || + client->ps.stats[ STAT_WEAPON ] == WP_HBUILD ) { - if( client->ps.stats[ STAT_MISC ] < LEVEL4_CHARGE_TIME && ucmd->buttons & BUTTON_ATTACK2 && - !client->charging ) - { - client->charging = qfalse; //should already be off, just making sure - client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; - - if( ucmd->forwardmove > 0 ) - { - //trigger charge sound...is quite annoying - //if( client->ps.stats[ STAT_MISC ] <= 0 ) - // G_AddEvent( ent, EV_LEV4_CHARGE_PREPARE, 0 ); - - client->ps.stats[ STAT_MISC ] += (int)( 100 * (float)LEVEL4_CHARGE_CHARGE_RATIO ); - - if( client->ps.stats[ STAT_MISC ] > LEVEL4_CHARGE_TIME ) - client->ps.stats[ STAT_MISC ] = LEVEL4_CHARGE_TIME; - } - else - client->ps.stats[ STAT_MISC ] = 0; - } - - if( !( ucmd->buttons & BUTTON_ATTACK2 ) || client->charging || - client->ps.stats[ STAT_MISC ] == LEVEL4_CHARGE_TIME ) - { - if( client->ps.stats[ STAT_MISC ] > LEVEL4_MIN_CHARGE_TIME ) - { + // Update build timer + if( client->ps.stats[ STAT_MISC ] > 0 ) client->ps.stats[ STAT_MISC ] -= 100; - if( client->charging == qfalse ) - G_AddEvent( ent, EV_LEV4_CHARGE_START, 0 ); - - client->charging = qtrue; - client->ps.stats[ STAT_STATE ] |= SS_CHARGING; - - //if the charger has stopped moving take a chunk of charge away - if( VectorLength( client->ps.velocity ) < 64.0f || aRight ) - client->ps.stats[ STAT_MISC ] = client->ps.stats[ STAT_MISC ] / 2; - - //can't charge backwards - if( ucmd->forwardmove < 0 ) - client->ps.stats[ STAT_MISC ] = 0; - } - else - client->ps.stats[ STAT_MISC ] = 0; - - - if( client->ps.stats[ STAT_MISC ] <= 0 ) - { + if( client->ps.stats[ STAT_MISC ] < 0 ) client->ps.stats[ STAT_MISC ] = 0; - client->charging = qfalse; - client->ps.stats[ STAT_STATE ] &= ~SS_CHARGING; - } - } - } - - //client is charging up an lcannon - if( client->ps.weapon == WP_LUCIFER_CANNON ) - { - int ammo; - - ammo = client->ps.ammo; - - if( client->ps.stats[ STAT_MISC ] < LCANNON_TOTAL_CHARGE && ucmd->buttons & BUTTON_ATTACK ) - client->ps.stats[ STAT_MISC ] += ( 100.0f / LCANNON_CHARGE_TIME ) * LCANNON_TOTAL_CHARGE; - - if( client->ps.stats[ STAT_MISC ] > LCANNON_TOTAL_CHARGE ) - client->ps.stats[ STAT_MISC ] = LCANNON_TOTAL_CHARGE; - - if( client->ps.stats[ STAT_MISC ] > ( ammo * LCANNON_TOTAL_CHARGE ) / 10 ) - client->ps.stats[ STAT_MISC ] = ammo * LCANNON_TOTAL_CHARGE / 10; } - switch( client->ps.weapon ) + switch( weapon ) { case WP_ABUILD: case WP_ABUILD2: case WP_HBUILD: - case WP_HBUILD2: - //set validity bit on buildable + + // Set validity bit on buildable if( ( client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE ) { - int dist = BG_FindBuildDistForClass( ent->client->ps.stats[ STAT_PCLASS ] ); - vec3_t dummy; + int dist = BG_Class( ent->client->ps.stats[ STAT_CLASS ] )->buildDist; + vec3_t dummy, dummy2; + int dummy3; if( G_CanBuild( ent, client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT, - dist, dummy ) == IBE_NONE ) + dist, dummy, dummy2, &dummy3 ) == IBE_NONE ) client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT; else client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT; - } - case WP_BLASTER: - //update build timer - if( client->ps.stats[ STAT_MISC ] > 0 ) - client->ps.stats[ STAT_MISC ] -= 100; - - if( client->ps.stats[ STAT_MISC ] < 0 ) - client->ps.stats[ STAT_MISC ] = 0; + // Let the client know which buildables will be removed by building + for( i = 0; i < MAX_MISC; i++ ) + { + if( i < level.numBuildablesForRemoval ) + client->ps.misc[ i ] = level.markedBuildables[ i ]->s.number; + else + client->ps.misc[ i ] = 0; + } + } + else + { + for( i = 0; i < MAX_MISC; i++ ) + client->ps.misc[ i ] = 0; + } break; default: break; } - if( client->ps.stats[ STAT_STATE ] & SS_MEDKIT_ACTIVE ) + if( ent->client->pers.teamSelection == TEAM_HUMANS && + ( client->ps.stats[ STAT_STATE ] & SS_HEALING_2X ) ) { int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime ); @@ -777,9 +677,10 @@ void ClientTimerActions( gentity_t *ent, int msec ) { ent->client->medKitHealthToRestore--; ent->health++; + ent->client->ps.stats[ STAT_HEALTH ] = ent->health; } else - ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; + ent->client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_2X; } else { @@ -792,13 +693,14 @@ void ClientTimerActions( gentity_t *ent, int msec ) { ent->client->medKitHealthToRestore--; ent->health++; + ent->client->ps.stats[ STAT_HEALTH ] = ent->health; client->medKitIncrementTime = level.time + ( remainingStartupTime / MEDKIT_STARTUP_SPEED ); } } else - ent->client->ps.stats[ STAT_STATE ] &= ~SS_MEDKIT_ACTIVE; + ent->client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_2X; } } } @@ -807,20 +709,17 @@ void ClientTimerActions( gentity_t *ent, int msec ) { client->time1000 -= 1000; - //client is poison clouded - if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED ) - G_Damage( ent, client->lastPoisonCloudedClient, client->lastPoisonCloudedClient, NULL, NULL, - LEVEL1_PCLOUD_DMG, 0, MOD_LEVEL1_PCLOUD ); - //client is poisoned if( client->ps.stats[ STAT_STATE ] & SS_POISONED ) { - int damage = ALIEN_POISON_DMG; - + int damage = ALIEN_POISON_DMG; + if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) ) damage -= BSUIT_POISON_PROTECTION; + if( BG_InventoryContainsUpgrade( UP_HELMET, client->ps.stats ) ) damage -= HELMET_POISON_PROTECTION; + if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) ) damage -= LIGHTARMOUR_POISON_PROTECTION; @@ -828,135 +727,37 @@ void ClientTimerActions( gentity_t *ent, int msec ) 0, damage, 0, MOD_POISON ); } - //replenish alien health - if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && - level.surrenderTeam != PTE_ALIENS ) - { - int entityList[ MAX_GENTITIES ]; - vec3_t range = { LEVEL1_REGEN_RANGE, LEVEL1_REGEN_RANGE, LEVEL1_REGEN_RANGE }; - vec3_t mins, maxs; - int i, num; - gentity_t *boostEntity; - float modifier = 1.0f; - - 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++ ) - { - boostEntity = &g_entities[ entityList[ i ] ]; - - if( boostEntity->client && boostEntity->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && - boostEntity->client->ps.stats[ STAT_PCLASS ] == PCL_ALIEN_LEVEL1_UPG ) - { - modifier = LEVEL1_REGEN_MOD; - break; - } - else if( boostEntity->s.eType == ET_BUILDABLE && - boostEntity->s.modelindex == BA_A_BOOSTER && - boostEntity->spawned && boostEntity->health > 0 ) - { - modifier = BOOSTER_REGEN_MOD; - break; - } - } - - if( ent->health > 0 && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] && - !level.paused && - ( ent->lastDamageTime + ALIEN_REGEN_DAMAGE_TIME ) < level.time ) - { - ent->health += BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ) * modifier; - - // if completely healed, cancel retribution - if( ent->health >= client->ps.stats[ STAT_MAX_HEALTH ] ) - { - for( i = 0; i < MAX_CLIENTS; i++ ) - ent->client->tkcredits[ i ] = 0; - } - } - - if( ent->health > client->ps.stats[ STAT_MAX_HEALTH ] ) - ent->health = client->ps.stats[ STAT_MAX_HEALTH ]; - } - - - if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) - { - ent->client->pers.statscounters.timealive++; - level.alienStatsCounters.timealive++; - if( G_BuildableRange( ent->client->ps.origin, 900, BA_A_OVERMIND ) ) - { - ent->client->pers.statscounters.timeinbase++; - level.alienStatsCounters.timeinbase++; - } - if( BG_ClassHasAbility( ent->client->ps.stats[ STAT_PCLASS ], SCA_WALLCLIMBER ) ) - { - ent->client->pers.statscounters.dretchbasytime++; - level.alienStatsCounters.dretchbasytime++; - if( ent->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBING || ent->client->ps.stats[ STAT_STATE ] & SS_WALLCLIMBINGCEILING) - { - ent->client->pers.statscounters.jetpackusewallwalkusetime++; - level.alienStatsCounters.jetpackusewallwalkusetime++; - } - } - } - else if( ent->client->ps.stats[ STAT_HEALTH ] > 0 && ent->client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) - { - ent->client->pers.statscounters.timealive++; - level.humanStatsCounters.timealive++; - if( G_BuildableRange( ent->client->ps.origin, 900, BA_H_REACTOR ) ) - { - ent->client->pers.statscounters.timeinbase++; - level.humanStatsCounters.timeinbase++; - } - if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) ) - { - if( client->ps.pm_type == PM_JETPACK ) - { - ent->client->pers.statscounters.jetpackusewallwalkusetime++; - level.humanStatsCounters.jetpackusewallwalkusetime++; - } - } - } - - // turn off life support when a team admits defeat - if( client->ps.stats[ STAT_PTEAM ] == PTE_ALIENS && - level.surrenderTeam == PTE_ALIENS ) + // turn off life support when a team admits defeat + if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS && + level.surrenderTeam == TEAM_ALIENS ) { G_Damage( ent, NULL, NULL, NULL, NULL, - BG_FindRegenRateForClass( client->ps.stats[ STAT_PCLASS ] ), + BG_Class( client->ps.stats[ STAT_CLASS ] )->regenRate, DAMAGE_NO_ARMOR, MOD_SUICIDE ); } - else if( client->ps.stats[ STAT_PTEAM ] == PTE_HUMANS && - level.surrenderTeam == PTE_HUMANS ) + else if( client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS && + level.surrenderTeam == TEAM_HUMANS ) { G_Damage( ent, NULL, NULL, NULL, NULL, 5, DAMAGE_NO_ARMOR, MOD_SUICIDE ); } - //my new jetpack code - if( mod_jetpackFuel.value >= 10.0f ) { - //if we have jetpack and its on - if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) { - //check if fuels 0 if so deactivate it if not give a 10 second fuel low warning and take JETPACK_USE_RATE from fuel - if( client->jetpackfuel <= 0.0f ) { - BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats ); - } else if( client->jetpackfuel < 10.0f && client->jetpackfuel > 0.0f) { - client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value; - trap_SendServerCommand( client - level.clients, "cp \"^3Fuel ^1Low!!!!!^7\nLand now.\"" ); - } else { - client->jetpackfuel = client->jetpackfuel - mod_jetpackConsume.value; - } + // lose some voice enthusiasm + if( client->voiceEnthusiasm > 0.0f ) + client->voiceEnthusiasm -= VOICE_ENTHUSIASM_DECAY; + else + client->voiceEnthusiasm = 0.0f; - //if jetpack isnt active regenerate fuel and give a message when its full - } else if( BG_InventoryContainsUpgrade( UP_JETPACK, client->ps.stats ) && !BG_UpgradeIsActive( UP_JETPACK, client->ps.stats ) ) { - if( client->jetpackfuel > ( mod_jetpackFuel.value - 10.0f ) && client->jetpackfuel <= mod_jetpackFuel.value ) { - client->jetpackfuel = client->jetpackfuel + mod_jetpackRegen.value; - trap_SendServerCommand( client - level.clients, "cp \"^3Fuel Status: ^2Full!^7\n\"" ); - } else if( client->jetpackfuel < mod_jetpackFuel.value ) { - //regenerate some fuel - client->jetpackfuel = client->jetpackfuel + mod_jetpackRegen.value; - } + client->pers.secondsAlive++; + if( g_freeFundPeriod.integer > 0 && + client->pers.secondsAlive % g_freeFundPeriod.integer == 0 ) + { + // Give clients some credit periodically + if( G_TimeTilSuddenDeath( ) > 0 ) + { + if( client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + G_AddCreditToClient( client, FREEKILL_ALIEN, qtrue ); + else if( client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) + G_AddCreditToClient( client, FREEKILL_HUMAN, qtrue ); } } } @@ -965,21 +766,42 @@ void ClientTimerActions( gentity_t *ent, int msec ) { client->time10000 -= 10000; - if( client->ps.weapon == WP_ALEVEL3_UPG ) + if( ent->client->ps.weapon == WP_ABUILD || + ent->client->ps.weapon == WP_ABUILD2 ) { - int ammo, maxAmmo; + AddScore( ent, ALIEN_BUILDER_SCOREINC ); + } + else if( ent->client->ps.weapon == WP_HBUILD ) + { + AddScore( ent, HUMAN_BUILDER_SCOREINC ); + } - BG_FindAmmoForWeapon( WP_ALEVEL3_UPG, &maxAmmo, NULL ); - ammo = client->ps.ammo; + // Give score to basis that healed other aliens + if( ent->client->pers.hasHealed ) + { + if( client->ps.weapon == WP_ALEVEL1 ) + AddScore( ent, LEVEL1_REGEN_SCOREINC ); + else if( client->ps.weapon == WP_ALEVEL1_UPG ) + AddScore( ent, LEVEL1_UPG_REGEN_SCOREINC ); + + ent->client->pers.hasHealed = qfalse; + } + } - if( ammo < maxAmmo ) + // Regenerate Adv. Dragoon barbs + if( client->ps.weapon == WP_ALEVEL3_UPG ) + { + if( client->ps.ammo < BG_Weapon( WP_ALEVEL3_UPG )->maxAmmo ) + { + if( ent->timestamp + LEVEL3_BOUNCEBALL_REGEN < level.time ) { - ammo++; - client->ps.ammo = ammo; - client->ps.clips = 0; + client->ps.ammo++; + ent->timestamp = level.time; } } - } + else + ent->timestamp = level.time; + } } /* @@ -989,7 +811,6 @@ ClientIntermissionThink */ void ClientIntermissionThink( gclient_t *client ) { - client->ps.eFlags &= ~EF_TALK; client->ps.eFlags &= ~EF_FIRING; client->ps.eFlags &= ~EF_FIRING2; @@ -1020,10 +841,10 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) vec3_t dir; vec3_t point, mins; float fallDistance; - pClass_t class; + class_t class; client = ent->client; - class = client->ps.stats[ STAT_PCLASS ]; + class = client->ps.stats[ STAT_CLASS ]; if( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS ) oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS; @@ -1047,11 +868,11 @@ void ClientEvents( gentity_t *ent, int oldEventSequence ) else if( fallDistance > 1.0f ) fallDistance = 1.0f; - damage = (int)( (float)BG_FindHealthForClass( class ) * - BG_FindFallDamageForClass( class ) * fallDistance ); + damage = (int)( (float)BG_Class( class )->health * + BG_Class( class )->fallDamage * fallDistance ); VectorSet( dir, 0, 0, 1 ); - BG_FindBBoxForClass( class, mins, NULL, NULL, NULL, NULL ); + BG_ClassBoundingBox( class, mins, NULL, NULL, NULL, NULL ); mins[ 0 ] = mins[ 1 ] = 0.0f; VectorAdd( client->ps.origin, mins, point ); @@ -1122,8 +943,8 @@ void SendPendingPredictableEvents( playerState_t *ps ) ============== G_UnlaggedStore - Called on every server frame. Stores position data for the client at that - into client->unlaggedHist[] and the time into level.unlaggedTimes[]. + Called on every server frame. Stores position data for the client at that + into client->unlaggedHist[] and the time into level.unlaggedTimes[]. This data is used by G_UnlaggedCalc() ============== */ @@ -1132,24 +953,24 @@ void G_UnlaggedStore( void ) int i = 0; gentity_t *ent; unlagged_t *save; - + if( !g_unlagged.integer ) return; - level.unlaggedIndex++; + level.unlaggedIndex++; if( level.unlaggedIndex >= MAX_UNLAGGED_MARKERS ) level.unlaggedIndex = 0; level.unlaggedTimes[ level.unlaggedIndex ] = level.time; - + for( i = 0; i < level.maxclients; i++ ) { ent = &g_entities[ i ]; save = &ent->client->unlaggedHist[ level.unlaggedIndex ]; - save->used = qfalse; + save->used = qfalse; if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) ) continue; if( ent->client->pers.connected != CON_CONNECTED ) - continue; + continue; VectorCopy( ent->r.mins, save->mins ); VectorCopy( ent->r.maxs, save->maxs ); VectorCopy( ent->s.pos.trBase, save->origin ); @@ -1160,7 +981,7 @@ void G_UnlaggedStore( void ) /* ============== G_UnlaggedClear - + Mark all unlaggedHist[] markers for this client invalid. Useful for preventing teleporting and death. ============== @@ -1185,14 +1006,14 @@ void G_UnlaggedCalc( int time, gentity_t *rewindEnt ) { int i = 0; gentity_t *ent; - int startIndex = level.unlaggedIndex; - int stopIndex = -1; - int frameMsec = 0; - float lerp = 0.5f; + int startIndex; + int stopIndex; + int frameMsec; + float lerp; if( !g_unlagged.integer ) return; - + // clear any calculated values from a previous run for( i = 0; i < level.maxclients; i++ ) { @@ -1200,13 +1021,18 @@ void G_UnlaggedCalc( int time, gentity_t *rewindEnt ) ent->client->unlaggedCalc.used = qfalse; } - for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ ) + // client is on the current frame, no need for unlagged + if( level.unlaggedTimes[ level.unlaggedIndex ] <= time ) + return; + + startIndex = level.unlaggedIndex; + for( i = 1; i < MAX_UNLAGGED_MARKERS; i++ ) { - if( level.unlaggedTimes[ startIndex ] <= time ) - break; stopIndex = startIndex; if( --startIndex < 0 ) startIndex = MAX_UNLAGGED_MARKERS - 1; + if( level.unlaggedTimes[ startIndex ] <= time ) + break; } if( i == MAX_UNLAGGED_MARKERS ) { @@ -1214,20 +1040,13 @@ void G_UnlaggedCalc( int time, gentity_t *rewindEnt ) // just use the oldest marker with no lerping lerp = 0.0f; } - - // client is on the current frame, no need for unlagged - if( stopIndex == -1 ) - return; - - // lerp between two markers - frameMsec = level.unlaggedTimes[ stopIndex ] - - level.unlaggedTimes[ startIndex ]; - if( frameMsec > 0 ) + else { - lerp = ( float )( time - level.unlaggedTimes[ startIndex ] ) - / ( float )frameMsec; + // lerp between two markers + frameMsec = level.unlaggedTimes[ stopIndex ] - level.unlaggedTimes[ startIndex ]; + lerp = ( float )( time - level.unlaggedTimes[ startIndex ] ) / ( float )frameMsec; } - + for( i = 0; i < level.maxclients; i++ ) { ent = &g_entities[ i ]; @@ -1243,13 +1062,13 @@ void G_UnlaggedCalc( int time, gentity_t *rewindEnt ) continue; // between two unlagged markers - VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].mins, + VectorLerp2( lerp, ent->client->unlaggedHist[ startIndex ].mins, ent->client->unlaggedHist[ stopIndex ].mins, ent->client->unlaggedCalc.mins ); - VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].maxs, + VectorLerp2( lerp, ent->client->unlaggedHist[ startIndex ].maxs, ent->client->unlaggedHist[ stopIndex ].maxs, ent->client->unlaggedCalc.maxs ); - VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].origin, + VectorLerp2( lerp, ent->client->unlaggedHist[ startIndex ].origin, ent->client->unlaggedHist[ stopIndex ].origin, ent->client->unlaggedCalc.origin ); @@ -1268,10 +1087,10 @@ void G_UnlaggedOff( void ) { int i = 0; gentity_t *ent; - + if( !g_unlagged.integer ) return; - + for( i = 0; i < level.maxclients; i++ ) { ent = &g_entities[ i ]; @@ -1304,13 +1123,13 @@ void G_UnlaggedOn( gentity_t *attacker, vec3_t muzzle, float range ) int i = 0; gentity_t *ent; unlagged_t *calc; - + if( !g_unlagged.integer ) return; if( !attacker->client->pers.useUnlagged ) return; - + for( i = 0; i < level.maxclients; i++ ) { ent = &g_entities[ i ]; @@ -1331,7 +1150,7 @@ void G_UnlaggedOn( gentity_t *attacker, vec3_t muzzle, float range ) float maxRadius = ( r1 > r2 ) ? r1 : r2; if( Distance( muzzle, calc->origin ) > range + maxRadius ) - continue; + continue; } // create a backup of the real positions @@ -1355,7 +1174,7 @@ void G_UnlaggedOn( gentity_t *attacker, vec3_t muzzle, float range ) the current time, but only updates other player's positions up to the postition sent in the most recent snapshot. - This allows player X to essentially "move through" the position of player Y + This allows player X to essentially "move through" the position of player Y when player X's cmd is processed with Pmove() on the server. This is because player Y was clipping player X's Pmove() on his client, but when the same cmd is processed with Pmove on the server it is not clipped. @@ -1378,6 +1197,7 @@ static void G_UnlaggedDetectCollisions( gentity_t *ent ) if( !g_unlagged.integer ) return; + if( !ent->client->pers.useUnlagged ) return; @@ -1400,47 +1220,11 @@ static void G_UnlaggedDetectCollisions( gentity_t *ent ) trap_Trace(&tr, ent->client->oldOrigin, ent->r.mins, ent->r.maxs, ent->client->ps.origin, ent->s.number, MASK_PLAYERSOLID ); if( tr.entityNum >= 0 && tr.entityNum < MAX_CLIENTS ) - g_entities[ tr.entityNum ].client->unlaggedCalc.used = qfalse; + g_entities[ tr.entityNum ].client->unlaggedCalc.used = qfalse; G_UnlaggedOff( ); } -/* -============== -ClientGradualFunds - -g_gradualFreeFunds values: -0 - disabled -1 - vanilla behavior -2 - 1 and counters don't reset on death or evolution -3 - 2 and funds are given even during SD -============== -*/ -static void ClientGradualFunds( gentity_t *ent ) -{ - if( !g_gradualFreeFunds.integer ) - return; - - if( ent->client->pers.lastFreekillTime + FREEKILL_PERIOD >= level.time ) - return; - - if( g_suddenDeath.integer && g_gradualFreeFunds.integer < 3 ) - return; - - switch ( ent->client->ps.stats[ STAT_PTEAM ] ) - { - case PTE_ALIENS: - G_AddCreditToClient( ent->client, FREEKILL_ALIEN, qtrue ); - break; - - case PTE_HUMANS: - G_AddCreditToClient( ent->client, FREEKILL_HUMAN, qtrue ); - break; - } - - ent->client->pers.lastFreekillTime += FREEKILL_PERIOD; -} - /* ============== ClientThink @@ -1459,7 +1243,6 @@ void ClientThink_real( gentity_t *ent ) int oldEventSequence; int msec; usercmd_t *ucmd; - int real_pm_type; client = ent->client; @@ -1470,34 +1253,12 @@ void ClientThink_real( gentity_t *ent ) // mark the time, so the connection sprite can be removed ucmd = &ent->client->pers.cmd; - if( client->pers.paused ) - ucmd->forwardmove = ucmd->rightmove = ucmd->upmove = ucmd->buttons = 0; - // 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" ); - } - - // ucmd->serverTime is a client predicted value, but it works for making a - // replacement for client->ps.ping when in SPECTATOR_FOLLOW - client->pers.ping = level.time - ucmd->serverTime; - - // account for the one frame of delay on client side - client->pers.ping -= level.time - level.previousTime; - - // account for the time that's elapsed since the last ClientEndFrame() - client->pers.ping += trap_Milliseconds( ) - level.frameMsec; - - if( client->pers.ping < 0 ) - client->pers.ping = 0; msec = ucmd->serverTime - client->ps.commandTime; // following others may result in bad times, but we still want @@ -1511,9 +1272,15 @@ void ClientThink_real( gentity_t *ent ) client->unlaggedTime = ucmd->serverTime; if( pmove_msec.integer < 8 ) + { trap_Cvar_Set( "pmove_msec", "8" ); + trap_Cvar_Update(&pmove_msec); + } else if( pmove_msec.integer > 33 ) + { trap_Cvar_Set( "pmove_msec", "33" ); + trap_Cvar_Update(&pmove_msec); + } if( pmove_fixed.integer || client->pers.pmoveFixed ) { @@ -1527,21 +1294,12 @@ void ClientThink_real( gentity_t *ent ) // if( level.intermissiontime ) { - if( level.mapRotationVoteTime ) - { - SpectatorThink( ent, ucmd ); - return; - } - ClientIntermissionThink( client ); return; } - if( client->pers.teamSelection != PTE_NONE && client->pers.joinedATeam ) - G_UpdatePTRConnection( client ); - // spectators don't do much - if( client->sess.sessionTeam == TEAM_SPECTATOR ) + if( client->sess.spectatorState != SPECTATOR_NOT ) { if( client->sess.spectatorState == SPECTATOR_SCOREBOARD ) return; @@ -1550,20 +1308,19 @@ void ClientThink_real( gentity_t *ent ) return; } + G_namelog_update_score( client ); + // check for inactivity timer, but never drop the local client of a non-dedicated server - if( !ClientInactivityTimer( client ) ) + if( !ClientInactivityTimer( ent ) ) return; - // calculate where ent is currently seeing all the other active clients + // calculate where ent is currently seeing all the other active clients G_UnlaggedCalc( ent->client->unlaggedTime, ent ); 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; @@ -1572,31 +1329,33 @@ void ClientThink_real( gentity_t *ent ) else client->ps.pm_type = PM_NORMAL; - // paused - real_pm_type = client->ps.pm_type; - if ( level.paused ) client->ps.pm_type = PM_SPECTATOR; - - if( client->ps.stats[ STAT_STATE ] & SS_GRABBED && + 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 && + if( ( client->ps.stats[ STAT_STATE ] & SS_BLOBLOCKED ) && client->lastLockTime + LOCKBLOB_LOCKTIME < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED; - if( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED && + if( ( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED ) && client->lastSlowTime + 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; + // Update boosted state flags + client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTEDWARNING; + if( client->ps.stats[ STAT_STATE ] & SS_BOOSTED ) + { + if( level.time - client->boostedTime >= BOOST_TIME ) + client->ps.stats[ STAT_STATE ] &= ~SS_BOOSTED; + else if( level.time - client->boostedTime >= BOOST_WARN_TIME ) + client->ps.stats[ STAT_STATE ] |= SS_BOOSTEDWARNING; + } - if( client->ps.stats[ STAT_STATE ] & SS_POISONCLOUDED && - client->lastPoisonCloudedTime + LEVEL1_PCLOUD_TIME < level.time ) - client->ps.stats[ STAT_STATE ] &= ~SS_POISONCLOUDED; + // Check if poison cloud has worn off + if( ( client->ps.eFlags & EF_POISONCLOUDED ) && + BG_PlayerPoisonCloudTime( &client->ps ) - level.time + + client->lastPoisonCloudedTime <= 0 ) + client->ps.eFlags &= ~EF_POISONCLOUDED; if( client->ps.stats[ STAT_STATE ] & SS_POISONED && client->lastPoisonTime + ALIEN_POISON_TIME < level.time ) @@ -1608,13 +1367,13 @@ void ClientThink_real( gentity_t *ent ) 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 || + if( client->ps.stats[ STAT_STATE ] & SS_HEALING_2X || ( 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 && !level.paused ) + else if( client->ps.stats[ STAT_HEALTH ] > 0 ) { //remove anti toxin BG_DeactivateUpgrade( UP_MEDKIT, client->ps.stats ); @@ -1623,7 +1382,7 @@ void ClientThink_real( gentity_t *ent ) client->ps.stats[ STAT_STATE ] &= ~SS_POISONED; client->poisonImmunityTime = level.time + MEDKIT_POISON_IMMUNITY_TIME; - client->ps.stats[ STAT_STATE ] |= SS_MEDKIT_ACTIVE; + client->ps.stats[ STAT_STATE ] |= SS_HEALING_2X; client->lastMedKitTime = level.time; client->medKitHealthToRestore = client->ps.stats[ STAT_MAX_HEALTH ] - client->ps.stats[ STAT_HEALTH ]; @@ -1634,6 +1393,102 @@ void ClientThink_real( gentity_t *ent ) } } + // Replenish alien health + if( level.surrenderTeam != client->pers.teamSelection && + ent->nextRegenTime >= 0 && ent->nextRegenTime < level.time ) + { + float regenRate = + BG_Class( ent->client->ps.stats[ STAT_CLASS ] )->regenRate; + + if( ent->health <= 0 || ent->nextRegenTime < 0 || regenRate == 0 ) + ent->nextRegenTime = -1; // no regen + else + { + int entityList[ MAX_GENTITIES ]; + int i, num; + int count, interval; + vec3_t range, mins, maxs; + float modifier = 1.0f; + + VectorSet( range, REGEN_BOOST_RANGE, REGEN_BOOST_RANGE, + REGEN_BOOST_RANGE ); + 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++ ) + { + gentity_t *boost = &g_entities[ entityList[ i ] ]; + + if( Distance( client->ps.origin, boost->r.currentOrigin ) > REGEN_BOOST_RANGE ) + continue; + + if( modifier < BOOSTER_REGEN_MOD && boost->s.eType == ET_BUILDABLE && + boost->s.modelindex == BA_A_BOOSTER && boost->spawned && + boost->health > 0 && boost->powered ) + { + modifier = BOOSTER_REGEN_MOD; + continue; + } + + if( boost->s.eType == ET_PLAYER && boost->client && + boost->client->pers.teamSelection == + ent->client->pers.teamSelection && boost->health > 0 ) + { + class_t class = boost->client->ps.stats[ STAT_CLASS ]; + qboolean didBoost = qfalse; + + if( class == PCL_ALIEN_LEVEL1 && modifier < LEVEL1_REGEN_MOD ) + { + modifier = LEVEL1_REGEN_MOD; + didBoost = qtrue; + } + else if( class == PCL_ALIEN_LEVEL1_UPG && + modifier < LEVEL1_UPG_REGEN_MOD ) + { + modifier = LEVEL1_UPG_REGEN_MOD; + didBoost = qtrue; + } + + if( didBoost && ent->health < client->ps.stats[ STAT_MAX_HEALTH ] ) + boost->client->pers.hasHealed = qtrue; + } + } + + // Transmit heal rate to the client so it can be displayed on the HUD + client->ps.stats[ STAT_STATE ] |= SS_HEALING_ACTIVE; + client->ps.stats[ STAT_STATE ] &= ~( SS_HEALING_2X | SS_HEALING_3X ); + if( modifier == 1.0f && !G_FindCreep( ent ) ) + { + client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_ACTIVE; + modifier *= ALIEN_REGEN_NOCREEP_MOD; + } + else if( modifier >= 3.0f ) + client->ps.stats[ STAT_STATE ] |= SS_HEALING_3X; + else if( modifier >= 2.0f ) + client->ps.stats[ STAT_STATE ] |= SS_HEALING_2X; + + interval = 1000 / ( regenRate * modifier ); + // if recovery interval is less than frametime, compensate + count = 1 + ( level.time - ent->nextRegenTime ) / interval; + ent->nextRegenTime += count * interval; + + if( ent->health < client->ps.stats[ STAT_MAX_HEALTH ] ) + { + ent->health += count; + client->ps.stats[ STAT_HEALTH ] = ent->health; + + // if at max health, clear damage counters + if( ent->health >= client->ps.stats[ STAT_MAX_HEALTH ] ) + { + ent->health = client->ps.stats[ STAT_HEALTH ] = client->ps.stats[ STAT_MAX_HEALTH ]; + for( i = 0; i < MAX_CLIENTS; i++ ) + ent->credits[ i ] = 0; + } + } + } + } + if( BG_InventoryContainsUpgrade( UP_GRENADE, client->ps.stats ) && BG_UpgradeIsActive( UP_GRENADE, client->ps.stats ) ) { @@ -1650,10 +1505,11 @@ void ClientThink_real( gentity_t *ent ) } // set speed - client->ps.speed = g_speed.value * BG_FindSpeedForClass( client->ps.stats[ STAT_PCLASS ] ); - - if( client->pers.paused ) - client->ps.speed = 0; + if( client->ps.pm_type == PM_NOCLIP ) + client->ps.speed = client->pers.flySpeed; + else + client->ps.speed = g_speed.value * + BG_Class( client->ps.stats[ STAT_CLASS ] )->speed; if( client->lastCreepSlowTime + CREEP_TIMEOUT < level.time ) client->ps.stats[ STAT_STATE ] &= ~SS_CREEPSLOWED; @@ -1669,7 +1525,7 @@ void ClientThink_real( gentity_t *ent ) } //switch jetpack off if no reactor - if( !level.reactorPresent ) + if( !G_Reactor( ) ) BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats ); } @@ -1678,50 +1534,19 @@ void ClientThink_real( gentity_t *ent ) memset( &pm, 0, sizeof( pm ) ); - if( !( ucmd->buttons & BUTTON_TALK ) && !( client->ps.pm_flags & PMF_RESPAWNED ) ) - { - 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; } + + // clear fall velocity before every pmove + client->pmext.fallVelocity = 0.0f; pm.ps = &client->ps; pm.pmext = &client->pmext; 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.tracemask = ent->clipmask; pm.trace = trap_Trace; pm.pointcontents = trap_PointContents; pm.debugLevel = g_debugMove.integer; @@ -1734,8 +1559,7 @@ void ClientThink_real( gentity_t *ent ) // moved from after Pmove -- potentially the cause of // future triggering bugs - if( !ent->client->noclip ) - G_TouchTriggers( ent ); + G_TouchTriggers( ent ); Pmove( &pm ); @@ -1745,13 +1569,61 @@ void ClientThink_real( gentity_t *ent ) if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; - if ( level.paused ) client->ps.pm_type = real_pm_type; - + VectorCopy( ent->client->ps.viewangles, ent->r.currentAngles ); 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 ); + switch( client->ps.weapon ) + { + case WP_ALEVEL0: + if( !CheckVenomAttack( ent ) ) + { + client->ps.weaponstate = WEAPON_READY; + } + else + { + client->ps.generic1 = WPM_PRIMARY; + G_AddEvent( ent, EV_FIRE_WEAPON, 0 ); + } + break; + + case WP_ALEVEL1: + case WP_ALEVEL1_UPG: + CheckGrabAttack( ent ); + break; + + case WP_ALEVEL3: + case WP_ALEVEL3_UPG: + if( !CheckPounceAttack( ent ) ) + { + client->ps.weaponstate = WEAPON_READY; + } + else + { + client->ps.generic1 = WPM_SECONDARY; + G_AddEvent( ent, EV_FIRE_WEAPON2, 0 ); + } + break; + + case WP_ALEVEL4: + // If not currently in a trample, reset the trample bookkeeping data + if( !( client->ps.pm_flags & PMF_CHARGE ) && client->trampleBuildablesHitPos ) + { + ent->client->trampleBuildablesHitPos = 0; + memset( ent->client->trampleBuildablesHit, 0, sizeof( ent->client->trampleBuildablesHit ) ); + } + break; + + case WP_HBUILD: + CheckCkitRepair( ent ); + break; + + default: + break; + } + SendPendingPredictableEvents( &ent->client->ps ); if( !( ent->client->ps.eFlags & EF_FIRING ) ) @@ -1770,7 +1642,7 @@ void ClientThink_real( gentity_t *ent ) // touch other objects ClientImpacts( ent, &pm ); - + // execute client events ClientEvents( ent, oldEventSequence ); @@ -1779,12 +1651,12 @@ void ClientThink_real( gentity_t *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 ); + VectorCopy( ent->client->ps.origin, ent->s.pos.trBase ); // save results of triggers and client events if( ent->client->ps.eventSequence != oldEventSequence ) ent->eventTime = level.time; - + // Don't think anymore if dead if( client->ps.stats[ STAT_HEALTH ] <= 0 ) return; @@ -1794,121 +1666,84 @@ void ClientThink_real( gentity_t *ent ) client->buttons = ucmd->buttons; client->latched_buttons |= client->buttons & ~client->oldbuttons; - if( ( client->buttons & BUTTON_GETFLAG ) && !( client->oldbuttons & BUTTON_GETFLAG ) && + if( ( client->buttons & BUTTON_USE_EVOLVE ) && !( client->oldbuttons & BUTTON_USE_EVOLVE ) && 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; +#define USE_OBJECT_RANGE 64 - //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; - G_UnlaggedClear( ent ); + int entityList[ MAX_GENTITIES ]; + vec3_t range = { USE_OBJECT_RANGE, USE_OBJECT_RANGE, USE_OBJECT_RANGE }; + vec3_t mins, maxs; + int i, num; - //client leaves hovel - client->ps.stats[ STAT_STATE ] &= ~SS_HOVELING; + // 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 ); - //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 ); - } - } + traceEnt = &g_entities[ trace.entityNum ]; + + if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use ) + traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context 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; + //no entity in front of player - do a small area search - //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 ]; + VectorAdd( client->ps.origin, range, maxs ); + VectorSubtract( client->ps.origin, range, mins ); - 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 + num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); + for( i = 0; i < num; i++ ) { - //no entity in front of player - do a small area search - - VectorAdd( client->ps.origin, range, maxs ); - VectorSubtract( client->ps.origin, range, mins ); + traceEnt = &g_entities[ entityList[ i ] ]; - num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES ); - for( i = 0; i < num; i++ ) + if( traceEnt && traceEnt->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use ) { - 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; - } + 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( i == num && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS ) + { + if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ], + client->pers.credit, + g_alienStage.integer ) ) { - 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 ); - } + //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 ); } } } } - if( level.framenum > client->retriggerArmouryMenu && client->retriggerArmouryMenu ) - { - G_TriggerMenu( client->ps.clientNum, MN_H_ARMOURY ); - - client->retriggerArmouryMenu = 0; - } + client->ps.persistant[ PERS_BP ] = G_GetBuildPoints( client->ps.origin, + client->ps.stats[ STAT_TEAM ] ); + client->ps.persistant[ PERS_MARKEDBP ] = G_GetMarkedBuildPoints( client->ps.origin, + client->ps.stats[ STAT_TEAM ] ); - // Give clients some credit periodically - ClientGradualFunds( ent ); + if( client->ps.persistant[ PERS_BP ] < 0 ) + client->ps.persistant[ PERS_BP ] = 0; // 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; } - if( client->pers.bubbleTime && client->pers.bubbleTime < level.time ) - { - gentity_t *bubble; - - client->pers.bubbleTime = level.time + 500; - bubble = G_TempEntity( client->ps.origin, EV_PLAYER_TELEPORT_OUT ); - bubble->s.clientNum = ent->s.clientNum; - } } /* @@ -1953,52 +1788,30 @@ SpectatorClientEndFrame void SpectatorClientEndFrame( gentity_t *ent ) { gclient_t *cl; - int clientNum, flags; + int clientNum; int score, ping; - vec3_t spawn_origin, spawn_angles; // if we are doing a chase cam or a remote view, grab the latest info if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { clientNum = ent->client->sess.spectatorClient; - - if( clientNum >= 0 ) + if( clientNum >= 0 && clientNum < level.maxclients ) { cl = &level.clients[ clientNum ]; - if( cl->pers.connected == CON_CONNECTED ) { - - if( cl -> sess.spectatorState != SPECTATOR_FOLLOW ) - { - flags = ( cl->ps.eFlags & ~( EF_VOTED | EF_TEAMVOTED ) ) | - ( ent->client->ps.eFlags & ( EF_VOTED | EF_TEAMVOTED ) ); - score = ent->client->ps.persistant[ PERS_SCORE ]; - ping = ent->client->ps.ping; - ent->client->ps = cl->ps; - ent->client->ps.persistant[ PERS_SCORE ] = score; - ent->client->ps.ping = ping; - ent->client->ps.eFlags = flags; - ent->client->ps.pm_flags |= PMF_FOLLOW; - ent->client->ps.pm_flags &= ~PMF_QUEUED; - } - else //we are stickyspec-spectating someone who is spectating someone else - { - ent->client->ps.clientNum = (g_entities + clientNum)->s.number; - ent->client->ps.commandTime = cl->ps.commandTime; - ent->client->ps.weapon = 0; - ent->client->ps.pm_flags |= PMF_FOLLOW; - ent->client->ps.stats[ STAT_PCLASS ] = PCL_NONE; - - if( cl->pers.teamSelection == PTE_ALIENS ) - G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles ); - else if( cl->pers.teamSelection == PTE_HUMANS ) - G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles ); - - G_SetOrigin( ent, spawn_origin ); - VectorCopy( spawn_origin, ent->client->ps.origin ); - G_SetClientViewAngle( ent, spawn_angles ); - } + score = ent->client->ps.persistant[ PERS_SCORE ]; + ping = ent->client->ps.ping; + + // Copy + ent->client->ps = cl->ps; + + // Restore + ent->client->ps.persistant[ PERS_SCORE ] = score; + ent->client->ps.ping = ping; + + ent->client->ps.pm_flags |= PMF_FOLLOW; + ent->client->ps.pm_flags &= ~PMF_QUEUED; } } } @@ -2015,20 +1828,12 @@ while a slow client may have multiple ClientEndFrame between ClientThink. */ void ClientEndFrame( gentity_t *ent ) { - clientPersistant_t *pers; - - if( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) + if( ent->client->sess.spectatorState != SPECTATOR_NOT ) { SpectatorClientEndFrame( ent ); return; } - pers = &ent->client->pers; - - // save a copy of certain playerState values in case of SPECTATOR_FOLLOW - pers->score = ent->client->ps.persistant[ PERS_SCORE ]; - pers->credit = ent->client->ps.persistant[ PERS_CREDIT ]; - // // If the end of unit layout is displayed, don't give // the player any normal movement attributes @@ -2048,8 +1853,6 @@ void ClientEndFrame( gentity_t *ent ) else ent->s.eFlags &= ~EF_CONNECTION; - ent->client->ps.stats[ STAT_HEALTH ] = ent->health; // FIXME: get rid of ent->health... - // respawn if dead if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 && level.time >= ent->client->respawnTime ) respawn( ent ); @@ -2064,5 +1867,3 @@ void ClientEndFrame( gentity_t *ent ) SendPendingPredictableEvents( &ent->client->ps ); } - - -- cgit