summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaweł Redman <trem.redman@gmail.com>2013-09-07 09:56:40 +0200
committerPaweł Redman <trem.redman@gmail.com>2013-09-07 09:56:40 +0200
commit997376603b684e13d6b1c21708db88fc55e9e0eb (patch)
tree36a764e3a15640bf7c0fc9bb8bc7e000a9a513ee
parent243e5506e880f4338f185492e2ca1aebb4b1323d (diff)
Remove .orig files
-rw-r--r--src/cgame/cg_event.c.orig1086
-rw-r--r--src/cgame/cg_main.c.orig1982
-rw-r--r--src/game/g_active.c.orig1937
-rw-r--r--src/game/g_client.c.orig1600
-rw-r--r--src/game/g_cmds.c.orig3588
-rw-r--r--src/game/g_combat.c.orig1468
-rw-r--r--src/game/g_main.c.orig2548
7 files changed, 0 insertions, 14209 deletions
diff --git a/src/cgame/cg_event.c.orig b/src/cgame/cg_event.c.orig
deleted file mode 100644
index e394023..0000000
--- a/src/cgame/cg_event.c.orig
+++ /dev/null
@@ -1,1086 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-// cg_event.c -- handle entity events at snapshot or playerstate transitions
-
-
-#include "cg_local.h"
-
-/*
-=============
-CG_Obituary
-=============
-*/
-static void CG_Obituary( entityState_t *ent )
-{
- int mod;
- int target, attacker;
- char *message;
- char *message2;
- const char *targetInfo;
- const char *attackerInfo;
- char targetName[ MAX_NAME_LENGTH ];
- char attackerName[ MAX_NAME_LENGTH ];
- char className[ 64 ];
- gender_t gender;
- clientInfo_t *ci;
- qboolean teamKill = qfalse;
-
- target = ent->otherEntityNum;
- attacker = ent->otherEntityNum2;
- mod = ent->eventParm;
-
- if( target < 0 || target >= MAX_CLIENTS )
- CG_Error( "CG_Obituary: target out of range" );
-
- ci = &cgs.clientinfo[ target ];
- gender = ci->gender;
-
- if( attacker < 0 || attacker >= MAX_CLIENTS )
- {
- attacker = ENTITYNUM_WORLD;
- attackerInfo = NULL;
- }
- else
- {
- attackerInfo = CG_ConfigString( CS_PLAYERS + attacker );
- if( ci && cgs.clientinfo[ attacker ].team == ci->team )
- teamKill = qtrue;
- }
-
- targetInfo = CG_ConfigString( CS_PLAYERS + target );
-
- if( !targetInfo )
- return;
-
- Q_strncpyz( targetName, Info_ValueForKey( targetInfo, "n" ), sizeof( targetName ));
-
- message2 = "";
-
- // check for single client messages
-
- switch( mod )
- {
- case MOD_FALLING:
- message = "fell fowl to gravity";
- break;
- case MOD_CRUSH:
- message = "was squished";
- break;
- case MOD_WATER:
- message = "forgot to pack a snorkel";
- break;
- case MOD_SLIME:
- message = "melted";
- break;
- case MOD_LAVA:
- message = "did a back flip into the lava";
- break;
- case MOD_TARGET_LASER:
- message = "saw the light";
- break;
- case MOD_TRIGGER_HURT:
- message = "was in the wrong place";
- break;
- case MOD_HSPAWN:
- message = "should have run further";
- break;
- case MOD_ASPAWN:
- message = "shouldn't have trod in the acid";
- break;
- case MOD_MGTURRET:
- message = "was gunned down by a turret";
- break;
- case MOD_TESLAGEN:
- message = "was zapped by a tesla generator";
- break;
- case MOD_ATUBE:
- message = "was melted by an acid tube";
- break;
- case MOD_OVERMIND:
- message = "got too close to the overmind";
- break;
- case MOD_REACTOR:
- message = "got too close to the reactor";
- break;
- case MOD_SLOWBLOB:
- message = "should have visited a medical station";
- break;
- case MOD_SWARM:
- message = "was hunted down by the swarm";
- break;
- default:
- message = NULL;
- break;
- }
-
- if( !message && attacker == target )
- {
- switch( mod )
- {
- case MOD_FLAMER_SPLASH:
- if( gender == GENDER_FEMALE )
- message = "toasted herself";
- else if( gender == GENDER_NEUTER )
- message = "toasted itself";
- else
- message = "toasted himself";
- break;
-
- case MOD_LCANNON_SPLASH:
- if( gender == GENDER_FEMALE )
- message = "irradiated herself";
- else if( gender == GENDER_NEUTER )
- message = "irradiated itself";
- else
- message = "irradiated himself";
- break;
-
- case MOD_GRENADE:
- if( gender == GENDER_FEMALE )
- message = "blew herself up";
- else if( gender == GENDER_NEUTER )
- message = "blew itself up";
- else
- message = "blew himself up";
- break;
-
- case MOD_LEVEL3_BOUNCEBALL:
- if( gender == GENDER_FEMALE )
- message = "sniped herself";
- else if( gender == GENDER_NEUTER )
- message = "sniped itself";
- else
- message = "sniped himself";
- break;
-
- case MOD_PRIFLE:
- if( gender == GENDER_FEMALE )
- message = "pulse rifled herself";
- else if( gender == GENDER_NEUTER )
- message = "pulse rifled itself";
- else
- message = "pulse rifled himself";
- break;
-
- default:
- if( gender == GENDER_FEMALE )
- message = "killed herself";
- else if( gender == GENDER_NEUTER )
- message = "killed itself";
- else
- message = "killed himself";
- break;
- }
- }
-
- if( message )
- {
- CG_Printf( "%s" S_COLOR_WHITE " %s\n", targetName, message );
- return;
- }
-
- // check for double client messages
- if( !attackerInfo )
- {
- attacker = ENTITYNUM_WORLD;
- strcpy( attackerName, "noname" );
- }
- else
- {
- Q_strncpyz( attackerName, Info_ValueForKey( attackerInfo, "n" ), sizeof( attackerName ));
- // check for kill messages about the current clientNum
- if( target == cg.snap->ps.clientNum )
- Q_strncpyz( cg.killerName, attackerName, sizeof( cg.killerName ) );
- }
-
- if( attacker != ENTITYNUM_WORLD )
- {
- switch( mod )
- {
- case MOD_PAINSAW:
- message = "was sawn by";
- break;
- case MOD_BLASTER:
- message = "was blasted by";
- break;
- case MOD_MACHINEGUN:
- message = "was machinegunned by";
- break;
- case MOD_CHAINGUN:
- message = "was chaingunned by";
- break;
- case MOD_SHOTGUN:
- message = "was gunned down by";
- break;
- case MOD_PRIFLE:
- message = "was pulse rifled by";
- break;
- case MOD_MDRIVER:
- message = "was mass driven by";
- break;
- case MOD_LASGUN:
- message = "was lasgunned by";
- break;
- case MOD_FLAMER:
- message = "was grilled by";
- message2 = "'s flamer";
- break;
- case MOD_FLAMER_SPLASH:
- message = "was toasted by";
- message2 = "'s flamer";
- break;
- case MOD_LCANNON:
- message = "felt the full force of";
- message2 = "'s lucifer cannon";
- break;
- case MOD_LCANNON_SPLASH:
- message = "was caught in the fallout of";
- message2 = "'s lucifer cannon";
- break;
- case MOD_GRENADE:
- message = "couldn't escape";
- message2 = "'s grenade";
- break;
-
- case MOD_ABUILDER_CLAW:
- message = "should leave";
- message2 = "'s buildings alone";
- break;
- case MOD_LEVEL0_BITE:
- message = "was bitten by";
- break;
- case MOD_LEVEL1_CLAW:
- message = "was swiped by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL2_CLAW:
- message = "was clawed by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL2_ZAP:
- message = "was zapped by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL2 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL3_CLAW:
- message = "was chomped by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL3_POUNCE:
- message = "was pounced upon by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL3_BOUNCEBALL:
- message = "was sniped by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL3 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL4_CLAW:
- message = "was mauled by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL4_TRAMPLE:
- message = "should have gotten out of the way of";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL4 )->humanName );
- message2 = className;
- break;
- case MOD_LEVEL4_CRUSH:
- message = "was crushed under";
- message2 = "'s weight";
- break;
-
- case MOD_POISON:
- message = "should have used a medkit against";
- message2 = "'s poison";
- break;
- case MOD_LEVEL1_PCLOUD:
- message = "was gassed by";
- Com_sprintf( className, 64, "'s %s",
- BG_ClassConfig( PCL_ALIEN_LEVEL1 )->humanName );
- message2 = className;
- break;
-
-
- case MOD_TELEFRAG:
- message = "tried to invade";
- message2 = "'s personal space";
- break;
- default:
- message = "was killed by";
- break;
- }
-
- if( message )
- {
- CG_Printf( "%s" S_COLOR_WHITE " %s %s%s" S_COLOR_WHITE "%s\n",
- targetName, message,
- ( teamKill ) ? S_COLOR_RED "TEAMMATE " S_COLOR_WHITE : "",
- attackerName, message2 );
- if( teamKill && attacker == cg.clientNum )
- {
- CG_CenterPrint( va ( "You killed " S_COLOR_RED "TEAMMATE "
- S_COLOR_WHITE "%s", targetName ),
- SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH );
- }
- return;
- }
- }
-
- // we don't know what it was
- CG_Printf( "%s" S_COLOR_WHITE " died\n", targetName );
-}
-
-
-
-//==========================================================================
-
-/*
-================
-CG_PainEvent
-
-Also called by playerstate transition
-================
-*/
-void CG_PainEvent( centity_t *cent, int health )
-{
- char *snd;
-
- // don't do more than two pain sounds a second
- if( cg.time - cent->pe.painTime < 500 )
- return;
-
- if( health < 25 )
- snd = "*pain25_1.wav";
- else if( health < 50 )
- snd = "*pain50_1.wav";
- else if( health < 75 )
- snd = "*pain75_1.wav";
- else
- snd = "*pain100_1.wav";
-
- trap_S_StartSound( NULL, cent->currentState.number, CHAN_VOICE,
- CG_CustomSound( cent->currentState.number, snd ) );
-
- // save pain time for programitic twitch animation
- cent->pe.painTime = cg.time;
- cent->pe.painDirection ^= 1;
-}
-
-/*
-=========================
-CG_Level2Zap
-=========================
-*/
-static void CG_Level2Zap( entityState_t *es )
-{
- int i;
- centity_t *source = NULL, *target = NULL;
-
- if( es->misc < 0 || es->misc >= MAX_CLIENTS )
- return;
-
- source = &cg_entities[ es->misc ];
- for( i = 0; i <= 2; i++ )
- {
- switch( i )
- {
- case 0:
- if( es->time <= 0 )
- continue;
-
- target = &cg_entities[ es->time ];
- break;
-
- case 1:
- if( es->time2 <= 0 )
- continue;
-
- target = &cg_entities[ es->time2 ];
- break;
-
- case 2:
- if( es->constantLight <= 0 )
- continue;
-
- target = &cg_entities[ es->constantLight ];
- break;
- }
-
- if( !CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
- source->level2ZapTS[ i ] = CG_SpawnNewTrailSystem( cgs.media.level2ZapTS );
-
- if( CG_IsTrailSystemValid( &source->level2ZapTS[ i ] ) )
- {
- CG_SetAttachmentCent( &source->level2ZapTS[ i ]->frontAttachment, source );
- CG_SetAttachmentCent( &source->level2ZapTS[ i ]->backAttachment, target );
- CG_AttachToCent( &source->level2ZapTS[ i ]->frontAttachment );
- CG_AttachToCent( &source->level2ZapTS[ i ]->backAttachment );
- }
- }
- source->level2ZapTime = cg.time;
-}
-
-/*
-==============
-CG_EntityEvent
-
-An entity has an event value
-also called by CG_CheckPlayerstateEvents
-==============
-*/
-void CG_EntityEvent( centity_t *cent, vec3_t position )
-{
- entityState_t *es;
- int event;
- vec3_t dir;
- const char *s;
- int clientNum;
- clientInfo_t *ci;
- int steptime;
- vec3_t cuboid;
- buildable_t cuboidType;
-
- if( cg.snap->ps.persistant[ PERS_SPECSTATE ] != SPECTATOR_NOT )
- steptime = 200;
- else
- steptime = BG_Class( cg.snap->ps.stats[ STAT_CLASS ] )->steptime;
-
- es = &cent->currentState;
- event = es->event & ~EV_EVENT_BITS;
-
- if( cg_debugEvents.integer )
- CG_Printf( "ent:%3i event:%3i %s\n", es->number, event,
- BG_EventName( event ) );
-
- if( !event )
- return;
-
- clientNum = es->clientNum;
- if( clientNum < 0 || clientNum >= MAX_CLIENTS )
- clientNum = 0;
-
- ci = &cgs.clientinfo[ clientNum ];
-
- switch( event )
- {
- //
- // movement generated events
- //
- case EV_FOOTSTEP:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- if( ci->footsteps == FOOTSTEP_CUSTOM )
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- ci->customFootsteps[ rand( ) & 3 ] );
- else
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ ci->footsteps ][ rand( ) & 3 ] );
- }
- break;
-
- case EV_FOOTSTEP_METAL:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- if( ci->footsteps == FOOTSTEP_CUSTOM )
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- ci->customMetalFootsteps[ rand( ) & 3 ] );
- else
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ FOOTSTEP_METAL ][ rand( ) & 3 ] );
- }
- break;
-
- case EV_FOOTSTEP_SQUELCH:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ FOOTSTEP_FLESH ][ rand( ) & 3 ] );
- }
- break;
-
- case EV_FOOTSPLASH:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
- }
- break;
-
- case EV_FOOTWADE:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
- }
- break;
-
- case EV_SWIM:
- if( cg_footsteps.integer && ci->footsteps != FOOTSTEP_NONE )
- {
- trap_S_StartSound( NULL, es->number, CHAN_BODY,
- cgs.media.footsteps[ FOOTSTEP_SPLASH ][ rand( ) & 3 ] );
- }
- break;
-
-
- case EV_FALL_SHORT:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.landSound );
-
- if( clientNum == cg.predictedPlayerState.clientNum )
- {
- // smooth landing z changes
- cg.landChange = -8;
- cg.landTime = cg.time;
- }
- break;
-
- case EV_FALL_MEDIUM:
- // use normal pain sound
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*pain100_1.wav" ) );
-
- if( clientNum == cg.predictedPlayerState.clientNum )
- {
- // smooth landing z changes
- cg.landChange = -16;
- cg.landTime = cg.time;
- }
- break;
-
- case EV_FALL_FAR:
- trap_S_StartSound (NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*fall1.wav" ) );
- cent->pe.painTime = cg.time; // don't play a pain sound right after this
-
- if( clientNum == cg.predictedPlayerState.clientNum )
- {
- // smooth landing z changes
- cg.landChange = -24;
- cg.landTime = cg.time;
- }
- break;
-
- case EV_FALLING:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*falling1.wav" ) );
- break;
-
- case EV_STEP_4:
- case EV_STEP_8:
- case EV_STEP_12:
- case EV_STEP_16: // smooth out step up transitions
- case EV_STEPDN_4:
- case EV_STEPDN_8:
- case EV_STEPDN_12:
- case EV_STEPDN_16: // smooth out step down transitions
- {
- float oldStep;
- int delta;
- int step;
-
- if( clientNum != cg.predictedPlayerState.clientNum )
- break;
-
- // if we are interpolating, we don't need to smooth steps
- if( cg.demoPlayback || ( cg.snap->ps.pm_flags & PMF_FOLLOW ) ||
- cg_nopredict.integer || cg_synchronousClients.integer )
- break;
-
- // check for stepping up before a previous step is completed
- delta = cg.time - cg.stepTime;
-
- if( delta < steptime )
- oldStep = cg.stepChange * ( steptime - delta ) / steptime;
- else
- oldStep = 0;
-
- // add this amount
- if( event >= EV_STEPDN_4 )
- {
- step = 4 * ( event - EV_STEPDN_4 + 1 );
- cg.stepChange = oldStep - step;
- }
- else
- {
- step = 4 * ( event - EV_STEP_4 + 1 );
- cg.stepChange = oldStep + step;
- }
-
- if( cg.stepChange > MAX_STEP_CHANGE )
- cg.stepChange = MAX_STEP_CHANGE;
- else if( cg.stepChange < -MAX_STEP_CHANGE )
- cg.stepChange = -MAX_STEP_CHANGE;
-
- cg.stepTime = cg.time;
- break;
- }
-
- case EV_JUMP:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*jump1.wav" ) );
-
- if( BG_ClassHasAbility( cg.predictedPlayerState.stats[ STAT_CLASS ], SCA_WALLJUMPER ) )
- {
- vec3_t surfNormal, refNormal = { 0.0f, 0.0f, 1.0f };
- vec3_t rotAxis;
-
- if( clientNum != cg.predictedPlayerState.clientNum )
- break;
-
- //set surfNormal
- VectorCopy( cg.predictedPlayerState.grapplePoint, surfNormal );
-
- //if we are moving from one surface to another smooth the transition
- if( !VectorCompare( surfNormal, cg.lastNormal ) && surfNormal[ 2 ] != 1.0f )
- {
- CrossProduct( refNormal, surfNormal, rotAxis );
- VectorNormalize( rotAxis );
-
- //add the op
- CG_addSmoothOp( rotAxis, 15.0f, 1.0f );
- }
-
- //copy the current normal to the lastNormal
- VectorCopy( surfNormal, cg.lastNormal );
- }
-
- break;
-
- case EV_JETJUMP:
- cent->jetPackJumpTime = cg.time; //for visual effects
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.jetpackJumpSound );
- break;
-
-
- case EV_JETPACK_DEACTIVATE:
- switch( cent->jetPackState )
- {
- case JPS_DESCENDING:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.jetpackDescendDeactivateSound );
- break;
- case JPS_HOVERING:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.jetpackIdleDeactivateSound );
- break;
- case JPS_ASCENDING:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.jetpackAscendDeactivateSound );
- break;
- }
- break;
-
- case EV_JETPACK_REFUEL:
- if( cent->jetPackRefuelTime + 1000 < cg.time )
- {
- cent->jetPackRefuelTime = cg.time;
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.jetpackRefuelSound);
- }
- break;
-
- case EV_LEV1_GRAB:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL1Grab );
- break;
-
- case EV_LEV4_TRAMPLE_PREPARE:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargePrepare );
- break;
-
- case EV_LEV4_TRAMPLE_START:
- //FIXME: stop cgs.media.alienL4ChargePrepare playing here
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.media.alienL4ChargeStart );
- break;
-
- case EV_TAUNT:
- if( !cg_noTaunt.integer )
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, "*taunt.wav" ) );
- break;
-
- case EV_WATER_TOUCH:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrInSound );
- break;
-
- case EV_WATER_LEAVE:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrOutSound );
- break;
-
- case EV_WATER_UNDER:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.watrUnSound );
- break;
-
- case EV_WATER_CLEAR:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, CG_CustomSound( es->number, "*gasp.wav" ) );
- break;
-
- //
- // weapon events
- //
- case EV_NOAMMO:
- trap_S_StartSound( NULL, es->number, CHAN_WEAPON,
- cgs.media.weaponEmptyClick );
- break;
-
- case EV_CHANGE_WEAPON:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.selectSound );
- break;
-
- case EV_FIRE_WEAPON:
- CG_FireWeapon( cent, WPM_PRIMARY );
- break;
-
- case EV_FIRE_WEAPON2:
- CG_FireWeapon( cent, WPM_SECONDARY );
- break;
-
- case EV_FIRE_WEAPON3:
- CG_FireWeapon( cent, WPM_TERTIARY );
- break;
-
- //=================================================================
-
- //
- // other events
- //
- case EV_PLAYER_TELEPORT_IN:
- //deprecated
- break;
-
- case EV_PLAYER_TELEPORT_OUT:
- CG_PlayerDisconnect( position );
- break;
-
- case EV_BUILD_CONSTRUCT:
- //do something useful here
- break;
-
- case EV_BUILD_DESTROY:
- //do something useful here
- break;
-
- case EV_RPTUSE_SOUND:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.repeaterUseSound );
- break;
-
- case EV_GRENADE_BOUNCE:
- if( rand( ) & 1 )
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound1 );
- else
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.hardBounceSound2 );
- break;
-
- //
- // missile impacts
- //
- case EV_MISSILE_HIT:
- ByteToDir( es->eventParm, dir );
- CG_MissileHitEntity( es->weapon, es->generic1, position, dir, es->otherEntityNum, es->torsoAnim );
- break;
-
- case EV_MISSILE_MISS:
- ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_DEFAULT, es->torsoAnim );
- break;
-
- case EV_MISSILE_MISS_METAL:
- ByteToDir( es->eventParm, dir );
- CG_MissileHitWall( es->weapon, es->generic1, 0, position, dir, IMPACTSOUND_METAL, es->torsoAnim );
- break;
-
- case EV_HUMAN_BUILDABLE_EXPLOSION:
- ByteToDir( es->eventParm, dir );
- CG_HumanBuildableExplosion( position, dir );
- break;
-
- case EV_ALIEN_BUILDABLE_EXPLOSION:
- ByteToDir( es->eventParm, dir );
- CG_AlienBuildableExplosion( position, dir );
- break;
-
- case EV_CUBOID_EXPLOSION:
- cuboidType=es->modelindex;
- CG_CuboidExplosion(cuboidType,position,es->angles);
- break;
-
- case EV_TESLATRAIL:
- cent->currentState.weapon = WP_TESLAGEN;
- {
- centity_t *source = &cg_entities[ es->generic1 ];
- centity_t *target = &cg_entities[ es->clientNum ];
- vec3_t sourceOffset = { 0.0f, 0.0f, 28.0f };
-
- if( !CG_IsTrailSystemValid( &source->muzzleTS ) )
- {
- source->muzzleTS = CG_SpawnNewTrailSystem( cgs.media.teslaZapTS );
-
- if( CG_IsTrailSystemValid( &source->muzzleTS ) )
- {
- CG_SetAttachmentCent( &source->muzzleTS->frontAttachment, source );
- CG_SetAttachmentCent( &source->muzzleTS->backAttachment, target );
- CG_AttachToCent( &source->muzzleTS->frontAttachment );
- CG_AttachToCent( &source->muzzleTS->backAttachment );
- CG_SetAttachmentOffset( &source->muzzleTS->frontAttachment, sourceOffset );
-
- source->muzzleTSDeathTime = cg.time + cg_teslaTrailTime.integer;
- }
- }
- }
- break;
-
- case EV_BULLET_HIT_WALL:
- ByteToDir( es->eventParm, dir );
- CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qfalse, ENTITYNUM_WORLD );
- break;
-
- case EV_BULLET_HIT_FLESH:
- CG_Bullet( es->pos.trBase, es->otherEntityNum, dir, qtrue, es->eventParm );
- break;
-
- case EV_SHOTGUN:
- CG_ShotgunFire( es );
- break;
-
- case EV_GENERAL_SOUND:
- if( cgs.gameSounds[ es->eventParm ] )
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, cgs.gameSounds[ es->eventParm ] );
- else
- {
- s = CG_ConfigString( CS_SOUNDS + es->eventParm );
- trap_S_StartSound( NULL, es->number, CHAN_VOICE, CG_CustomSound( es->number, s ) );
- }
- break;
-
- case EV_GLOBAL_SOUND: // play from the player's head so it never diminishes
- if( cgs.gameSounds[ es->eventParm ] )
- trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, cgs.gameSounds[ es->eventParm ] );
- else
- {
- s = CG_ConfigString( CS_SOUNDS + es->eventParm );
- trap_S_StartSound( NULL, cg.snap->ps.clientNum, CHAN_AUTO, CG_CustomSound( es->number, s ) );
- }
- break;
-
- case EV_PAIN:
- // local player sounds are triggered in CG_CheckLocalSounds,
- // so ignore events on the player
- if( cent->currentState.number != cg.snap->ps.clientNum )
- CG_PainEvent( cent, es->eventParm );
- break;
-
- case EV_DEATH1:
- case EV_DEATH2:
- case EV_DEATH3:
- trap_S_StartSound( NULL, es->number, CHAN_VOICE,
- CG_CustomSound( es->number, va( "*death%i.wav", event - EV_DEATH1 + 1 ) ) );
- break;
-
- case EV_OBITUARY:
- CG_Obituary( es );
- break;
-
- case EV_GIB_PLAYER:
- // no gibbing
- break;
-
- case EV_STOPLOOPINGSOUND:
- trap_S_StopLoopingSound( es->number );
- es->loopSound = 0;
- break;
-
- case EV_DEBUG_LINE:
- CG_Beam( cent );
- break;
-
- case EV_BUILD_DELAY:
- if( clientNum == cg.predictedPlayerState.clientNum )
- {
- trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
- cg.lastBuildAttempt = cg.time;
- }
- break;
-
- case EV_BUILD_REPAIR:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairSound );
- break;
-
- case EV_BUILD_REPAIRED:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.buildableRepairedSound );
- break;
-
- case EV_OVERMIND_ATTACK:
- if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- trap_S_StartLocalSound( cgs.media.alienOvermindAttack, CHAN_ANNOUNCER );
- CG_CenterPrint( "The Overmind is under attack!", 200, GIANTCHAR_WIDTH * 4 );
- }
- break;
-
- case EV_OVERMIND_DYING:
- if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- trap_S_StartLocalSound( cgs.media.alienOvermindDying, CHAN_ANNOUNCER );
- CG_CenterPrint( "The Overmind is dying!", 200, GIANTCHAR_WIDTH * 4 );
- }
- break;
-
- case EV_DCC_ATTACK:
- if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_HUMANS )
- {
- //trap_S_StartLocalSound( cgs.media.humanDCCAttack, CHAN_ANNOUNCER );
- CG_CenterPrint( "Our base is under attack!", 200, GIANTCHAR_WIDTH * 4 );
- }
- break;
-
- case EV_MGTURRET_SPINUP:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.turretSpinupSound );
- break;
-
- case EV_OVERMIND_SPAWNS:
- if( cg.predictedPlayerState.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- trap_S_StartLocalSound( cgs.media.alienOvermindSpawns, CHAN_ANNOUNCER );
- CG_CenterPrint( "The Overmind needs spawns!", 200, GIANTCHAR_WIDTH * 4 );
- }
- break;
-
- case EV_ALIEN_EVOLVE:
- trap_S_StartSound( NULL, es->number, CHAN_BODY, cgs.media.alienEvolveSound );
- {
- particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienEvolvePS );
-
- if( CG_IsParticleSystemValid( &ps ) )
- {
- CG_SetAttachmentCent( &ps->attachment, cent );
- CG_AttachToCent( &ps->attachment );
- }
- }
-
- if( es->number == cg.clientNum )
- {
- CG_ResetPainBlend( );
- cg.spawnTime = cg.time;
- }
- break;
-
- case EV_ALIEN_EVOLVE_FAILED:
- if( clientNum == cg.predictedPlayerState.clientNum )
- {
- //FIXME: change to "negative" sound
- trap_S_StartLocalSound( cgs.media.buildableRepairedSound, CHAN_LOCAL_SOUND );
- cg.lastEvolveAttempt = cg.time;
- }
- break;
-
- case EV_ALIEN_ACIDTUBE:
- {
- particleSystem_t *ps = CG_SpawnNewParticleSystem( cgs.media.alienAcidTubePS );
-
- if( CG_IsParticleSystemValid( &ps ) )
- {
- CG_SetAttachmentCent( &ps->attachment, cent );
- ByteToDir( es->eventParm, dir );
- CG_SetParticleSystemNormal( ps, dir );
- CG_AttachToCent( &ps->attachment );
- }
- }
- break;
-
- case EV_MEDKIT_USED:
- trap_S_StartSound( NULL, es->number, CHAN_AUTO, cgs.media.medkitUseSound );
- break;
-
- case EV_PLAYER_RESPAWN:
- if( es->number == cg.clientNum )
- cg.spawnTime = cg.time;
- break;
-
- case EV_LEV2_ZAP:
- CG_Level2Zap( es );
-
- default:
- CG_Error( "Unknown event: %i", event );
- break;
- }
-}
-
-
-/*
-==============
-CG_CheckEvents
-
-==============
-*/
-void CG_CheckEvents( centity_t *cent )
-{
- entity_event_t event;
- entity_event_t oldEvent = EV_NONE;
-
- // check for event-only entities
- if( cent->currentState.eType > ET_EVENTS )
- {
- event = cent->currentState.eType - ET_EVENTS;
-
- if( cent->previousEvent )
- return; // already fired
-
- cent->previousEvent = 1;
-
- cent->currentState.event = cent->currentState.eType - ET_EVENTS;
-
- // Move the pointer to the entity that the
- // event was originally attached to
- if( cent->currentState.eFlags & EF_PLAYER_EVENT )
- {
- cent = &cg_entities[ cent->currentState.otherEntityNum ];
- oldEvent = cent->currentState.event;
- cent->currentState.event = event;
- }
- }
- else
- {
- // check for events riding with another entity
- if( cent->currentState.event == cent->previousEvent )
- return;
-
- cent->previousEvent = cent->currentState.event;
- if( ( cent->currentState.event & ~EV_EVENT_BITS ) == 0 )
- return;
- }
-
- // calculate the position at exactly the frame time
- BG_EvaluateTrajectory( &cent->currentState.pos, cg.snap->serverTime, cent->lerpOrigin );
- CG_SetEntitySoundPosition( cent );
-
- CG_EntityEvent( cent, cent->lerpOrigin );
-
- // If this was a reattached spilled event, restore the original event
- if( oldEvent != EV_NONE )
- cent->currentState.event = oldEvent;
-}
-
diff --git a/src/cgame/cg_main.c.orig b/src/cgame/cg_main.c.orig
deleted file mode 100644
index caa80f4..0000000
--- a/src/cgame/cg_main.c.orig
+++ /dev/null
@@ -1,1982 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-// cg_main.c -- initialization and primary entry point for cgame
-
-
-#include "cg_local.h"
-
-#include "../ui/ui_shared.h"
-// display context for new ui stuff
-displayContextDef_t cgDC;
-
-void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum );
-void CG_Shutdown( void );
-static char *CG_VoIPString( void );
-
-/*
-================
-vmMain
-
-This is the only way control passes into the module.
-This must be the very first function compiled into the .q3vm file
-================
-*/
-Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3,
- int arg4, int arg5, int arg6, int arg7,
- int arg8, int arg9, int arg10, int arg11 )
-{
- switch( command )
- {
- case CG_INIT:
- CG_Init( arg0, arg1, arg2 );
- return 0;
-
- case CG_SHUTDOWN:
- CG_Shutdown( );
- return 0;
-
- case CG_CONSOLE_COMMAND:
- return CG_ConsoleCommand( );
-
- case CG_CONSOLE_TEXT:
- CG_AddNotifyText( );
- return 0;
-
- case CG_DRAW_ACTIVE_FRAME:
- CG_DrawActiveFrame( arg0, arg1, arg2 );
- return 0;
-
- case CG_CROSSHAIR_PLAYER:
- return CG_CrosshairPlayer( );
-
- case CG_LAST_ATTACKER:
- return CG_LastAttacker( );
-
- case CG_KEY_EVENT:
- CG_KeyEvent( arg0, arg1 );
- return 0;
-
- case CG_MOUSE_EVENT:
- // cgame doesn't care where the cursor is
- return 0;
-
- case CG_EVENT_HANDLING:
- CG_EventHandling( arg0 );
- return 0;
-
- case CG_VOIP_STRING:
- return (intptr_t)CG_VoIPString( );
-
- default:
- CG_Error( "vmMain: unknown command %i", command );
- break;
- }
-
- return -1;
-}
-
-
-cg_t cg;
-cgs_t cgs;
-centity_t cg_entities[ MAX_GENTITIES ];
-
-weaponInfo_t cg_weapons[ 32 ];
-upgradeInfo_t cg_upgrades[ 32 ];
-
-buildableInfo_t cg_buildables[ CUBOID_FIRST ];
-cuboidInfo_t cg_cuboids[ CUBOID_TYPES ];
-
-vmCvar_t cg_teslaTrailTime;
-vmCvar_t cg_centertime;
-vmCvar_t cg_runpitch;
-vmCvar_t cg_runroll;
-vmCvar_t cg_swingSpeed;
-vmCvar_t cg_shadows;
-vmCvar_t cg_drawTimer;
-vmCvar_t cg_drawClock;
-vmCvar_t cg_drawFPS;
-vmCvar_t cg_drawDemoState;
-vmCvar_t cg_drawSnapshot;
-vmCvar_t cg_drawChargeBar;
-vmCvar_t cg_drawCrosshair;
-vmCvar_t cg_drawCrosshairNames;
-vmCvar_t cg_crosshairSize;
-vmCvar_t cg_draw2D;
-vmCvar_t cg_animSpeed;
-vmCvar_t cg_debugAnim;
-vmCvar_t cg_debugPosition;
-vmCvar_t cg_debugEvents;
-vmCvar_t cg_errorDecay;
-vmCvar_t cg_nopredict;
-vmCvar_t cg_debugMove;
-vmCvar_t cg_noPlayerAnims;
-vmCvar_t cg_showmiss;
-vmCvar_t cg_footsteps;
-vmCvar_t cg_addMarks;
-vmCvar_t cg_viewsize;
-vmCvar_t cg_drawGun;
-vmCvar_t cg_gun_frame;
-vmCvar_t cg_gun_x;
-vmCvar_t cg_gun_y;
-vmCvar_t cg_gun_z;
-vmCvar_t cg_tracerChance;
-vmCvar_t cg_tracerWidth;
-vmCvar_t cg_tracerLength;
-vmCvar_t cg_thirdPerson;
-vmCvar_t cg_thirdPersonAngle;
-vmCvar_t cg_thirdPersonShoulderViewMode;
-vmCvar_t cg_staticDeathCam;
-vmCvar_t cg_thirdPersonPitchFollow;
-vmCvar_t cg_thirdPersonRange;
-vmCvar_t cg_stereoSeparation;
-vmCvar_t cg_lagometer;
-vmCvar_t cg_drawSpeed;
-vmCvar_t cg_synchronousClients;
-vmCvar_t cg_stats;
-vmCvar_t cg_paused;
-vmCvar_t cg_blood;
-vmCvar_t cg_teamChatsOnly;
-vmCvar_t cg_drawTeamOverlay;
-vmCvar_t cg_teamOverlaySortMode;
-vmCvar_t cg_teamOverlayMaxPlayers;
-vmCvar_t cg_teamOverlayUserinfo;
-vmCvar_t cg_noPrintDuplicate;
-vmCvar_t cg_noVoiceChats;
-vmCvar_t cg_noVoiceText;
-vmCvar_t cg_hudFiles;
-vmCvar_t cg_hudFilesEnable;
-vmCvar_t cg_smoothClients;
-vmCvar_t pmove_fixed;
-vmCvar_t pmove_msec;
-vmCvar_t cg_cameraMode;
-vmCvar_t cg_timescaleFadeEnd;
-vmCvar_t cg_timescaleFadeSpeed;
-vmCvar_t cg_timescale;
-vmCvar_t cg_noTaunt;
-vmCvar_t cg_drawSurfNormal;
-vmCvar_t cg_drawBBOX;
-vmCvar_t cg_wwSmoothTime;
-vmCvar_t cg_disableBlueprintErrors;
-vmCvar_t cg_depthSortParticles;
-vmCvar_t cg_bounceParticles;
-vmCvar_t cg_consoleLatency;
-vmCvar_t cg_lightFlare;
-vmCvar_t cg_debugParticles;
-vmCvar_t cg_debugTrails;
-vmCvar_t cg_debugPVS;
-vmCvar_t cg_disableWarningDialogs;
-vmCvar_t cg_disableUpgradeDialogs;
-vmCvar_t cg_disableBuildDialogs;
-vmCvar_t cg_disableCommandDialogs;
-vmCvar_t cg_disableScannerPlane;
-vmCvar_t cg_tutorial;
-
-vmCvar_t cg_modTutorial;
-vmCvar_t cg_modTutorialReference;
-vmCvar_t cg_lastModVersion;
-
-vmCvar_t cg_painBlendUpRate;
-vmCvar_t cg_painBlendDownRate;
-vmCvar_t cg_painBlendMax;
-vmCvar_t cg_painBlendScale;
-vmCvar_t cg_painBlendZoom;
-
-vmCvar_t cg_stickySpec;
-vmCvar_t cg_sprintToggle;
-vmCvar_t cg_unlagged;
-
-vmCvar_t cg_debugVoices;
-
-vmCvar_t ui_currentClass;
-vmCvar_t ui_carriage;
-vmCvar_t ui_stages;
-vmCvar_t ui_dialog;
-vmCvar_t ui_voteActive;
-vmCvar_t ui_alienTeamVoteActive;
-vmCvar_t ui_humanTeamVoteActive;
-
-vmCvar_t cg_debugRandom;
-
-vmCvar_t cg_optimizePrediction;
-vmCvar_t cg_projectileNudge;
-
-vmCvar_t cg_voice;
-
-vmCvar_t cg_emoticons;
-
-vmCvar_t cg_chatTeamPrefix;
-
-vmCvar_t cg_cuboidResizeAxis;
-vmCvar_t cg_cuboidPSQuality;
-vmCvar_t cg_cuboidInfoX;
-vmCvar_t cg_cuboidInfoY;
-
-vmCvar_t cg_fuelInfoX;
-vmCvar_t cg_fuelInfoY;
-vmCvar_t cg_fuelInfoScale;
-
-typedef struct
-{
- vmCvar_t *vmCvar;
- char *cvarName;
- char *defaultString;
- int cvarFlags;
-} cvarTable_t;
-
-static cvarTable_t cvarTable[ ] =
-{
- { &cg_drawGun, "cg_drawGun", "1", CVAR_ARCHIVE },
- { &cg_viewsize, "cg_viewsize", "100", CVAR_ARCHIVE },
- { &cg_stereoSeparation, "cg_stereoSeparation", "0.4", CVAR_ARCHIVE },
- { &cg_shadows, "cg_shadows", "1", CVAR_ARCHIVE },
- { &cg_draw2D, "cg_draw2D", "1", CVAR_ARCHIVE },
- { &cg_drawTimer, "cg_drawTimer", "1", CVAR_ARCHIVE },
- { &cg_drawClock, "cg_drawClock", "0", CVAR_ARCHIVE },
- { &cg_drawFPS, "cg_drawFPS", "1", CVAR_ARCHIVE },
- { &cg_drawDemoState, "cg_drawDemoState", "1", CVAR_ARCHIVE },
- { &cg_drawSnapshot, "cg_drawSnapshot", "0", CVAR_ARCHIVE },
- { &cg_drawChargeBar, "cg_drawChargeBar", "1", CVAR_ARCHIVE },
- { &cg_drawCrosshair, "cg_drawCrosshair", "2", CVAR_ARCHIVE },
- { &cg_drawCrosshairNames, "cg_drawCrosshairNames", "1", CVAR_ARCHIVE },
- { &cg_crosshairSize, "cg_crosshairSize", "1", CVAR_ARCHIVE },
- { &cg_addMarks, "cg_marks", "1", CVAR_ARCHIVE },
- { &cg_lagometer, "cg_lagometer", "0", CVAR_ARCHIVE },
- { &cg_drawSpeed, "cg_drawSpeed", "0", CVAR_ARCHIVE },
- { &cg_teslaTrailTime, "cg_teslaTrailTime", "250", CVAR_ARCHIVE },
- { &cg_gun_x, "cg_gunX", "0", CVAR_CHEAT },
- { &cg_gun_y, "cg_gunY", "0", CVAR_CHEAT },
- { &cg_gun_z, "cg_gunZ", "0", CVAR_CHEAT },
- { &cg_centertime, "cg_centertime", "3", CVAR_CHEAT },
- { &cg_runpitch, "cg_runpitch", "0.002", CVAR_ARCHIVE},
- { &cg_runroll, "cg_runroll", "0.005", CVAR_ARCHIVE },
- { &cg_swingSpeed, "cg_swingSpeed", "0.3", CVAR_CHEAT },
- { &cg_animSpeed, "cg_animspeed", "1", CVAR_CHEAT },
- { &cg_debugAnim, "cg_debuganim", "0", CVAR_CHEAT },
- { &cg_debugPosition, "cg_debugposition", "0", CVAR_CHEAT },
- { &cg_debugEvents, "cg_debugevents", "0", CVAR_CHEAT },
- { &cg_errorDecay, "cg_errordecay", "100", 0 },
- { &cg_nopredict, "cg_nopredict", "0", 0 },
- { &cg_debugMove, "cg_debugMove", "0", 0 },
- { &cg_noPlayerAnims, "cg_noplayeranims", "0", CVAR_CHEAT },
- { &cg_showmiss, "cg_showmiss", "0", 0 },
- { &cg_footsteps, "cg_footsteps", "1", CVAR_CHEAT },
- { &cg_tracerChance, "cg_tracerchance", "0.4", CVAR_CHEAT },
- { &cg_tracerWidth, "cg_tracerwidth", "1", CVAR_CHEAT },
- { &cg_tracerLength, "cg_tracerlength", "100", CVAR_CHEAT },
- { &cg_thirdPersonRange, "cg_thirdPersonRange", "75", CVAR_ARCHIVE },
- { &cg_thirdPerson, "cg_thirdPerson", "0", CVAR_CHEAT },
- { &cg_thirdPersonAngle, "cg_thirdPersonAngle", "0", CVAR_CHEAT },
- { &cg_thirdPersonPitchFollow, "cg_thirdPersonPitchFollow", "0", 0 },
- { &cg_thirdPersonShoulderViewMode, "cg_thirdPersonShoulderViewMode", "1", CVAR_ARCHIVE },
- { &cg_staticDeathCam, "cg_staticDeathCam", "0", CVAR_ARCHIVE },
- { &cg_stats, "cg_stats", "0", 0 },
- { &cg_drawTeamOverlay, "cg_drawTeamOverlay", "1", CVAR_ARCHIVE },
- { &cg_teamOverlaySortMode, "cg_teamOverlaySortMode", "1", CVAR_ARCHIVE },
- { &cg_teamOverlayMaxPlayers, "cg_teamOverlayMaxPlayers", "8", CVAR_ARCHIVE },
- { &cg_teamOverlayUserinfo, "teamoverlay", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_teamChatsOnly, "cg_teamChatsOnly", "0", CVAR_ARCHIVE },
- { &cg_noPrintDuplicate, "cg_noPrintDuplicate", "0", CVAR_ARCHIVE },
- { &cg_noVoiceChats, "cg_noVoiceChats", "0", CVAR_ARCHIVE },
- { &cg_noVoiceText, "cg_noVoiceText", "0", CVAR_ARCHIVE },
- { &cg_drawSurfNormal, "cg_drawSurfNormal", "0", CVAR_CHEAT },
- { &cg_drawBBOX, "cg_drawBBOX", "0", CVAR_CHEAT },
- { &cg_wwSmoothTime, "cg_wwSmoothTime", "300", CVAR_ARCHIVE },
- { NULL, "cg_wwFollow", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { NULL, "cg_wwToggle", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { NULL, "cg_disableBlueprintErrors", "0", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_stickySpec, "cg_stickySpec", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_sprintToggle, "cg_sprintToggle", "0", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_unlagged, "cg_unlagged", "1", CVAR_ARCHIVE|CVAR_USERINFO },
- { NULL, "cg_flySpeed", "600", CVAR_ARCHIVE|CVAR_USERINFO },
- { &cg_depthSortParticles, "cg_depthSortParticles", "1", CVAR_ARCHIVE },
- { &cg_bounceParticles, "cg_bounceParticles", "0", CVAR_ARCHIVE },
- { &cg_consoleLatency, "cg_consoleLatency", "3000", CVAR_ARCHIVE },
- { &cg_lightFlare, "cg_lightFlare", "3", CVAR_ARCHIVE },
- { &cg_debugParticles, "cg_debugParticles", "0", CVAR_CHEAT },
- { &cg_debugTrails, "cg_debugTrails", "0", CVAR_CHEAT },
- { &cg_debugPVS, "cg_debugPVS", "0", CVAR_CHEAT },
- { &cg_disableWarningDialogs, "cg_disableWarningDialogs", "0", CVAR_ARCHIVE },
- { &cg_disableUpgradeDialogs, "cg_disableUpgradeDialogs", "0", CVAR_ARCHIVE },
- { &cg_disableBuildDialogs, "cg_disableBuildDialogs", "0", CVAR_ARCHIVE },
- { &cg_disableCommandDialogs, "cg_disableCommandDialogs", "0", CVAR_ARCHIVE },
- { &cg_disableScannerPlane, "cg_disableScannerPlane", "0", CVAR_ARCHIVE },
- { &cg_tutorial, "cg_tutorial", "1", CVAR_ARCHIVE },
- { &cg_hudFiles, "cg_hudFiles", "ui/hud.txt", CVAR_ARCHIVE},
- { &cg_hudFilesEnable, "cg_hudFilesEnable", "0", CVAR_ARCHIVE},
- { NULL, "cg_alienConfig", "", CVAR_ARCHIVE },
- { NULL, "cg_humanConfig", "", CVAR_ARCHIVE },
- { NULL, "cg_spectatorConfig", "", CVAR_ARCHIVE },
-
- { &cg_painBlendUpRate, "cg_painBlendUpRate", "10.0", 0 },
- { &cg_painBlendDownRate, "cg_painBlendDownRate", "0.5", 0 },
- { &cg_painBlendMax, "cg_painBlendMax", "0.7", 0 },
- { &cg_painBlendScale, "cg_painBlendScale", "7.0", 0 },
- { &cg_painBlendZoom, "cg_painBlendZoom", "0.65", 0 },
-
- { &cg_debugVoices, "cg_debugVoices", "0", 0 },
-
- // communication cvars set by the cgame to be read by ui
- { &ui_currentClass, "ui_currentClass", "0", CVAR_ROM },
- { &ui_carriage, "ui_carriage", "", CVAR_ROM },
- { &ui_stages, "ui_stages", "0 0", CVAR_ROM },
- { &ui_dialog, "ui_dialog", "Text not set", CVAR_ROM },
- { &ui_voteActive, "ui_voteActive", "0", CVAR_ROM },
- { &ui_humanTeamVoteActive, "ui_humanTeamVoteActive", "0", CVAR_ROM },
- { &ui_alienTeamVoteActive, "ui_alienTeamVoteActive", "0", CVAR_ROM },
-
- { &cg_debugRandom, "cg_debugRandom", "0", 0 },
-
- { &cg_optimizePrediction, "cg_optimizePrediction", "1", CVAR_ARCHIVE },
- { &cg_projectileNudge, "cg_projectileNudge", "1", CVAR_ARCHIVE },
-
- // the following variables are created in other parts of the system,
- // but we also reference them here
-
- { &cg_paused, "cl_paused", "0", CVAR_ROM },
- { &cg_blood, "com_blood", "1", CVAR_ARCHIVE },
- { &cg_synchronousClients, "g_synchronousClients", "0", 0 }, // communicated by systeminfo
- { &cg_timescaleFadeEnd, "cg_timescaleFadeEnd", "1", CVAR_CHEAT },
- { &cg_timescaleFadeSpeed, "cg_timescaleFadeSpeed", "0", CVAR_CHEAT },
- { &cg_timescale, "timescale", "1", 0},
- { &cg_smoothClients, "cg_smoothClients", "0", CVAR_USERINFO | CVAR_ARCHIVE},
- { &cg_cameraMode, "com_cameraMode", "0", CVAR_CHEAT},
-
- { &pmove_fixed, "pmove_fixed", "0", 0},
- { &pmove_msec, "pmove_msec", "8", 0},
- { &cg_noTaunt, "cg_noTaunt", "0", CVAR_ARCHIVE},
-
- { &cg_voice, "voice", "default", CVAR_USERINFO|CVAR_ARCHIVE},
-
- { &cg_emoticons, "cg_emoticons", "1", CVAR_LATCH|CVAR_ARCHIVE},
-
- { &cg_chatTeamPrefix, "cg_chatTeamPrefix", "1", CVAR_ARCHIVE},
-
- { &cg_cuboidResizeAxis, "cg_cuboidResizeAxis", "2", 0},
- { &cg_cuboidPSQuality, "cg_cuboidPSQuality", "3", CVAR_ARCHIVE},
-
- { &cg_cuboidInfoX, "cg_cuboidInfoX" ,"0", CVAR_ARCHIVE },
- { &cg_cuboidInfoY, "cg_cuboidInfoY" ,"150", CVAR_ARCHIVE },
-
- { &cg_modTutorial, "cg_modTutorial", "1", CVAR_ARCHIVE },
- { &cg_modTutorialReference, "cg_modTutorialReference", "0", CVAR_ARCHIVE },
- { &cg_lastModVersion, "cg_lastModVersion", "0", CVAR_ARCHIVE },
-
- { &cg_fuelInfoX, "cg_fuelInfoX" ,"0", CVAR_ARCHIVE },
- { &cg_fuelInfoY, "cg_fuelInfoY" ,"150", CVAR_ARCHIVE },
- { &cg_fuelInfoScale, "cg_fuelInfoScale" ,"0.5", CVAR_ARCHIVE }
-};
-
-static int cvarTableSize = sizeof( cvarTable ) / sizeof( cvarTable[0] );
-
-/*
-=================
-CG_RegisterCvars
-=================
-*/
-void CG_RegisterCvars( void )
-{
- int i;
- cvarTable_t *cv;
- char var[ MAX_TOKEN_CHARS ];
-
- for( i = 0, cv = cvarTable; i < cvarTableSize; i++, cv++ )
- {
- trap_Cvar_Register( cv->vmCvar, cv->cvarName,
- cv->defaultString, cv->cvarFlags );
- }
-
- // see if we are also running the server on this machine
- trap_Cvar_VariableStringBuffer( "sv_running", var, sizeof( var ) );
- cgs.localServer = atoi( var );
-}
-
-
-/*
-===============
-CG_SetUIVars
-
-Set some cvars used by the UI
-===============
-*/
-static void CG_SetUIVars( void )
-{
- int i;
- char carriageCvar[ MAX_TOKEN_CHARS ];
-
- if( !cg.snap )
- return;
-
- *carriageCvar = 0;
-
- //determine what the player is carrying
- for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
- {
- if( BG_InventoryContainsWeapon( i, cg.snap->ps.stats ) &&
- BG_Weapon( i )->purchasable )
- strcat( carriageCvar, va( "W%d ", i ) );
- }
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- if( BG_InventoryContainsUpgrade( i, cg.snap->ps.stats ) &&
- BG_Upgrade( i )->purchasable )
- strcat( carriageCvar, va( "U%d ", i ) );
- }
- strcat( carriageCvar, "$" );
-
- trap_Cvar_Set( "ui_carriage", carriageCvar );
-
- trap_Cvar_Set( "ui_stages", va( "%d %d", cgs.alienStage, cgs.humanStage ) );
-}
-
-/*
-=================
-CG_UpdateCvars
-=================
-*/
-void CG_UpdateCvars( void )
-{
- int i;
- cvarTable_t *cv;
-
- for( i = 0, cv = cvarTable; i < cvarTableSize; i++, cv++ )
- if( cv->vmCvar )
- trap_Cvar_Update( cv->vmCvar );
-
- // check for modications here
-
- CG_SetUIVars( );
-
-}
-
-
-int CG_CrosshairPlayer( void )
-{
- if( cg.time > ( cg.crosshairClientTime + 1000 ) )
- return -1;
-
- return cg.crosshairClientNum;
-}
-
-
-int CG_LastAttacker( void )
-{
- if( !cg.attackerTime )
- return -1;
-
- return cg.snap->ps.persistant[ PERS_ATTACKER ];
-}
-
-
-/*
-=================
-CG_RemoveNotifyLine
-=================
-*/
-void CG_RemoveNotifyLine( void )
-{
- int i, offset, totalLength;
-
- if( cg.numConsoleLines == 0 )
- return;
-
- offset = cg.consoleLines[ 0 ].length;
- totalLength = strlen( cg.consoleText ) - offset;
-
- //slide up consoleText
- for( i = 0; i <= totalLength; i++ )
- cg.consoleText[ i ] = cg.consoleText[ i + offset ];
-
- //pop up the first consoleLine
- for( i = 0; i < cg.numConsoleLines; i++ )
- cg.consoleLines[ i ] = cg.consoleLines[ i + 1 ];
-
- cg.numConsoleLines--;
-}
-
-/*
-=================
-CG_AddNotifyText
-=================
-*/
-void CG_AddNotifyText( void )
-{
- char buffer[ BIG_INFO_STRING ];
- int bufferLen, textLen;
-
- trap_LiteralArgs( buffer, BIG_INFO_STRING );
-
- if( !buffer[ 0 ] )
- {
- cg.consoleText[ 0 ] = '\0';
- cg.numConsoleLines = 0;
- return;
- }
-
- bufferLen = strlen( buffer );
- textLen = strlen( cg.consoleText );
-
- // Ignore console messages that were just printed
- if( cg_noPrintDuplicate.integer && textLen >= bufferLen &&
- !strcmp( cg.consoleText + textLen - bufferLen, buffer ) )
- return;
-
- if( cg.numConsoleLines == MAX_CONSOLE_LINES )
- CG_RemoveNotifyLine( );
-
- Q_strcat( cg.consoleText, MAX_CONSOLE_TEXT, buffer );
- cg.consoleLines[ cg.numConsoleLines ].time = cg.time;
- cg.consoleLines[ cg.numConsoleLines ].length = bufferLen;
- cg.numConsoleLines++;
-}
-
-void QDECL CG_Printf( const char *msg, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, msg );
- Q_vsnprintf( text, sizeof( text ), msg, argptr );
- va_end( argptr );
-
- trap_Print( text );
-}
-
-void QDECL CG_Error( const char *msg, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, msg );
- Q_vsnprintf( text, sizeof( text ), msg, argptr );
- va_end( argptr );
-
- trap_Error( text );
-}
-
-void QDECL Com_Error( int level, const char *error, ... )
-{
- va_list argptr;
- char text[1024];
-
- va_start( argptr, error );
- Q_vsnprintf( text, sizeof( text ), error, argptr );
- va_end( argptr );
-
- CG_Error( "%s", text );
-}
-
-void QDECL Com_Printf( const char *msg, ... ) {
- va_list argptr;
- char text[1024];
-
- va_start( argptr, msg );
- Q_vsnprintf( text, sizeof( text ), msg, argptr );
- va_end( argptr );
-
- CG_Printf ("%s", text);
-}
-
-
-
-/*
-================
-CG_Argv
-================
-*/
-const char *CG_Argv( int arg )
-{
- static char buffer[ MAX_STRING_CHARS ];
-
- trap_Argv( arg, buffer, sizeof( buffer ) );
-
- return buffer;
-}
-
-
-//========================================================================
-
-/*
-=================
-CG_FileExists
-
-Test if a specific file exists or not
-=================
-*/
-qboolean CG_FileExists( char *filename )
-{
- return trap_FS_FOpenFile( filename, NULL, FS_READ );
-}
-
-/*
-=================
-CG_RegisterSounds
-
-called during a precache command
-=================
-*/
-static void CG_RegisterSounds( void )
-{
- int i;
- char name[ MAX_QPATH ];
- const char *soundName;
- const cuboidAttributes_t *cuboid;
-
- cgs.media.alienStageTransition = trap_S_RegisterSound( "sound/announcements/overmindevolved.wav", qtrue );
- cgs.media.humanStageTransition = trap_S_RegisterSound( "sound/announcements/reinforcement.wav", qtrue );
-
- cgs.media.alienOvermindAttack = trap_S_RegisterSound( "sound/announcements/overmindattack.wav", qtrue );
- cgs.media.alienOvermindDying = trap_S_RegisterSound( "sound/announcements/overminddying.wav", qtrue );
- cgs.media.alienOvermindSpawns = trap_S_RegisterSound( "sound/announcements/overmindspawns.wav", qtrue );
-
- cgs.media.alienL1Grab = trap_S_RegisterSound( "sound/player/level1/grab.wav", qtrue );
- cgs.media.alienL4ChargePrepare = trap_S_RegisterSound( "sound/player/level4/charge_prepare.wav", qtrue );
- cgs.media.alienL4ChargeStart = trap_S_RegisterSound( "sound/player/level4/charge_start.wav", qtrue );
-
- cgs.media.tracerSound = trap_S_RegisterSound( "sound/weapons/tracer.wav", qfalse );
- cgs.media.selectSound = trap_S_RegisterSound( "sound/weapons/change.wav", qfalse );
- cgs.media.turretSpinupSound = trap_S_RegisterSound( "sound/buildables/mgturret/spinup.wav", qfalse );
- cgs.media.weaponEmptyClick = trap_S_RegisterSound( "sound/weapons/click.wav", qfalse );
-
- cgs.media.talkSound = trap_S_RegisterSound( "sound/misc/talk.wav", qfalse );
- cgs.media.alienTalkSound = trap_S_RegisterSound( "sound/misc/alien_talk.wav", qfalse );
- cgs.media.humanTalkSound = trap_S_RegisterSound( "sound/misc/human_talk.wav", qfalse );
- cgs.media.landSound = trap_S_RegisterSound( "sound/player/land1.wav", qfalse );
-
- cgs.media.watrInSound = trap_S_RegisterSound( "sound/player/watr_in.wav", qfalse );
- cgs.media.watrOutSound = trap_S_RegisterSound( "sound/player/watr_out.wav", qfalse );
- cgs.media.watrUnSound = trap_S_RegisterSound( "sound/player/watr_un.wav", qfalse );
-
- cgs.media.disconnectSound = trap_S_RegisterSound( "sound/misc/disconnect.wav", qfalse );
-
- for( i = 0; i < 4; i++ )
- {
- Com_sprintf( name, sizeof( name ), "sound/player/footsteps/step%i.wav", i + 1 );
- cgs.media.footsteps[ FOOTSTEP_NORMAL ][ i ] = trap_S_RegisterSound( name, qfalse );
-
- Com_sprintf( name, sizeof( name ), "sound/player/footsteps/flesh%i.wav", i + 1 );
- cgs.media.footsteps[ FOOTSTEP_FLESH ][ i ] = trap_S_RegisterSound( name, qfalse );
-
- Com_sprintf( name, sizeof( name ), "sound/player/footsteps/splash%i.wav", i + 1 );
- cgs.media.footsteps[ FOOTSTEP_SPLASH ][ i ] = trap_S_RegisterSound( name, qfalse );
-
- Com_sprintf( name, sizeof( name ), "sound/player/footsteps/clank%i.wav", i + 1 );
- cgs.media.footsteps[ FOOTSTEP_METAL ][ i ] = trap_S_RegisterSound( name, qfalse );
- }
-
- for( i = 1 ; i < MAX_SOUNDS ; i++ )
- {
- soundName = CG_ConfigString( CS_SOUNDS + i );
-
- if( !soundName[ 0 ] )
- break;
-
- if( soundName[ 0 ] == '*' )
- continue; // custom sound
-
- cgs.gameSounds[ i ] = trap_S_RegisterSound( soundName, qfalse );
- }
-
- cgs.media.jetpackDescendSound = trap_S_RegisterSound( "sound/upgrades/jetpack/low.wav", qfalse );
- cgs.media.jetpackIdleSound = trap_S_RegisterSound( "sound/upgrades/jetpack/idle.wav", qfalse );
- cgs.media.jetpackAscendSound = trap_S_RegisterSound( "sound/upgrades/jetpack/hi.wav", qfalse );
- cgs.media.jetpackDescendDeactivateSound = trap_S_RegisterSound( "sound/upgrades/jetpack/low_off.wav", qfalse );
- cgs.media.jetpackIdleDeactivateSound = trap_S_RegisterSound( "sound/upgrades/jetpack/idle_off.wav", qfalse );
- cgs.media.jetpackAscendDeactivateSound = trap_S_RegisterSound( "sound/upgrades/jetpack/hi_off.wav", qfalse );
- cgs.media.jetpackJumpSound = trap_S_RegisterSound( "sound/upgrades/jetpack/jump.wav", qfalse );
- cgs.media.jetpackLowFuelSound = trap_S_RegisterSound( "sound/upgrades/jetpack/lowfuel.wav", qfalse );
- cgs.media.jetpackNoJumpFuelSound = trap_S_RegisterSound( "sound/upgrades/jetpack/nojumpfuel.wav", qfalse );
- cgs.media.jetpackRefuelSound = trap_S_RegisterSound( "sound/upgrades/jetpack/refuel.wav", qfalse );
-
- cgs.media.medkitUseSound = trap_S_RegisterSound( "sound/upgrades/medkit/medkit.wav", qfalse );
-
- cgs.media.alienEvolveSound = trap_S_RegisterSound( "sound/player/alienevolve.wav", qfalse );
-
- cgs.media.alienBuildableExplosion = trap_S_RegisterSound( "sound/buildables/alien/explosion.wav", qfalse );
- cgs.media.alienBuildableDamage = trap_S_RegisterSound( "sound/buildables/alien/damage.wav", qfalse );
- cgs.media.alienBuildablePrebuild = trap_S_RegisterSound( "sound/buildables/alien/prebuild.wav", qfalse );
-
- cgs.media.humanBuildableExplosion = trap_S_RegisterSound( "sound/buildables/human/explosion.wav", qfalse );
- cgs.media.humanBuildablePrebuild = trap_S_RegisterSound( "sound/buildables/human/prebuild.wav", qfalse );
-
- for( i = 0; i < 4; i++ )
- cgs.media.humanBuildableDamage[ i ] = trap_S_RegisterSound(
- va( "sound/buildables/human/damage%d.wav", i ), qfalse );
-
- cgs.media.hardBounceSound1 = trap_S_RegisterSound( "sound/misc/hard_bounce1.wav", qfalse );
- cgs.media.hardBounceSound2 = trap_S_RegisterSound( "sound/misc/hard_bounce2.wav", qfalse );
-
- cgs.media.repeaterUseSound = trap_S_RegisterSound( "sound/buildables/repeater/use.wav", qfalse );
-
- cgs.media.buildableRepairSound = trap_S_RegisterSound( "sound/buildables/human/repair.wav", qfalse );
- cgs.media.buildableRepairedSound = trap_S_RegisterSound( "sound/buildables/human/repaired.wav", qfalse );
-
- cgs.media.lCannonWarningSound = trap_S_RegisterSound( "models/weapons/lcannon/warning.wav", qfalse );
- cgs.media.lCannonWarningSound2 = trap_S_RegisterSound( "models/weapons/lcannon/warning2.wav", qfalse );
-
- cgs.media.cuboidErrorSound = trap_S_RegisterSound( "sound/cuboid/error.wav", qfalse );
- cg.lastCuboidError = cg.time;
- cgs.media.cuboidResizeSoundA = trap_S_RegisterSound( "sound/cuboid/resizea.wav", qfalse );
- cgs.media.cuboidResizeSoundB = trap_S_RegisterSound( "sound/cuboid/resizeb.wav", qfalse );
- cgs.media.cuboidRotateSound = trap_S_RegisterSound( "sound/cuboid/rotate.wav", qfalse );
- cgs.media.cuboidAxisChangeSound = trap_S_RegisterSound( "sound/cuboid/axischange.wav", qfalse );
-}
-
-
-//===================================================================================
-
-/*
-=================
-CG_RegisterGraphics
-
-This function may execute for a couple of minutes with a slow disk.
-=================
-*/
-static void CG_RegisterGraphics( void )
-{
- int i,j;
- const cuboidAttributes_t *cuboid;
-
- static char *sb_nums[ 11 ] =
- {
- "gfx/2d/numbers/zero_32b",
- "gfx/2d/numbers/one_32b",
- "gfx/2d/numbers/two_32b",
- "gfx/2d/numbers/three_32b",
- "gfx/2d/numbers/four_32b",
- "gfx/2d/numbers/five_32b",
- "gfx/2d/numbers/six_32b",
- "gfx/2d/numbers/seven_32b",
- "gfx/2d/numbers/eight_32b",
- "gfx/2d/numbers/nine_32b",
- "gfx/2d/numbers/minus_32b",
- };
- static char *buildWeaponTimerPieShaders[ 8 ] =
- {
- "ui/assets/neutral/1_5pie",
- "ui/assets/neutral/3_0pie",
- "ui/assets/neutral/4_5pie",
- "ui/assets/neutral/6_0pie",
- "ui/assets/neutral/7_5pie",
- "ui/assets/neutral/9_0pie",
- "ui/assets/neutral/10_5pie",
- "ui/assets/neutral/12_0pie",
- };
-
- // clear any references to old media
- memset( &cg.refdef, 0, sizeof( cg.refdef ) );
- trap_R_ClearScene( );
-
- trap_R_LoadWorldMap( cgs.mapname );
- CG_UpdateMediaFraction( 0.66f );
-
- for( i = 0; i < 11; i++ )
- cgs.media.numberShaders[ i ] = trap_R_RegisterShader( sb_nums[ i ] );
-
- cgs.media.viewBloodShader = trap_R_RegisterShader( "gfx/damage/fullscreen_painblend" );
-
- cgs.media.connectionShader = trap_R_RegisterShader( "gfx/2d/net" );
-
- cgs.media.creepShader = trap_R_RegisterShader( "creep" );
-
- cgs.media.scannerBlipShader = trap_R_RegisterShader( "gfx/2d/blip" );
- cgs.media.scannerLineShader = trap_R_RegisterShader( "gfx/2d/stalk" );
-
- cgs.media.teamOverlayShader = trap_R_RegisterShader( "gfx/2d/teamoverlay" );
-
- cgs.media.tracerShader = trap_R_RegisterShader( "gfx/misc/tracer" );
-
- cgs.media.backTileShader = trap_R_RegisterShader( "console" );
-
-
- // building shaders
- cgs.media.greenBuildShader = trap_R_RegisterShader("gfx/misc/greenbuild" );
- cgs.media.redBuildShader = trap_R_RegisterShader("gfx/misc/redbuild" );
- cgs.media.humanSpawningShader = trap_R_RegisterShader("models/buildables/telenode/rep_cyl" );
-
- for( i = 0; i < CUBOID_CRACK_TEXTURES - 1; i++ )
- cgs.media.cuboidCracks[ i ] = trap_R_RegisterShader( va( "models/cuboid/cracks_%i", i ) );
-
- cgs.media.cuboidModel = trap_R_RegisterModel( "models/cuboid/cuboid.md3" );
- cgs.media.cuboidRedBuildShader = trap_R_RegisterShader( "gfx/cuboid/build_red" );
- cgs.media.cuboidYellowBuildShader = trap_R_RegisterShader( "gfx/cuboid/build_yellow" );
- cgs.media.cuboidGreenBuildShader = trap_R_RegisterShader( "gfx/cuboid/build_green" );
- cgs.media.cuboidAxis = trap_R_RegisterShader( "gfx/cuboid/build_axis" );
- cgs.media.cuboidAlienPrebuild = trap_R_RegisterShader( "gfx/cuboid/prebuild_alien" );
-
- cg.forbidCuboids=qfalse;
- cg.latestCBNumber=0;
-
- for( i = 0; i < 15; i++ )
- cgs.media.splashLogo[ i ] = trap_R_RegisterShader( va( "cuboid/logo_%i.tga", i ) );
- cgs.media.splashLeft = trap_R_RegisterShader( "cuboid/logo_left.tga" );
- cgs.media.splashRight = trap_R_RegisterShader( "cuboid/logo_right.tga" );
-
-
- for( i = 0; i < 8; i++ )
- cgs.media.buildWeaponTimerPie[ i ] = trap_R_RegisterShader( buildWeaponTimerPieShaders[ i ] );
-
- // player health cross shaders
- cgs.media.healthCross = trap_R_RegisterShader( "ui/assets/neutral/cross.tga" );
- cgs.media.healthCross2X = trap_R_RegisterShader( "ui/assets/neutral/cross2.tga" );
- cgs.media.healthCross3X = trap_R_RegisterShader( "ui/assets/neutral/cross3.tga" );
- cgs.media.healthCrossMedkit = trap_R_RegisterShader( "ui/assets/neutral/cross_medkit.tga" );
- cgs.media.healthCrossPoisoned = trap_R_RegisterShader( "ui/assets/neutral/cross_poison.tga" );
-
- cgs.media.upgradeClassIconShader = trap_R_RegisterShader( "icons/icona_upgrade.tga" );
-
- cgs.media.balloonShader = trap_R_RegisterShader( "gfx/sprites/chatballoon" );
-
- cgs.media.disconnectPS = CG_RegisterParticleSystem( "disconnectPS" );
-
- CG_UpdateMediaFraction( 0.7f );
-
- memset( cg_weapons, 0, sizeof( cg_weapons ) );
- memset( cg_upgrades, 0, sizeof( cg_upgrades ) );
-
- cgs.media.shadowMarkShader = trap_R_RegisterShader( "gfx/marks/shadow" );
- cgs.media.wakeMarkShader = trap_R_RegisterShader( "gfx/marks/wake" );
-
- cgs.media.poisonCloudPS = CG_RegisterParticleSystem( "firstPersonPoisonCloudPS" );
- cgs.media.poisonCloudedPS = CG_RegisterParticleSystem( "poisonCloudedPS" );
- cgs.media.alienEvolvePS = CG_RegisterParticleSystem( "alienEvolvePS" );
- cgs.media.alienAcidTubePS = CG_RegisterParticleSystem( "alienAcidTubePS" );
-
- cgs.media.jetPackDescendPS = CG_RegisterParticleSystem( "jetPackDescendPS" );
- cgs.media.jetPackHoverPS = CG_RegisterParticleSystem( "jetPackHoverPS" );
- cgs.media.jetPackAscendPS = CG_RegisterParticleSystem( "jetPackAscendPS" );
-
- cgs.media.humanBuildableDamagedPS = CG_RegisterParticleSystem( "humanBuildableDamagedPS" );
- cgs.media.alienBuildableDamagedPS = CG_RegisterParticleSystem( "alienBuildableDamagedPS" );
- cgs.media.humanBuildableDestroyedPS = CG_RegisterParticleSystem( "humanBuildableDestroyedPS" );
- cgs.media.alienBuildableDestroyedPS = CG_RegisterParticleSystem( "alienBuildableDestroyedPS" );
-
- cgs.media.humanBuildableBleedPS = CG_RegisterParticleSystem( "humanBuildableBleedPS");
- cgs.media.alienBuildableBleedPS = CG_RegisterParticleSystem( "alienBuildableBleedPS" );
-
- cgs.media.alienBleedPS = CG_RegisterParticleSystem( "alienBleedPS" );
- cgs.media.humanBleedPS = CG_RegisterParticleSystem( "humanBleedPS" );
-
- CG_BuildableStatusParse( "ui/assets/human/buildstat.cfg", &cgs.humanBuildStat );
- CG_BuildableStatusParse( "ui/assets/alien/buildstat.cfg", &cgs.alienBuildStat );
-
- // register the inline models
- cgs.numInlineModels = trap_CM_NumInlineModels( );
-
- for( i = 1; i < cgs.numInlineModels; i++ )
- {
- char name[ 10 ];
- vec3_t mins, maxs;
- int j;
-
- Com_sprintf( name, sizeof( name ), "*%i", i );
-
- cgs.inlineDrawModel[ i ] = trap_R_RegisterModel( name );
- trap_R_ModelBounds( cgs.inlineDrawModel[ i ], mins, maxs );
-
- for( j = 0 ; j < 3 ; j++ )
- cgs.inlineModelMidpoints[ i ][ j ] = mins[ j ] + 0.5 * ( maxs[ j ] - mins[ j ] );
- }
-
- // register all the server specified models
- for( i = 1; i < MAX_MODELS; i++ )
- {
- const char *modelName;
-
- modelName = CG_ConfigString( CS_MODELS + i );
-
- if( !modelName[ 0 ] )
- break;
-
- cgs.gameModels[ i ] = trap_R_RegisterModel( modelName );
- }
-
- CG_UpdateMediaFraction( 0.8f );
-
- // register all the server specified shaders
- for( i = 1; i < MAX_GAME_SHADERS; i++ )
- {
- const char *shaderName;
-
- shaderName = CG_ConfigString( CS_SHADERS + i );
-
- if( !shaderName[ 0 ] )
- break;
-
- cgs.gameShaders[ i ] = trap_R_RegisterShader( shaderName );
- }
-
- CG_UpdateMediaFraction( 0.9f );
-
- // register all the server specified particle systems
- for( i = 1; i < MAX_GAME_PARTICLE_SYSTEMS; i++ )
- {
- const char *psName;
-
- psName = CG_ConfigString( CS_PARTICLE_SYSTEMS + i );
-
- if( !psName[ 0 ] )
- break;
-
- cgs.gameParticleSystems[ i ] = CG_RegisterParticleSystem( (char *)psName );
- }
-}
-
-
-/*
-=======================
-CG_BuildSpectatorString
-
-=======================
-*/
-void CG_BuildSpectatorString( void )
-{
- int i;
-
- cg.spectatorList[ 0 ] = 0;
-
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- if( cgs.clientinfo[ i ].infoValid && cgs.clientinfo[ i ].team == TEAM_NONE )
- {
- Q_strcat( cg.spectatorList, sizeof( cg.spectatorList ),
- va( S_COLOR_WHITE "%s ", cgs.clientinfo[ i ].name ) );
- }
- }
-}
-
-
-
-/*
-===================
-CG_RegisterClients
-
-===================
-*/
-static void CG_RegisterClients( void )
-{
- int i;
-
- cg.charModelFraction = 0.0f;
-
- //precache all the models/sounds/etc
- for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
- {
- CG_PrecacheClientInfo( i, BG_ClassConfig( i )->modelName,
- BG_ClassConfig( i )->skinName );
-
- cg.charModelFraction = (float)i / (float)PCL_NUM_CLASSES;
- trap_UpdateScreen( );
- }
-
- cgs.media.larmourHeadSkin = trap_R_RegisterSkin( "models/players/human_base/head_light.skin" );
- cgs.media.larmourMk2HeadSkin = trap_R_RegisterSkin( "models/players/human_base/head_light_mk2.skin" );
- cgs.media.larmourLegsSkin = trap_R_RegisterSkin( "models/players/human_base/lower_light.skin" );
- cgs.media.larmourTorsoSkin = trap_R_RegisterSkin( "models/players/human_base/upper_light.skin" );
-
- cgs.media.jetpackModel = trap_R_RegisterModel( "models/players/human_base/jetpack.md3" );
- cgs.media.jetpackFlashModel = trap_R_RegisterModel( "models/players/human_base/jetpack_flash.md3" );
- cgs.media.battpackModel = trap_R_RegisterModel( "models/players/human_base/battpack.md3" );
-
- cg.charModelFraction = 1.0f;
- trap_UpdateScreen( );
-
- //load all the clientinfos of clients already connected to the server
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- const char *clientInfo;
-
- clientInfo = CG_ConfigString( CS_PLAYERS + i );
- if( !clientInfo[ 0 ] )
- continue;
-
- CG_NewClientInfo( i );
- }
-
- CG_BuildSpectatorString( );
-}
-
-//===========================================================================
-
-/*
-=================
-CG_ConfigString
-=================
-*/
-const char *CG_ConfigString( int index )
-{
- if( index < 0 || index >= MAX_CONFIGSTRINGS )
- CG_Error( "CG_ConfigString: bad index: %i", index );
-
- return cgs.gameState.stringData + cgs.gameState.stringOffsets[ index ];
-}
-
-//==================================================================
-
-/*
-======================
-CG_StartMusic
-
-======================
-*/
-void CG_StartMusic( void )
-{
- char *s;
- char parm1[ MAX_QPATH ], parm2[ MAX_QPATH ];
-
- // start the background music
- s = (char *)CG_ConfigString( CS_MUSIC );
- Q_strncpyz( parm1, COM_Parse( &s ), sizeof( parm1 ) );
- Q_strncpyz( parm2, COM_Parse( &s ), sizeof( parm2 ) );
-
- trap_S_StartBackgroundTrack( parm1, parm2 );
-}
-
-/*
-======================
-CG_PlayerCount
-======================
-*/
-int CG_PlayerCount( void )
-{
- int i, count = 0;
-
- CG_RequestScores( );
-
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == TEAM_ALIENS ||
- cg.scores[ i ].team == TEAM_HUMANS )
- count++;
- }
-
- return count;
-}
-
-//
-// ==============================
-// new hud stuff ( mission pack )
-// ==============================
-//
-char *CG_GetMenuBuffer( const char *filename )
-{
- int len;
- fileHandle_t f;
- static char buf[ MAX_MENUFILE ];
-
- len = trap_FS_FOpenFile( filename, &f, FS_READ );
-
- if( !f )
- {
- trap_Print( va( S_COLOR_RED "menu file not found: %s, using default\n", filename ) );
- return NULL;
- }
-
- if( len >= MAX_MENUFILE )
- {
- trap_Print( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i",
- filename, len, MAX_MENUFILE ) );
- trap_FS_FCloseFile( f );
- return NULL;
- }
-
- trap_FS_Read( buf, len, f );
- buf[len] = 0;
- trap_FS_FCloseFile( f );
-
- return buf;
-}
-
-qboolean CG_Asset_Parse( int handle )
-{
- pc_token_t token;
- const char *tempStr;
-
- if( !trap_Parse_ReadToken( handle, &token ) )
- return qfalse;
-
- if( Q_stricmp( token.string, "{" ) != 0 )
- return qfalse;
-
- while( 1 )
- {
- if( !trap_Parse_ReadToken( handle, &token ) )
- return qfalse;
-
- if( Q_stricmp( token.string, "}" ) == 0 )
- return qtrue;
-
- // font
- if( Q_stricmp( token.string, "font" ) == 0 )
- {
- int pointSize;
-
- if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
- return qfalse;
-
- cgDC.registerFont( tempStr, pointSize, &cgDC.Assets.textFont );
- continue;
- }
-
- // smallFont
- if( Q_stricmp( token.string, "smallFont" ) == 0 )
- {
- int pointSize;
-
- if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
- return qfalse;
-
- cgDC.registerFont( tempStr, pointSize, &cgDC.Assets.smallFont );
- continue;
- }
-
- // font
- if( Q_stricmp( token.string, "bigfont" ) == 0 )
- {
- int pointSize;
-
- if( !PC_String_Parse( handle, &tempStr ) || !PC_Int_Parse( handle, &pointSize ) )
- return qfalse;
-
- cgDC.registerFont( tempStr, pointSize, &cgDC.Assets.bigFont );
- continue;
- }
-
- // gradientbar
- if( Q_stricmp( token.string, "gradientbar" ) == 0 )
- {
- if( !PC_String_Parse( handle, &tempStr ) )
- return qfalse;
-
- cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( tempStr );
- continue;
- }
-
- // enterMenuSound
- if( Q_stricmp( token.string, "menuEnterSound" ) == 0 )
- {
- if( !PC_String_Parse( handle, &tempStr ) )
- return qfalse;
-
- cgDC.Assets.menuEnterSound = trap_S_RegisterSound( tempStr, qfalse );
- continue;
- }
-
- // exitMenuSound
- if( Q_stricmp( token.string, "menuExitSound" ) == 0 )
- {
- if( !PC_String_Parse( handle, &tempStr ) )
- return qfalse;
-
- cgDC.Assets.menuExitSound = trap_S_RegisterSound( tempStr, qfalse );
- continue;
- }
-
- // itemFocusSound
- if( Q_stricmp( token.string, "itemFocusSound" ) == 0 )
- {
- if( !PC_String_Parse( handle, &tempStr ) )
- return qfalse;
-
- cgDC.Assets.itemFocusSound = trap_S_RegisterSound( tempStr, qfalse );
- continue;
- }
-
- // menuBuzzSound
- if( Q_stricmp( token.string, "menuBuzzSound" ) == 0 )
- {
- if( !PC_String_Parse( handle, &tempStr ) )
- return qfalse;
-
- cgDC.Assets.menuBuzzSound = trap_S_RegisterSound( tempStr, qfalse );
- continue;
- }
-
- if( Q_stricmp( token.string, "cursor" ) == 0 )
- {
- if( !PC_String_Parse( handle, &cgDC.Assets.cursorStr ) )
- return qfalse;
-
- cgDC.Assets.cursor = trap_R_RegisterShaderNoMip( cgDC.Assets.cursorStr );
- continue;
- }
-
- if( Q_stricmp( token.string, "fadeClamp" ) == 0 )
- {
- if( !PC_Float_Parse( handle, &cgDC.Assets.fadeClamp ) )
- return qfalse;
-
- continue;
- }
-
- if( Q_stricmp( token.string, "fadeCycle" ) == 0 )
- {
- if( !PC_Int_Parse( handle, &cgDC.Assets.fadeCycle ) )
- return qfalse;
-
- continue;
- }
-
- if( Q_stricmp( token.string, "fadeAmount" ) == 0 )
- {
- if( !PC_Float_Parse( handle, &cgDC.Assets.fadeAmount ) )
- return qfalse;
-
- continue;
- }
-
- if( Q_stricmp( token.string, "shadowX" ) == 0 )
- {
- if( !PC_Float_Parse( handle, &cgDC.Assets.shadowX ) )
- return qfalse;
-
- continue;
- }
-
- if( Q_stricmp( token.string, "shadowY" ) == 0 )
- {
- if( !PC_Float_Parse( handle, &cgDC.Assets.shadowY ) )
- return qfalse;
-
- continue;
- }
-
- if( Q_stricmp( token.string, "shadowColor" ) == 0 )
- {
- if( !PC_Color_Parse( handle, &cgDC.Assets.shadowColor ) )
- return qfalse;
-
- cgDC.Assets.shadowFadeClamp = cgDC.Assets.shadowColor[ 3 ];
- continue;
- }
- }
-
- return qfalse;
-}
-
-void CG_ParseMenu( const char *menuFile )
-{
- pc_token_t token;
- int handle;
-
- handle = trap_Parse_LoadSource( menuFile );
-
- if( !handle )
- handle = trap_Parse_LoadSource( "ui/testhud.menu" );
-
- if( !handle )
- return;
-
- while( 1 )
- {
- if( !trap_Parse_ReadToken( handle, &token ) )
- break;
-
- //if ( Q_stricmp( token, "{" ) ) {
- // Com_Printf( "Missing { in menu file\n" );
- // break;
- //}
-
- //if ( menuCount == MAX_MENUS ) {
- // Com_Printf( "Too many menus!\n" );
- // break;
- //}
-
- if( token.string[ 0 ] == '}' )
- break;
-
- if( Q_stricmp( token.string, "assetGlobalDef" ) == 0 )
- {
- if( CG_Asset_Parse( handle ) )
- continue;
- else
- break;
- }
-
-
- if( Q_stricmp( token.string, "menudef" ) == 0 )
- {
- // start a new menu
- Menu_New( handle );
- }
- }
-
- trap_Parse_FreeSource( handle );
-}
-
-qboolean CG_Load_Menu( char **p )
-{
- char *token;
-
- token = COM_ParseExt( p, qtrue );
-
- if( token[ 0 ] != '{' )
- return qfalse;
-
- while( 1 )
- {
- token = COM_ParseExt( p, qtrue );
-
- if( Q_stricmp( token, "}" ) == 0 )
- return qtrue;
-
- if( !token || token[ 0 ] == 0 )
- return qfalse;
-
- CG_ParseMenu( token );
- }
- return qfalse;
-}
-
-
-
-void CG_LoadMenus( const char *menuFile )
-{
- char *token;
- char *p;
- int len, start;
- fileHandle_t f;
- static char buf[ MAX_MENUDEFFILE ];
-
- start = trap_Milliseconds( );
-
- len = trap_FS_FOpenFile( menuFile, &f, FS_READ );
-
- if( !f )
- {
- trap_Error( va( S_COLOR_YELLOW "menu file not found: %s, using default\n", menuFile ) );
- len = trap_FS_FOpenFile( "ui/hud.txt", &f, FS_READ );
-
- if( !f )
- trap_Error( va( S_COLOR_RED "default menu file not found: ui/hud.txt, unable to continue!\n" ) );
- }
-
- if( len >= MAX_MENUDEFFILE )
- {
- trap_Error( va( S_COLOR_RED "menu file too large: %s is %i, max allowed is %i",
- menuFile, len, MAX_MENUDEFFILE ) );
- trap_FS_FCloseFile( f );
- return;
- }
-
- trap_FS_Read( buf, len, f );
- buf[ len ] = 0;
- trap_FS_FCloseFile( f );
-
- COM_Compress( buf );
-
- Menu_Reset( );
-
- p = buf;
-
- while( 1 )
- {
- token = COM_ParseExt( &p, qtrue );
-
- if( !token || token[ 0 ] == 0 || token[ 0 ] == '}' )
- break;
-
- if( Q_stricmp( token, "}" ) == 0 )
- break;
-
- if( Q_stricmp( token, "loadmenu" ) == 0 )
- {
- if( CG_Load_Menu( &p ) )
- continue;
- else
- break;
- }
- }
-
- Com_Printf( "UI menu load time = %d milli seconds\n", trap_Milliseconds( ) - start );
-}
-
-
-
-static qboolean CG_OwnerDrawHandleKey( int ownerDraw, int key )
-{
- return qfalse;
-}
-
-
-static int CG_FeederCount( int feederID )
-{
- int i, count = 0;
-
- if( feederID == FEEDER_ALIENTEAM_LIST )
- {
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == TEAM_ALIENS )
- count++;
- }
- }
- else if( feederID == FEEDER_HUMANTEAM_LIST )
- {
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == TEAM_HUMANS )
- count++;
- }
- }
-
- return count;
-}
-
-
-void CG_SetScoreSelection( void *p )
-{
- menuDef_t *menu = (menuDef_t*)p;
- playerState_t *ps = &cg.snap->ps;
- int i, alien, human;
- int feeder;
-
- alien = human = 0;
-
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == TEAM_ALIENS )
- alien++;
- else if( cg.scores[ i ].team == TEAM_HUMANS )
- human++;
-
- if( ps->clientNum == cg.scores[ i ].client )
- cg.selectedScore = i;
- }
-
- if( menu == NULL )
- // just interested in setting the selected score
- return;
-
- feeder = FEEDER_ALIENTEAM_LIST;
- i = alien;
-
- if( cg.scores[ cg.selectedScore ].team == TEAM_HUMANS )
- {
- feeder = FEEDER_HUMANTEAM_LIST;
- i = human;
- }
-
- Menu_SetFeederSelection(menu, feeder, i, NULL);
-}
-
-// FIXME: might need to cache this info
-static clientInfo_t * CG_InfoFromScoreIndex( int index, int team, int *scoreIndex )
-{
- int i, count;
- count = 0;
-
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == team )
- {
- if( count == index )
- {
- *scoreIndex = i;
- return &cgs.clientinfo[ cg.scores[ i ].client ];
- }
- count++;
- }
- }
-
- *scoreIndex = index;
- return &cgs.clientinfo[ cg.scores[ index ].client ];
-}
-
-qboolean CG_ClientIsReady( int clientNum )
-{
- clientList_t ready;
-
- Com_ClientListParse( &ready, CG_ConfigString( CS_CLIENTS_READY ) );
-
- return Com_ClientListContains( &ready, clientNum );
-}
-
-static const char *CG_FeederItemText( int feederID, int index, int column, qhandle_t *handle )
-{
- int scoreIndex = 0;
- clientInfo_t *info = NULL;
- int team = -1;
- score_t *sp = NULL;
- qboolean showIcons = qfalse;
-
- *handle = -1;
-
- if( feederID == FEEDER_ALIENTEAM_LIST )
- team = TEAM_ALIENS;
- else if( feederID == FEEDER_HUMANTEAM_LIST )
- team = TEAM_HUMANS;
-
- info = CG_InfoFromScoreIndex( index, team, &scoreIndex );
- sp = &cg.scores[ scoreIndex ];
-
- if( cg.intermissionStarted && CG_ClientIsReady( sp->client ) )
- showIcons = qfalse;
- else if( cg.snap->ps.pm_type == PM_SPECTATOR ||
- cg.snap->ps.pm_type == PM_NOCLIP ||
- cg.snap->ps.pm_flags & PMF_FOLLOW ||
- team == cg.snap->ps.stats[ STAT_TEAM ] ||
- cg.intermissionStarted )
- {
- showIcons = qtrue;
- }
-
- if( info && info->infoValid )
- {
- switch( column )
- {
- case 0:
- if( showIcons )
- {
- if( sp->weapon != WP_NONE )
- *handle = cg_weapons[ sp->weapon ].weaponIcon;
- }
- break;
-
- case 1:
- if( showIcons )
- {
- if( sp->team == TEAM_HUMANS && sp->upgrade != UP_NONE )
- *handle = cg_upgrades[ sp->upgrade ].upgradeIcon;
- else if( sp->team == TEAM_ALIENS )
- {
- switch( sp->weapon )
- {
- case WP_ABUILD2:
- case WP_ALEVEL1_UPG:
- case WP_ALEVEL2_UPG:
- case WP_ALEVEL3_UPG:
- *handle = cgs.media.upgradeClassIconShader;
- break;
-
- default:
- break;
- }
- }
- }
- break;
-
- case 2:
- if( cg.intermissionStarted && CG_ClientIsReady( sp->client ) )
- return "Ready";
- break;
-
- case 3:
- return va( S_COLOR_WHITE "%s", info->name );
- break;
-
- case 4:
- return va( "%d", sp->score );
- break;
-
- case 5:
- return va( "%4d", sp->time );
- break;
-
- case 6:
- if( sp->ping == -1 )
- return "";
-
- return va( "%4d", sp->ping );
- break;
- }
- }
-
- return "";
-}
-
-static qhandle_t CG_FeederItemImage( int feederID, int index )
-{
- return 0;
-}
-
-static void CG_FeederSelection( int feederID, int index )
-{
- int i, count;
- int team = ( feederID == FEEDER_ALIENTEAM_LIST ) ? TEAM_ALIENS : TEAM_HUMANS;
- count = 0;
-
- for( i = 0; i < cg.numScores; i++ )
- {
- if( cg.scores[ i ].team == team )
- {
- if( index == count )
- cg.selectedScore = i;
-
- count++;
- }
- }
-}
-
-static float CG_Cvar_Get( const char *cvar )
-{
- char buff[ 128 ];
-
- memset( buff, 0, sizeof( buff ) );
- trap_Cvar_VariableStringBuffer( cvar, buff, sizeof( buff ) );
- return atof( buff );
-}
-
-void CG_Text_PaintWithCursor( float x, float y, float scale, vec4_t color, const char *text,
- int cursorPos, char cursor, int limit, int style )
-{
- UI_Text_Paint( x, y, scale, color, text, 0, limit, style );
-}
-
-static int CG_OwnerDrawWidth( int ownerDraw, float scale )
-{
- switch( ownerDraw )
- {
- case CG_KILLER:
- return UI_Text_Width( CG_GetKillerText( ), scale );
- break;
- }
-
- return 0;
-}
-
-static int CG_PlayCinematic( const char *name, float x, float y, float w, float h )
-{
- return trap_CIN_PlayCinematic( name, x, y, w, h, CIN_loop );
-}
-
-static void CG_StopCinematic( int handle )
-{
- trap_CIN_StopCinematic( handle );
-}
-
-static void CG_DrawCinematic( int handle, float x, float y, float w, float h )
-{
- trap_CIN_SetExtents( handle, x, y, w, h );
- trap_CIN_DrawCinematic( handle );
-}
-
-static void CG_RunCinematicFrame( int handle )
-{
- trap_CIN_RunCinematic( handle );
-}
-
-// hack to prevent warning
-static qboolean CG_OwnerDrawVisible( int parameter )
-{
- return qfalse;
-}
-
-/*
-=================
-CG_LoadHudMenu
-=================
-*/
-void CG_LoadHudMenu( void )
-{
- char buff[ 1024 ];
- const char *hudSet;
-
- cgDC.aspectScale = ( ( 640.0f * cgs.glconfig.vidHeight ) /
- ( 480.0f * cgs.glconfig.vidWidth ) );
- cgDC.xscale = cgs.glconfig.vidWidth / 640.0f;
- cgDC.yscale = cgs.glconfig.vidHeight / 480.0f;
-
- cgDC.smallFontScale = CG_Cvar_Get( "ui_smallFont" );
- cgDC.bigFontScale = CG_Cvar_Get( "ui_bigFont" );
-
- cgDC.registerShaderNoMip = &trap_R_RegisterShaderNoMip;
- cgDC.setColor = &trap_R_SetColor;
- cgDC.drawHandlePic = &CG_DrawPic;
- cgDC.drawStretchPic = &trap_R_DrawStretchPic;
- cgDC.registerModel = &trap_R_RegisterModel;
- cgDC.modelBounds = &trap_R_ModelBounds;
- cgDC.fillRect = &CG_FillRect;
- cgDC.drawRect = &CG_DrawRect;
- cgDC.drawSides = &CG_DrawSides;
- cgDC.drawTopBottom = &CG_DrawTopBottom;
- cgDC.clearScene = &trap_R_ClearScene;
- cgDC.addRefEntityToScene = &trap_R_AddRefEntityToScene;
- cgDC.renderScene = &trap_R_RenderScene;
- cgDC.registerFont = &trap_R_RegisterFont;
- cgDC.ownerDrawItem = &CG_OwnerDraw;
- cgDC.getValue = &CG_GetValue;
- cgDC.ownerDrawVisible = &CG_OwnerDrawVisible;
- cgDC.runScript = &CG_RunMenuScript;
- cgDC.setCVar = trap_Cvar_Set;
- cgDC.getCVarString = trap_Cvar_VariableStringBuffer;
- cgDC.getCVarValue = CG_Cvar_Get;
- cgDC.setOverstrikeMode = &trap_Key_SetOverstrikeMode;
- cgDC.getOverstrikeMode = &trap_Key_GetOverstrikeMode;
- cgDC.startLocalSound = &trap_S_StartLocalSound;
- cgDC.ownerDrawHandleKey = &CG_OwnerDrawHandleKey;
- cgDC.feederCount = &CG_FeederCount;
- cgDC.feederItemImage = &CG_FeederItemImage;
- cgDC.feederItemText = &CG_FeederItemText;
- cgDC.feederSelection = &CG_FeederSelection;
- //cgDC.setBinding = &trap_Key_SetBinding;
- //cgDC.getBindingBuf = &trap_Key_GetBindingBuf;
- //cgDC.keynumToStringBuf = &trap_Key_KeynumToStringBuf;
- //cgDC.executeText = &trap_Cmd_ExecuteText;
- cgDC.Error = &Com_Error;
- cgDC.Print = &Com_Printf;
- cgDC.ownerDrawWidth = &CG_OwnerDrawWidth;
- //cgDC.ownerDrawText = &CG_OwnerDrawText;
- //cgDC.Pause = &CG_Pause;
- cgDC.registerSound = &trap_S_RegisterSound;
- cgDC.startBackgroundTrack = &trap_S_StartBackgroundTrack;
- cgDC.stopBackgroundTrack = &trap_S_StopBackgroundTrack;
- cgDC.playCinematic = &CG_PlayCinematic;
- cgDC.stopCinematic = &CG_StopCinematic;
- cgDC.drawCinematic = &CG_DrawCinematic;
- cgDC.runCinematicFrame = &CG_RunCinematicFrame;
-
- Init_Display( &cgDC );
-
- Menu_Reset( );
-
- trap_Cvar_VariableStringBuffer( "cg_hudFiles", buff, sizeof( buff ) );
- hudSet = buff;
-
- if( !cg_hudFilesEnable.integer || hudSet[ 0 ] == '\0' )
- hudSet = "ui/hud.txt";
-
- CG_LoadMenus( hudSet );
-}
-
-void CG_AssetCache( void )
-{
- int i;
-
- cgDC.Assets.gradientBar = trap_R_RegisterShaderNoMip( ASSET_GRADIENTBAR );
- cgDC.Assets.scrollBar = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR );
- cgDC.Assets.scrollBarArrowDown = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWDOWN );
- cgDC.Assets.scrollBarArrowUp = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWUP );
- cgDC.Assets.scrollBarArrowLeft = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWLEFT );
- cgDC.Assets.scrollBarArrowRight = trap_R_RegisterShaderNoMip( ASSET_SCROLLBAR_ARROWRIGHT );
- cgDC.Assets.scrollBarThumb = trap_R_RegisterShaderNoMip( ASSET_SCROLL_THUMB );
- cgDC.Assets.sliderBar = trap_R_RegisterShaderNoMip( ASSET_SLIDER_BAR );
- cgDC.Assets.sliderThumb = trap_R_RegisterShaderNoMip( ASSET_SLIDER_THUMB );
-
- if( cg_emoticons.integer )
- {
- cgDC.Assets.emoticonCount = BG_LoadEmoticons( cgDC.Assets.emoticons,
- MAX_EMOTICONS );
- }
- else
- cgDC.Assets.emoticonCount = 0;
-
- for( i = 0; i < cgDC.Assets.emoticonCount; i++ )
- {
- cgDC.Assets.emoticons[ i ].shader = trap_R_RegisterShaderNoMip(
- va( "emoticons/%s_%dx1.tga", cgDC.Assets.emoticons[ i ].name,
- cgDC.Assets.emoticons[ i ].width ) );
- }
-}
-
-/*
-=================
-CG_Init
-
-Called after every level change or subsystem restart
-Will perform callbacks to make the loading info screen update.
-=================
-*/
-void CG_Init( int serverMessageNum, int serverCommandSequence, int clientNum )
-{
- const char *s;
-
- // clear everything
- memset( &cgs, 0, sizeof( cgs ) );
- memset( &cg, 0, sizeof( cg ) );
- memset( &cg.pmext, 0, sizeof( cg.pmext ) );
- memset( cg_entities, 0, sizeof( cg_entities ) );
-
- cg.clientNum = clientNum;
-
- cgs.processedSnapshotNum = serverMessageNum;
- cgs.serverCommandSequence = serverCommandSequence;
-
- // get the rendering configuration from the client system
- trap_GetGlconfig( &cgs.glconfig );
- cgs.screenXScale = cgs.glconfig.vidWidth / 640.0f;
- cgs.screenYScale = cgs.glconfig.vidHeight / 480.0f;
-
- // load a few needed things before we do any screen updates
- cgs.media.whiteShader = trap_R_RegisterShader( "white" );
- cgs.media.charsetShader = trap_R_RegisterShader( "gfx/2d/bigchars" );
- cgs.media.outlineShader = trap_R_RegisterShader( "outline" );
-
- // load overrides
- BG_InitClassConfigs( );
- BG_InitBuildableConfigs( );
- BG_InitAllowedGameElements( );
-
- // Dynamic memory
- BG_InitMemory( );
-
- CG_RegisterCvars( );
-
- CG_InitConsoleCommands( );
-
- String_Init( );
-
- CG_AssetCache( );
- CG_LoadHudMenu( );
-
- cg.weaponSelect = WP_NONE;
-
- // old servers
-
- // get the gamestate from the client system
- trap_GetGameState( &cgs.gameState );
-
- // copy vote display strings so they don't show up blank if we see
- // the same one directly after connecting
- Q_strncpyz( cgs.voteString[ TEAM_NONE ],
- CG_ConfigString( CS_VOTE_STRING + TEAM_NONE ),
- sizeof( cgs.voteString ) );
- Q_strncpyz( cgs.voteString[ TEAM_ALIENS ],
- CG_ConfigString( CS_VOTE_STRING + TEAM_ALIENS ),
- sizeof( cgs.voteString[ TEAM_ALIENS ] ) );
- Q_strncpyz( cgs.voteString[ TEAM_HUMANS ],
- CG_ConfigString( CS_VOTE_STRING + TEAM_ALIENS ),
- sizeof( cgs.voteString[ TEAM_HUMANS ] ) );
-
- // check version
- s = CG_ConfigString( CS_GAME_VERSION );
-
- if( strcmp( s, GAME_VERSION ) )
- CG_Error( "Client/Server game mismatch: %s/%s", GAME_VERSION, s );
-
- s = CG_ConfigString( CS_LEVEL_START_TIME );
- cgs.levelStartTime = atoi( s );
-
- CG_ParseServerinfo( );
-
- // load the new map
- trap_CM_LoadMap( cgs.mapname );
-
- cg.loading = qtrue; // force players to load instead of defer
-
- CG_LoadTrailSystems( );
- CG_UpdateMediaFraction( 0.05f );
-
- CG_LoadParticleSystems( );
- CG_UpdateMediaFraction( 0.05f );
-
- CG_RegisterSounds( );
- CG_UpdateMediaFraction( 0.60f );
-
- CG_RegisterGraphics( );
- CG_UpdateMediaFraction( 0.90f );
-
- CG_InitWeapons( );
- CG_UpdateMediaFraction( 0.95f );
-
- CG_InitUpgrades( );
- CG_UpdateMediaFraction( 1.0f );
-
- CG_InitBuildables( );
-
- cgs.voices = BG_VoiceInit( );
- BG_PrintVoices( cgs.voices, cg_debugVoices.integer );
-
- CG_RegisterClients( ); // if low on memory, some clients will be deferred
-
- cg.loading = qfalse; // future players will be deferred
-
- CG_InitMarkPolys( );
-
- // remove the last loading update
- cg.infoScreenText[ 0 ] = 0;
-
- // Make sure we have update values (scores)
- CG_SetConfigValues( );
-
- CG_StartMusic( );
-
- CG_ShaderStateChanged( );
-
- trap_S_ClearLoopingSounds( qtrue );
-}
-
-/*
-=================
-CG_Shutdown
-
-Called before every level change or subsystem restart
-=================
-*/
-void CG_Shutdown( void )
-{
- // some mods may need to do cleanup work here,
- // like closing files or archiving session data
-}
-
-/*
-================
-CG_VoIPString
-================
-*/
-static char *CG_VoIPString( void )
-{
- // a generous overestimate of the space needed for 0,1,2...61,62,63
- static char voipString[ MAX_CLIENTS * 4 ];
- char voipSendTarget[ MAX_CVAR_VALUE_STRING ];
-
- trap_Cvar_VariableStringBuffer( "cl_voipSendTarget", voipSendTarget,
- sizeof( voipSendTarget ) );
-
- if( Q_stricmp( voipSendTarget, "team" ) == 0 )
- {
- int i, slen, nlen;
- for( slen = i = 0; i < cgs.maxclients; i++ )
- {
- if( !cgs.clientinfo[ i ].infoValid || i == cg.clientNum )
- continue;
- if( cgs.clientinfo[ i ].team != cgs.clientinfo[ cg.clientNum ].team )
- continue;
-
- nlen = Q_snprintf( &voipString[ slen ], sizeof( voipString ) - slen,
- "%s%d", ( slen > 0 ) ? "," : "", i );
- if( slen + nlen + 1 >= sizeof( voipString ) )
- {
- CG_Printf( S_COLOR_YELLOW "WARNING: voipString overflowed\n" );
- break;
- }
-
- slen += nlen;
- }
-
- // Notice that if the snprintf was truncated, slen was not updated
- // so this will remove any trailing commas or partially-completed numbers
- voipString[ slen ] = '\0';
- }
- else if( Q_stricmp( voipSendTarget, "crosshair" ) == 0 )
- Com_sprintf( voipString, sizeof( voipString ), "%d",
- CG_CrosshairPlayer( ) );
- else if( Q_stricmp( voipSendTarget, "attacker" ) == 0 )
- Com_sprintf( voipString, sizeof( voipString ), "%d",
- CG_LastAttacker( ) );
- else
- return NULL;
-
- return voipString;
-}
-
diff --git a/src/game/g_active.c.orig b/src/game/g_active.c.orig
deleted file mode 100644
index cfa6f89..0000000
--- a/src/game/g_active.c.orig
+++ /dev/null
@@ -1,1937 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-#include "g_local.h"
-
-/* To create useful and interesting cuboids (like ladders), the ability to specify
- * custom surface flags for buildings was needed. This is what the following function
- * was written for. It replaces the regular tracing function in PM code (also on the
- * client side, grep for CG_PMTraceHack). This function also takes into account
- * the modelindex of the entity it found and adjusts the surfaceFlags output
- * accordingly. This way custom surface flags for buildings can be done without
- * messing around with the PM code or the engine itself.
- */
-void G_PMTraceHack(trace_t *results,const vec3_t start,const vec3_t mins,const vec3_t maxs,const vec3_t end,int passEntityNum,int contentmask )
-{
- int modelindex;
- trap_Trace(results,start,mins,maxs,end,passEntityNum,contentmask);
- if(results->entityNum!=ENTITYNUM_NONE)
- {
- modelindex=g_entities[results->entityNum].s.modelindex;
- if(modelindex>=CUBOID_FIRST&&modelindex<=CUBOID_LAST&&(g_entities[results->entityNum].s.eFlags&EF_B_SPAWNED))
- results->surfaceFlags|=BG_CuboidAttributes(modelindex)->surfaceFlags;
- }
-}
-
-
-/*
-===============
-G_DamageFeedback
-
-Called just before a snapshot is sent to the given player.
-Totals up all damage and generates both the player_state_t
-damage values to that client for pain blends and kicks, and
-global pain sound events for all clients.
-===============
-*/
-void P_DamageFeedback( gentity_t *player )
-{
- gclient_t *client;
- float count;
- vec3_t angles;
-
- client = player->client;
- if( !PM_Live( client->ps.pm_type ) )
- return;
-
- // total points of damage shot at the player this frame
- count = client->damage_blood + client->damage_armor;
- if( count == 0 )
- return; // didn't take any damage
-
- if( count > 255 )
- count = 255;
-
- // send the information to the client
-
- // world damage (falling, slime, etc) uses a special code
- // to make the blend blob centered instead of positional
- if( client->damage_fromWorld )
- {
- client->ps.damagePitch = 255;
- client->ps.damageYaw = 255;
-
- client->damage_fromWorld = qfalse;
- }
- else
- {
- vectoangles( client->damage_from, angles );
- client->ps.damagePitch = angles[ PITCH ] / 360.0 * 256;
- client->ps.damageYaw = angles[ YAW ] / 360.0 * 256;
- }
-
- // play an apropriate pain sound
- if( ( level.time > player->pain_debounce_time ) && !( player->flags & FL_GODMODE ) )
- {
- player->pain_debounce_time = level.time + 700;
- G_AddEvent( player, EV_PAIN, player->health > 255 ? 255 : player->health );
- client->ps.damageEvent++;
- }
-
-
- client->ps.damageCount = count;
-
- //
- // clear totals
- //
- client->damage_blood = 0;
- client->damage_armor = 0;
- client->damage_knockback = 0;
-}
-
-
-
-/*
-=============
-P_WorldEffects
-
-Check for lava / slime contents and drowning
-=============
-*/
-void P_WorldEffects( gentity_t *ent )
-{
- int waterlevel;
-
- if( ent->client->noclip )
- {
- ent->client->airOutTime = level.time + 12000; // don't need air
- return;
- }
-
- waterlevel = ent->waterlevel;
-
- //
- // check for drowning
- //
- if( waterlevel == 3 )
- {
- // if out of air, start drowning
- if( ent->client->airOutTime < level.time)
- {
- // drown!
- ent->client->airOutTime += 1000;
- if( ent->health > 0 )
- {
- // take more damage the longer underwater
- ent->damage += 2;
- if( ent->damage > 15 )
- ent->damage = 15;
-
- // play a gurp sound instead of a normal pain sound
- if( ent->health <= ent->damage )
- G_Sound( ent, CHAN_VOICE, G_SoundIndex( "*drown.wav" ) );
- 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" ) );
-
- // don't play a normal pain sound
- ent->pain_debounce_time = level.time + 200;
-
- G_Damage( ent, NULL, NULL, NULL, NULL,
- ent->damage, DAMAGE_NO_ARMOR, MOD_WATER );
- }
- }
- }
- else
- {
- ent->client->airOutTime = level.time + 12000;
- ent->damage = 2;
- }
-
- //
- // check for sizzle damage (move to pmove?)
- //
- if( waterlevel &&
- ( ent->watertype & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) )
- {
- if( ent->health > 0 &&
- ent->pain_debounce_time <= level.time )
- {
- if( ent->watertype & CONTENTS_LAVA )
- {
- G_Damage( ent, NULL, NULL, NULL, NULL,
- 30 * waterlevel, 0, MOD_LAVA );
- }
-
- if( ent->watertype & CONTENTS_SLIME )
- {
- G_Damage( ent, NULL, NULL, NULL, NULL,
- 10 * waterlevel, 0, MOD_SLIME );
- }
- }
- }
-}
-
-
-
-/*
-===============
-G_SetClientSound
-===============
-*/
-void G_SetClientSound( gentity_t *ent )
-{
- if( ent->waterlevel && ( ent->watertype & ( CONTENTS_LAVA | CONTENTS_SLIME ) ) )
- ent->client->ps.loopSound = level.snd_fry;
- else
- ent->client->ps.loopSound = 0;
-}
-
-
-
-//==============================================================
-
-/*
-==============
-ClientShove
-==============
-*/
-static int GetClientMass( gentity_t *ent )
-{
- int entMass = 100;
-
- 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;
-
- // 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 );
- 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;
- }
-}
-
-/*
-==============
-ClientImpacts
-==============
-*/
-void ClientImpacts( gentity_t *ent, pmove_t *pm )
-{
- 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++ )
- {
- 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
- // blocking or server-side Pmove() from reaching it
- if( other->client && other->client->unlaggedCalc.used )
- other->client->unlaggedCalc.used = qfalse;
-
- // 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 )
- ClientShove( ent, other );
-
- // touch triggers
- if( other->touch )
- other->touch( other, ent, &trace );
- }
-}
-
-/*
-============
-G_TouchTriggers
-
-Find all trigger entities that ent's current position touches.
-Spectators will only interact with teleporters.
-============
-*/
-void G_TouchTriggers( gentity_t *ent )
-{
- int i, num;
- int touch[MAX_GENTITIES];
- gentity_t *hit;
- trace_t trace;
- vec3_t mins, maxs;
- vec3_t pmins, pmaxs;
- static vec3_t range = { 10, 10, 10 };
-
- if( !ent->client )
- return;
-
- // dead clients don't activate triggers!
- if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 )
- return;
-
- BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ],
- pmins, pmaxs, NULL, NULL, NULL );
-
- VectorAdd( ent->client->ps.origin, pmins, mins );
- VectorAdd( ent->client->ps.origin, pmaxs, maxs );
-
- VectorSubtract( mins, range, mins );
- VectorAdd( maxs, range, maxs );
-
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
-
- // can't use ent->absmin, because that has a one unit pad
- VectorAdd( ent->client->ps.origin, ent->r.mins, mins );
- VectorAdd( ent->client->ps.origin, ent->r.maxs, maxs );
-
- for( i = 0; i < num; i++ )
- {
- hit = &g_entities[ touch[ i ] ];
-
- if( !hit->touch && !ent->touch )
- continue;
-
- if( !( hit->r.contents & CONTENTS_TRIGGER ) )
- continue;
-
- // ignore most entities if a spectator
- if( ent->client->sess.spectatorState != SPECTATOR_NOT )
- {
- if( hit->s.eType != ET_TELEPORT_TRIGGER &&
- // this is ugly but adding a new ET_? type will
- // most likely cause network incompatibilities
- hit->touch != Touch_DoorTrigger )
- {
- //check for manually triggered doors
- manualTriggerSpectator( hit, ent );
- continue;
- }
- }
-
- if( !trap_EntityContact( mins, maxs, hit ) )
- continue;
-
- memset( &trace, 0, sizeof( trace ) );
-
- if( hit->touch )
- hit->touch( hit, ent, &trace );
- }
-}
-
-/*
-=================
-SpectatorThink
-=================
-*/
-void SpectatorThink( gentity_t *ent, usercmd_t *ucmd )
-{
- pmove_t pm;
- gclient_t *client;
- int clientNum;
- qboolean attack1, attack3, 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 );
-
- // We are in following mode only if we are following a non-spectating client
- following = client->sess.spectatorState == SPECTATOR_FOLLOW;
- if( following )
- {
- clientNum = client->sess.spectatorClient;
- if( clientNum < 0 || clientNum > level.maxclients ||
- !g_entities[ clientNum ].client ||
- g_entities[ clientNum ].client->sess.spectatorState != SPECTATOR_NOT )
- following = qfalse;
- }
-
- // 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
- queued = qfalse;
-
- // Wants to get out of spawn queue
- if( attack1 && queued )
- {
- 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->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 );
- }
-
- // We are either not following anyone or following a spectator
- if( !following )
- {
- 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->buildTimer=0;
- client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
- client->ps.stats[ STAT_CLASS ] = PCL_NONE;
- client->ps.weapon = WP_NONE;
-
- // Set up for pmove
- memset( &pm, 0, sizeof( pm ) );
- pm.ps = &client->ps;
- pm.pmext = &client->pmext;
- pm.cmd = *ucmd;
- pm.tracemask = MASK_DEADSOLID; // spectators can fly through bodies
- pm.trace = G_PMTraceHack;
- pm.pointcontents = trap_PointContents;
-
- // Perform a pmove
- Pmove( &pm );
-
- // Save results of pmove
- VectorCopy( client->ps.origin, ent->s.origin );
-
- G_TouchTriggers( ent );
- trap_UnlinkEntity( ent );
-
- // Set the queue position and spawn count for the client side
- if( client->ps.pm_flags & PMF_QUEUED )
- {
- 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_TEAM ] == TEAM_HUMANS )
- {
- client->ps.persistant[ PERS_QUEUEPOS ] =
- G_GetPosInSpawnQueue( &level.humanSpawnQueue, client->ps.clientNum );
- client->ps.persistant[ PERS_SPAWNS ] = level.numHumanSpawns;
- }
- }
- }
-}
-
-
-
-/*
-=================
-ClientInactivityTimer
-
-Returns qfalse if the client is dropped
-=================
-*/
-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
- // gameplay, everyone isn't kicked
- client->inactivityTime = level.time + 60 * 1000;
- client->inactivityWarning = qfalse;
- }
- else if( client->pers.cmd.forwardmove ||
- client->pers.cmd.rightmove ||
- client->pers.cmd.upmove ||
- ( client->pers.cmd.buttons & BUTTON_ATTACK ) )
- {
- client->inactivityTime = level.time + g_inactivity.integer * 1000;
- client->inactivityWarning = qfalse;
- }
- else if( !client->pers.localClient )
- {
- 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 &&
- !G_admin_permission( ent, ADMF_ACTIVITY ) )
- {
- client->inactivityWarning = qtrue;
- trap_SendServerCommand( client - level.clients, "cp \"Ten seconds until inactivity drop!\n\"" );
- }
- }
-
- return qtrue;
-}
-
-/*
-==================
-ClientTimerActions
-
-Actions that happen once a second
-==================
-*/
-void ClientTimerActions( gentity_t *ent, int msec )
-{
- gclient_t *client;
- usercmd_t *ucmd;
- int aForward, aRight;
- qboolean walking = qfalse, stopped = qfalse,
- crouched = qfalse, jumping = qfalse,
- strafing = qfalse;
- int i;
-
- ucmd = &ent->client->pers.cmd;
-
- aForward = abs( ucmd->forwardmove );
- aRight = abs( ucmd->rightmove );
-
- 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 )
- 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;
-
- // Restore or subtract stamina
- if( stopped || client->ps.pm_type == PM_JETPACK )
- client->ps.stats[ STAT_STAMINA ] += STAMINA_STOP_RESTORE;
- 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 ||
- BG_InventoryContainsWeapon( WP_HBUILD, client->ps.stats ) )
- {
- client->buildTimer-=MIN(100,client->buildTimer);
- G_RecalcBuildTimer(client);
- }
-
- switch( weapon )
- {
- case WP_ABUILD:
- case WP_ABUILD2:
- case WP_HBUILD:
-
- // Set validity bit on buildable
- if( ( client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT ) > BA_NONE )
- {
- int dist = BG_Class( ent->client->ps.stats[ STAT_CLASS ] )->buildDist;
- vec3_t dummy, dummy2;
-
- if( G_CanBuild( ent, client->ps.stats[ STAT_BUILDABLE ] & ~SB_VALID_TOGGLEBIT,
- dist, dummy, dummy2, client->cuboidSelection ) == IBE_NONE )
- client->ps.stats[ STAT_BUILDABLE ] |= SB_VALID_TOGGLEBIT;
- else
- client->ps.stats[ STAT_BUILDABLE ] &= ~SB_VALID_TOGGLEBIT;
-
- // 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( ent->client->pers.teamSelection == TEAM_HUMANS &&
- ( client->ps.stats[ STAT_STATE ] & SS_HEALING_2X ) )
- {
- int remainingStartupTime = MEDKIT_STARTUP_TIME - ( level.time - client->lastMedKitTime );
-
- if( remainingStartupTime < 0 )
- {
- if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] &&
- ent->client->medKitHealthToRestore &&
- ent->client->ps.pm_type != PM_DEAD )
- {
- ent->client->medKitHealthToRestore--;
- ent->health++;
- }
- else
- ent->client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_2X;
- }
- else
- {
- if( ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] &&
- ent->client->medKitHealthToRestore &&
- ent->client->ps.pm_type != PM_DEAD )
- {
- //partial increase
- if( level.time > client->medKitIncrementTime )
- {
- ent->client->medKitHealthToRestore--;
- ent->health++;
-
- client->medKitIncrementTime = level.time +
- ( remainingStartupTime / MEDKIT_STARTUP_SPEED );
- }
- }
- else
- ent->client->ps.stats[ STAT_STATE ] &= ~SS_HEALING_2X;
- }
- }
-
- //use fuel
- if( BG_InventoryContainsUpgrade( UP_JETPACK, ent->client->ps.stats ) &&
- BG_UpgradeIsActive( UP_JETPACK, ent->client->ps.stats ) &&
- ent->client->ps.stats[ STAT_FUEL ] > 0 )
- {
- ent->client->ps.stats[ STAT_FUEL ] -= JETPACK_FUEL_USAGE;
- if( ent->client->ps.stats[ STAT_FUEL ] <= 0 )
- {
- ent->client->ps.stats[ STAT_FUEL ] = 0;
- BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
- BG_AddPredictableEventToPlayerstate( EV_JETPACK_DEACTIVATE, 0, &client->ps );
- }
- }
-
- //biores
- if( BG_InventoryContainsUpgrade( UP_BIORES, ent->client->ps.stats ) &&
- ent->health < ent->client->ps.stats[ STAT_MAX_HEALTH ] )
- {
- float hp, dmod;
-
- dmod = MIN( level.time - ent->lastDamageTime, 3000 ) / 3000.0f;
- hp = (float)ent->health / ent->client->ps.stats[ STAT_MAX_HEALTH ];
- ent->client->bioresHealTimer += (BIORES_EQUATION) * 10 * dmod;
- }
-
- if( ent->client->bioresHealTimer >= 100 )
- {
- int delta;
-
- delta = ent->client->bioresHealTimer / 100;
- ent->health = MAX( MIN( ent->health+delta, ent->client->ps.stats[ STAT_MAX_HEALTH ] ), 1 );
-
- ent->client->bioresHealTimer %= 100;
- }
-
- }
-
- while( client->time1000 >= 1000 )
- {
- client->time1000 -= 1000;
-
- //client is poisoned
- if( client->ps.stats[ STAT_STATE ] & SS_POISONED )
- {
- int damage = ALIEN_POISON_DMG;
-
- if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, client->ps.stats ) )
- damage -= BSUIT_POISON_PROTECTION;
-
- if( BG_InventoryContainsUpgrade( UP_HELMET_MK1, client->ps.stats ) )
- damage -= HELMET_MK1_POISON_PROTECTION;
-
- if( BG_InventoryContainsUpgrade( UP_HELMET_MK2, client->ps.stats ) )
- damage -= HELMET_MK2_POISON_PROTECTION;
-
- if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, client->ps.stats ) )
- damage -= LIGHTARMOUR_POISON_PROTECTION;
-
- if( BG_InventoryContainsUpgrade( UP_BIORES, client->ps.stats ) )
- damage *= BIORES_POISON_MODIFIER;
-
- G_Damage( ent, client->lastPoisonClient, client->lastPoisonClient, NULL,
- 0, damage, 0, MOD_POISON );
- }
-
- // 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_Class( client->ps.stats[ STAT_CLASS ] )->regenRate,
- DAMAGE_NO_ARMOR, MOD_SUICIDE );
- }
- 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 );
- }
-
- // lose some voice enthusiasm
- if( client->voiceEnthusiasm > 0.0f )
- client->voiceEnthusiasm -= VOICE_ENTHUSIASM_DECAY;
- else
- client->voiceEnthusiasm = 0.0f;
-
- client->pers.aliveSeconds++;
- if( g_freeFundPeriod.integer > 0 &&
- client->pers.aliveSeconds % 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 );
- }
- }
- }
-
- while( client->time10000 >= 10000 )
- {
- client->time10000 -= 10000;
-
- if( ent->client->ps.weapon == WP_ABUILD ||
- ent->client->ps.weapon == WP_ABUILD2 )
- {
- AddScore( ent, ALIEN_BUILDER_SCOREINC );
- }
- else if( ent->client->ps.weapon == WP_HBUILD )
- {
- AddScore( ent, HUMAN_BUILDER_SCOREINC );
- }
-
- // 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;
- }
- }
-
- // 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 )
- {
- client->ps.ammo++;
- ent->timestamp = level.time;
- }
- }
- else
- ent->timestamp = level.time;
- }
-}
-
-/*
-====================
-ClientIntermissionThink
-====================
-*/
-void ClientIntermissionThink( gclient_t *client )
-{
- client->ps.eFlags &= ~EF_FIRING;
- client->ps.eFlags &= ~EF_FIRING2;
-
- // the level will exit when everyone wants to or after timeouts
-
- // swap and latch button actions
- client->oldbuttons = client->buttons;
- client->buttons = client->pers.cmd.buttons;
- if( client->buttons & ( BUTTON_ATTACK | BUTTON_USE_HOLDABLE ) & ( client->oldbuttons ^ client->buttons ) )
- client->readyToExit = 1;
-}
-
-
-/*
-================
-ClientEvents
-
-Events will be passed on to the clients for presentation,
-but any server game effects are handled here
-================
-*/
-void ClientEvents( gentity_t *ent, int oldEventSequence )
-{
- int i;
- int event;
- gclient_t *client;
- int damage;
- vec3_t dir;
- vec3_t point, mins;
- float fallDistance;
- class_t class;
-
- client = ent->client;
- class = client->ps.stats[ STAT_CLASS ];
-
- if( oldEventSequence < client->ps.eventSequence - MAX_PS_EVENTS )
- oldEventSequence = client->ps.eventSequence - MAX_PS_EVENTS;
-
- for( i = oldEventSequence; i < client->ps.eventSequence; i++ )
- {
- event = client->ps.events[ i & ( MAX_PS_EVENTS - 1 ) ];
-
- switch( event )
- {
- case EV_FALL_MEDIUM:
- case EV_FALL_FAR:
- if( ent->s.eType != ET_PLAYER )
- break; // not in the player model
-
- fallDistance = ( (float)client->ps.stats[ STAT_FALLDIST ] - MIN_FALL_DISTANCE ) /
- ( MAX_FALL_DISTANCE - MIN_FALL_DISTANCE );
-
- if( fallDistance < 0.0f )
- fallDistance = 0.0f;
- else if( fallDistance > 1.0f )
- fallDistance = 1.0f;
-
- damage = (int)( (float)BG_Class( class )->health *
- BG_Class( class )->fallDamage * fallDistance );
-
- VectorSet( dir, 0, 0, 1 );
- BG_ClassBoundingBox( class, mins, NULL, NULL, NULL, NULL );
- mins[ 0 ] = mins[ 1 ] = 0.0f;
- VectorAdd( client->ps.origin, mins, point );
-
- ent->pain_debounce_time = level.time + 200; // no normal pain sound
- G_Damage( ent, NULL, NULL, dir, point, damage, DAMAGE_NO_LOCDAMAGE, MOD_FALLING );
- break;
-
- case EV_FIRE_WEAPON:
- FireWeapon( ent );
- break;
-
- case EV_FIRE_WEAPON2:
- FireWeapon2( ent );
- break;
-
- case EV_FIRE_WEAPON3:
- FireWeapon3( ent );
- break;
-
- case EV_NOAMMO:
- break;
-
- default:
- break;
- }
- }
-}
-
-
-/*
-==============
-SendPendingPredictableEvents
-==============
-*/
-void SendPendingPredictableEvents( playerState_t *ps )
-{
- gentity_t *t;
- int event, seq;
- int extEvent, number;
-
- // if there are still events pending
- if( ps->entityEventSequence < ps->eventSequence )
- {
- // create a temporary entity for this event which is sent to everyone
- // except the client who generated the event
- seq = ps->entityEventSequence & ( MAX_PS_EVENTS - 1 );
- event = ps->events[ seq ] | ( ( ps->entityEventSequence & 3 ) << 8 );
- // set external event to zero before calling BG_PlayerStateToEntityState
- extEvent = ps->externalEvent;
- ps->externalEvent = 0;
- // create temporary entity for event
- t = G_TempEntity( ps->origin, event );
- number = t->s.number;
- BG_PlayerStateToEntityState( ps, &t->s, qtrue );
- t->s.number = number;
- t->s.eType = ET_EVENTS + event;
- t->s.eFlags |= EF_PLAYER_EVENT;
- t->s.otherEntityNum = ps->clientNum;
- // send to everyone except the client who generated the event
- t->r.svFlags |= SVF_NOTSINGLECLIENT;
- t->r.singleClient = ps->clientNum;
- // set back external event
- ps->externalEvent = extEvent;
- }
-}
-
-/*
-==============
- G_UnlaggedStore
-
- 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()
-==============
-*/
-void G_UnlaggedStore( void )
-{
- int i = 0;
- gentity_t *ent;
- unlagged_t *save;
-
- if( !g_unlagged.integer )
- return;
- 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;
- if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
- continue;
- if( ent->client->pers.connected != CON_CONNECTED )
- continue;
- VectorCopy( ent->r.mins, save->mins );
- VectorCopy( ent->r.maxs, save->maxs );
- VectorCopy( ent->s.pos.trBase, save->origin );
- save->used = qtrue;
- }
-}
-
-/*
-==============
- G_UnlaggedClear
-
- Mark all unlaggedHist[] markers for this client invalid. Useful for
- preventing teleporting and death.
-==============
-*/
-void G_UnlaggedClear( gentity_t *ent )
-{
- int i;
-
- for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ )
- ent->client->unlaggedHist[ i ].used = qfalse;
-}
-
-/*
-==============
- G_UnlaggedCalc
-
- Loops through all active clients and calculates their predicted position
- for time then stores it in client->unlaggedCalc
-==============
-*/
-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;
-
- if( !g_unlagged.integer )
- return;
-
- // clear any calculated values from a previous run
- for( i = 0; i < level.maxclients; i++ )
- {
- ent = &g_entities[ i ];
- ent->client->unlaggedCalc.used = qfalse;
- }
-
- for( i = 0; i < MAX_UNLAGGED_MARKERS; i++ )
- {
- if( level.unlaggedTimes[ startIndex ] <= time )
- break;
- stopIndex = startIndex;
- if( --startIndex < 0 )
- startIndex = MAX_UNLAGGED_MARKERS - 1;
- }
- if( i == MAX_UNLAGGED_MARKERS )
- {
- // if we searched all markers and the oldest one still isn't old enough
- // 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 )
- {
- lerp = ( float )( time - level.unlaggedTimes[ startIndex ] ) /
- ( float )frameMsec;
- }
-
- for( i = 0; i < level.maxclients; i++ )
- {
- ent = &g_entities[ i ];
- if( ent == rewindEnt )
- continue;
- if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
- continue;
- if( ent->client->pers.connected != CON_CONNECTED )
- continue;
- if( !ent->client->unlaggedHist[ startIndex ].used )
- continue;
- if( !ent->client->unlaggedHist[ stopIndex ].used )
- continue;
-
- // between two unlagged markers
- VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].mins,
- ent->client->unlaggedHist[ stopIndex ].mins,
- ent->client->unlaggedCalc.mins );
- VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].maxs,
- ent->client->unlaggedHist[ stopIndex ].maxs,
- ent->client->unlaggedCalc.maxs );
- VectorLerp( lerp, ent->client->unlaggedHist[ startIndex ].origin,
- ent->client->unlaggedHist[ stopIndex ].origin,
- ent->client->unlaggedCalc.origin );
-
- ent->client->unlaggedCalc.used = qtrue;
- }
-}
-
-/*
-==============
- G_UnlaggedOff
-
- Reverses the changes made to all active clients by G_UnlaggedOn()
-==============
-*/
-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 ];
- if( !ent->client->unlaggedBackup.used )
- continue;
- VectorCopy( ent->client->unlaggedBackup.mins, ent->r.mins );
- VectorCopy( ent->client->unlaggedBackup.maxs, ent->r.maxs );
- VectorCopy( ent->client->unlaggedBackup.origin, ent->r.currentOrigin );
- ent->client->unlaggedBackup.used = qfalse;
- trap_LinkEntity( ent );
- }
-}
-
-/*
-==============
- G_UnlaggedOn
-
- Called after G_UnlaggedCalc() to apply the calculated values to all active
- clients. Once finished tracing, G_UnlaggedOff() must be called to restore
- the clients' position data
-
- As an optimization, all clients that have an unlagged position that is
- not touchable at "range" from "muzzle" will be ignored. This is required
- to prevent a huge amount of trap_LinkEntity() calls per user cmd.
-==============
-*/
-
-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 ];
- calc = &ent->client->unlaggedCalc;
-
- if( !calc->used )
- continue;
- if( ent->client->unlaggedBackup.used )
- continue;
- if( !ent->r.linked || !( ent->r.contents & CONTENTS_BODY ) )
- continue;
- if( VectorCompare( ent->r.currentOrigin, calc->origin ) )
- continue;
- if( muzzle )
- {
- float r1 = Distance( calc->origin, calc->maxs );
- float r2 = Distance( calc->origin, calc->mins );
- float maxRadius = ( r1 > r2 ) ? r1 : r2;
-
- if( Distance( muzzle, calc->origin ) > range + maxRadius )
- continue;
- }
-
- // create a backup of the real positions
- VectorCopy( ent->r.mins, ent->client->unlaggedBackup.mins );
- VectorCopy( ent->r.maxs, ent->client->unlaggedBackup.maxs );
- VectorCopy( ent->r.currentOrigin, ent->client->unlaggedBackup.origin );
- ent->client->unlaggedBackup.used = qtrue;
-
- // move the client to the calculated unlagged position
- VectorCopy( calc->mins, ent->r.mins );
- VectorCopy( calc->maxs, ent->r.maxs );
- VectorCopy( calc->origin, ent->r.currentOrigin );
- trap_LinkEntity( ent );
- }
-}
-/*
-==============
- G_UnlaggedDetectCollisions
-
- cgame prediction will predict a client's own position all the way up to
- 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
- 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.
-
- Long story short (too late): don't use unlagged positions for players who
- were blocking this player X's client-side Pmove(). This makes the assumption
- that if player X's movement was blocked in the client he's going to still
- be up against player Y when the Pmove() is run on the server with the
- same cmd.
-
- NOTE: this must be called after Pmove() and G_UnlaggedCalc()
-==============
-*/
-static void G_UnlaggedDetectCollisions( gentity_t *ent )
-{
- unlagged_t *calc;
- trace_t tr;
- float r1, r2;
- float range;
-
- if( !g_unlagged.integer )
- return;
-
- if( !ent->client->pers.useUnlagged )
- return;
-
- calc = &ent->client->unlaggedCalc;
-
- // if the client isn't moving, this is not necessary
- if( VectorCompare( ent->client->oldOrigin, ent->client->ps.origin ) )
- return;
-
- range = Distance( ent->client->oldOrigin, ent->client->ps.origin );
-
- // increase the range by the player's largest possible radius since it's
- // the players bounding box that collides, not their origin
- r1 = Distance( calc->origin, calc->mins );
- r2 = Distance( calc->origin, calc->maxs );
- range += ( r1 > r2 ) ? r1 : r2;
-
- G_UnlaggedOn( ent, ent->client->oldOrigin, range );
-
- 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_UnlaggedOff( );
-}
-
-/*
-==============
-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;
-
- client->unlaggedTime = ucmd->serverTime;
-
- 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.spectatorState != SPECTATOR_NOT )
- {
- if( client->sess.spectatorState == SPECTATOR_SCOREBOARD )
- return;
-
- SpectatorThink( ent, ucmd );
- return;
- }
-
- G_namelog_update_score( client );
-
- // check for inactivity timer, but never drop the local client of a non-dedicated server
- if( !ClientInactivityTimer( ent ) )
- return;
-
- // 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_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 + LOCKBLOB_LOCKTIME < level.time )
- client->ps.stats[ STAT_STATE ] &= ~SS_BLOBLOCKED;
-
- if( ( client->ps.stats[ STAT_STATE ] & SS_SLOWLOCKED ) &&
- client->lastSlowTime + ABUILDER_BLOB_TIME < level.time )
- client->ps.stats[ STAT_STATE ] &= ~SS_SLOWLOCKED;
-
- // 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;
- }
-
- // 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 )
- 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_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 )
- {
- //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_HEALING_2X;
- 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 );
- }
- }
-
- // 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->s.origin ) > 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->health += count;
- ent->nextRegenTime += count * interval;
-
- // if at max health, clear damage counters
- if( ent->health >= client->ps.stats[ STAT_MAX_HEALTH ] )
- {
- ent->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 ) )
- {
- 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
- 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;
-
- //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( !G_Reactor( ) )
- // BG_DeactivateUpgrade( UP_JETPACK, client->ps.stats );
- }
-
- // 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;
- }
-
- // 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_DEADSOLID;
- else
- pm.tracemask = MASK_PLAYERSOLID;
-
- pm.trace = G_PMTraceHack;
- pm.pointcontents = trap_PointContents;
- pm.debugLevel = g_debugMove.integer;
- pm.noFootsteps = 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 );
-
- G_UnlaggedDetectCollisions( ent );
-
- // 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 );
-
- 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 ) )
- 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;
-
- // touch other objects
- ClientImpacts( ent, &pm );
-
- // 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 );
-
- // 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;
-
- // swap and latch button actions
- client->oldbuttons = client->buttons;
- client->buttons = ucmd->buttons;
- client->latched_buttons |= client->buttons & ~client->oldbuttons;
-
- if( ( client->buttons & BUTTON_USE_EVOLVE ) && !( client->oldbuttons & BUTTON_USE_EVOLVE ) &&
- client->ps.stats[ STAT_HEALTH ] > 0 )
- {
- trace_t trace;
- vec3_t eyes, view, point;
- gentity_t *traceEnt;
-
-#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;
-
- // look for object infront of player
- AngleVectors( client->ps.viewangles, view, NULL, NULL );
- BG_GetClientViewOrigin( &client->ps, eyes ); // !@#CUBOID
- VectorMA( eyes, 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->buildableTeam == client->ps.stats[ STAT_TEAM ] && 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->buildableTeam == client->ps.stats[ STAT_TEAM ] && traceEnt->use )
- {
- traceEnt->use( traceEnt, ent, ent ); //other and activator are the same in this context
- break;
- }
- }
-
- if( i == num && client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- if( BG_AlienCanEvolve( client->ps.stats[ STAT_CLASS ],
- client->pers.credit,
- g_alienStage.integer ) )
- {
- //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 );
- }
- }
- }
- }
-
- 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 ] );
-
- 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;
- }
-}
-
-/*
-==================
-ClientThink
-
-A new command has arrived from the client
-==================
-*/
-void ClientThink( int clientNum )
-{
- gentity_t *ent;
-
- ent = g_entities + clientNum;
- trap_GetUsercmd( clientNum, &ent->client->pers.cmd );
-
- // mark the time we got info, so we can display the
- // phone jack if they don't get any for a while
- ent->client->lastCmdTime = level.time;
-
- if( !g_synchronousClients.integer )
- ClientThink_real( ent );
-}
-
-
-void G_RunClient( gentity_t *ent )
-{
- if( !g_synchronousClients.integer )
- return;
-
- ent->client->pers.cmd.serverTime = level.time;
- ClientThink_real( ent );
-}
-
-
-/*
-==================
-SpectatorClientEndFrame
-
-==================
-*/
-void SpectatorClientEndFrame( gentity_t *ent )
-{
- gclient_t *cl;
- int clientNum;
- int score, ping;
-
- // 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 && clientNum < level.maxclients )
- {
- cl = &level.clients[ clientNum ];
- if( cl->pers.connected == CON_CONNECTED )
- {
- 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;
- }
- }
- }
-}
-
-/*
-==============
-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 )
-{
- clientPersistant_t *pers;
-
- if( ent->client->sess.spectatorState != SPECTATOR_NOT )
- {
- SpectatorClientEndFrame( ent );
- return;
- }
-
- pers = &ent->client->pers;
-
- //
- // 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...
-
- // respawn if dead
- if( ent->client->ps.stats[ STAT_HEALTH ] <= 0 && level.time >= ent->client->respawnTime )
- respawn( ent );
-
- 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 );
-}
-
diff --git a/src/game/g_client.c.orig b/src/game/g_client.c.orig
deleted file mode 100644
index 735c59d..0000000
--- a/src/game/g_client.c.orig
+++ /dev/null
@@ -1,1600 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-#include "g_local.h"
-
-// g_client.c -- client functions that don't happen every frame
-
-static vec3_t playerMins = {-15, -15, -24};
-static vec3_t playerMaxs = {15, 15, 32};
-
-/*QUAKED info_player_deathmatch (1 0 1) (-16 -16 -24) (16 16 32) initial
-potential spawning position for deathmatch games.
-The first time a player enters the game, they will be at an 'initial' spot.
-Targets will be fired when someone spawns in on them.
-"nobots" will prevent bots from using this spot.
-"nohumans" will prevent non-bots from using this spot.
-*/
-void SP_info_player_deathmatch( gentity_t *ent )
-{
- int i;
-
- G_SpawnInt( "nobots", "0", &i);
-
- if( i )
- ent->flags |= FL_NO_BOTS;
-
- G_SpawnInt( "nohumans", "0", &i );
- if( i )
- ent->flags |= FL_NO_HUMANS;
-}
-
-/*QUAKED info_player_start (1 0 0) (-16 -16 -24) (16 16 32)
-equivelant to info_player_deathmatch
-*/
-void SP_info_player_start( gentity_t *ent )
-{
- ent->classname = "info_player_deathmatch";
- SP_info_player_deathmatch( ent );
-}
-
-/*QUAKED info_player_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_player_intermission( gentity_t *ent )
-{
-}
-
-/*QUAKED info_alien_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_alien_intermission( gentity_t *ent )
-{
-}
-
-/*QUAKED info_human_intermission (1 0 1) (-16 -16 -24) (16 16 32)
-The intermission will be viewed from this point. Target an info_notnull for the view direction.
-*/
-void SP_info_human_intermission( gentity_t *ent )
-{
-}
-
-/*
-===============
-G_AddCreditToClient
-===============
-*/
-void G_AddCreditToClient( gclient_t *client, short credit, qboolean cap )
-{
- int capAmount;
-
- if( !client )
- return;
-
- if( cap && credit > 0 )
- {
- capAmount = client->pers.teamSelection == TEAM_ALIENS ?
- ALIEN_MAX_CREDITS : HUMAN_MAX_CREDITS;
- if( client->pers.credit < capAmount )
- {
- client->pers.credit += credit;
- if( client->pers.credit > capAmount )
- client->pers.credit = capAmount;
- }
- }
- else
- client->pers.credit += credit;
-
- if( client->pers.credit < 0 )
- client->pers.credit = 0;
-
- // Copy to ps so the client can access it
- client->ps.persistant[ PERS_CREDIT ] = client->pers.credit;
-}
-
-
-/*
-=======================================================================
-
- G_SelectSpawnPoint
-
-=======================================================================
-*/
-
-/*
-================
-SpotWouldTelefrag
-
-================
-*/
-qboolean SpotWouldTelefrag( gentity_t *spot )
-{
- int i, num;
- int touch[ MAX_GENTITIES ];
- gentity_t *hit;
- vec3_t mins, maxs;
-
- VectorAdd( spot->s.origin, playerMins, mins );
- VectorAdd( spot->s.origin, playerMaxs, maxs );
- num = trap_EntitiesInBox( mins, maxs, touch, MAX_GENTITIES );
-
- for( i = 0; i < num; i++ )
- {
- hit = &g_entities[ touch[ i ] ];
- //if ( hit->client && hit->client->ps.stats[STAT_HEALTH] > 0 ) {
- if( hit->client )
- return qtrue;
- }
-
- return qfalse;
-}
-
-
-/*
-===========
-G_SelectRandomFurthestSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-static gentity_t *G_SelectRandomFurthestSpawnPoint ( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
- vec3_t delta;
- float dist;
- float list_dist[ 64 ];
- gentity_t *list_spot[ 64 ];
- int numSpots, rnd, i, j;
-
- numSpots = 0;
- spot = NULL;
-
- while( ( spot = G_Find( spot, FOFS( classname ), "info_player_deathmatch" ) ) != NULL )
- {
- if( SpotWouldTelefrag( spot ) )
- continue;
-
- VectorSubtract( spot->s.origin, avoidPoint, delta );
- dist = VectorLength( delta );
-
- for( i = 0; i < numSpots; i++ )
- {
- if( dist > list_dist[ i ] )
- {
- if( numSpots >= 64 )
- numSpots = 64 - 1;
-
- for( j = numSpots; j > i; j-- )
- {
- list_dist[ j ] = list_dist[ j - 1 ];
- list_spot[ j ] = list_spot[ j - 1 ];
- }
-
- list_dist[ i ] = dist;
- list_spot[ i ] = spot;
- numSpots++;
-
- if( numSpots > 64 )
- numSpots = 64;
-
- break;
- }
- }
-
- if( i >= numSpots && numSpots < 64 )
- {
- list_dist[ numSpots ] = dist;
- list_spot[ numSpots ] = spot;
- numSpots++;
- }
- }
-
- if( !numSpots )
- {
- spot = G_Find( NULL, FOFS( classname ), "info_player_deathmatch" );
-
- if( !spot )
- G_Error( "Couldn't find a spawn point" );
-
- VectorCopy( spot->s.origin, origin );
- origin[ 2 ] += 9;
- VectorCopy( spot->s.angles, angles );
- return spot;
- }
-
- // select a random spot from the spawn points furthest away
- rnd = random( ) * ( numSpots / 2 );
-
- VectorCopy( list_spot[ rnd ]->s.origin, origin );
- origin[ 2 ] += 9;
- VectorCopy( list_spot[ rnd ]->s.angles, angles );
-
- return list_spot[ rnd ];
-}
-
-
-/*
-================
-G_SelectSpawnBuildable
-
-find the nearest buildable of the right type that is
-spawned/healthy/unblocked etc.
-================
-*/
-static gentity_t *G_SelectSpawnBuildable( vec3_t preference, buildable_t buildable )
-{
- gentity_t *search, *spot;
-
- search = spot = NULL;
-
- while( ( search = G_Find( search, FOFS( classname ),
- BG_Buildable( buildable, NULL )->entityName ) ) != NULL )
- {
- if( !search->spawned )
- continue;
-
- if( search->health <= 0 )
- continue;
-
- if( !search->s.groundEntityNum )
- continue;
-
- if( search->clientSpawnTime > 0 )
- continue;
-
- if( G_CheckSpawnPoint( search->s.number, search->s.origin,
- search->s.origin2, buildable, NULL ) != NULL )
- continue;
-
- if( !spot || DistanceSquared( preference, search->s.origin ) <
- DistanceSquared( preference, spot->s.origin ) )
- spot = search;
- }
-
- return spot;
-}
-
-/*
-===========
-G_SelectSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-gentity_t *G_SelectSpawnPoint( vec3_t avoidPoint, vec3_t origin, vec3_t angles )
-{
- return G_SelectRandomFurthestSpawnPoint( avoidPoint, origin, angles );
-}
-
-
-/*
-===========
-G_SelectTremulousSpawnPoint
-
-Chooses a player start, deathmatch start, etc
-============
-*/
-gentity_t *G_SelectTremulousSpawnPoint( team_t team, vec3_t preference, vec3_t origin, vec3_t angles )
-{
- gentity_t *spot = NULL;
-
- if( team == TEAM_ALIENS )
- {
- if( level.numAlienSpawns <= 0 )
- return NULL;
-
- spot = G_SelectSpawnBuildable( preference, BA_A_SPAWN );
- }
- else if( team == TEAM_HUMANS )
- {
- if( level.numHumanSpawns <= 0 )
- return NULL;
-
- spot = G_SelectSpawnBuildable( preference, BA_H_SPAWN );
- }
-
- //no available spots
- if( !spot )
- return NULL;
-
- if( team == TEAM_ALIENS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_A_SPAWN, origin );
- else if( team == TEAM_HUMANS )
- G_CheckSpawnPoint( spot->s.number, spot->s.origin, spot->s.origin2, BA_H_SPAWN, origin );
-
- VectorCopy( spot->s.angles, angles );
- angles[ ROLL ] = 0;
-
- return spot;
-
-}
-
-
-/*
-===========
-G_SelectSpectatorSpawnPoint
-
-============
-*/
-static gentity_t *G_SelectSpectatorSpawnPoint( vec3_t origin, vec3_t angles )
-{
- FindIntermissionPoint( );
-
- VectorCopy( level.intermission_origin, origin );
- VectorCopy( level.intermission_angle, angles );
-
- return NULL;
-}
-
-
-/*
-===========
-G_SelectAlienLockSpawnPoint
-
-Try to find a spawn point for alien intermission otherwise
-use normal intermission spawn.
-============
-*/
-gentity_t *G_SelectAlienLockSpawnPoint( vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
-
- spot = NULL;
- spot = G_Find( spot, FOFS( classname ), "info_alien_intermission" );
-
- if( !spot )
- return G_SelectSpectatorSpawnPoint( origin, angles );
-
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
-
- return spot;
-}
-
-
-/*
-===========
-G_SelectHumanLockSpawnPoint
-
-Try to find a spawn point for human intermission otherwise
-use normal intermission spawn.
-============
-*/
-gentity_t *G_SelectHumanLockSpawnPoint( vec3_t origin, vec3_t angles )
-{
- gentity_t *spot;
-
- spot = NULL;
- spot = G_Find( spot, FOFS( classname ), "info_human_intermission" );
-
- if( !spot )
- return G_SelectSpectatorSpawnPoint( origin, angles );
-
- VectorCopy( spot->s.origin, origin );
- VectorCopy( spot->s.angles, angles );
-
- return spot;
-}
-
-
-/*
-=======================================================================
-
-BODYQUE
-
-=======================================================================
-*/
-
-
-/*
-=============
-BodySink
-
-After sitting around for five seconds, fall into the ground and dissapear
-=============
-*/
-static void BodySink( gentity_t *ent )
-{
- //run on first BodySink call
- if( !ent->active )
- {
- ent->active = qtrue;
-
- //sinking bodies can't be infested
- ent->killedBy = ent->s.misc = MAX_CLIENTS;
- ent->timestamp = level.time;
- }
-
- if( level.time - ent->timestamp > 6500 )
- {
- G_FreeEntity( ent );
- return;
- }
-
- ent->nextthink = level.time + 100;
- ent->s.pos.trBase[ 2 ] -= 1;
-}
-
-
-/*
-=============
-SpawnCorpse
-
-A player is respawning, so make an entity that looks
-just like the existing corpse to leave behind.
-=============
-*/
-static void SpawnCorpse( gentity_t *ent )
-{
- gentity_t *body;
- int contents;
- vec3_t origin, dest;
- trace_t tr;
- float vDiff;
-
- VectorCopy( ent->r.currentOrigin, origin );
-
- trap_UnlinkEntity( ent );
-
- // if client is in a nodrop area, don't leave the body
- contents = trap_PointContents( origin, -1 );
- if( contents & CONTENTS_NODROP )
- return;
-
- body = G_Spawn( );
-
- VectorCopy( ent->s.apos.trBase, body->s.angles );
- body->s.eFlags = EF_DEAD;
- body->s.eType = ET_CORPSE;
- body->s.number = body - g_entities;
- body->timestamp = level.time;
- body->s.event = 0;
- body->r.contents = CONTENTS_CORPSE;
- body->s.clientNum = ent->client->ps.stats[ STAT_CLASS ];
- body->nonSegModel = ent->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL;
-
- if( ent->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- body->classname = "humanCorpse";
- else
- body->classname = "alienCorpse";
-
- body->s.misc = MAX_CLIENTS;
-
- body->think = BodySink;
- body->nextthink = level.time + 20000;
-
- body->s.legsAnim = ent->s.legsAnim;
-
- if( !body->nonSegModel )
- {
- switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
- {
- case BOTH_DEATH1:
- case BOTH_DEAD1:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD1;
- break;
- case BOTH_DEATH2:
- case BOTH_DEAD2:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD2;
- break;
- case BOTH_DEATH3:
- case BOTH_DEAD3:
- default:
- body->s.torsoAnim = body->s.legsAnim = BOTH_DEAD3;
- break;
- }
- }
- else
- {
- switch( body->s.legsAnim & ~ANIM_TOGGLEBIT )
- {
- case NSPA_DEATH1:
- case NSPA_DEAD1:
- body->s.legsAnim = NSPA_DEAD1;
- break;
- case NSPA_DEATH2:
- case NSPA_DEAD2:
- body->s.legsAnim = NSPA_DEAD2;
- break;
- case NSPA_DEATH3:
- case NSPA_DEAD3:
- default:
- body->s.legsAnim = NSPA_DEAD3;
- break;
- }
- }
-
- body->takedamage = qfalse;
-
- body->health = ent->health = ent->client->ps.stats[ STAT_HEALTH ];
- ent->health = 0;
-
- //change body dimensions
- BG_ClassBoundingBox( ent->client->ps.stats[ STAT_CLASS ], NULL, NULL, NULL, body->r.mins, body->r.maxs );
- vDiff = body->r.mins[ 2 ] - ent->r.mins[ 2 ];
-
- //drop down to match the *model* origins of ent and body
- VectorSet( dest, origin[ 0 ], origin[ 1 ], origin[ 2 ] - vDiff );
- trap_Trace( &tr, origin, body->r.mins, body->r.maxs, dest, body->s.number, body->clipmask );
- VectorCopy( tr.endpos, origin );
-
- G_SetOrigin( body, origin );
- VectorCopy( origin, body->s.origin );
- body->s.pos.trType = TR_GRAVITY;
- body->s.pos.trTime = level.time;
- VectorCopy( ent->client->ps.velocity, body->s.pos.trDelta );
-
- VectorCopy ( body->s.pos.trBase, body->r.currentOrigin );
- trap_LinkEntity( body );
-}
-
-//======================================================================
-
-
-/*
-==================
-G_SetClientViewAngle
-
-==================
-*/
-void G_SetClientViewAngle( gentity_t *ent, vec3_t angle )
-{
- int i;
-
- // set the delta angle
- for( i = 0; i < 3; i++ )
- {
- int cmdAngle;
-
- cmdAngle = ANGLE2SHORT( angle[ i ] );
- ent->client->ps.delta_angles[ i ] = cmdAngle - ent->client->pers.cmd.angles[ i ];
- }
-
- VectorCopy( angle, ent->s.angles );
- VectorCopy( ent->s.angles, ent->client->ps.viewangles );
-}
-
-/*
-================
-respawn
-================
-*/
-void respawn( gentity_t *ent )
-{
- int i;
-
- SpawnCorpse( ent );
-
- // Clients can't respawn - they must go through the class cmd
- ent->client->pers.classSelection = PCL_NONE;
- ClientSpawn( ent, NULL, NULL, NULL );
-
- // stop any following clients that don't have sticky spec on
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
- level.clients[ i ].sess.spectatorClient == ent - g_entities )
- {
- if( !( level.clients[ i ].pers.stickySpec ) )
- {
- if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
- G_StopFollowing( &g_entities[ i ] );
- }
- else
- G_FollowLockView( &g_entities[ i ] );
- }
- }
-}
-
-static qboolean G_IsEmoticon( const char *s, qboolean *escaped )
-{
- int i, j;
- const char *p = s;
- char emoticon[ MAX_EMOTICON_NAME_LEN ] = {""};
- qboolean escape = qfalse;
-
- if( *p != '[' )
- return qfalse;
- p++;
- if( *p == '[' )
- {
- escape = qtrue;
- p++;
- }
- i = 0;
- while( *p && i < ( MAX_EMOTICON_NAME_LEN - 1 ) )
- {
- if( *p == ']' )
- {
- for( j = 0; j < level.emoticonCount; j++ )
- {
- if( !Q_stricmp( emoticon, level.emoticons[ j ].name ) )
- {
- *escaped = escape;
- return qtrue;
- }
- }
- return qfalse;
- }
- emoticon[ i++ ] = *p;
- emoticon[ i ] = '\0';
- p++;
- }
- return qfalse;
-}
-
-/*
-===========
-G_ClientCleanName
-============
-*/
-static void G_ClientCleanName( const char *in, char *out, int outSize )
-{
- int len, colorlessLen;
- char *p;
- int spaces;
- qboolean escaped;
- qboolean invalid = qfalse;
-
- //save room for trailing null byte
- outSize--;
-
- len = 0;
- colorlessLen = 0;
- p = out;
- *p = 0;
- spaces = 0;
-
- for( ; *in; in++ )
- {
- // don't allow leading spaces
- if( colorlessLen == 0 && *in == ' ' )
- continue;
-
- // don't allow nonprinting characters or (dead) console keys
- if( *in < ' ' || *in > '}' || *in == '`' )
- continue;
-
- // check colors
- if( Q_IsColorString( in ) )
- {
- in++;
-
- // make sure room in dest for both chars
- if( len > outSize - 2 )
- break;
-
- *out++ = Q_COLOR_ESCAPE;
-
- *out++ = *in;
-
- len += 2;
- continue;
- }
- else if( !g_emoticonsAllowedInNames.integer && G_IsEmoticon( in, &escaped ) )
- {
- // make sure room in dest for both chars
- if( len > outSize - 2 )
- break;
-
- *out++ = '[';
- *out++ = '[';
- len += 2;
- if( escaped )
- in++;
- continue;
- }
-
- // don't allow too many consecutive spaces
- if( *in == ' ' )
- {
- spaces++;
- if( spaces > 3 )
- continue;
- }
- else
- spaces = 0;
-
- if( len > outSize - 1 )
- break;
-
- *out++ = *in;
- colorlessLen++;
- len++;
- }
-
- *out = 0;
-
- // don't allow names beginning with "[skipnotify]" because it messes up /ignore-related code
- if( !Q_stricmpn( p, "[skipnotify]", 12 ) )
- invalid = qtrue;
-
- // don't allow comment-beginning strings because it messes up various parsers
- if( strstr( p, "//" ) || strstr( p, "/*" ) )
- invalid = qtrue;
-
- // don't allow empty names
- if( *p == 0 || colorlessLen == 0 )
- invalid = qtrue;
-
- // if something made the name bad, put them back to UnnamedPlayer
- if( invalid )
- Q_strncpyz( p, "UnnamedPlayer", outSize );
-}
-
-
-/*
-======================
-G_NonSegModel
-
-Reads an animation.cfg to check for nonsegmentation
-======================
-*/
-static qboolean G_NonSegModel( const char *filename )
-{
- char *text_p;
- int len;
- char *token;
- char text[ 20000 ];
- fileHandle_t f;
-
- // load the file
- len = trap_FS_FOpenFile( filename, &f, FS_READ );
- if( !f )
- {
- G_Printf( "File not found: %s\n", filename );
- return qfalse;
- }
-
- if( len < 0 )
- return qfalse;
-
- if( len == 0 || len >= sizeof( text ) - 1 )
- {
- trap_FS_FCloseFile( f );
- G_Printf( "File %s is %s\n", filename, len == 0 ? "empty" : "too long" );
- return qfalse;
- }
-
- trap_FS_Read( text, len, f );
- text[ len ] = 0;
- trap_FS_FCloseFile( f );
-
- // parse the text
- text_p = text;
-
- // read optional parameters
- while( 1 )
- {
- token = COM_Parse( &text_p );
-
- //EOF
- if( !token[ 0 ] )
- break;
-
- if( !Q_stricmp( token, "nonsegmented" ) )
- return qtrue;
- }
-
- return qfalse;
-}
-
-/*
-===========
-ClientUserInfoChanged
-
-Called from ClientConnect when the player first connects and
-directly by the server system when the player updates a userinfo variable.
-
-The game can override any of the settings and call trap_SetUserinfo
-if desired.
-============
-*/
-char *ClientUserinfoChanged( int clientNum, qboolean forceName )
-{
- gentity_t *ent;
- char *s;
- char model[ MAX_QPATH ];
- char buffer[ MAX_QPATH ];
- char filename[ MAX_QPATH ];
- char oldname[ MAX_NAME_LENGTH ];
- char newname[ MAX_NAME_LENGTH ];
- char err[ MAX_STRING_CHARS ];
- qboolean revertName = qfalse;
- gclient_t *client;
- char userinfo[ MAX_INFO_STRING ];
-
- ent = g_entities + clientNum;
- client = ent->client;
-
- trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
-
- // check for malformed or illegal info strings
- if( !Info_Validate(userinfo) )
- {
- trap_SendServerCommand( ent - g_entities,
- "disconnect \"illegal or malformed userinfo\n\"" );
- trap_DropClient( ent - g_entities,
- "dropped: illegal or malformed userinfo");
- return "Illegal or malformed userinfo";
- }
- // If their userinfo overflowed, tremded is in the process of disconnecting them.
- // If we send our own disconnect, it won't work, so just return to prevent crashes later
- // in this function. This check must come after the Info_Validate call.
- else if( !userinfo[ 0 ] )
- return "Empty (overflowed) userinfo";
-
- // stickyspec toggle
- s = Info_ValueForKey( userinfo, "cg_stickySpec" );
- client->pers.stickySpec = atoi( s ) != 0;
-
- // set name
- Q_strncpyz( oldname, client->pers.netname, sizeof( oldname ) );
- s = Info_ValueForKey( userinfo, "name" );
- G_ClientCleanName( s, newname, sizeof( newname ) );
-
- if( strcmp( oldname, newname ) )
- {
- if( !forceName && client->pers.namelog->nameChangeTime &&
- level.time - client->pers.namelog->nameChangeTime <=
- g_minNameChangePeriod.value * 1000 )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Name change spam protection (g_minNameChangePeriod = %d)\n\"",
- g_minNameChangePeriod.integer ) );
- revertName = qtrue;
- }
- else if( !forceName && g_maxNameChanges.integer > 0 &&
- client->pers.namelog->nameChanges >= g_maxNameChanges.integer )
- {
- trap_SendServerCommand( ent - g_entities, va(
- "print \"Maximum name changes reached (g_maxNameChanges = %d)\n\"",
- g_maxNameChanges.integer ) );
- revertName = qtrue;
- }
- else if( !forceName && client->pers.namelog->muted )
- {
- trap_SendServerCommand( ent - g_entities,
- "print \"You cannot change your name while you are muted\n\"" );
- revertName = qtrue;
- }
- else if( !G_admin_name_check( ent, newname, err, sizeof( err ) ) )
- {
- trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", err ) );
- revertName = qtrue;
- }
-
- if( revertName )
- {
- Q_strncpyz( client->pers.netname, *oldname ? oldname : "UnnamedPlayer",
- sizeof( client->pers.netname ) );
- Info_SetValueForKey( userinfo, "name", oldname );
- trap_SetUserinfo( clientNum, userinfo );
- }
- else
- {
- G_CensorString( client->pers.netname, newname,
- sizeof( client->pers.netname ), ent );
- if( !forceName && client->pers.connected == CON_CONNECTED )
- {
- client->pers.namelog->nameChangeTime = level.time;
- client->pers.namelog->nameChanges++;
- }
- if( *oldname )
- {
- G_LogPrintf( "ClientRename: %i [%s] (%s) \"%s^7\" -> \"%s^7\" \"%c%s%c^7\"\n",
- clientNum, client->pers.ip.str, client->pers.guid,
- oldname, client->pers.netname,
- DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
- }
- }
- G_namelog_update_name( client );
- }
-
- if( client->pers.classSelection == PCL_NONE )
- {
- //This looks hacky and frankly it is. The clientInfo string needs to hold different
- //model details to that of the spawning class or the info change will not be
- //registered and an axis appears instead of the player model. There is zero chance
- //the player can spawn with the battlesuit, hence this choice.
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( PCL_HUMAN_BSUIT )->modelName,
- BG_ClassConfig( PCL_HUMAN_BSUIT )->skinName );
- }
- else
- {
- Com_sprintf( buffer, MAX_QPATH, "%s/%s", BG_ClassConfig( client->pers.classSelection )->modelName,
- BG_ClassConfig( client->pers.classSelection )->skinName );
-
- //model segmentation
- Com_sprintf( filename, sizeof( filename ), "models/players/%s/animation.cfg",
- BG_ClassConfig( client->pers.classSelection )->modelName );
-
- if( G_NonSegModel( filename ) )
- client->ps.persistant[ PERS_STATE ] |= PS_NONSEGMODEL;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_NONSEGMODEL;
- }
- Q_strncpyz( model, buffer, sizeof( model ) );
-
- // wallwalk follow
- s = Info_ValueForKey( userinfo, "cg_wwFollow" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGFOLLOW;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGFOLLOW;
-
- // wallwalk toggle
- s = Info_ValueForKey( userinfo, "cg_wwToggle" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_WALLCLIMBINGTOGGLE;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_WALLCLIMBINGTOGGLE;
-
- // always sprint
- s = Info_ValueForKey( userinfo, "cg_sprintToggle" );
-
- if( atoi( s ) )
- client->ps.persistant[ PERS_STATE ] |= PS_SPRINTTOGGLE;
- else
- client->ps.persistant[ PERS_STATE ] &= ~PS_SPRINTTOGGLE;
-
- // fly speed
- s = Info_ValueForKey( userinfo, "cg_flySpeed" );
-
- if( *s )
- client->pers.flySpeed = atoi( s );
- else
- client->pers.flySpeed = BG_Class( PCL_NONE )->speed;
-
- // disable blueprint errors
- s = Info_ValueForKey( userinfo, "cg_disableBlueprintErrors" );
-
- if( atoi( s ) )
- client->pers.disableBlueprintErrors = qtrue;
- else
- client->pers.disableBlueprintErrors = qfalse;
-
- // teamInfo
- s = Info_ValueForKey( userinfo, "teamoverlay" );
-
- if( atoi( s ) != 0 )
- client->pers.teamInfo = qtrue;
- else
- client->pers.teamInfo = qfalse;
-
- s = Info_ValueForKey( userinfo, "cg_unlagged" );
- if( !s[0] || atoi( s ) != 0 )
- client->pers.useUnlagged = qtrue;
- else
- client->pers.useUnlagged = qfalse;
-
- Q_strncpyz( client->pers.voice, Info_ValueForKey( userinfo, "voice" ),
- sizeof( client->pers.voice ) );
-
- // send over a subset of the userinfo keys so other clients can
- // print scoreboards, display models, and play custom sounds
-
- Com_sprintf( userinfo, sizeof( userinfo ),
- "n\\%s\\t\\%i\\model\\%s\\ig\\%16s\\v\\%s",
- client->pers.netname, client->pers.teamSelection, model,
- Com_ClientListString( &client->sess.ignoreList ),
- client->pers.voice );
-
- trap_SetConfigstring( CS_PLAYERS + clientNum, userinfo );
-
- /*G_LogPrintf( "ClientUserinfoChanged: %i %s\n", clientNum, userinfo );*/
-
- return NULL;
-}
-
-
-/*
-===========
-ClientConnect
-
-Called when a player begins connecting to the server.
-Called again for every map change or tournement restart.
-
-The session information will be valid after exit.
-
-Return NULL if the client should be allowed, otherwise return
-a string with the reason for denial.
-
-Otherwise, the client will be sent the current gamestate
-and will eventually get to ClientBegin.
-
-firstTime will be qtrue the very first time a client connects
-to the server machine, but qfalse on map changes and tournement
-restarts.
-============
-*/
-char *ClientConnect( int clientNum, qboolean firstTime )
-{
- char *value;
- char *userInfoError;
- gclient_t *client;
- char userinfo[ MAX_INFO_STRING ];
- gentity_t *ent;
- char reason[ MAX_STRING_CHARS ] = {""};
- int i;
-
- ent = &g_entities[ clientNum ];
- client = &level.clients[ clientNum ];
-
- // ignore if client already connected
- if( client->pers.connected != CON_DISCONNECTED )
- return NULL;
-
- ent->client = client;
- memset( client, 0, sizeof( *client ) );
-
- trap_GetUserinfo( clientNum, userinfo, sizeof( userinfo ) );
-
- value = Info_ValueForKey( userinfo, "cl_guid" );
- Q_strncpyz( client->pers.guid, value, sizeof( client->pers.guid ) );
-
- value = Info_ValueForKey( userinfo, "ip" );
- // check for local client
- if( !strcmp( value, "localhost" ) )
- client->pers.localClient = qtrue;
- G_AddressParse( value, &client->pers.ip );
-
- client->pers.admin = G_admin_admin( client->pers.guid );
-
- // check for admin ban
- if( G_admin_ban_check( ent, reason, sizeof( reason ) ) )
- {
- return va( "%s", reason );
- }
-
- // check for a password
- value = Info_ValueForKey( userinfo, "password" );
-
- if( g_password.string[ 0 ] && Q_stricmp( g_password.string, "none" ) &&
- strcmp( g_password.string, value ) != 0 )
- return "Invalid password";
-
- // add guid to session so we don't have to keep parsing userinfo everywhere
- for( i = 0; i < sizeof( client->pers.guid ) - 1 &&
- isxdigit( client->pers.guid[ i ] ); i++ );
-
- if( i < sizeof( client->pers.guid ) - 1 )
- return "Invalid GUID";
-
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_DISCONNECTED )
- continue;
-
- if( !Q_stricmp( client->pers.guid, level.clients[ i ].pers.guid ) )
- {
- if( !G_ClientIsLagging( level.clients + i ) )
- {
- trap_SendServerCommand( i, "cp \"Your GUID is not secure\"" );
- return "Duplicate GUID";
- }
- trap_DropClient( i, "Ghost" );
- }
- }
-
- client->pers.connected = CON_CONNECTING;
-
- // read or initialize the session data
- if( firstTime || level.newSession )
- G_InitSessionData( client, userinfo );
-
- G_ReadSessionData( client );
-
- // get and distribute relevent paramters
- G_namelog_connect( client );
- userInfoError = ClientUserinfoChanged( clientNum, qfalse );
- if( userInfoError != NULL )
- return userInfoError;
-
- G_LogPrintf( "ClientConnect: %i [%s] (%s) \"%s^7\" \"%c%s%c^7\"\n",
- clientNum, client->pers.ip.str, client->pers.guid,
- client->pers.netname,
- DECOLOR_OFF, client->pers.netname, DECOLOR_ON );
-
- // don't do the "xxx connected" messages if they were caried over from previous level
- if( firstTime )
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " connected\n\"",
- client->pers.netname ) );
-
- if( client->pers.admin )
- G_admin_authlog( ent );
-
- // count current clients and rank for scoreboard
- CalculateRanks( );
-
-
- // if this is after !restart keepteams or !restart switchteams, apply said selection
- if ( client->sess.restartTeam != TEAM_NONE )
- {
- G_ChangeTeam( ent, client->sess.restartTeam );
- client->sess.restartTeam = TEAM_NONE;
- }
-
-
- return NULL;
-}
-
-/*
-===========
-ClientBegin
-
-called when a client has finished connecting, and is ready
-to be placed into the level. This will happen every level load,
-and on transition between teams, but doesn't happen on respawns
-============
-*/
-void ClientBegin( int clientNum )
-{
- gentity_t *ent;
- gclient_t *client;
- int flags;
-
- ent = g_entities + clientNum;
-
- client = level.clients + clientNum;
-
- // ignore if client already entered the game
- if( client->pers.connected != CON_CONNECTING )
- return;
-
- if( ent->r.linked )
- trap_UnlinkEntity( ent );
-
- G_InitGentity( ent );
- ent->touch = 0;
- ent->pain = 0;
- ent->client = client;
-
- client->pers.connected = CON_CONNECTED;
- client->pers.enterTime = level.time;
-
- // save eflags around this, because changing teams will
- // cause this to happen with a valid entity, and we
- // want to make sure the teleport bit is set right
- // so the viewpoint doesn't interpolate through the
- // world to the new position
- flags = client->ps.eFlags;
- memset( &client->ps, 0, sizeof( client->ps ) );
- memset( &client->pmext, 0, sizeof( client->pmext ) );
- client->ps.eFlags = flags;
-
- // locate ent at a spawn point
- ClientSpawn( ent, NULL, NULL, NULL );
-
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE " entered the game\n\"", client->pers.netname ) );
-
- G_namelog_restore( client );
-
- G_LogPrintf( "ClientBegin: %i\n", clientNum );
-
- // count current clients and rank for scoreboard
- CalculateRanks( );
-
- // send the client a list of commands that can be used
- G_ListCommands( ent );
-
- // reset cuboidSelection
- client->cuboidSelection[ 0 ] =
- client->cuboidSelection[ 1 ] =
- client->cuboidSelection[ 2 ] = 32;
-}
-
-/*
-===========
-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->buildTimer = 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 );
-
- // 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;
- }
- }
- }
-
- client->lastRantBombTime = level.time;
-
- // 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 );
-}
-
-
-/*
-===========
-ClientDisconnect
-
-Called when a player drops from the server.
-Will not be called between levels.
-
-This should NOT be called directly by any game logic,
-call trap_DropClient(), which will call this and do
-server system housekeeping.
-============
-*/
-void ClientDisconnect( int clientNum )
-{
- gentity_t *ent;
- gentity_t *tent;
- int i;
-
- ent = g_entities + clientNum;
-
- if( !ent->client || ent->client->pers.connected == CON_DISCONNECTED )
- return;
-
- G_LeaveTeam( ent );
- G_namelog_disconnect( ent->client );
- G_Vote( ent, TEAM_NONE, qfalse );
-
- // stop any following clients
- for( i = 0; i < level.maxclients; i++ )
- {
- // remove any /ignore settings for this clientNum
- Com_ClientListRemove( &level.clients[ i ].sess.ignoreList, clientNum );
- }
-
- // send effect if they were completely connected
- if( ent->client->pers.connected == CON_CONNECTED &&
- ent->client->sess.spectatorState == SPECTATOR_NOT )
- {
- tent = G_TempEntity( ent->client->ps.origin, EV_PLAYER_TELEPORT_OUT );
- tent->s.clientNum = ent->s.clientNum;
- }
-
- G_LogPrintf( "ClientDisconnect: %i [%s] (%s) \"%s^7\"\n", clientNum,
- ent->client->pers.ip.str, ent->client->pers.guid, ent->client->pers.netname );
-
- trap_UnlinkEntity( ent );
- ent->s.modelindex = 0;
- ent->inuse = qfalse;
- ent->classname = "disconnected";
- ent->client->pers.connected = CON_DISCONNECTED;
- ent->client->sess.spectatorState =
- ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_NOT;
-
- trap_SetConfigstring( CS_PLAYERS + clientNum, "");
-
- CalculateRanks( );
-}
-
-/*
-===========
-G_RelayCuboidToSpectators
-
-Called everytime a player changes his cuboid size.
-A server command is issued to everyone spectating him
-so that their clients can know the cuboid size as well.
-============
-*/
-void G_RelayCuboidToSpectators(gentity_t *self)
-{
-}
-
diff --git a/src/game/g_cmds.c.orig b/src/game/g_cmds.c.orig
deleted file mode 100644
index 5a30f7b..0000000
--- a/src/game/g_cmds.c.orig
+++ /dev/null
@@ -1,3588 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-#include "g_local.h"
-
-/*
-==================
-G_SanitiseString
-
-Remove color codes and non-alphanumeric characters from a string
-==================
-*/
-void G_SanitiseString( char *in, char *out, int len )
-{
- len--;
-
- while( *in && len > 0 )
- {
- if( Q_IsColorString( in ) )
- {
- in += 2; // skip color code
- continue;
- }
-
- if( isalnum( *in ) )
- {
- *out++ = tolower( *in );
- len--;
- }
- in++;
- }
- *out = 0;
-}
-
-/*
-==================
-G_ClientNumberFromString
-
-Returns a player number for either a number or name string
-Returns -1 and optionally sets err if invalid or not exactly 1 match
-err will have a trailing \n if set
-==================
-*/
-int G_ClientNumberFromString( char *s, char *err, int len )
-{
- gclient_t *cl;
- int i, found = 0, m = -1;
- char s2[ MAX_NAME_LENGTH ];
- char n2[ MAX_NAME_LENGTH ];
- char *p = err;
- int l, l2 = len;
-
- if( !s[ 0 ] )
- {
- if( p )
- Q_strncpyz( p, "no player name or slot # provided\n", len );
-
- return -1;
- }
-
- // numeric values are just slot numbers
- for( i = 0; s[ i ] && isdigit( s[ i ] ); i++ );
- if( !s[ i ] )
- {
- i = atoi( s );
-
- if( i < 0 || i >= level.maxclients )
- return -1;
-
- cl = &level.clients[ i ];
-
- if( cl->pers.connected == CON_DISCONNECTED )
- {
- if( p )
- Q_strncpyz( p, "no player connected in that slot #\n", len );
-
- return -1;
- }
-
- return i;
- }
-
- G_SanitiseString( s, s2, sizeof( s2 ) );
- if( !s2[ 0 ] )
- {
- if( p )
- Q_strncpyz( p, "no player name provided\n", len );
-
- return -1;
- }
-
- if( p )
- {
- Q_strncpyz( p, "more than one player name matches. "
- "be more specific or use the slot #:\n", l2 );
- l = strlen( p );
- p += l;
- l2 -= l;
- }
-
- // check for a name match
- for( i = 0, cl = level.clients; i < level.maxclients; i++, cl++ )
- {
- if( cl->pers.connected == CON_DISCONNECTED )
- continue;
-
- G_SanitiseString( cl->pers.netname, n2, sizeof( n2 ) );
-
- if( !strcmp( n2, s2 ) )
- return i;
-
- if( strstr( n2, s2 ) )
- {
- if( p )
- {
- l = Q_snprintf( p, l2, "%-2d - %s^7\n", i, cl->pers.netname );
- p += l;
- l2 -= l;
- }
-
- found++;
- m = i;
- }
- }
-
- if( found == 1 )
- return m;
-
- if( found == 0 && err )
- Q_strncpyz( err, "no connected player by that name or slot #\n", len );
-
- return -1;
-}
-
-/*
-==================
-G_ClientNumbersFromString
-
-Sets plist to an array of integers that represent client numbers that have
-names that are a partial match for s.
-
-Returns number of matching clientids up to max.
-==================
-*/
-int G_ClientNumbersFromString( char *s, int *plist, int max )
-{
- gclient_t *p;
- int i, found = 0;
- char *endptr;
- char n2[ MAX_NAME_LENGTH ] = {""};
- char s2[ MAX_NAME_LENGTH ] = {""};
-
- if( max == 0 )
- return 0;
-
- if( !s[ 0 ] )
- return 0;
-
- // if a number is provided, it is a clientnum
- i = strtol( s, &endptr, 10 );
- if( *endptr == '\0' )
- {
- if( i >= 0 && i < level.maxclients )
- {
- p = &level.clients[ i ];
- if( p->pers.connected != CON_DISCONNECTED )
- {
- *plist = i;
- return 1;
- }
- }
- // we must assume that if only a number is provided, it is a clientNum
- return 0;
- }
-
- // now look for name matches
- G_SanitiseString( s, s2, sizeof( s2 ) );
- if( !s2[ 0 ] )
- return 0;
- for( i = 0; i < level.maxclients && found < max; i++ )
- {
- p = &level.clients[ i ];
- if( p->pers.connected == CON_DISCONNECTED )
- {
- continue;
- }
- G_SanitiseString( p->pers.netname, n2, sizeof( n2 ) );
- if( strstr( n2, s2 ) )
- {
- *plist++ = i;
- found++;
- }
- }
- return found;
-}
-
-/*
-==================
-ScoreboardMessage
-
-==================
-*/
-void ScoreboardMessage( gentity_t *ent )
-{
- char entry[ 1024 ];
- char string[ 1400 ];
- int stringlength;
- int i, j;
- gclient_t *cl;
- int numSorted;
- weapon_t weapon = WP_NONE;
- upgrade_t upgrade = UP_NONE;
-
- // send the latest information on all clients
- string[ 0 ] = 0;
- stringlength = 0;
-
- numSorted = level.numConnectedClients;
-
- for( i = 0; i < numSorted; i++ )
- {
- int ping;
-
- cl = &level.clients[ level.sortedClients[ i ] ];
-
- if( cl->pers.connected == CON_CONNECTING )
- ping = -1;
- else
- ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
-
- if( cl->sess.spectatorState == SPECTATOR_NOT &&
- ( ent->client->pers.teamSelection == TEAM_NONE ||
- cl->pers.teamSelection == ent->client->pers.teamSelection ) )
- {
- weapon = cl->ps.weapon;
-
- if( BG_InventoryContainsUpgrade( UP_BATTLESUIT, cl->ps.stats ) )
- upgrade = UP_BATTLESUIT;
- else if( BG_InventoryContainsUpgrade( UP_JETPACK, cl->ps.stats ) )
- upgrade = UP_JETPACK;
- else if( BG_InventoryContainsUpgrade( UP_BATTPACK, cl->ps.stats ) )
- upgrade = UP_BATTPACK;
- else if( BG_InventoryContainsUpgrade( UP_HELMET_MK1, cl->ps.stats ) )
- upgrade = UP_HELMET_MK1;
- else if( BG_InventoryContainsUpgrade( UP_HELMET_MK2, cl->ps.stats ) )
- upgrade = UP_HELMET_MK2;
- else if( BG_InventoryContainsUpgrade( UP_LIGHTARMOUR, cl->ps.stats ) )
- upgrade = UP_LIGHTARMOUR;
- else
- upgrade = UP_NONE;
- }
- else
- {
- weapon = WP_NONE;
- upgrade = UP_NONE;
- }
-
- Com_sprintf( entry, sizeof( entry ),
- " %d %d %d %d %d %d", level.sortedClients[ i ], cl->ps.persistant[ PERS_SCORE ],
- ping, ( level.time - cl->pers.enterTime ) / 60000, weapon, upgrade );
-
- j = strlen( entry );
-
- if( stringlength + j >= 1024 )
- break;
-
- strcpy( string + stringlength, entry );
- stringlength += j;
- }
-
- trap_SendServerCommand( ent-g_entities, va( "scores %i %i%s",
- level.alienKills, level.humanKills, string ) );
-}
-
-
-/*
-==================
-ConcatArgs
-==================
-*/
-char *ConcatArgs( int start )
-{
- int i, c, tlen;
- static char line[ MAX_STRING_CHARS ];
- int len;
- char arg[ MAX_STRING_CHARS ];
-
- len = 0;
- c = trap_Argc( );
-
- for( i = start; i < c; i++ )
- {
- trap_Argv( i, arg, sizeof( arg ) );
- tlen = strlen( arg );
-
- if( len + tlen >= MAX_STRING_CHARS - 1 )
- break;
-
- memcpy( line + len, arg, tlen );
- len += tlen;
-
- if( len == MAX_STRING_CHARS - 1 )
- break;
-
- if( i != c - 1 )
- {
- line[ len ] = ' ';
- len++;
- }
- }
-
- line[ len ] = 0;
-
- return line;
-}
-
-/*
-==================
-ConcatArgsPrintable
-Duplicate of concatargs but enquotes things that need to be
-Used to log command arguments in a way that preserves user intended tokenizing
-==================
-*/
-char *ConcatArgsPrintable( int start )
-{
- int i, c, tlen;
- static char line[ MAX_STRING_CHARS ];
- int len;
- char arg[ MAX_STRING_CHARS + 2 ];
- char *printArg;
-
- len = 0;
- c = trap_Argc( );
-
- for( i = start; i < c; i++ )
- {
- printArg = arg;
- trap_Argv( i, arg, sizeof( arg ) );
- if( strchr( arg, ' ' ) )
- printArg = va( "\"%s\"", arg );
- tlen = strlen( printArg );
-
- if( len + tlen >= MAX_STRING_CHARS - 1 )
- break;
-
- memcpy( line + len, printArg, tlen );
- len += tlen;
-
- if( len == MAX_STRING_CHARS - 1 )
- break;
-
- if( i != c - 1 )
- {
- line[ len ] = ' ';
- len++;
- }
- }
-
- line[ len ] = 0;
-
- return line;
-}
-
-
-/*
-==================
-Cmd_Give_f
-
-Give items to a client
-==================
-*/
-void Cmd_Give_f( gentity_t *ent )
-{
- char *name;
- qboolean give_all = qfalse;
-
- if( trap_Argc( ) < 2 )
- {
- ADMP( "usage: give [what]\n" );
- ADMP( "usage: valid choices are: all, health, funds [amount], stamina, "
- "poison, gas, ammo\n" );
- return;
- }
-
- name = ConcatArgs( 1 );
- if( Q_stricmp( name, "all" ) == 0 )
- give_all = qtrue;
-
- if( give_all || Q_stricmp( name, "health" ) == 0 )
- {
- ent->health = ent->client->ps.stats[ STAT_MAX_HEALTH ];
- BG_AddUpgradeToInventory( UP_MEDKIT, ent->client->ps.stats );
- }
-
- if( give_all || Q_stricmpn( name, "funds", 5 ) == 0 )
- {
- float credits;
-
- if( give_all || trap_Argc( ) < 3 )
- credits = 30000.0f;
- else
- {
- credits = atof( name + 6 ) *
- ( ent->client->pers.teamSelection ==
- TEAM_ALIENS ? ALIEN_CREDITS_PER_KILL : 1.0f );
-
- // clamp credits manually, as G_AddCreditToClient() expects a short int
- if( credits > SHRT_MAX )
- credits = 30000.0f;
- else if( credits < SHRT_MIN )
- credits = -30000.0f;
- }
-
- G_AddCreditToClient( ent->client, (short)credits, qtrue );
- }
-
- if( give_all || Q_stricmp( name, "stamina" ) == 0 )
- ent->client->ps.stats[ STAT_STAMINA ] = STAMINA_MAX;
-
- if( Q_stricmp( name, "poison" ) == 0 )
- {
- if( ent->client->pers.teamSelection == TEAM_HUMANS )
- {
- ent->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
- ent->client->lastPoisonTime = level.time;
- ent->client->lastPoisonClient = ent;
- }
- else
- {
- ent->client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- ent->client->boostedTime = level.time;
- }
- }
-
- if( Q_stricmp( name, "gas" ) == 0 )
- {
- ent->client->ps.eFlags |= EF_POISONCLOUDED;
- ent->client->lastPoisonCloudedTime = level.time;
- trap_SendServerCommand( ent->client->ps.clientNum,
- "poisoncloud" );
- }
-
- if( give_all || Q_stricmp( name, "ammo" ) == 0 )
- {
- gclient_t *client = ent->client;
-
- if( client->ps.weapon != WP_ALEVEL3_UPG &&
- BG_Weapon( client->ps.weapon )->infiniteAmmo )
- return;
-
- client->ps.ammo = BG_Weapon( client->ps.weapon )->maxAmmo;
- client->ps.clips = BG_Weapon( client->ps.weapon )->maxClips;
-
- if( BG_Weapon( client->ps.weapon )->usesEnergy &&
- BG_InventoryContainsUpgrade( UP_BATTPACK, client->ps.stats ) )
- client->ps.ammo = (int)( (float)client->ps.ammo * BATTPACK_MODIFIER );
- }
-
- if( give_all || Q_stricmp( name, "fuel" ) == 0 )
- {
- if( BG_InventoryContainsUpgrade( UP_JETPACK, ent->client->ps.stats ) )
- ent->client->ps.stats[ STAT_FUEL ] = JETPACK_FUEL_FULL;
- }
-}
-
-
-/*
-==================
-Cmd_God_f
-
-Sets client to godmode
-
-argv(0) god
-==================
-*/
-void Cmd_God_f( gentity_t *ent )
-{
- char *msg;
-
- ent->flags ^= FL_GODMODE;
-
- if( !( ent->flags & FL_GODMODE ) )
- msg = "godmode OFF\n";
- else
- msg = "godmode ON\n";
-
- trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
-}
-
-
-/*
-==================
-Cmd_Notarget_f
-
-Sets client to notarget
-
-argv(0) notarget
-==================
-*/
-void Cmd_Notarget_f( gentity_t *ent )
-{
- char *msg;
-
- ent->flags ^= FL_NOTARGET;
-
- if( !( ent->flags & FL_NOTARGET ) )
- msg = "notarget OFF\n";
- else
- msg = "notarget ON\n";
-
- trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
-}
-
-
-/*
-==================
-Cmd_Noclip_f
-
-argv(0) noclip
-==================
-*/
-void Cmd_Noclip_f( gentity_t *ent )
-{
- char *msg;
-
- if( ent->client->noclip )
- msg = "noclip OFF\n";
- else
- msg = "noclip ON\n";
-
- ent->client->noclip = !ent->client->noclip;
-
- trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) );
-}
-
-
-/*
-==================
-Cmd_LevelShot_f
-
-This is just to help generate the level pictures
-for the menus. It goes to the intermission immediately
-and sends over a command to the client to resize the view,
-hide the scoreboard, and take a special screenshot
-==================
-*/
-void Cmd_LevelShot_f( gentity_t *ent )
-{
- BeginIntermission( );
- trap_SendServerCommand( ent - g_entities, "clientLevelShot" );
-}
-
-/*
-=================
-Cmd_Kill_f
-=================
-*/
-void Cmd_Kill_f( gentity_t *ent )
-{
- if( g_cheats.integer )
- {
- ent->flags &= ~FL_GODMODE;
- ent->client->ps.stats[ STAT_HEALTH ] = ent->health = 0;
- player_die( ent, ent, ent, 100000, MOD_SUICIDE );
- }
- else
- {
- if( ent->suicideTime == 0 )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You will suicide in 20 seconds\n\"" );
- ent->suicideTime = level.time + 20000;
- }
- else if( ent->suicideTime > level.time )
- {
- trap_SendServerCommand( ent-g_entities, "print \"Suicide cancelled\n\"" );
- ent->suicideTime = 0;
- }
- }
-}
-
-/*
-=================
-Cmd_Team_f
-=================
-*/
-void Cmd_Team_f( gentity_t *ent )
-{
- team_t team;
- team_t oldteam = ent->client->pers.teamSelection;
- char s[ MAX_TOKEN_CHARS ];
- qboolean force = G_admin_permission( ent, ADMF_FORCETEAMCHANGE );
- int aliens = level.numAlienClients;
- int humans = level.numHumanClients;
-
- if( oldteam == TEAM_ALIENS )
- aliens--;
- else if( oldteam == TEAM_HUMANS )
- humans--;
-
- // stop team join spam
- if( ent->client->pers.teamChangeTime &&
- level.time - ent->client->pers.teamChangeTime < 1000 )
- return;
-
- // stop switching teams for gameplay exploit reasons by enforcing a long
- // wait before they can come back
- if( !force && !g_cheats.integer && ent->client->pers.aliveSeconds &&
- level.time - ent->client->pers.teamChangeTime < 30000 )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"You must wait another %d seconds before changing teams again\n\"",
- (int) ( ( 30000 - ( level.time - ent->client->pers.teamChangeTime ) ) / 1000.f ) ) );
- return;
- }
-
- // disallow joining teams during warmup
- if( g_doWarmup.integer && ( ( level.warmupTime - level.time ) / 1000 ) > 0 )
- {
- G_TriggerMenu( ent - g_entities, MN_WARMUP );
- return;
- }
-
- trap_Argv( 1, s, sizeof( s ) );
-
- if( !s[ 0 ] )
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"team: %s\n\"",
- BG_TeamName( oldteam ) ) );
- return;
- }
-
- if( !Q_stricmp( s, "auto" ) )
- {
- if( level.humanTeamLocked && level.alienTeamLocked )
- team = TEAM_NONE;
- else if( level.humanTeamLocked || humans > aliens )
- team = TEAM_ALIENS;
-
- else if( level.alienTeamLocked || aliens > humans )
- team = TEAM_HUMANS;
- else
- team = TEAM_ALIENS + rand( ) / ( RAND_MAX / 2 + 1 );
- }
- else switch( G_TeamFromString( s ) )
- {
- case TEAM_NONE:
- team = TEAM_NONE;
- break;
-
- case TEAM_ALIENS:
- if( level.alienTeamLocked )
- {
- G_TriggerMenu( ent - g_entities, MN_A_TEAMLOCKED );
- return;
- }
- else if( level.humanTeamLocked )
- force = qtrue;
-
- if( !force && g_teamForceBalance.integer && aliens > humans )
- {
- G_TriggerMenu( ent - g_entities, MN_A_TEAMFULL );
- return;
- }
-
- team = TEAM_ALIENS;
- break;
-
- case TEAM_HUMANS:
- if( level.humanTeamLocked )
- {
- G_TriggerMenu( ent - g_entities, MN_H_TEAMLOCKED );
- return;
- }
- else if( level.alienTeamLocked )
- force = qtrue;
-
- if( !force && g_teamForceBalance.integer && humans > aliens )
- {
- G_TriggerMenu( ent - g_entities, MN_H_TEAMFULL );
- return;
- }
-
- team = TEAM_HUMANS;
- break;
-
- default:
- trap_SendServerCommand( ent-g_entities,
- va( "print \"Unknown team: %s\n\"", s ) );
- return;
- }
-
- // stop team join spam
- if( oldteam == team )
- return;
-
- if( team != TEAM_NONE && g_maxGameClients.integer &&
- level.numPlayingClients >= g_maxGameClients.integer )
- {
- G_TriggerMenu( ent - g_entities, MN_PLAYERLIMIT );
- return;
- }
-
- // Apply the change
- G_ChangeTeam( ent, team );
-}
-
-/*
-==================
-G_CensorString
-==================
-*/
-static char censors[ 20000 ];
-static int numcensors;
-
-void G_LoadCensors( void )
-{
- char *text_p, *token;
- char text[ 20000 ];
- char *term;
- int len;
- fileHandle_t f;
-
- numcensors = 0;
-
- if( !g_censorship.string[ 0 ] )
- return;
-
- len = trap_FS_FOpenFile( g_censorship.string, &f, FS_READ );
- if( len < 0 )
- {
- Com_Printf( S_COLOR_RED "ERROR: Censors file %s doesn't exist\n",
- g_censorship.string );
- return;
- }
- if( len == 0 || len >= sizeof( text ) - 1 )
- {
- trap_FS_FCloseFile( f );
- Com_Printf( S_COLOR_RED "ERROR: Censors file %s is %s\n",
- g_censorship.string, len == 0 ? "empty" : "too long" );
- return;
- }
- trap_FS_Read( text, len, f );
- trap_FS_FCloseFile( f );
- text[ len ] = 0;
-
- term = censors;
-
- text_p = text;
- while( 1 )
- {
- token = COM_Parse( &text_p );
- if( !*token || sizeof( censors ) - ( term - censors ) < 4 )
- break;
- Q_strncpyz( term, token, sizeof( censors ) - ( term - censors ) );
- Q_strlwr( term );
- term += strlen( term ) + 1;
- if( sizeof( censors ) - ( term - censors ) == 0 )
- break;
- token = COM_ParseExt( &text_p, qfalse );
- Q_strncpyz( term, token, sizeof( censors ) - ( term - censors ) );
- term += strlen( term ) + 1;
- numcensors++;
- }
- G_Printf( "Parsed %d string replacements\n", numcensors );
-}
-
-void G_CensorString( char *out, const char *in, int len, gentity_t *ent )
-{
- const char *s, *m;
- int i;
-
- if( !numcensors || G_admin_permission( ent, ADMF_NOCENSORFLOOD) )
- {
- Q_strncpyz( out, in, len );
- return;
- }
-
- len--;
- while( *in )
- {
- if( Q_IsColorString( in ) )
- {
- if( len < 2 )
- break;
- *out++ = *in++;
- *out++ = *in++;
- len -= 2;
- continue;
- }
- if( !isalnum( *in ) )
- {
- if( len < 1 )
- break;
- *out++ = *in++;
- len--;
- continue;
- }
- m = censors;
- for( i = 0; i < numcensors; i++, m++ )
- {
- s = in;
- while( *s && *m )
- {
- if( Q_IsColorString( s ) )
- {
- s += 2;
- continue;
- }
- if( !isalnum( *s ) )
- {
- s++;
- continue;
- }
- if( tolower( *s ) != *m )
- break;
- s++;
- m++;
- }
- // match
- if( !*m )
- {
- in = s;
- m++;
- while( *m )
- {
- if( len < 1 )
- break;
- *out++ = *m++;
- len--;
- }
- break;
- }
- else
- {
- while( *m )
- m++;
- m++;
- while( *m )
- m++;
- }
- }
- if( len < 1 )
- break;
- // no match
- if( i == numcensors )
- {
- *out++ = *in++;
- len--;
- }
- }
- *out = 0;
-}
-
-/*
-==================
-G_Say
-==================
-*/
-static qboolean G_SayTo( gentity_t *ent, gentity_t *other, saymode_t mode, const char *message )
-{
- if( !other )
- return qfalse;
-
- if( !other->inuse )
- return qfalse;
-
- if( !other->client )
- return qfalse;
-
- if( other->client->pers.connected != CON_CONNECTED )
- return qfalse;
-
- if( ( ent && !OnSameTeam( ent, other ) ) &&
- ( mode == SAY_TEAM || mode == SAY_AREA || mode == SAY_TPRIVMSG ) )
- {
- if( other->client->pers.teamSelection != TEAM_NONE )
- return qfalse;
-
- // specs with ADMF_SPEC_ALLCHAT flag can see team chat
- if( !G_admin_permission( other, ADMF_SPEC_ALLCHAT ) && mode != SAY_TPRIVMSG )
- return qfalse;
- }
-
- trap_SendServerCommand( other-g_entities, va( "chat %d %d \"%s\"",
- ent ? ent-g_entities : -1,
- mode,
- message ) );
-
- return qtrue;
-}
-
-void G_Say( gentity_t *ent, saymode_t mode, const char *chatText )
-{
- int j;
- gentity_t *other;
- // don't let text be too long for malicious reasons
- char text[ MAX_SAY_TEXT ];
-
- // check if blocked by g_specChat 0
- if( ( !g_specChat.integer ) && ( mode != SAY_TEAM ) &&
- ( ent ) && ( ent->client->pers.teamSelection == TEAM_NONE ) &&
- ( !G_admin_permission( ent, ADMF_NOCENSORFLOOD ) ) )
- {
- trap_SendServerCommand( ent-g_entities, "print \"say: Global chatting for "
- "spectators has been disabled. You may only use team chat.\n\"" );
- mode = SAY_TEAM;
- }
-
- switch( mode )
- {
- case SAY_ALL:
- G_LogPrintf( "Say: %d \"%s" S_COLOR_WHITE "\": " S_COLOR_GREEN "%s\n",
- ( ent ) ? ent - g_entities : -1,
- ( ent ) ? ent->client->pers.netname : "console", chatText );
- break;
- case SAY_TEAM:
- // console say_team is handled in g_svscmds, not here
- if( !ent || !ent->client )
- Com_Error( ERR_FATAL, "SAY_TEAM by non-client entity\n" );
- G_LogPrintf( "SayTeam: %d \"%s" S_COLOR_WHITE "\": " S_COLOR_CYAN "%s\n",
- ent - g_entities, ent->client->pers.netname, chatText );
- break;
- case SAY_RAW:
- if( ent )
- Com_Error( ERR_FATAL, "SAY_RAW by client entity\n" );
- G_LogPrintf( "Chat: -1 \"console\": %s\n", chatText );
- default:
- break;
- }
-
- G_CensorString( text, chatText, sizeof( text ), ent );
-
- // send it to all the apropriate clients
- for( j = 0; j < level.maxclients; j++ )
- {
- other = &g_entities[ j ];
- G_SayTo( ent, other, mode, text );
- }
-}
-
-/*
-==================
-Cmd_SayArea_f
-==================
-*/
-static void Cmd_SayArea_f( gentity_t *ent )
-{
- int entityList[ MAX_GENTITIES ];
- int num, i;
- vec3_t range = { 1000.0f, 1000.0f, 1000.0f };
- vec3_t mins, maxs;
- char *msg;
-
- if( trap_Argc( ) < 2 )
- {
- ADMP( "usage: say_area [message]\n" );
- return;
- }
-
- msg = ConcatArgs( 1 );
-
- for(i = 0; i < 3; i++ )
- range[ i ] = g_sayAreaRange.value;
-
- G_LogPrintf( "SayArea: %d \"%s" S_COLOR_WHITE "\": " S_COLOR_BLUE "%s\n",
- ent - g_entities, ent->client->pers.netname, msg );
-
- VectorAdd( ent->s.origin, range, maxs );
- VectorSubtract( ent->s.origin, range, mins );
-
- num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- for( i = 0; i < num; i++ )
- G_SayTo( ent, &g_entities[ entityList[ i ] ], SAY_AREA, msg );
-
- //Send to ADMF_SPEC_ALLCHAT candidates
- for( i = 0; i < level.maxclients; i++ )
- {
- if( g_entities[ i ].client->pers.teamSelection == TEAM_NONE &&
- G_admin_permission( &g_entities[ i ], ADMF_SPEC_ALLCHAT ) )
- {
- G_SayTo( ent, &g_entities[ i ], SAY_AREA, msg );
- }
- }
-}
-
-
-/*
-==================
-Cmd_Say_f
-==================
-*/
-static void Cmd_Say_f( gentity_t *ent )
-{
- char *p;
- char cmd[ MAX_TOKEN_CHARS ];
- saymode_t mode = SAY_ALL;
-
- if( trap_Argc( ) < 2 )
- return;
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- if( Q_stricmp( cmd, "say_team" ) == 0 )
- mode = SAY_TEAM;
-
- p = ConcatArgs( 1 );
-
- G_Say( ent, mode, p );
-}
-
-/*
-==================
-Cmd_VSay_f
-==================
-*/
-void Cmd_VSay_f( gentity_t *ent )
-{
- char arg[MAX_TOKEN_CHARS];
- char text[ MAX_TOKEN_CHARS ];
- voiceChannel_t vchan;
- voice_t *voice;
- voiceCmd_t *cmd;
- voiceTrack_t *track;
- int cmdNum = 0;
- int trackNum = 0;
- char voiceName[ MAX_VOICE_NAME_LEN ] = {"default"};
- char voiceCmd[ MAX_VOICE_CMD_LEN ] = {""};
- char vsay[ 12 ] = {""};
- weapon_t weapon;
-
- if( !ent || !ent->client )
- Com_Error( ERR_FATAL, "Cmd_VSay_f() called by non-client entity\n" );
-
- trap_Argv( 0, arg, sizeof( arg ) );
- if( trap_Argc( ) < 2 )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"usage: %s command [text] \n\"", arg ) );
- return;
- }
- if( !level.voices )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: voice system is not installed on this server\n\"", arg ) );
- return;
- }
- if( !g_voiceChats.integer )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: voice system administratively disabled on this server\n\"",
- arg ) );
- return;
- }
- if( !Q_stricmp( arg, "vsay" ) )
- vchan = VOICE_CHAN_ALL;
- else if( !Q_stricmp( arg, "vsay_team" ) )
- vchan = VOICE_CHAN_TEAM;
- else if( !Q_stricmp( arg, "vsay_local" ) )
- vchan = VOICE_CHAN_LOCAL;
- else
- return;
- Q_strncpyz( vsay, arg, sizeof( vsay ) );
-
- if( ent->client->pers.voice[ 0 ] )
- Q_strncpyz( voiceName, ent->client->pers.voice, sizeof( voiceName ) );
- voice = BG_VoiceByName( level.voices, voiceName );
- if( !voice )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: voice '%s' not found\n\"", vsay, voiceName ) );
- return;
- }
-
- trap_Argv( 1, voiceCmd, sizeof( voiceCmd ) ) ;
- cmd = BG_VoiceCmdFind( voice->cmds, voiceCmd, &cmdNum );
- if( !cmd )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: command '%s' not found in voice '%s'\n\"",
- vsay, voiceCmd, voiceName ) );
- return;
- }
-
- // filter non-spec humans by their primary weapon as well
- weapon = WP_NONE;
- if( ent->client->sess.spectatorState == SPECTATOR_NOT )
- {
- weapon = BG_PrimaryWeapon( ent->client->ps.stats );
- }
-
- track = BG_VoiceTrackFind( cmd->tracks, ent->client->pers.teamSelection,
- ent->client->pers.classSelection, weapon, (int)ent->client->voiceEnthusiasm,
- &trackNum );
- if( !track )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: no available track for command '%s', team %d, "
- "class %d, weapon %d, and enthusiasm %d in voice '%s'\n\"",
- vsay, voiceCmd, ent->client->pers.teamSelection,
- ent->client->pers.classSelection, weapon,
- (int)ent->client->voiceEnthusiasm, voiceName ) );
- return;
- }
-
- if( !Q_stricmp( ent->client->lastVoiceCmd, cmd->cmd ) )
- ent->client->voiceEnthusiasm++;
-
- Q_strncpyz( ent->client->lastVoiceCmd, cmd->cmd,
- sizeof( ent->client->lastVoiceCmd ) );
-
- // optional user supplied text
- trap_Argv( 2, arg, sizeof( arg ) );
- G_CensorString( text, arg, sizeof( text ), ent );
-
- switch( vchan )
- {
- case VOICE_CHAN_ALL:
- case VOICE_CHAN_LOCAL:
- trap_SendServerCommand( -1, va(
- "voice %d %d %d %d \"%s\"\n",
- ent-g_entities, vchan, cmdNum, trackNum, text ) );
- break;
- case VOICE_CHAN_TEAM:
- G_TeamCommand( ent->client->pers.teamSelection, va(
- "voice %d %d %d %d \"%s\"\n",
- ent-g_entities, vchan, cmdNum, trackNum, text ) );
- break;
- default:
- break;
- }
-}
-
-/*
-==================
-Cmd_Where_f
-==================
-*/
-void Cmd_Where_f( gentity_t *ent )
-{
- if( !ent->client )
- return;
- trap_SendServerCommand( ent - g_entities,
- va( "print \"origin: %f %f %f\n\"",
- ent->s.origin[ 0 ], ent->s.origin[ 1 ],
- ent->s.origin[ 2 ] ) );
-}
-
-/*
-==================
-Cmd_CallVote_f
-==================
-*/
-void Cmd_CallVote_f( gentity_t *ent )
-{
- char cmd[ MAX_TOKEN_CHARS ],
- vote[ MAX_TOKEN_CHARS ],
- arg[ MAX_TOKEN_CHARS ];
- char name[ MAX_NAME_LENGTH ] = "";
- char caller[ MAX_NAME_LENGTH ] = "";
- char reason[ MAX_TOKEN_CHARS ];
- char *creason;
- int clientNum = -1;
- int id = -1;
- team_t team;
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- trap_Argv( 1, vote, sizeof( vote ) );
- trap_Argv( 2, arg, sizeof( arg ) );
- creason = ConcatArgs( 3 );
- G_DecolorString( creason, reason, sizeof( reason ) );
-
- if( !Q_stricmp( cmd, "callteamvote" ) )
- team = ent->client->pers.teamSelection;
- else
- team = TEAM_NONE;
-
- if( !g_allowVote.integer )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: voting not allowed here\n\"", cmd ) );
- return;
- }
-
- if( level.voteTime[ team ] )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: a vote is already in progress\n\"", cmd ) );
- return;
- }
-
- if( level.voteExecuteTime[ team ] )
- G_ExecuteVote( team );
-
- level.voteDelay[ team ] = 0;
- level.voteThreshold[ team ] = 50;
-
- if( g_voteLimit.integer > 0 &&
- ent->client->pers.namelog->voteCount >= g_voteLimit.integer &&
- !G_admin_permission( ent, ADMF_NO_VOTE_LIMIT ) )
- {
- trap_SendServerCommand( ent-g_entities, va(
- "print \"%s: you have already called the maximum number of votes (%d)\n\"",
- cmd, g_voteLimit.integer ) );
- return;
- }
-
- // kick, mute, unmute, denybuild, allowbuild
- if( !Q_stricmp( vote, "kick" ) ||
- !Q_stricmp( vote, "mute" ) || !Q_stricmp( vote, "unmute" ) ||
- !Q_stricmp( vote, "denybuild" ) || !Q_stricmp( vote, "allowbuild" ) )
- {
- char err[ MAX_STRING_CHARS ];
-
- if( !arg[ 0 ] )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: no target\n\"", cmd ) );
- return;
- }
-
- // with a little extra work only players from the right team are considered
- clientNum = G_ClientNumberFromString( arg, err, sizeof( err ) );
-
- if( clientNum == -1 )
- {
- ADMP( va( "%s: %s", cmd, err ) );
- return;
- }
-
- G_DecolorString( level.clients[ clientNum ].pers.netname, name, sizeof( name ) );
- id = level.clients[ clientNum ].pers.namelog->id;
-
- if( !Q_stricmp( vote, "kick" ) || !Q_stricmp( vote, "mute" ) ||
- !Q_stricmp( vote, "denybuild" ) )
- {
- if( G_admin_permission( g_entities + clientNum, ADMF_IMMUNITY ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: admin is immune\n\"", cmd ) );
-
- G_AdminMessage( NULL, va( S_COLOR_WHITE "%s" S_COLOR_YELLOW " attempted %s %s"
- " on immune admin " S_COLOR_WHITE "%s" S_COLOR_YELLOW
- " for: %s",
- ent->client->pers.netname, cmd, vote,
- g_entities[ clientNum ].client->pers.netname,
- reason[ 0 ] ? reason : "no reason" ) );
- return;
- }
-
- if( team != TEAM_NONE &&
- ( ent->client->pers.teamSelection !=
- level.clients[ clientNum ].pers.teamSelection ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: player is not on your team\n\"", cmd ) );
- return;
- }
-
- if( !reason[ 0 ] && !G_admin_permission( ent, ADMF_UNACCOUNTABLE ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: You must provide a reason\n\"", cmd ) );
- return;
- }
- }
- }
-
- if( !Q_stricmp( vote, "kick" ) )
- {
- if( level.clients[ clientNum ].pers.localClient )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: admin is immune\n\"", cmd ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "ban %s \"1s%s\" vote kick (%s)", level.clients[ clientNum ].pers.ip.str,
- g_adminTempBan.string, reason );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ), "Kick player '%s'", name );
- if( reason[ 0 ] )
- {
- Q_strcat( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ), va( " for '%s'", reason ) );
- }
- }
- else if( team == TEAM_NONE )
- {
- if( !Q_stricmp( vote, "mute" ) )
- {
- if( level.clients[ clientNum ].pers.namelog->muted )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: player is already muted\n\"", cmd ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "mute %d", id );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Mute player '%s'", name );
- if( reason[ 0 ] )
- {
- Q_strcat( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ), va( " for '%s'", reason ) );
- }
- }
- else if( !Q_stricmp( vote, "unmute" ) )
- {
- if( !level.clients[ clientNum ].pers.namelog->muted )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: player is not currently muted\n\"", cmd ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "unmute %d", id );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Unmute player '%s'", name );
- }
- else if( !Q_stricmp( vote, "map_restart" ) )
- {
- strcpy( level.voteString[ team ], vote );
- strcpy( level.voteDisplayString[ team ], "Restart current map" );
- // map_restart comes with a default delay
- }
- else if( !Q_stricmp( vote, "map" ) )
- {
- if( !G_MapExists( arg ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: 'maps/%s.bsp' could not be found on the server\n\"",
- cmd, arg ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString ),
- "%s \"%s\"", vote, arg );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Change to map '%s'", arg );
- level.voteDelay[ team ] = 3000;
- }
- else if( !Q_stricmp( vote, "nextmap" ) )
- {
- if( G_MapExists( g_nextMap.string ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: the next map is already set to '%s'\n\"",
- cmd, g_nextMap.string ) );
- return;
- }
-
- if( !G_MapExists( arg ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: 'maps/%s.bsp' could not be found on the server\n\"",
- cmd, arg ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "set g_nextMap \"%s\"", arg );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Set the next map to '%s'", arg );
- }
- else if( !Q_stricmp( vote, "draw" ) )
- {
- strcpy( level.voteString[ team ], "evacuation" );
- strcpy( level.voteDisplayString[ team ], "End match in a draw" );
- level.voteDelay[ team ] = 3000;
- }
- else if( !Q_stricmp( vote, "sudden_death" ) )
- {
- if(!g_suddenDeathVotePercent.integer)
- {
- trap_SendServerCommand( ent-g_entities,
- "print \"Sudden Death votes have been disabled\n\"" );
- return;
- }
- if( G_TimeTilSuddenDeath( ) <= 0 )
- {
- trap_SendServerCommand( ent - g_entities,
- va( "print \"callvote: Sudden Death has already begun\n\"") );
- return;
- }
- if( level.suddenDeathBeginTime > 0 &&
- G_TimeTilSuddenDeath() <= g_suddenDeathVoteDelay.integer * 1000 )
- {
- trap_SendServerCommand( ent - g_entities,
- va( "print \"callvote: Sudden Death is imminent\n\"") );
- return;
- }
- level.voteThreshold[ team ] = g_suddenDeathVotePercent.integer;
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "suddendeath %d", g_suddenDeathVoteDelay.integer );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Begin sudden death in %d seconds",
- g_suddenDeathVoteDelay.integer );
- }
- else
- {
- trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" );
- trap_SendServerCommand( ent-g_entities, "print \"Valid vote commands are: "
- "map, nextmap, map_restart, draw, sudden_death, kick, mute and unmute\n" );
- return;
- }
- }
- else if( !Q_stricmp( vote, "denybuild" ) )
- {
- if( level.clients[ clientNum ].pers.namelog->denyBuild )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: player already lost building rights\n\"", cmd ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "denybuild %d", id );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Take away building rights from '%s'", name );
- if( reason[ 0 ] )
- {
- Q_strcat( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ), va( " for '%s'", reason ) );
- }
- }
- else if( !Q_stricmp( vote, "allowbuild" ) )
- {
- if( !level.clients[ clientNum ].pers.namelog->denyBuild )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: player already has building rights\n\"", cmd ) );
- return;
- }
-
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "allowbuild %d", id );
- Com_sprintf( level.voteDisplayString[ team ],
- sizeof( level.voteDisplayString[ team ] ),
- "Allow '%s' to build", name );
- }
- else if( !Q_stricmp( vote, "admitdefeat" ) )
- {
- Com_sprintf( level.voteString[ team ], sizeof( level.voteString[ team ] ),
- "admitdefeat %d", team );
- strcpy( level.voteDisplayString[ team ], "Admit Defeat" );
- level.voteDelay[ team ] = 3000;
- }
- else
- {
- trap_SendServerCommand( ent-g_entities, "print \"Invalid vote string\n\"" );
- trap_SendServerCommand( ent-g_entities,
- "print \"Valid team vote commands are: "
- "kick, denybuild, allowbuild and admitdefeat\n\"" );
- return;
- }
-
- G_LogPrintf( "%s: %d \"%s" S_COLOR_WHITE "\": %s\n",
- team == TEAM_NONE ? "CallVote" : "CallTeamVote",
- ent - g_entities, ent->client->pers.netname, level.voteString[ team ] );
-
- if( team == TEAM_NONE )
- {
- trap_SendServerCommand( -1, va( "print \"%s" S_COLOR_WHITE
- " called a vote: %s\n\"", ent->client->pers.netname,
- level.voteDisplayString[ team ] ) );
- }
- else
- {
- int i;
-
- for( i = 0 ; i < level.maxclients ; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_CONNECTED )
- {
- if( level.clients[ i ].pers.teamSelection == team ||
- ( level.clients[ i ].pers.teamSelection == TEAM_NONE &&
- G_admin_permission( &g_entities[ i ], ADMF_SPEC_ALLCHAT ) ) )
- {
- trap_SendServerCommand( i, va( "print \"%s" S_COLOR_WHITE
- " called a team vote: %s\n\"", ent->client->pers.netname,
- level.voteDisplayString[ team ] ) );
- }
- else if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) )
- {
- trap_SendServerCommand( i, va( "chat -1 %d \"" S_COLOR_YELLOW "%s"
- S_COLOR_YELLOW " called a team vote (%ss): %s\"", SAY_ADMINS,
- ent->client->pers.netname, BG_TeamName( team ),
- level.voteDisplayString[ team ] ) );
- }
- }
- }
- }
-
- G_DecolorString( ent->client->pers.netname, caller, sizeof( caller ) );
-
- level.voteTime[ team ] = level.time;
- trap_SetConfigstring( CS_VOTE_TIME + team,
- va( "%d", level.voteTime[ team ] ) );
- trap_SetConfigstring( CS_VOTE_STRING + team,
- level.voteDisplayString[ team ] );
- trap_SetConfigstring( CS_VOTE_CALLER + team,
- caller );
-
- ent->client->pers.namelog->voteCount++;
- ent->client->pers.vote |= 1 << team;
- G_Vote( ent, team, qtrue );
-}
-
-/*
-==================
-Cmd_Vote_f
-==================
-*/
-void Cmd_Vote_f( gentity_t *ent )
-{
- char cmd[ MAX_TOKEN_CHARS ], vote[ MAX_TOKEN_CHARS ];
- team_t team = ent->client->pers.teamSelection;
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- if( Q_stricmp( cmd, "teamvote" ) )
- team = TEAM_NONE;
-
- if( !level.voteTime[ team ] )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: no vote in progress\n\"", cmd ) );
- return;
- }
-
- if( ent->client->pers.voted & ( 1 << team ) )
- {
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: vote already cast\n\"", cmd ) );
- return;
- }
-
- trap_SendServerCommand( ent-g_entities,
- va( "print \"%s: vote cast\n\"", cmd ) );
-
- trap_Argv( 1, vote, sizeof( vote ) );
- if( vote[ 0 ] == 'y' )
- ent->client->pers.vote |= 1 << team;
- else
- ent->client->pers.vote &= ~( 1 << team );
- G_Vote( ent, team, qtrue );
-}
-
-
-/*
-=================
-Cmd_SetViewpos_f
-=================
-*/
-void Cmd_SetViewpos_f( gentity_t *ent )
-{
- vec3_t origin, angles;
- char buffer[ MAX_TOKEN_CHARS ];
- int i;
-
- if( trap_Argc( ) != 5 )
- {
- trap_SendServerCommand( ent-g_entities, "print \"usage: setviewpos x y z yaw\n\"" );
- return;
- }
-
- VectorClear( angles );
-
- for( i = 0; i < 3; i++ )
- {
- trap_Argv( i + 1, buffer, sizeof( buffer ) );
- origin[ i ] = atof( buffer );
- }
-
- trap_Argv( 4, buffer, sizeof( buffer ) );
- angles[ YAW ] = atof( buffer );
-
- TeleportPlayer( ent, origin, angles );
-}
-
-#define AS_OVER_RT3 ((ALIENSENSE_RANGE*0.5f)/M_ROOT3)
-
-static qboolean G_RoomForClassChange( gentity_t *ent, class_t class,
- vec3_t newOrigin )
-{
- vec3_t fromMins, fromMaxs;
- vec3_t toMins, toMaxs;
- vec3_t temp;
- trace_t tr;
- float nudgeHeight;
- float maxHorizGrowth;
- class_t oldClass = ent->client->ps.stats[ STAT_CLASS ];
-
- BG_ClassBoundingBox( oldClass, fromMins, fromMaxs, NULL, NULL, NULL );
- BG_ClassBoundingBox( class, toMins, toMaxs, NULL, NULL, NULL );
-
- VectorCopy( ent->s.origin, newOrigin );
-
- // find max x/y diff
- maxHorizGrowth = toMaxs[ 0 ] - fromMaxs[ 0 ];
- if( toMaxs[ 1 ] - fromMaxs[ 1 ] > maxHorizGrowth )
- maxHorizGrowth = toMaxs[ 1 ] - fromMaxs[ 1 ];
- if( toMins[ 0 ] - fromMins[ 0 ] > -maxHorizGrowth )
- maxHorizGrowth = -( toMins[ 0 ] - fromMins[ 0 ] );
- if( toMins[ 1 ] - fromMins[ 1 ] > -maxHorizGrowth )
- maxHorizGrowth = -( toMins[ 1 ] - fromMins[ 1 ] );
-
- if( maxHorizGrowth > 0.0f )
- {
- // test by moving the player up the max required on a 60 degree slope
- nudgeHeight = maxHorizGrowth * 2.0f;
- }
- else
- {
- // player is shrinking, so there's no need to nudge them upwards
- nudgeHeight = 0.0f;
- }
-
- // find what the new origin would be on a level surface
- newOrigin[ 2 ] -= toMins[ 2 ] - fromMins[ 2 ];
-
- //compute a place up in the air to start the real trace
- VectorCopy( newOrigin, temp );
- temp[ 2 ] += nudgeHeight;
- trap_Trace( &tr, newOrigin, toMins, toMaxs, temp, ent->s.number, MASK_PLAYERSOLID );
-
- //trace down to the ground so that we can evolve on slopes
- VectorCopy( newOrigin, temp );
- temp[ 2 ] += ( nudgeHeight * tr.fraction );
- trap_Trace( &tr, temp, toMins, toMaxs, newOrigin, ent->s.number, MASK_PLAYERSOLID );
- VectorCopy( tr.endpos, newOrigin );
-
- //make REALLY sure
- trap_Trace( &tr, newOrigin, toMins, toMaxs, newOrigin,
- ent->s.number, MASK_PLAYERSOLID );
-
- //check there is room to evolve
- return ( !tr.startsolid && tr.fraction == 1.0f );
-}
-
-/*
-=================
-Cmd_Class_f
-=================
-*/
-void Cmd_Class_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- int clientNum;
- int i;
- vec3_t infestOrigin;
- class_t currentClass = ent->client->pers.classSelection;
- class_t newClass;
- int entityList[ MAX_GENTITIES ];
- vec3_t range = { AS_OVER_RT3, AS_OVER_RT3, AS_OVER_RT3 };
- vec3_t mins, maxs;
- int num;
- gentity_t *other;
- int oldBoostTime = -1;
- vec3_t oldVel;
-
- clientNum = ent->client - level.clients;
- trap_Argv( 1, s, sizeof( s ) );
- newClass = BG_ClassByName( s )->number;
-
- if( ent->client->sess.spectatorState != SPECTATOR_NOT )
- {
- if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
- G_StopFollowing( ent );
- if( ent->client->pers.teamSelection == TEAM_ALIENS )
- {
- if( newClass != PCL_ALIEN_BUILDER0 &&
- newClass != PCL_ALIEN_BUILDER0_UPG &&
- newClass != PCL_ALIEN_LEVEL0 )
- {
- G_TriggerMenuArgs( ent->client->ps.clientNum, MN_A_CLASSNOTSPAWN, newClass );
- return;
- }
-
- if( !BG_ClassIsAllowed( newClass ) )
- {
- G_TriggerMenuArgs( ent->client->ps.clientNum, MN_A_CLASSNOTALLOWED, newClass );
- return;
- }
-
- if( !BG_ClassAllowedInStage( newClass, g_alienStage.integer ) )
- {
- G_TriggerMenuArgs( ent->client->ps.clientNum, MN_A_CLASSNOTATSTAGE, newClass );
- return;
- }
-
- // spawn from an egg
- if( G_PushSpawnQueue( &level.alienSpawnQueue, clientNum ) )
- {
- ent->client->pers.classSelection = newClass;
- ent->client->ps.stats[ STAT_CLASS ] = newClass;
- }
- }
- else if( ent->client->pers.teamSelection == TEAM_HUMANS )
- {
- //set the item to spawn with
- if( !Q_stricmp( s, BG_Weapon( WP_MACHINEGUN )->name ) &&
- BG_WeaponIsAllowed( WP_MACHINEGUN ) )
- {
- ent->client->pers.humanItemSelection = WP_MACHINEGUN;
- }
- else if( !Q_stricmp( s, BG_Weapon( WP_HBUILD )->name ) &&
- BG_WeaponIsAllowed( WP_HBUILD ) )
- {
- ent->client->pers.humanItemSelection = WP_HBUILD;
- }
- else
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_UNKNOWNSPAWNITEM );
- return;
- }
- // spawn from a telenode
- if( G_PushSpawnQueue( &level.humanSpawnQueue, clientNum ) )
- {
- ent->client->pers.classSelection = PCL_HUMAN;
- ent->client->ps.stats[ STAT_CLASS ] = PCL_HUMAN;
- }
- }
- return;
- }
-
- if( ent->health <= 0 )
- return;
-
- if( ent->client->pers.teamSelection == TEAM_ALIENS )
- {
- if( newClass == PCL_NONE )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_A_UNKNOWNCLASS );
- return;
- }
-
- //if we are not currently spectating, we are attempting evolution
- if( ent->client->pers.classSelection != PCL_NONE )
- {
- int cost;
-
- //check that we have an overmind
- if( !G_Overmind( ) )
- {
- G_TriggerMenu( clientNum, MN_A_NOOVMND_EVOLVE );
- return;
- }
-
- //check there are no humans nearby
- VectorAdd( ent->client->ps.origin, range, maxs );
- VectorSubtract( ent->client->ps.origin, range, mins );
-
- num = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
- for( i = 0; i < num; i++ )
- {
- other = &g_entities[ entityList[ i ] ];
-
- if( ( other->client && other->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) ||
- ( other->s.eType == ET_BUILDABLE && other->buildableTeam == TEAM_HUMANS &&
- other->powered ) )
- {
- trace_t tr;
-
- trap_Trace( &tr, ent->client->ps.origin, NULL, NULL, other->s.origin, ent->client->ps.clientNum, MASK_SOLID );
-
- if( tr.fraction > 0.99f )
- {
- G_TriggerMenu( clientNum, MN_A_TOOCLOSE );
- return;
- }
- }
- }
-
- //check that we are not wallwalking
- if( ent->client->ps.eFlags & EF_WALLCLIMB )
- {
- G_TriggerMenu( clientNum, MN_A_EVOLVEWALLWALK );
- return;
- }
-
- //guard against selling the HBUILD weapons exploit
- if( ent->client->sess.spectatorState == SPECTATOR_NOT &&
- ( currentClass == PCL_ALIEN_BUILDER0 ||
- currentClass == PCL_ALIEN_BUILDER0_UPG ) &&
- ent->client->buildTimer )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_A_EVOLVEBUILDTIMER );
- return;
- }
-
- cost = BG_ClassCanEvolveFromTo( currentClass, newClass,
- ent->client->pers.credit,
- g_alienStage.integer, 0 );
-
- if( G_RoomForClassChange( ent, newClass, infestOrigin ) )
- {
- if( cost >= 0 )
- {
-
- ent->client->pers.evolveHealthFraction = (float)ent->client->ps.stats[ STAT_HEALTH ] /
- (float)BG_Class( currentClass )->health;
-
- if( ent->client->pers.evolveHealthFraction < 0.0f )
- ent->client->pers.evolveHealthFraction = 0.0f;
- else if( ent->client->pers.evolveHealthFraction > 1.0f )
- ent->client->pers.evolveHealthFraction = 1.0f;
-
- //remove credit
- G_AddCreditToClient( ent->client, -cost, qtrue );
- ent->client->pers.classSelection = newClass;
- ClientUserinfoChanged( clientNum, qfalse );
- VectorCopy( infestOrigin, ent->s.pos.trBase );
- VectorCopy( ent->client->ps.velocity, oldVel );
-
- if( ent->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
- oldBoostTime = ent->client->boostedTime;
-
- ClientSpawn( ent, ent, ent->s.pos.trBase, ent->s.apos.trBase );
-
- VectorCopy( oldVel, ent->client->ps.velocity );
- if( oldBoostTime > 0 )
- {
- ent->client->boostedTime = oldBoostTime;
- ent->client->ps.stats[ STAT_STATE ] |= SS_BOOSTED;
- }
- }
- else
- G_TriggerMenuArgs( clientNum, MN_A_CANTEVOLVE, newClass );
- }
- else
- G_TriggerMenu( clientNum, MN_A_NOEROOM );
- }
- }
- else if( ent->client->pers.teamSelection == TEAM_HUMANS )
- G_TriggerMenu( clientNum, MN_H_DEADTOCLASS );
-}
-
-
-/*
-=================
-Cmd_Destroy_f
-=================
-*/
-void Cmd_Destroy_f( gentity_t *ent )
-{
- vec3_t viewOrigin, forward, end;
- trace_t tr;
- gentity_t *traceEnt;
- char cmd[ 12 ];
- qboolean deconstruct = qtrue;
- qboolean lastSpawn = qfalse;
-
- if( ent->client->pers.namelog->denyBuild )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_REVOKED );
- return;
- }
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- if( Q_stricmp( cmd, "destroy" ) == 0 )
- deconstruct = qfalse;
-
- BG_GetClientViewOrigin( &ent->client->ps, viewOrigin );
- AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL );
- VectorMA( viewOrigin, 100, forward, end );
-
- trap_Trace( &tr, viewOrigin, NULL, NULL, end, ent->s.number, MASK_PLAYERSOLID );
- traceEnt = &g_entities[ tr.entityNum ];
-
- if( tr.fraction < 1.0f &&
- ( traceEnt->s.eType == ET_BUILDABLE ) &&
- ( traceEnt->buildableTeam == ent->client->pers.teamSelection ) &&
- ( ( ent->client->ps.weapon >= WP_ABUILD ) &&
- ( ent->client->ps.weapon <= WP_HBUILD ) ) )
- {
- // Always let the builder prevent the explosion
- if( traceEnt->health <= 0 )
- {
- G_QueueBuildPoints( traceEnt );
- G_RewardAttackers( traceEnt );
- G_FreeEntity( traceEnt );
- return;
- }
-
- // Cancel deconstruction (unmark)
- if( deconstruct && g_markDeconstruct.integer && traceEnt->deconstruct )
- {
- traceEnt->deconstruct = qfalse;
- return;
- }
-
- // Prevent destruction of the last spawn
- if( ent->client->pers.teamSelection == TEAM_ALIENS &&
- traceEnt->s.modelindex == BA_A_SPAWN )
- {
- if( level.numAlienSpawns <= 1 )
- lastSpawn = qtrue;
- }
- else if( ent->client->pers.teamSelection == TEAM_HUMANS &&
- traceEnt->s.modelindex == BA_H_SPAWN )
- {
- if( level.numHumanSpawns <= 1 )
- lastSpawn = qtrue;
- }
-
- if( lastSpawn && !g_cheats.integer &&
- !g_markDeconstruct.integer )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_LASTSPAWN );
- return;
- }
-
- // Don't allow destruction of buildables that cannot be rebuilt
- if( G_TimeTilSuddenDeath( ) <= 0 )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_SUDDENDEATH );
- return;
- }
-
- if( !g_markDeconstruct.integer ||
- ( ent->client->pers.teamSelection == TEAM_HUMANS &&
- !G_FindPower( traceEnt, qtrue ) ) )
- {
- if( ent->client->buildTimer )
- {
- G_AddEvent( ent, EV_BUILD_DELAY, ent->client->ps.clientNum );
- return;
- }
- }
-
- if( traceEnt->health > 0 )
- {
- if( !deconstruct )
- {
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- traceEnt->health, 0, MOD_SUICIDE );
- }
- else if( g_markDeconstruct.integer &&
- ( ent->client->pers.teamSelection != TEAM_HUMANS ||
- G_FindPower( traceEnt , qtrue ) || lastSpawn ) )
- {
- traceEnt->deconstruct = qtrue; // Mark buildable for deconstruction
- traceEnt->deconstructTime = level.time;
- }
- else
- {
- if( !g_cheats.integer && !g_instantBuild.integer ) // add a bit to the build timer
- {
- ent->client->buildTimer +=
- BG_Buildable( traceEnt->s.modelindex, traceEnt->cuboidSize )->buildTime / 4;
- G_RecalcBuildTimer(ent->client);
- }
- G_Damage( traceEnt, ent, ent, forward, tr.endpos,
- traceEnt->health, 0, MOD_DECONSTRUCT );
- G_FreeEntity( traceEnt );
- }
- }
- }
-}
-
-/*
-=================
-Cmd_ActivateItem_f
-
-Activate an item
-=================
-*/
-void Cmd_ActivateItem_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- int upgrade, weapon;
-
- trap_Argv( 1, s, sizeof( s ) );
-
- // "weapon" aliased to whatever weapon you have
- if( !Q_stricmp( "weapon", s ) )
- {
- if( ent->client->ps.weapon == WP_BLASTER &&
- BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- G_ForceWeaponChange( ent, WP_NONE );
- return;
- }
-
- upgrade = BG_UpgradeByName( s )->number;
- weapon = BG_WeaponByName( s )->number;
-
- if( upgrade != UP_NONE && BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
- BG_ActivateUpgrade( upgrade, ent->client->ps.stats );
- else if( weapon != WP_NONE &&
- BG_InventoryContainsWeapon( weapon, ent->client->ps.stats ) )
- {
- if( ent->client->ps.weapon != weapon &&
- BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- G_ForceWeaponChange( ent, weapon );
- }
- else
- trap_SendServerCommand( ent-g_entities, va( "print \"You don't have the %s\n\"", s ) );
-}
-
-
-/*
-=================
-Cmd_DeActivateItem_f
-
-Deactivate an item
-=================
-*/
-void Cmd_DeActivateItem_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- upgrade_t upgrade;
-
- trap_Argv( 1, s, sizeof( s ) );
- upgrade = BG_UpgradeByName( s )->number;
-
- if( BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
- {
- BG_DeactivateUpgrade( upgrade, ent->client->ps.stats );
- if( upgrade == UP_JETPACK )
- BG_AddPredictableEventToPlayerstate( EV_JETPACK_DEACTIVATE, 0, &ent->client->ps );
- }
- else
- trap_SendServerCommand( ent-g_entities, va( "print \"You don't have the %s\n\"", s ) );
-}
-
-
-/*
-=================
-Cmd_ToggleItem_f
-=================
-*/
-void Cmd_ToggleItem_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- weapon_t weapon;
- upgrade_t upgrade;
-
- trap_Argv( 1, s, sizeof( s ) );
- upgrade = BG_UpgradeByName( s )->number;
- weapon = BG_WeaponByName( s )->number;
-
- if( weapon != WP_NONE )
- {
- if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- return;
-
- //special case to allow switching between
- //the blaster and the primary weapon
- if( ent->client->ps.weapon != WP_BLASTER )
- weapon = WP_BLASTER;
- else
- weapon = WP_NONE;
-
- G_ForceWeaponChange( ent, weapon );
- }
- else if( BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
- {
- if( BG_UpgradeIsActive( upgrade, ent->client->ps.stats ) )
- {
- BG_DeactivateUpgrade( upgrade, ent->client->ps.stats );
- if( upgrade == UP_JETPACK )
- BG_AddPredictableEventToPlayerstate( EV_JETPACK_DEACTIVATE, 0, &ent->client->ps );
- }
- else
- BG_ActivateUpgrade( upgrade, ent->client->ps.stats );
- }
- else
- trap_SendServerCommand( ent-g_entities, va( "print \"You don't have the %s\n\"", s ) );
-}
-
-/*
-=================
-Cmd_Buy_f
-=================
-*/
-void Cmd_Buy_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- weapon_t weapon;
- upgrade_t upgrade;
- qboolean energyOnly, sellHelmet = qfalse;
-
- trap_Argv( 1, s, sizeof( s ) );
-
- weapon = BG_WeaponByName( s )->number;
- upgrade = BG_UpgradeByName( s )->number;
-
- if( upgrade == UP_NONE && !Q_stricmp(s, "helmet") )
- {
- if( g_humanStage.integer < S2 )
- upgrade = UP_HELMET_MK1;
- else
- upgrade = UP_HELMET_MK2;
- sellHelmet = qtrue;
- }
-
- // Only give energy from reactors or repeaters
- if( G_BuildableRange( ent->client->ps.origin, 100, BA_H_ARMOURY ) )
- energyOnly = qfalse;
- else if( upgrade == UP_AMMO &&
- BG_Weapon( ent->client->ps.stats[ STAT_WEAPON ] )->usesEnergy &&
- ( G_BuildableRange( ent->client->ps.origin, 100, BA_H_REACTOR ) ||
- G_BuildableRange( ent->client->ps.origin, 100, BA_H_REPEATER ) ) )
- energyOnly = qtrue;
- else
- {
- if( upgrade == UP_AMMO &&
- BG_Weapon( ent->client->ps.weapon )->usesEnergy )
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOENERGYAMMOHERE );
- else
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOARMOURYHERE );
- return;
- }
-
- if( weapon != WP_NONE )
- {
- //already got this?
- if( BG_InventoryContainsWeapon( weapon, ent->client->ps.stats ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_ITEMHELD );
- return;
- }
-
- // Only humans can buy stuff
- if( BG_Weapon( weapon )->team != TEAM_HUMANS )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy alien items\n\"" );
- return;
- }
-
- //are we /allowed/ to buy this?
- if( !BG_Weapon( weapon )->purchasable )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy this item\n\"" );
- return;
- }
-
- //are we /allowed/ to buy this?
- if( !BG_WeaponAllowedInStage( weapon, g_humanStage.integer ) || !BG_WeaponIsAllowed( weapon ) )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy this item\n\"" );
- return;
- }
-
- //can afford this?
- if( BG_Weapon( weapon )->price > (short)ent->client->pers.credit )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOFUNDS );
- return;
- }
-
- //have space to carry this?
- if( BG_Weapon( weapon )->slots & BG_SlotsForInventory( ent->client->ps.stats ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOSLOTS );
- return;
- }
-
- // In some instances, weapons can't be changed
- if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- return;
-
- ent->client->ps.stats[ STAT_WEAPON ] = weapon;
- ent->client->ps.ammo = BG_Weapon( weapon )->maxAmmo;
- ent->client->ps.clips = BG_Weapon( weapon )->maxClips;
-
- if( BG_Weapon( weapon )->usesEnergy &&
- BG_InventoryContainsUpgrade( UP_BATTPACK, ent->client->ps.stats ) )
- ent->client->ps.ammo *= BATTPACK_MODIFIER;
-
- G_ForceWeaponChange( ent, weapon );
-
- //set build delay/pounce etc to 0
- ent->client->ps.stats[ STAT_MISC ] = 0;
- ent->client->buildTimer = 0;
-
- //subtract from funds
- G_AddCreditToClient( ent->client, -(short)BG_Weapon( weapon )->price, qfalse );
- }
- else if( upgrade != UP_NONE )
- {
- //already got this?
- if( !sellHelmet && BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_ITEMHELD );
- return;
- }
-
- //can afford this?
- if( BG_Upgrade( upgrade )->price > (short)ent->client->pers.credit )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOFUNDS );
- return;
- }
-
- //have space to carry this?
- if( !sellHelmet && BG_Upgrade( upgrade )->slots & BG_SlotsForInventory( ent->client->ps.stats ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOSLOTS );
- return;
- }
-
- // Only humans can buy stuff
- if( BG_Upgrade( upgrade )->team != TEAM_HUMANS )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy alien items\n\"" );
- return;
- }
-
- //are we /allowed/ to buy this?
- if( !BG_Upgrade( upgrade )->purchasable )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy this item\n\"" );
- return;
- }
-
- //are we /allowed/ to buy this?
- if( !BG_UpgradeAllowedInStage( upgrade, g_humanStage.integer ) || !BG_UpgradeIsAllowed( upgrade ) )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't buy this item\n\"" );
- return;
- }
-
- if( upgrade == UP_AMMO )
- {
- G_GiveClientMaxAmmo( ent, energyOnly );
- if( !energyOnly && BG_InventoryContainsUpgrade( UP_JETPACK, ent->client->ps.stats ) &&
- ent->client->ps.stats[ STAT_FUEL ] < JETPACK_FUEL_FULL )
- {
- G_AddEvent( ent, EV_JETPACK_REFUEL, 0) ;
- ent->client->ps.stats[ STAT_FUEL ] = JETPACK_FUEL_FULL;
- }
- }
- else
- {
- if( upgrade == UP_BATTLESUIT )
- {
- vec3_t newOrigin;
-
- if( !G_RoomForClassChange( ent, PCL_HUMAN_BSUIT, newOrigin ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOROOMBSUITON );
- return;
- }
- VectorCopy( newOrigin, ent->client->ps.origin );
- ent->client->ps.stats[ STAT_CLASS ] = PCL_HUMAN_BSUIT;
- ent->client->pers.classSelection = PCL_HUMAN_BSUIT;
- ent->client->ps.eFlags ^= EF_TELEPORT_BIT;
- }
- else if( upgrade == UP_JETPACK )
- ent->client->ps.stats[ STAT_FUEL ] = JETPACK_FUEL_FULL;
-
- if( sellHelmet )
- {
- BG_RemoveUpgradeFromInventory( UP_HELMET_MK1, ent->client->ps.stats );
- BG_RemoveUpgradeFromInventory( UP_HELMET_MK2, ent->client->ps.stats );
- }
-
- //add to inventory
- BG_AddUpgradeToInventory( upgrade, ent->client->ps.stats );
- }
-
- if( upgrade == UP_BATTPACK )
- G_GiveClientMaxAmmo( ent, qtrue );
-
- //subtract from funds
- G_AddCreditToClient( ent->client, -(short)BG_Upgrade( upgrade )->price, qfalse );
- }
- else
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_UNKNOWNITEM );
-
- //update ClientInfo
- ClientUserinfoChanged( ent->client->ps.clientNum, qfalse );
-}
-
-
-/*
-=================
-Cmd_Sell_f
-=================
-*/
-void Cmd_Sell_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- int i;
- weapon_t weapon;
- upgrade_t upgrade;
-
- trap_Argv( 1, s, sizeof( s ) );
-
- //no armoury nearby
- if( !G_BuildableRange( ent->client->ps.origin, 100, BA_H_ARMOURY ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOARMOURYHERE );
- return;
- }
-
- if( !Q_stricmpn( s, "weapon", 6 ) )
- weapon = ent->client->ps.stats[ STAT_WEAPON ];
- else
- weapon = BG_WeaponByName( s )->number;
-
- upgrade = BG_UpgradeByName( s )->number;
-
- if( !upgrade && !Q_stricmp( s, "helmet" ) )
- {
- if( BG_InventoryContainsUpgrade( UP_HELMET_MK1, ent->client->ps.stats ) )
- upgrade = UP_HELMET_MK1;
- else if( BG_InventoryContainsUpgrade( UP_HELMET_MK2, ent->client->ps.stats ) )
- upgrade = UP_HELMET_MK2;
- }
-
- if( weapon != WP_NONE )
- {
- weapon_t selected = BG_GetPlayerWeapon( &ent->client->ps );
-
- if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- return;
-
- //are we /allowed/ to sell this?
- if( !BG_Weapon( weapon )->purchasable )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't sell this weapon\n\"" );
- return;
- }
-
- //remove weapon if carried
- if( BG_InventoryContainsWeapon( weapon, ent->client->ps.stats ) )
- {
- //guard against selling the HBUILD weapons exploit
- if( weapon == WP_HBUILD && ent->client->buildTimer )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_ARMOURYBUILDTIMER );
- return;
- }
-
- ent->client->ps.stats[ STAT_WEAPON ] = WP_NONE;
- // Cancel ghost buildables
- ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
-
- //add to funds
- G_AddCreditToClient( ent->client, (short)BG_Weapon( weapon )->price, qfalse );
- }
-
- //if we have this weapon selected, force a new selection
- if( weapon == selected )
- G_ForceWeaponChange( ent, WP_NONE );
- }
- else if( upgrade != UP_NONE )
- {
- //are we /allowed/ to sell this?
- if( !BG_Upgrade( upgrade )->purchasable )
- {
- trap_SendServerCommand( ent-g_entities, "print \"You can't sell this item\n\"" );
- return;
- }
- //remove upgrade if carried
- if( BG_InventoryContainsUpgrade( upgrade, ent->client->ps.stats ) )
- {
- // shouldn't really need to test for this, but just to be safe
- if( upgrade == UP_BATTLESUIT )
- {
- vec3_t newOrigin;
-
- if( !G_RoomForClassChange( ent, PCL_HUMAN, newOrigin ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOROOMBSUITOFF );
- return;
- }
- VectorCopy( newOrigin, ent->client->ps.origin );
- ent->client->ps.stats[ STAT_CLASS ] = PCL_HUMAN;
- ent->client->pers.classSelection = PCL_HUMAN;
- ent->client->ps.eFlags ^= EF_TELEPORT_BIT;
- }
-
- //add to inventory
- BG_RemoveUpgradeFromInventory( upgrade, ent->client->ps.stats );
-
- if( upgrade == UP_BATTPACK )
- G_GiveClientMaxAmmo( ent, qtrue );
-
- //add to funds
- G_AddCreditToClient( ent->client, (short)BG_Upgrade( upgrade )->price, qfalse );
- }
- }
- else if( !Q_stricmp( s, "weapons" ) )
- {
- weapon_t selected = BG_GetPlayerWeapon( &ent->client->ps );
-
- if( !BG_PlayerCanChangeWeapon( &ent->client->ps ) )
- return;
-
- for( i = WP_NONE + 1; i < WP_NUM_WEAPONS; i++ )
- {
- //guard against selling the HBUILD weapons exploit
- if( i == WP_HBUILD && ent->client->buildTimer )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_ARMOURYBUILDTIMER );
- continue;
- }
-
- if( BG_InventoryContainsWeapon( i, ent->client->ps.stats ) &&
- BG_Weapon( i )->purchasable )
- {
- ent->client->ps.stats[ STAT_WEAPON ] = WP_NONE;
-
- //add to funds
- G_AddCreditToClient( ent->client, (short)BG_Weapon( i )->price, qfalse );
- }
-
- //if we have this weapon selected, force a new selection
- if( i == selected )
- G_ForceWeaponChange( ent, WP_NONE );
- }
- }
- else if( !Q_stricmp( s, "upgrades" ) )
- {
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- //remove upgrade if carried
- if( BG_InventoryContainsUpgrade( i, ent->client->ps.stats ) &&
- BG_Upgrade( i )->purchasable )
- {
-
- // shouldn't really need to test for this, but just to be safe
- if( i == UP_BATTLESUIT )
- {
- vec3_t newOrigin;
-
- if( !G_RoomForClassChange( ent, PCL_HUMAN, newOrigin ) )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_NOROOMBSUITOFF );
- continue;
- }
- VectorCopy( newOrigin, ent->client->ps.origin );
- ent->client->ps.stats[ STAT_CLASS ] = PCL_HUMAN;
- ent->client->pers.classSelection = PCL_HUMAN;
- ent->client->ps.eFlags ^= EF_TELEPORT_BIT;
- }
-
- BG_RemoveUpgradeFromInventory( i, ent->client->ps.stats );
-
- if( i == UP_BATTPACK )
- G_GiveClientMaxAmmo( ent, qtrue );
-
- //add to funds
- G_AddCreditToClient( ent->client, (short)BG_Upgrade( i )->price, qfalse );
- }
- }
- }
- else
- G_TriggerMenu( ent->client->ps.clientNum, MN_H_UNKNOWNITEM );
-
- //update ClientInfo
- ClientUserinfoChanged( ent->client->ps.clientNum, qfalse );
-}
-
-/*
-=================
-Cmd_CheckCuboidSize
-
-Check if the specified dimensions are valid.
-=================
-*/
-qboolean Cmd_CheckCuboidSize(vec3_t dims)
-{
- if(g_cuboidSizeLimit.integer)
- if(dims[0]>g_cuboidSizeLimit.integer||dims[1]>g_cuboidSizeLimit.integer||dims[2]>g_cuboidSizeLimit.integer)
- return qfalse;
- if(dims[0]*dims[1]*dims[2]<CUBOID_MINVOLUME)
- return qfalse;
- if(dims[0]<1||dims[1]<1||dims[2]<1)
- return qfalse;
- return qtrue;
-}
-
-/*
-=================
-Cmd_Cb_f
-
-Sent by cgame in background (not directly by a person).
-Update player's cuboid selection (after validation) with data sent over network.
-=================
-*/
-void Cmd_Cb_f(gentity_t *ent)
-{
- char s[MAX_TOKEN_CHARS];
- int echo;
- vec3_t dims;
-
- if(trap_Argc()!=5)
- return;
- trap_Argv(1,s,sizeof(s));
- echo=atoi(s);
- trap_Argv(2,s,sizeof(s));
- dims[0]=atof(s);
- trap_Argv(3,s,sizeof(s));
- dims[1]=atof(s);
- trap_Argv(4,s,sizeof(s));
- dims[2]=atof(s);
- if(Cmd_CheckCuboidSize(dims))
- {
- VectorCopy(dims,ent->client->cuboidSelection);
- trap_SendServerCommand(ent->client-level.clients,va("cb3 %i\n",echo));
- G_RelayCuboidToSpectators(ent->client);
- }
- else
- {
- if(Cmd_CheckCuboidSize(ent->client->cuboidSelection))
- trap_SendServerCommand(ent->client-level.clients,va("cb3 %i %f %f %f\n",echo,ent->client->cuboidSelection[0],ent->client->cuboidSelection[1],ent->client->cuboidSelection[2]));
- else
- trap_SendServerCommand(ent->client-level.clients,va("cb3 %i %f %f %f\n",echo,g_cuboidSizeLimit.integer,g_cuboidSizeLimit.integer,g_cuboidSizeLimit.integer));
- }
-}
-
-
-/*
-=================
-Cmd_Build_f
-=================
-*/
-void Cmd_Build_f( gentity_t *ent )
-{
- char s[ MAX_TOKEN_CHARS ];
- buildable_t buildable;
- float dist;
- vec3_t origin, normal;
- team_t team;
- char buf[128];
- vec3_t dims;
-
- if( ent->client->pers.namelog->denyBuild )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_REVOKED );
- return;
- }
-
- if( ent->client->pers.teamSelection == level.surrenderTeam )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_SURRENDER );
- return;
- }
-
- trap_Argv( 1, s, sizeof( s ) );
-
- buildable = BG_BuildableByName( s )->number;
-
- /* To allow players build cuboids with completely arbitrary
- * dimensions (the current resizing method doesn't provide
- * much precision) the build command was extended with the
- * following syntax:
- * build [building] [X] [Y] [Z]
- * where X, Y and Z are respectively the cuboid's length
- * on the X, Y and Z axis.
- */
- if( BG_Buildable(buildable,NULL)->cuboid )
- {
- if( trap_Argc() >= 5 )
- {
- trap_Argv(2,s,sizeof(s));
- dims[0]=MAX(1,atof(s));
- trap_Argv(3,s,sizeof(s));
- dims[1]=MAX(1,atof(s));
- trap_Argv(4,s,sizeof(s));
- dims[2]=MAX(1,atof(s));
- if(!Cmd_CheckCuboidSize(dims))
- {
- Com_sprintf(buf,sizeof(buf),"print \"^1error: invalid cuboid size (min volume: %i, max size: %s)\n\"",
- CUBOID_MINVOLUME,(g_cuboidSizeLimit.integer?va("%ix%ix%i",g_cuboidSizeLimit.integer,g_cuboidSizeLimit.integer, g_cuboidSizeLimit.integer):"no limit"));
- trap_SendServerCommand(ent->client-level.clients,buf);
- return;
- }
- VectorCopy(dims,ent->client->cuboidSelection);
- }
- // client is building a cuboid for the first time so reset the selection to default
- if(!Cmd_CheckCuboidSize(ent->client->cuboidSelection))
- {
- ent->client->cuboidSelection[0]=32;
- ent->client->cuboidSelection[1]=32;
- ent->client->cuboidSelection[2]=32;
- trap_SendServerCommand(ent->client-level.clients,"cb2 32 32 32");
- G_RelayCuboidToSpectators(ent->client);
- }
-
- if(!BG_CuboidAllowed((team==TEAM_ALIENS?g_alienStage.integer:g_humanStage.integer)))
- {
- if(BG_CuboidMode()==1)
- G_TriggerMenu(ent->client->ps.clientNum,MN_B_CUBOID_MODE1);
- else
- G_TriggerMenu(ent->client->ps.clientNum,MN_B_CUBOID_MODE2);
- return;
- }
- }
-
- if( G_TimeTilSuddenDeath( ) <= 0 )
- {
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_SUDDENDEATH );
- return;
- }
-
- team = ent->client->ps.stats[ STAT_TEAM ];
-
- if( buildable != BA_NONE &&
- ( ( 1 << ent->client->ps.weapon ) & BG_Buildable( buildable, NULL )->buildWeapon ) &&
- BG_BuildableIsAllowed( buildable ) &&
- ( ( team == TEAM_ALIENS && BG_BuildableAllowedInStage( buildable, g_alienStage.integer ) ) ||
- ( team == TEAM_HUMANS && BG_BuildableAllowedInStage( buildable, g_humanStage.integer ) ) ) )
- {
- dynMenu_t err;
- dist = BG_Class( ent->client->ps.stats[ STAT_CLASS ] )->buildDist;
-
- ent->client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
-
- //these are the errors displayed when the builder first selects something to use
- switch( G_CanBuild( ent, buildable, dist, origin, normal, ent->client->cuboidSelection ) )
- {
- // can place right away, set the blueprint and the valid togglebit
- case IBE_NONE:
- case IBE_TNODEWARN:
- case IBE_RPTNOREAC:
- case IBE_RPTPOWERHERE:
- case IBE_SPWNWARN:
- err = MN_NONE;
- // we OR-in the selected builable later
- ent->client->ps.stats[ STAT_BUILDABLE ] = SB_VALID_TOGGLEBIT;
- break;
-
- // can't place yet but maybe soon: start with valid togglebit off
- case IBE_NORMAL:
- case IBE_NOCREEP:
- case IBE_NOROOM:
- case IBE_NOOVERMIND:
- case IBE_NOPOWERHERE:
- case IBE_NOSURF:
- err = MN_NONE;
- break;
-
- // more serious errors just pop a menu
- case IBE_NOALIENBP:
- err = MN_A_NOBP;
- break;
-
- case IBE_ONEOVERMIND:
- err = MN_A_ONEOVERMIND;
- break;
-
- case IBE_ONEREACTOR:
- err = MN_H_ONEREACTOR;
- break;
-
- case IBE_NOHUMANBP:
- err = MN_H_NOBP;
- break;
-
- case IBE_NODCC:
- err = MN_H_NODCC;
- break;
-
- case IBE_PERMISSION:
- err = MN_B_CANNOT;
- break;
-
- case IBE_LASTSPAWN:
- err = MN_B_LASTSPAWN;
- break;
-
- case IBE_TOODENSE:
- err = MN_B_TOODENSE;
- break;
-
- default:
- err = -1; // stop uninitialised warning
- break;
- }
-
- if( ( err == MN_A_NOBP || err == MN_H_NOBP ) && BG_Buildable(buildable,NULL)->cuboid )
- {
- err = MN_NONE;
- ent->client->ps.stats[ STAT_BUILDABLE ] = SB_VALID_TOGGLEBIT;
- }
-
- if( err == MN_NONE || ent->client->pers.disableBlueprintErrors )
- {
- trap_SendServerCommand(ent->client-level.clients,va("cb2 %f %f %f\n",
- ent->client->cuboidSelection[0],
- ent->client->cuboidSelection[1],
- ent->client->cuboidSelection[2]));
- G_RelayCuboidToSpectators(ent->client);
- ent->client->ps.stats[ STAT_BUILDABLE ] |= buildable;
- }
- else
- G_TriggerMenu( ent->client->ps.clientNum, err );
- }
- else
- G_TriggerMenu( ent->client->ps.clientNum, MN_B_CANNOT );
-}
-
-/*
-=================
-Cmd_Reload_f
-=================
-*/
-void Cmd_Reload_f( gentity_t *ent )
-{
- playerState_t *ps = &ent->client->ps;
- int ammo;
-
- // weapon doesn't ever need reloading
- if( BG_Weapon( ps->weapon )->infiniteAmmo )
- return;
-
- if( ps->clips <= 0 )
- return;
-
- if( BG_Weapon( ps->weapon )->usesEnergy &&
- BG_InventoryContainsUpgrade( UP_BATTPACK, ps->stats ) )
- ammo = BG_Weapon( ps->weapon )->maxAmmo * BATTPACK_MODIFIER;
- else
- ammo = BG_Weapon( ps->weapon )->maxAmmo;
-
- // don't reload when full
- if( ps->ammo >= ammo )
- return;
-
- // the animation, ammo refilling etc. is handled by PM_Weapon
- if( ent->client->ps.weaponstate != WEAPON_RELOADING )
- ent->client->ps.pm_flags |= PMF_WEAPON_RELOAD;
-}
-
-/*
-=================
-G_StopFromFollowing
-
-stops any other clients from following this one
-called when a player leaves a team or dies
-=================
-*/
-void G_StopFromFollowing( gentity_t *ent )
-{
- int i;
-
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].sess.spectatorState == SPECTATOR_FOLLOW &&
- level.clients[ i ].sess.spectatorClient == ent->client->ps.clientNum )
- {
- if( !G_FollowNewClient( &g_entities[ i ], 1 ) )
- G_StopFollowing( &g_entities[ i ] );
- }
- }
-}
-
-/*
-=================
-G_StopFollowing
-
-If the client being followed leaves the game, or you just want to drop
-to free floating spectator mode
-=================
-*/
-void G_StopFollowing( gentity_t *ent )
-{
- ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;
-
- if( ent->client->pers.teamSelection == TEAM_NONE )
- {
- ent->client->sess.spectatorState =
- ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_FREE;
- }
- else
- {
- vec3_t spawn_origin, spawn_angles;
-
- ent->client->sess.spectatorState =
- ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_LOCKED;
-
- if( ent->client->pers.teamSelection == TEAM_ALIENS )
- G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
- else if( ent->client->pers.teamSelection == TEAM_HUMANS )
- G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
-
- G_SetOrigin( ent, spawn_origin );
- VectorCopy( spawn_origin, ent->client->ps.origin );
- G_SetClientViewAngle( ent, spawn_angles );
- }
- ent->client->sess.spectatorClient = -1;
- ent->client->ps.pm_flags &= ~PMF_FOLLOW;
- ent->client->ps.groundEntityNum = ENTITYNUM_NONE;
- ent->client->ps.stats[ STAT_STATE ] = 0;
- ent->client->ps.stats[ STAT_VIEWLOCK ] = 0;
- ent->client->ps.eFlags &= ~( EF_WALLCLIMB | EF_WALLCLIMBCEILING );
- ent->client->ps.viewangles[ PITCH ] = 0.0f;
- ent->client->ps.clientNum = ent - g_entities;
- ent->client->ps.persistant[ PERS_CREDIT ] = ent->client->pers.credit;
-
- CalculateRanks( );
-}
-
-/*
-=================
-G_FollowLockView
-
-Client is still following a player, but that player has gone to spectator
-mode and cannot be followed for the moment
-=================
-*/
-void G_FollowLockView( gentity_t *ent )
-{
- vec3_t spawn_origin, spawn_angles;
- int clientNum;
-
- clientNum = ent->client->sess.spectatorClient;
- ent->client->sess.spectatorState =
- ent->client->ps.persistant[ PERS_SPECSTATE ] = SPECTATOR_FOLLOW;
- ent->client->ps.clientNum = clientNum;
- ent->client->ps.pm_flags &= ~PMF_FOLLOW;
- ent->client->ps.stats[ STAT_TEAM ] = ent->client->pers.teamSelection;
- ent->client->ps.stats[ STAT_STATE ] &= ~SS_WALLCLIMBING;
- ent->client->ps.stats[ STAT_VIEWLOCK ] = 0;
- ent->client->ps.eFlags &= ~( EF_WALLCLIMB | EF_WALLCLIMBCEILING );
- ent->client->ps.eFlags ^= EF_TELEPORT_BIT;
- ent->client->ps.viewangles[ PITCH ] = 0.0f;
-
- // Put the view at the team spectator lock position
- if( level.clients[ clientNum ].pers.teamSelection == TEAM_ALIENS )
- G_SelectAlienLockSpawnPoint( spawn_origin, spawn_angles );
- else if( level.clients[ clientNum ].pers.teamSelection == TEAM_HUMANS )
- G_SelectHumanLockSpawnPoint( spawn_origin, spawn_angles );
-
- G_SetOrigin( ent, spawn_origin );
- VectorCopy( spawn_origin, ent->client->ps.origin );
- G_SetClientViewAngle( ent, spawn_angles );
-}
-
-/*
-=================
-G_FollowNewClient
-
-This was a really nice, elegant function. Then I fucked it up.
-=================
-*/
-qboolean G_FollowNewClient( gentity_t *ent, int dir )
-{
- int clientnum = ent->client->sess.spectatorClient;
- int original = clientnum;
- qboolean selectAny = qfalse;
-
- if( dir > 1 )
- dir = 1;
- else if( dir < -1 )
- dir = -1;
- else if( dir == 0 )
- return qtrue;
-
- if( ent->client->sess.spectatorState == SPECTATOR_NOT )
- return qfalse;
-
- // select any if no target exists
- if( clientnum < 0 || clientnum >= level.maxclients )
- {
- clientnum = original = 0;
- selectAny = qtrue;
- }
-
- do
- {
- clientnum += dir;
-
- if( clientnum >= level.maxclients )
- clientnum = 0;
-
- if( clientnum < 0 )
- clientnum = level.maxclients - 1;
-
- // can't follow self
- if( &g_entities[ clientnum ] == ent )
- continue;
-
- // avoid selecting existing follow target
- if( clientnum == original && !selectAny )
- continue; //effectively break;
-
- // can only follow connected clients
- if( level.clients[ clientnum ].pers.connected != CON_CONNECTED )
- continue;
-
- // can't follow a spectator
- if( level.clients[ clientnum ].pers.teamSelection == TEAM_NONE )
- continue;
-
- // if stickyspec is disabled, can't follow someone in queue either
- if( !ent->client->pers.stickySpec &&
- level.clients[ clientnum ].sess.spectatorState != SPECTATOR_NOT )
- continue;
-
- // can only follow teammates when dead and on a team
- if( ent->client->pers.teamSelection != TEAM_NONE &&
- ( level.clients[ clientnum ].pers.teamSelection !=
- ent->client->pers.teamSelection ) )
- continue;
-
- // this is good, we can use it
- ent->client->sess.spectatorClient = clientnum;
- ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
-
- // if this client is in the spawn queue, we need to do something special
- if( level.clients[ clientnum ].sess.spectatorState != SPECTATOR_NOT )
- G_FollowLockView( ent );
-
- return qtrue;
-
- } while( clientnum != original );
-
- return qfalse;
-}
-
-/*
-=================
-G_ToggleFollow
-=================
-*/
-void G_ToggleFollow( gentity_t *ent )
-{
- if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
- G_StopFollowing( ent );
- else
- G_FollowNewClient( ent, 1 );
-}
-
-/*
-=================
-Cmd_Follow_f
-=================
-*/
-void Cmd_Follow_f( gentity_t *ent )
-{
- int i;
- char arg[ MAX_NAME_LENGTH ];
-
- // won't work unless spectating
- if( ent->client->sess.spectatorState == SPECTATOR_NOT )
- return;
-
- if( trap_Argc( ) != 2 )
- {
- G_ToggleFollow( ent );
- }
- else
- {
- char err[ MAX_STRING_CHARS ];
- trap_Argv( 1, arg, sizeof( arg ) );
-
- i = G_ClientNumberFromString( arg, err, sizeof( err ) );
-
- if( i == -1 )
- {
- trap_SendServerCommand( ent - g_entities,
- va( "print \"follow: %s\"", err ) );
- return;
- }
-
- // can't follow self
- if( &level.clients[ i ] == ent->client )
- return;
-
- // can't follow another spectator if sticky spec is off
- if( !ent->client->pers.stickySpec &&
- level.clients[ i ].sess.spectatorState != SPECTATOR_NOT )
- return;
-
- // if not on team spectator, you can only follow teammates
- if( ent->client->pers.teamSelection != TEAM_NONE &&
- ( level.clients[ i ].pers.teamSelection !=
- ent->client->pers.teamSelection ) )
- return;
-
- ent->client->sess.spectatorState = SPECTATOR_FOLLOW;
- ent->client->sess.spectatorClient = i;
- }
-}
-
-/*
-=================
-Cmd_FollowCycle_f
-=================
-*/
-void Cmd_FollowCycle_f( gentity_t *ent )
-{
- char args[ 11 ];
- int dir = 1;
-
- trap_Argv( 0, args, sizeof( args ) );
- if( Q_stricmp( args, "followprev" ) == 0 )
- dir = -1;
-
- // won't work unless spectating
- if( ent->client->sess.spectatorState == SPECTATOR_NOT )
- return;
-
- G_FollowNewClient( ent, dir );
-}
-
-static void Cmd_Ignore_f( gentity_t *ent )
-{
- int pids[ MAX_CLIENTS ];
- char name[ MAX_NAME_LENGTH ];
- char cmd[ 9 ];
- int matches = 0;
- int i;
- qboolean ignore = qfalse;
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- if( Q_stricmp( cmd, "ignore" ) == 0 )
- ignore = qtrue;
-
- if( trap_Argc() < 2 )
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "usage: %s [clientNum | partial name match]\n\"", cmd ) );
- return;
- }
-
- Q_strncpyz( name, ConcatArgs( 1 ), sizeof( name ) );
- matches = G_ClientNumbersFromString( name, pids, MAX_CLIENTS );
- if( matches < 1 )
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "%s: no clients match the name '%s'\n\"", cmd, name ) );
- return;
- }
-
- for( i = 0; i < matches; i++ )
- {
- if( ignore )
- {
- if( !Com_ClientListContains( &ent->client->sess.ignoreList, pids[ i ] ) )
- {
- Com_ClientListAdd( &ent->client->sess.ignoreList, pids[ i ] );
- ClientUserinfoChanged( ent->client->ps.clientNum, qfalse );
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "ignore: added %s^7 to your ignore list\n\"",
- level.clients[ pids[ i ] ].pers.netname ) );
- }
- else
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "ignore: %s^7 is already on your ignore list\n\"",
- level.clients[ pids[ i ] ].pers.netname ) );
- }
- }
- else
- {
- if( Com_ClientListContains( &ent->client->sess.ignoreList, pids[ i ] ) )
- {
- Com_ClientListRemove( &ent->client->sess.ignoreList, pids[ i ] );
- ClientUserinfoChanged( ent->client->ps.clientNum, qfalse );
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "unignore: removed %s^7 from your ignore list\n\"",
- level.clients[ pids[ i ] ].pers.netname ) );
- }
- else
- {
- trap_SendServerCommand( ent-g_entities, va( "print \"[skipnotify]"
- "unignore: %s^7 is not on your ignore list\n\"",
- level.clients[ pids[ i ] ].pers.netname ) );
- }
- }
- }
-}
-
-/*
-=================
-Cmd_ListMaps_f
-
-List all maps on the server
-=================
-*/
-
-static int SortMaps( const void *a, const void *b )
-{
- return strcmp( *(char **)a, *(char **)b );
-}
-
-#define MAX_MAPLIST_MAPS 256
-#define MAX_MAPLIST_ROWS 9
-void Cmd_ListMaps_f( gentity_t *ent )
-{
- char search[ 16 ] = {""};
- char fileList[ 4096 ] = {""};
- char *fileSort[ MAX_MAPLIST_MAPS ];
- char *filePtr, *p;
- int numFiles;
- int fileLen = 0;
- int shown = 0;
- int count = 0;
- int page = 0;
- int pages;
- int row, rows;
- int start, i, j;
-
- if( trap_Argc( ) > 1 )
- {
- trap_Argv( 1, search, sizeof( search ) );
- for( p = search; ( *p ) && isdigit( *p ); p++ );
- if( !( *p ) )
- {
- page = atoi( search );
- search[ 0 ] = '\0';
- }
- else if( trap_Argc( ) > 2 )
- {
- char lp[ 8 ];
- trap_Argv( 2, lp, sizeof( lp ) );
- page = atoi( lp );
- }
-
- if( page > 0 )
- page--;
- else if( page < 0 )
- page = 0;
- }
-
- numFiles = trap_FS_GetFileList( "maps/", ".bsp",
- fileList, sizeof( fileList ) );
- filePtr = fileList;
- for( i = 0; i < numFiles && count < MAX_MAPLIST_MAPS; i++, filePtr += fileLen + 1 )
- {
- fileLen = strlen( filePtr );
- if ( fileLen < 5 )
- continue;
-
- filePtr[ fileLen - 4 ] = '\0';
-
- if( search[ 0 ] && !strstr( filePtr, search ) )
- continue;
-
- fileSort[ count ] = filePtr;
- count++;
- }
- qsort( fileSort, count, sizeof( fileSort[ 0 ] ), SortMaps );
-
- rows = ( count + 2 ) / 3;
- pages = MAX( 1, ( rows + MAX_MAPLIST_ROWS - 1 ) / MAX_MAPLIST_ROWS );
- if( page >= pages )
- page = pages - 1;
-
- start = page * MAX_MAPLIST_ROWS * 3;
- if( count < start + ( 3 * MAX_MAPLIST_ROWS ) )
- rows = ( count - start + 2 ) / 3;
- else
- rows = MAX_MAPLIST_ROWS;
-
- ADMBP_begin( );
- for( row = 0; row < rows; row++ )
- {
- for( i = start + row, j = 0; i < count && j < 3; i += rows, j++ )
- {
- ADMBP( va( "^7 %-20s", fileSort[ i ] ) );
- shown++;
- }
- ADMBP( "\n" );
- }
- if ( search[ 0 ] )
- ADMBP( va( "^3listmaps: ^7found %d maps matching '%s^7'", count, search ) );
- else
- ADMBP( va( "^3listmaps: ^7listing %d of %d maps", shown, count ) );
- if( pages > 1 )
- ADMBP( va( ", page %d of %d", page + 1, pages ) );
- if( page + 1 < pages )
- ADMBP( va( ", use 'listmaps %s%s%d' to see more",
- search, ( search[ 0 ] ) ? " ": "", page + 2 ) );
- ADMBP( ".\n" );
- ADMBP_end( );
-}
-
-/*
-=================
-Cmd_Test_f
-=================
-*/
-void Cmd_Test_f( gentity_t *humanPlayer )
-{
-}
-
-/*
-=================
-Cmd_Damage_f
-
-Deals damage to you (for testing), arguments: [damage] [dx] [dy] [dz]
-The dx/dy arguments describe the damage point's offset from the entity origin
-=================
-*/
-void Cmd_Damage_f( gentity_t *ent )
-{
- vec3_t point;
- char arg[ 16 ];
- float dx = 0.0f, dy = 0.0f, dz = 100.0f;
- int damage = 100;
- qboolean nonloc = qtrue;
-
- if( trap_Argc() > 1 )
- {
- trap_Argv( 1, arg, sizeof( arg ) );
- damage = atoi( arg );
- }
- if( trap_Argc() > 4 )
- {
- trap_Argv( 2, arg, sizeof( arg ) );
- dx = atof( arg );
- trap_Argv( 3, arg, sizeof( arg ) );
- dy = atof( arg );
- trap_Argv( 4, arg, sizeof( arg ) );
- dz = atof( arg );
- nonloc = qfalse;
- }
- VectorCopy( ent->s.origin, point );
- point[ 0 ] += dx;
- point[ 1 ] += dy;
- point[ 2 ] += dz;
- G_Damage( ent, NULL, NULL, NULL, point, damage,
- ( nonloc ? DAMAGE_NO_LOCDAMAGE : 0 ), MOD_TARGET_LASER );
-}
-
-/*
-==================
-G_FloodLimited
-
-Determine whether a user is flood limited, and adjust their flood demerits
-Print them a warning message if they are over the limit
-Return is time in msec until the user can speak again
-==================
-*/
-int G_FloodLimited( gentity_t *ent )
-{
- int deltatime, ms;
-
- if( g_floodMinTime.integer <= 0 )
- return 0;
-
- // handles !ent
- if( G_admin_permission( ent, ADMF_NOCENSORFLOOD ) )
- return 0;
-
- deltatime = level.time - ent->client->pers.floodTime;
-
- ent->client->pers.floodDemerits += g_floodMinTime.integer - deltatime;
- if( ent->client->pers.floodDemerits < 0 )
- ent->client->pers.floodDemerits = 0;
- ent->client->pers.floodTime = level.time;
-
- ms = ent->client->pers.floodDemerits - g_floodMaxDemerits.integer;
- if( ms <= 0 )
- return 0;
- trap_SendServerCommand( ent - g_entities, va( "print \"You are flooding: "
- "please wait %d second%s before trying again\n",
- ( ms + 999 ) / 1000, ( ms > 1000 ) ? "s" : "" ) );
- return ms;
-}
-
-commands_t cmds[ ] = {
- { "a", CMD_MESSAGE|CMD_INTERMISSION, Cmd_AdminMessage_f },
- { "build", CMD_TEAM|CMD_LIVING, Cmd_Build_f },
- { "buy", CMD_HUMAN|CMD_LIVING, Cmd_Buy_f },
- { "callteamvote", CMD_MESSAGE|CMD_TEAM, Cmd_CallVote_f },
- { "callvote", CMD_MESSAGE, Cmd_CallVote_f },
- { "cb", 0, Cmd_Cb_f }, //NOTE: it's a command used only by cgame
- { "class", CMD_TEAM, Cmd_Class_f },
- { "damage", CMD_CHEAT|CMD_LIVING, Cmd_Damage_f },
- { "deconstruct", CMD_TEAM|CMD_LIVING, Cmd_Destroy_f },
- { "destroy", CMD_CHEAT|CMD_TEAM|CMD_LIVING, Cmd_Destroy_f },
- { "follow", CMD_SPEC, Cmd_Follow_f },
- { "follownext", CMD_SPEC, Cmd_FollowCycle_f },
- { "followprev", CMD_SPEC, Cmd_FollowCycle_f },
- { "give", CMD_CHEAT|CMD_TEAM|CMD_LIVING, Cmd_Give_f },
- { "god", CMD_CHEAT|CMD_TEAM|CMD_LIVING, Cmd_God_f },
- { "ignore", 0, Cmd_Ignore_f },
- { "itemact", CMD_HUMAN|CMD_LIVING, Cmd_ActivateItem_f },
- { "itemdeact", CMD_HUMAN|CMD_LIVING, Cmd_DeActivateItem_f },
- { "itemtoggle", CMD_HUMAN|CMD_LIVING, Cmd_ToggleItem_f },
- { "kill", CMD_TEAM|CMD_LIVING, Cmd_Kill_f },
- { "levelshot", CMD_CHEAT, Cmd_LevelShot_f },
- { "listmaps", CMD_MESSAGE|CMD_INTERMISSION, Cmd_ListMaps_f },
- { "m", CMD_MESSAGE|CMD_INTERMISSION, Cmd_PrivateMessage_f },
- { "mt", CMD_MESSAGE|CMD_INTERMISSION, Cmd_PrivateMessage_f },
- { "noclip", CMD_CHEAT_TEAM, Cmd_Noclip_f },
- { "notarget", CMD_CHEAT|CMD_TEAM|CMD_LIVING, Cmd_Notarget_f },
- { "reload", CMD_HUMAN|CMD_LIVING, Cmd_Reload_f },
- { "say", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f },
- { "say_area", CMD_MESSAGE|CMD_TEAM|CMD_LIVING, Cmd_SayArea_f },
- { "say_team", CMD_MESSAGE|CMD_INTERMISSION, Cmd_Say_f },
- { "score", CMD_INTERMISSION, ScoreboardMessage },
- { "sell", CMD_HUMAN|CMD_LIVING, Cmd_Sell_f },
- { "setviewpos", CMD_CHEAT_TEAM, Cmd_SetViewpos_f },
- { "team", 0, Cmd_Team_f },
- { "teamvote", CMD_TEAM, Cmd_Vote_f },
- { "test", CMD_CHEAT, Cmd_Test_f },
- { "unignore", 0, Cmd_Ignore_f },
- { "vote", 0, Cmd_Vote_f },
- { "vsay", CMD_MESSAGE|CMD_INTERMISSION, Cmd_VSay_f },
- { "vsay_local", CMD_MESSAGE|CMD_INTERMISSION, Cmd_VSay_f },
- { "vsay_team", CMD_MESSAGE|CMD_INTERMISSION, Cmd_VSay_f },
- { "where", 0, Cmd_Where_f }
-};
-static size_t numCmds = sizeof( cmds ) / sizeof( cmds[ 0 ] );
-
-/*
-=================
-ClientCommand
-=================
-*/
-void ClientCommand( int clientNum )
-{
- gentity_t *ent;
- char cmd[ MAX_TOKEN_CHARS ];
- commands_t *command;
-
- ent = g_entities + clientNum;
- if( !ent->client || ent->client->pers.connected != CON_CONNECTED )
- return; // not fully in game yet
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
-
- command = bsearch( cmd, cmds, numCmds, sizeof( cmds[ 0 ] ), cmdcmp );
-
- if( !command )
- {
- if( !G_admin_cmd_check( ent ) )
- trap_SendServerCommand( clientNum,
- va( "print \"Unknown command %s\n\"", cmd ) );
- return;
- }
-
- // do tests here to reduce the amount of repeated code
-
- if( !( command->cmdFlags & CMD_INTERMISSION ) &&
- ( level.intermissiontime || level.pausedTime ) )
- return;
-
- if( command->cmdFlags & CMD_CHEAT && !g_cheats.integer )
- {
- G_TriggerMenu( clientNum, MN_CMD_CHEAT );
- return;
- }
-
- if( command->cmdFlags & CMD_MESSAGE && ( ent->client->pers.namelog->muted ||
- G_FloodLimited( ent ) ) )
- return;
-
- if( command->cmdFlags & CMD_TEAM &&
- ent->client->pers.teamSelection == TEAM_NONE )
- {
- G_TriggerMenu( clientNum, MN_CMD_TEAM );
- return;
- }
-
- if( command->cmdFlags & CMD_CHEAT_TEAM && !g_cheats.integer &&
- ent->client->pers.teamSelection != TEAM_NONE )
- {
- G_TriggerMenu( clientNum, MN_CMD_CHEAT_TEAM );
- return;
- }
-
- if( command->cmdFlags & CMD_SPEC &&
- ent->client->sess.spectatorState == SPECTATOR_NOT )
- {
- G_TriggerMenu( clientNum, MN_CMD_SPEC );
- return;
- }
-
- if( command->cmdFlags & CMD_ALIEN &&
- ent->client->pers.teamSelection != TEAM_ALIENS )
- {
- G_TriggerMenu( clientNum, MN_CMD_ALIEN );
- return;
- }
-
- if( command->cmdFlags & CMD_HUMAN &&
- ent->client->pers.teamSelection != TEAM_HUMANS )
- {
- G_TriggerMenu( clientNum, MN_CMD_HUMAN );
- return;
- }
-
- if( command->cmdFlags & CMD_LIVING &&
- ( ent->client->ps.stats[ STAT_HEALTH ] <= 0 ||
- ent->client->sess.spectatorState != SPECTATOR_NOT ) )
- {
- G_TriggerMenu( clientNum, MN_CMD_LIVING );
- return;
- }
-
- command->cmdHandler( ent );
-}
-
-void G_ListCommands( gentity_t *ent )
-{
- int i;
- char out[ MAX_STRING_CHARS ] = "";
- int len, outlen;
-
- outlen = 0;
-
- for( i = 0; i < numCmds; i++ )
- {
- len = strlen( cmds[ i ].cmdName ) + 1;
- if( len + outlen >= sizeof( out ) - 1 )
- {
- trap_SendServerCommand( ent - g_entities, va( "cmds%s\n", out ) );
- outlen = 0;
- }
-
- strcpy( out + outlen, va( " %s", cmds[ i ].cmdName ) );
- outlen += len;
- }
-
- trap_SendServerCommand( ent - g_entities, va( "cmds%s\n", out ) );
- G_admin_cmdlist( ent );
-}
-
-void G_DecolorString( char *in, char *out, int len )
-{
- qboolean decolor = qtrue;
-
- len--;
-
- while( *in && len > 0 ) {
- if( *in == DECOLOR_OFF || *in == DECOLOR_ON )
- {
- decolor = ( *in == DECOLOR_ON );
- in++;
- continue;
- }
- if( Q_IsColorString( in ) && decolor ) {
- in += 2;
- continue;
- }
- *out++ = *in++;
- len--;
- }
- *out = '\0';
-}
-
-void G_UnEscapeString( char *in, char *out, int len )
-{
- len--;
-
- while( *in && len > 0 )
- {
- if( *in >= ' ' || *in == '\n' )
- {
- *out++ = *in;
- len--;
- }
- in++;
- }
- *out = '\0';
-}
-
-void Cmd_PrivateMessage_f( gentity_t *ent )
-{
- int pids[ MAX_CLIENTS ];
- char name[ MAX_NAME_LENGTH ];
- char cmd[ 12 ];
- char text[ MAX_STRING_CHARS ];
- char *msg;
- char color;
- int i, pcount;
- int count = 0;
- qboolean teamonly = qfalse;
- char recipients[ MAX_STRING_CHARS ] = "";
-
- if( !g_privateMessages.integer && ent )
- {
- ADMP( "Sorry, but private messages have been disabled\n" );
- return;
- }
-
- trap_Argv( 0, cmd, sizeof( cmd ) );
- if( trap_Argc( ) < 3 )
- {
- ADMP( va( "usage: %s [name|slot#] [message]\n", cmd ) );
- return;
- }
-
- if( !Q_stricmp( cmd, "mt" ) )
- teamonly = qtrue;
-
- trap_Argv( 1, name, sizeof( name ) );
- msg = ConcatArgs( 2 );
- pcount = G_ClientNumbersFromString( name, pids, MAX_CLIENTS );
-
- G_CensorString( text, msg, sizeof( text ), ent );
-
- // send the message
- for( i = 0; i < pcount; i++ )
- {
- if( G_SayTo( ent, &g_entities[ pids[ i ] ],
- teamonly ? SAY_TPRIVMSG : SAY_PRIVMSG, text ) )
- {
- count++;
- Q_strcat( recipients, sizeof( recipients ), va( "%s" S_COLOR_WHITE ", ",
- level.clients[ pids[ i ] ].pers.netname ) );
- }
- }
-
- // report the results
- color = teamonly ? COLOR_CYAN : COLOR_YELLOW;
-
- if( !count )
- ADMP( va( "^3No player matching ^7\'%s^7\' ^3to send message to.\n",
- name ) );
- else
- {
- ADMP( va( "^%cPrivate message: ^7%s\n", color, text ) );
- // remove trailing ", "
- recipients[ strlen( recipients ) - 2 ] = '\0';
- ADMP( va( "^%csent to %i player%s: " S_COLOR_WHITE "%s\n", color, count,
- count == 1 ? "" : "s", recipients ) );
-
- G_LogPrintf( "%s: %d \"%s" S_COLOR_WHITE "\" \"%s\": ^%c%s\n",
- ( teamonly ) ? "TPrivMsg" : "PrivMsg",
- ( ent ) ? ent - g_entities : -1,
- ( ent ) ? ent->client->pers.netname : "console",
- name, color, msg );
- }
-}
-
-/*
-=================
-Cmd_AdminMessage_f
-
-Send a message to all active admins
-=================
-*/
-void Cmd_AdminMessage_f( gentity_t *ent )
-{
- // Check permissions and add the appropriate user [prefix]
- if( !G_admin_permission( ent, ADMF_ADMINCHAT ) )
- {
- if( !g_publicAdminMessages.integer )
- {
- ADMP( "Sorry, but use of /a by non-admins has been disabled.\n" );
- return;
- }
- else
- {
- ADMP( "Your message has been sent to any available admins "
- "and to the server logs.\n" );
- }
- }
-
- if( trap_Argc( ) < 2 )
- {
- ADMP( "usage: a [message]\n" );
- return;
- }
-
- G_AdminMessage( ent, ConcatArgs( 1 ) );
-}
-
diff --git a/src/game/g_combat.c.orig b/src/game/g_combat.c.orig
deleted file mode 100644
index f4e89ee..0000000
--- a/src/game/g_combat.c.orig
+++ /dev/null
@@ -1,1468 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-#include "g_local.h"
-
-damageRegion_t g_damageRegions[ PCL_NUM_CLASSES ][ MAX_DAMAGE_REGIONS ];
-int g_numDamageRegions[ PCL_NUM_CLASSES ];
-
-damageRegion_t g_armourRegions[ UP_NUM_UPGRADES ][ MAX_DAMAGE_REGIONS ];
-int g_numArmourRegions[ UP_NUM_UPGRADES ];
-
-/*
-============
-AddScore
-
-Adds score to the client
-============
-*/
-void AddScore( gentity_t *ent, int score )
-{
- if( !ent->client )
- return;
-
- // make alien and human scores equivalent
- if ( ent->client->pers.teamSelection == TEAM_ALIENS )
- {
- score = rint( ((float)score) / 2.0f );
- }
-
- // scale values down to fit the scoreboard better
- score = rint( ((float)score) / 50.0f );
-
- ent->client->ps.persistant[ PERS_SCORE ] += score;
- CalculateRanks( );
-}
-
-/*
-==================
-LookAtKiller
-==================
-*/
-void LookAtKiller( gentity_t *self, gentity_t *inflictor, gentity_t *attacker )
-{
-
- if ( attacker && attacker != self )
- self->client->ps.stats[ STAT_VIEWLOCK ] = attacker - g_entities;
- else if( inflictor && inflictor != self )
- self->client->ps.stats[ STAT_VIEWLOCK ] = inflictor - g_entities;
- else
- self->client->ps.stats[ STAT_VIEWLOCK ] = self - g_entities;
-}
-
-// these are just for logging, the client prints its own messages
-char *modNames[ ] =
-{
- "MOD_UNKNOWN",
- "MOD_SHOTGUN",
- "MOD_BLASTER",
- "MOD_PAINSAW",
- "MOD_MACHINEGUN",
- "MOD_CHAINGUN",
- "MOD_PRIFLE",
- "MOD_MDRIVER",
- "MOD_LASGUN",
- "MOD_LCANNON",
- "MOD_LCANNON_SPLASH",
- "MOD_FLAMER",
- "MOD_FLAMER_SPLASH",
- "MOD_GRENADE",
- "MOD_WATER",
- "MOD_SLIME",
- "MOD_LAVA",
- "MOD_CRUSH",
- "MOD_TELEFRAG",
- "MOD_FALLING",
- "MOD_SUICIDE",
- "MOD_TARGET_LASER",
- "MOD_TRIGGER_HURT",
-
- "MOD_ABUILDER_CLAW",
- "MOD_LEVEL0_BITE",
- "MOD_LEVEL1_CLAW",
- "MOD_LEVEL1_PCLOUD",
- "MOD_LEVEL3_CLAW",
- "MOD_LEVEL3_POUNCE",
- "MOD_LEVEL3_BOUNCEBALL",
- "MOD_LEVEL2_CLAW",
- "MOD_LEVEL2_ZAP",
- "MOD_LEVEL4_CLAW",
- "MOD_LEVEL4_TRAMPLE",
- "MOD_LEVEL4_CRUSH",
-
- "MOD_SLOWBLOB",
- "MOD_POISON",
- "MOD_SWARM",
-
- "MOD_HSPAWN",
- "MOD_TESLAGEN",
- "MOD_MGTURRET",
- "MOD_REACTOR",
-
- "MOD_ASPAWN",
- "MOD_ATUBE",
- "MOD_OVERMIND",
- "MOD_DECONSTRUCT",
- "MOD_REPLACE",
- "MOD_NOCREEP"
-};
-
-/*
-==================
-G_RewardAttackers
-
-Function to distribute rewards to entities that killed this one.
-Returns the total damage dealt.
-==================
-*/
-float G_RewardAttackers( gentity_t *self )
-{
- float value, totalDamage = 0;
- int team, i, maxHealth = 0;
- int alienCredits = 0, humanCredits = 0;
- gentity_t *player;
-
- // Total up all the damage done by non-teammates
- for( i = 0; i < level.maxclients; i++ )
- {
- player = g_entities + i;
-
- if( !OnSameTeam( self, player ) ||
- self->buildableTeam != player->client->ps.stats[ STAT_TEAM ] )
- totalDamage += (float)self->credits[ i ];
- }
-
- if( totalDamage <= 0.0f )
- return 0.0f;
-
- // Only give credits for killing players and buildables
- if( self->client )
- {
- value = BG_GetValueOfPlayer( &self->client->ps );
- team = self->client->pers.teamSelection;
- maxHealth = self->client->ps.stats[ STAT_MAX_HEALTH ];
- }
- else if( self->s.eType == ET_BUILDABLE )
- {
- value = BG_Buildable( self->s.modelindex, self->cuboidSize )->value;
-
- // only give partial credits for a buildable not yet completed
- if( !self->spawned )
- {
- value *= (float)( level.time - self->buildTime ) /
- BG_Buildable( self->s.modelindex, self->cuboidSize )->buildTime;
- }
-
- team = self->buildableTeam;
- maxHealth = BG_Buildable( self->s.modelindex, self->cuboidSize )->health;
- }
- else
- return totalDamage;
-
- // Give credits and empty the array
- for( i = 0; i < level.maxclients; i++ )
- {
- int stageValue = value * self->credits[ i ] / totalDamage;
- player = g_entities + i;
-
- if( player->client->pers.teamSelection != team )
- {
- if( totalDamage < maxHealth )
- stageValue *= totalDamage / maxHealth;
-
- if( !self->credits[ i ] || player->client->ps.stats[ STAT_TEAM ] == team )
- continue;
-
- AddScore( player, stageValue );
-
- // killing buildables earns score, but not credits
- if( self->s.eType != ET_BUILDABLE )
- {
- G_AddCreditToClient( player->client, stageValue, qtrue );
-
- // add to stage counters
- if( player->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- alienCredits += stageValue;
- else if( player->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- humanCredits += stageValue;
- }
- }
- self->credits[ i ] = 0;
- }
-
- if( alienCredits )
- {
- trap_Cvar_Set( "g_alienCredits",
- va( "%d", g_alienCredits.integer + alienCredits ) );
- trap_Cvar_Update( &g_alienCredits );
- }
- if( humanCredits )
- {
- trap_Cvar_Set( "g_humanCredits",
- va( "%d", g_humanCredits.integer + humanCredits ) );
- trap_Cvar_Update( &g_humanCredits );
- }
-
- return totalDamage;
-}
-
-/*
-==================
-player_die
-==================
-*/
-void player_die( gentity_t *self, gentity_t *inflictor, gentity_t *attacker, int damage, int meansOfDeath )
-{
- gentity_t *ent, *ent2;
- int anim;
- int killer;
- int i;
- char *killerName, *obit;
- vec3_t dir;
-
- if( self->client->ps.pm_type == PM_DEAD )
- return;
-
- if( level.intermissiontime )
- return;
-
- self->client->ps.pm_type = PM_DEAD;
- self->suicideTime = 0;
-
- if( attacker )
- {
- killer = attacker->s.number;
-
- if( attacker->client )
- killerName = attacker->client->pers.netname;
- else
- killerName = "<world>";
- }
- else
- {
- killer = ENTITYNUM_WORLD;
- killerName = "<world>";
- }
-
- if( meansOfDeath < 0 || meansOfDeath >= sizeof( modNames ) / sizeof( modNames[0] ) )
- // fall back on the number
- obit = va( "%d", meansOfDeath );
- else
- obit = modNames[ meansOfDeath ];
-
- G_LogPrintf( "Die: %d %d %s: %s" S_COLOR_WHITE " killed %s\n",
- killer,
- self - g_entities,
- obit,
- killerName,
- self->client->pers.netname );
-
- // deactivate all upgrades
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- BG_DeactivateUpgrade( i, self->client->ps.stats );
-
- // kill all player's buildables if they havent spawned yet
- // this should eliminate build timer hacks for ever
- dir[0] = dir[1] = 0.0f;
- dir[2] = 1.0f;
-
- for( i = MAX_CLIENTS, ent = g_entities + i; i < level.num_entities; i++, ent++ )
- {
- if( ent->s.eType != ET_BUILDABLE )
- continue;
-
- if( ent == self )
- continue;
-
- if( ent->spawned )
- continue;
-
- if( ent->builtBy != self->client->ps.clientNum )
- continue;
-
- G_Damage( ent, self, attacker, dir, dir, ent->health, 0, MOD_DECONSTRUCT );
- }
-
- // broadcast the death event to everyone
- ent = G_TempEntity( self->r.currentOrigin, EV_OBITUARY );
- ent->s.eventParm = meansOfDeath;
- ent->s.otherEntityNum = self->s.number;
- ent->s.otherEntityNum2 = killer;
- ent->r.svFlags = SVF_BROADCAST; // send to everyone
-
- self->enemy = attacker;
- self->client->ps.persistant[ PERS_KILLED ]++;
-
- if( attacker && attacker->client )
- {
- attacker->client->lastkilled_client = self->s.number;
-
- if( ( attacker == self || OnSameTeam( self, attacker ) ) && meansOfDeath != MOD_HSPAWN )
- {
- //punish team kills and suicides
- if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- {
- G_AddCreditToClient( attacker->client, -ALIEN_TK_SUICIDE_PENALTY, qtrue );
- AddScore( attacker, -ALIEN_TK_SUICIDE_PENALTY );
- }
- else if( attacker->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- {
- G_AddCreditToClient( attacker->client, -HUMAN_TK_SUICIDE_PENALTY, qtrue );
- AddScore( attacker, -HUMAN_TK_SUICIDE_PENALTY );
- }
- }
- }
- else if( attacker->s.eType != ET_BUILDABLE )
- {
- if( self->client->ps.stats[ STAT_TEAM ] == TEAM_ALIENS )
- AddScore( self, -ALIEN_TK_SUICIDE_PENALTY );
- else if( self->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS )
- AddScore( self, -HUMAN_TK_SUICIDE_PENALTY );
- }
-
- // give credits for killing this player
- G_RewardAttackers( self );
-
- ScoreboardMessage( self ); // show scores
-
- // send updated scores to any clients that are following this one,
- // or they would get stale scoreboards
- for( i = 0 ; i < level.maxclients ; i++ )
- {
- gclient_t *client;
-
- client = &level.clients[ i ];
- if( client->pers.connected != CON_CONNECTED )
- continue;
-
- if( client->sess.spectatorState == SPECTATOR_NOT )
- continue;
-
- if( client->sess.spectatorClient == self->s.number )
- ScoreboardMessage( g_entities + i );
- }
-
- VectorCopy( self->s.origin, self->client->pers.lastDeathLocation );
-
- self->takedamage = qfalse; // can still be gibbed
-
- self->s.weapon = WP_NONE;
- self->r.contents = CONTENTS_CORPSE;
-
- self->s.angles[ PITCH ] = 0;
- self->s.angles[ ROLL ] = 0;
- self->s.angles[ YAW ] = self->s.apos.trBase[ YAW ];
- LookAtKiller( self, inflictor, attacker );
-
- VectorCopy( self->s.angles, self->client->ps.viewangles );
-
- self->s.loopSound = 0;
-
- self->r.maxs[ 2 ] = -8;
-
- // don't allow respawn until the death anim is done
- // g_forcerespawn may force spawning at some later time
- self->client->respawnTime = level.time + 1700;
-
- // clear misc
- memset( self->client->ps.misc, 0, sizeof( self->client->ps.misc ) );
-
- {
- // normal death
- static int i;
-
- if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
- {
- switch( i )
- {
- case 0:
- anim = BOTH_DEATH1;
- break;
- case 1:
- anim = BOTH_DEATH2;
- break;
- case 2:
- default:
- anim = BOTH_DEATH3;
- break;
- }
- }
- else
- {
- switch( i )
- {
- case 0:
- anim = NSPA_DEATH1;
- break;
- case 1:
- anim = NSPA_DEATH2;
- break;
- case 2:
- default:
- anim = NSPA_DEATH3;
- break;
- }
- }
-
- self->client->ps.legsAnim =
- ( ( self->client->ps.legsAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
-
- if( !( self->client->ps.persistant[ PERS_STATE ] & PS_NONSEGMODEL ) )
- {
- self->client->ps.torsoAnim =
- ( ( self->client->ps.torsoAnim & ANIM_TOGGLEBIT ) ^ ANIM_TOGGLEBIT ) | anim;
- }
-
- // use own entityid if killed by non-client to prevent uint8_t overflow
- G_AddEvent( self, EV_DEATH1 + i,
- ( killer < MAX_CLIENTS ) ? killer : self - g_entities );
-
- // globally cycle through the different death animations
- i = ( i + 1 ) % 3;
- }
-
- trap_LinkEntity( self );
-}
-
-/*
-===============
-G_ParseDmgScript
-===============
-*/
-static int G_ParseDmgScript( damageRegion_t *regions, char *buf )
-{
- char *token;
- float angleSpan, heightSpan;
- int count;
-
- for( count = 0; ; count++ )
- {
- token = COM_Parse( &buf );
- if( !token[ 0 ] )
- break;
-
- if( strcmp( token, "{" ) )
- {
- COM_ParseError( "Missing {" );
- break;
- }
-
- if( count >= MAX_DAMAGE_REGIONS )
- {
- COM_ParseError( "Max damage regions exceeded" );
- break;
- }
-
- // defaults
- regions[ count ].name[ 0 ] = '\0';
- regions[ count ].minHeight = 0.0f;
- regions[ count ].maxHeight = 1.0f;
- regions[ count ].minAngle = 0.0f;
- regions[ count ].maxAngle = 360.0f;
- regions[ count ].modifier = 1.0f;
- regions[ count ].crouch = qfalse;
-
- while( 1 )
- {
- token = COM_ParseExt( &buf, qtrue );
-
- if( !token[ 0 ] )
- {
- COM_ParseError( "Unexpected end of file" );
- break;
- }
-
- if( !Q_stricmp( token, "}" ) )
- {
- break;
- }
- else if( !strcmp( token, "name" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( token[ 0 ] )
- Q_strncpyz( regions[ count ].name, token,
- sizeof( regions[ count ].name ) );
- }
- else if( !strcmp( token, "minHeight" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( !token[ 0 ] )
- strcpy( token, "0" );
- regions[ count ].minHeight = atof( token );
- }
- else if( !strcmp( token, "maxHeight" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( !token[ 0 ] )
- strcpy( token, "100" );
- regions[ count ].maxHeight = atof( token );
- }
- else if( !strcmp( token, "minAngle" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( !token[ 0 ] )
- strcpy( token, "0" );
- regions[ count ].minAngle = atoi( token );
- }
- else if( !strcmp( token, "maxAngle" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( !token[ 0 ] )
- strcpy( token, "360" );
- regions[ count ].maxAngle = atoi( token );
- }
- else if( !strcmp( token, "modifier" ) )
- {
- token = COM_ParseExt( &buf, qfalse );
- if( !token[ 0 ] )
- strcpy( token, "1.0" );
- regions[ count ].modifier = atof( token );
- }
- else if( !strcmp( token, "crouch" ) )
- {
- regions[ count ].crouch = qtrue;
- }
- else
- {
- COM_ParseWarning("Unknown token \"%s\"", token);
- }
- }
-
- // Angle portion covered
- angleSpan = regions[ count ].maxAngle - regions[ count ].minAngle;
- if( angleSpan < 0.0f )
- angleSpan += 360.0f;
- angleSpan /= 360.0f;
-
- // Height portion covered
- heightSpan = regions[ count ].maxHeight - regions[ count ].minHeight;
- if( heightSpan < 0.0f )
- heightSpan = -heightSpan;
- if( heightSpan > 1.0f )
- heightSpan = 1.0f;
-
- regions[ count ].area = angleSpan * heightSpan;
- if( !regions[ count ].area )
- regions[ count ].area = 0.00001f;
- }
-
- return count;
-}
-
-/*
-============
-GetRegionDamageModifier
-============
-*/
-static float GetRegionDamageModifier( gentity_t *targ, int class, int piece )
-{
- damageRegion_t *regions, *overlap;
- float modifier = 0.0f, areaSum = 0.0f;
- int j, i;
- qboolean crouch;
-
- crouch = targ->client->ps.pm_flags & PMF_DUCKED;
- overlap = &g_damageRegions[ class ][ piece ];
-
- if( g_debugDamage.integer > 2 )
- G_Printf( "GetRegionDamageModifier():\n"
- ". bodyRegion = [%d %d %f %f] (%s)\n"
- ". modifier = %f\n",
- overlap->minAngle, overlap->maxAngle,
- overlap->minHeight, overlap->maxHeight,
- overlap->name, overlap->modifier );
-
- // Find the armour layer modifier, assuming that none of the armour regions
- // overlap and that any areas that are not covered have a modifier of 1.0
- for( j = UP_NONE + 1; j < UP_NUM_UPGRADES; j++ )
- {
- if( !BG_InventoryContainsUpgrade( j, targ->client->ps.stats ) ||
- !g_numArmourRegions[ j ] )
- continue;
- regions = g_armourRegions[ j ];
-
- for( i = 0; i < g_numArmourRegions[ j ]; i++ )
- {
- float overlapMaxA, regionMinA, regionMaxA, angleSpan, heightSpan, area;
-
- if( regions[ i ].crouch != crouch )
- continue;
-
- // Convert overlap angle to 0 to max
- overlapMaxA = overlap->maxAngle - overlap->minAngle;
- if( overlapMaxA < 0.0f )
- overlapMaxA += 360.0f;
-
- // Convert region angles to match overlap
- regionMinA = regions[ i ].minAngle - overlap->minAngle;
- if( regionMinA < 0.0f )
- regionMinA += 360.0f;
- regionMaxA = regions[ i ].maxAngle - overlap->minAngle;
- if( regionMaxA < 0.0f )
- regionMaxA += 360.0f;
-
- // Overlapping Angle portion
- if( regionMinA <= regionMaxA )
- {
- angleSpan = 0.0f;
- if( regionMinA < overlapMaxA )
- {
- if( regionMaxA > overlapMaxA )
- regionMaxA = overlapMaxA;
- angleSpan = regionMaxA - regionMinA;
- }
- }
- else
- {
- if( regionMaxA > overlapMaxA )
- regionMaxA = overlapMaxA;
- angleSpan = regionMaxA;
- if( regionMinA < overlapMaxA )
- angleSpan += overlapMaxA - regionMinA;
- }
- angleSpan /= 360.0f;
-
- // Overlapping height portion
- heightSpan = MIN( overlap->maxHeight, regions[ i ].maxHeight ) -
- MAX( overlap->minHeight, regions[ i ].minHeight );
- if( heightSpan < 0.0f )
- heightSpan = 0.0f;
- if( heightSpan > 1.0f )
- heightSpan = 1.0f;
-
- if( g_debugDamage.integer > 2 )
- G_Printf( ". armourRegion = [%d %d %f %f] (%s)\n"
- ". . modifier = %f\n"
- ". . angleSpan = %f\n"
- ". . heightSpan = %f\n",
- regions[ i ].minAngle, regions[ i ].maxAngle,
- regions[ i ].minHeight, regions[ i ].maxHeight,
- regions[ i ].name, regions[ i ].modifier,
- angleSpan, heightSpan );
-
- areaSum += area = angleSpan * heightSpan;
- modifier += regions[ i ].modifier * area;
- }
- }
-
- if( g_debugDamage.integer > 2 )
- G_Printf( ". areaSum = %f\n"
- ". armourModifier = %f\n", areaSum, modifier );
-
- return overlap->modifier * ( overlap->area + modifier - areaSum );
-}
-
-/*
-============
-GetNonLocDamageModifier
-============
-*/
-static float GetNonLocDamageModifier( gentity_t *targ, int class )
-{
- float modifier = 0.0f, area = 0.0f, scale = 0.0f;
- int i;
- qboolean crouch;
-
- // For every body region, use stretch-armor formula to apply armour modifier
- // for any overlapping area that armour shares with the body region
- crouch = targ->client->ps.pm_flags & PMF_DUCKED;
- for( i = 0; i < g_numDamageRegions[ class ]; i++ )
- {
- damageRegion_t *region;
-
- region = &g_damageRegions[ class ][ i ];
-
- if( region->crouch != crouch )
- continue;
-
- modifier += GetRegionDamageModifier( targ, class, i );
-
- scale += region->modifier * region->area;
- area += region->area;
-
- }
-
- modifier = !scale ? 1.0f : 1.0f + ( modifier / scale - 1.0f ) * area;
-
- if( g_debugDamage.integer > 1 )
- G_Printf( "GetNonLocDamageModifier() modifier:%f, area:%f, scale:%f\n",
- modifier, area, scale );
-
- return modifier;
-}
-
-/*
-============
-GetPointDamageModifier
-
-Returns the damage region given an angle and a height proportion
-============
-*/
-static float GetPointDamageModifier( gentity_t *targ, damageRegion_t *regions,
- int len, float angle, float height )
-{
- float modifier = 1.0f;
- int i;
-
- for( i = 0; i < len; i++ )
- {
- if( regions[ i ].crouch != ( targ->client->ps.pm_flags & PMF_DUCKED ) )
- continue;
-
- // Angle must be within range
- if( ( regions[ i ].minAngle <= regions[ i ].maxAngle &&
- ( angle < regions[ i ].minAngle ||
- angle > regions[ i ].maxAngle ) ) ||
- ( regions[ i ].minAngle > regions[ i ].maxAngle &&
- angle > regions[ i ].maxAngle && angle < regions[ i ].minAngle ) )
- continue;
-
- // Height must be within range
- if( height < regions[ i ].minHeight || height > regions[ i ].maxHeight )
- continue;
-
- modifier *= regions[ i ].modifier;
- }
-
- if( g_debugDamage.integer )
- G_Printf( "GetDamageRegionModifier(angle = %f, height = %f): %f\n",
- angle, height, modifier );
-
- return modifier;
-}
-
-/*
-============
-G_CalcDamageModifier
-============
-*/
-static float G_CalcDamageModifier( vec3_t point, gentity_t *targ, gentity_t *attacker, int class, int dflags )
-{
- vec3_t targOrigin, bulletPath, bulletAngle, pMINUSfloor, floor, normal;
- float clientHeight, hitRelative, hitRatio, modifier;
- int hitRotation, i;
-
- if( point == NULL )
- return 1.0f;
-
- // Don't need to calculate angles and height for non-locational damage
- if( dflags & DAMAGE_NO_LOCDAMAGE )
- return GetNonLocDamageModifier( targ, class );
-
- // Get the point location relative to the floor under the target
- if( g_unlagged.integer && targ->client && targ->client->unlaggedCalc.used )
- VectorCopy( targ->client->unlaggedCalc.origin, targOrigin );
- else
- VectorCopy( targ->r.currentOrigin, targOrigin );
-
- BG_GetClientNormal( &targ->client->ps, normal );
- VectorMA( targOrigin, targ->r.mins[ 2 ], normal, floor );
- VectorSubtract( point, floor, pMINUSfloor );
-
- // Get the proportion of the target height where the hit landed
- clientHeight = targ->r.maxs[ 2 ] - targ->r.mins[ 2 ];
-
- if( !clientHeight )
- clientHeight = 1.0f;
-
- hitRelative = DotProduct( normal, pMINUSfloor ) / VectorLength( normal );
-
- if( hitRelative < 0.0f )
- hitRelative = 0.0f;
-
- if( hitRelative > clientHeight )
- hitRelative = clientHeight;
-
- hitRatio = hitRelative / clientHeight;
-
- // Get the yaw of the attack relative to the target's view yaw
- VectorSubtract( point, targOrigin, bulletPath );
- vectoangles( bulletPath, bulletAngle );
-
- hitRotation = AngleNormalize360( targ->client->ps.viewangles[ YAW ] -
- bulletAngle[ YAW ] );
-
- // Get modifiers from the target's damage regions
- modifier = GetPointDamageModifier( targ, g_damageRegions[ class ],
- g_numDamageRegions[ class ],
- hitRotation, hitRatio );
-
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- if( BG_InventoryContainsUpgrade( i, targ->client->ps.stats ) )
- {
- modifier *= GetPointDamageModifier( targ, g_armourRegions[ i ],
- g_numArmourRegions[ i ],
- hitRotation, hitRatio );
- }
- }
-
- return modifier;
-}
-
-
-/*
-============
-G_InitDamageLocations
-============
-*/
-void G_InitDamageLocations( void )
-{
- char *modelName;
- char filename[ MAX_QPATH ];
- int i;
- int len;
- fileHandle_t fileHandle;
- char buffer[ MAX_DAMAGE_REGION_TEXT ];
-
- for( i = PCL_NONE + 1; i < PCL_NUM_CLASSES; i++ )
- {
- modelName = BG_ClassConfig( i )->modelName;
- Com_sprintf( filename, sizeof( filename ),
- "models/players/%s/locdamage.cfg", modelName );
-
- len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );
- if ( !fileHandle )
- {
- G_Printf( S_COLOR_RED "file not found: %s\n", filename );
- continue;
- }
-
- if( len >= MAX_DAMAGE_REGION_TEXT )
- {
- G_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i",
- filename, len, MAX_DAMAGE_REGION_TEXT );
- trap_FS_FCloseFile( fileHandle );
- continue;
- }
-
- COM_BeginParseSession( filename );
-
- trap_FS_Read( buffer, len, fileHandle );
- buffer[len] = 0;
- trap_FS_FCloseFile( fileHandle );
-
- g_numDamageRegions[ i ] = G_ParseDmgScript( g_damageRegions[ i ], buffer );
- }
-
- for( i = UP_NONE + 1; i < UP_NUM_UPGRADES; i++ )
- {
- modelName = BG_Upgrade( i )->name;
- Com_sprintf( filename, sizeof( filename ), "armour/%s.armour", modelName );
-
- len = trap_FS_FOpenFile( filename, &fileHandle, FS_READ );
-
- //no file - no parsage
- if ( !fileHandle )
- continue;
-
- if( len >= MAX_DAMAGE_REGION_TEXT )
- {
- G_Printf( S_COLOR_RED "file too large: %s is %i, max allowed is %i",
- filename, len, MAX_DAMAGE_REGION_TEXT );
- trap_FS_FCloseFile( fileHandle );
- continue;
- }
-
- COM_BeginParseSession( filename );
-
- trap_FS_Read( buffer, len, fileHandle );
- buffer[len] = 0;
- trap_FS_FCloseFile( fileHandle );
-
- g_numArmourRegions[ i ] = G_ParseDmgScript( g_armourRegions[ i ], buffer );
- }
-}
-
-
-/*
-============
-T_Damage
-
-targ entity that is being damaged
-inflictor entity that is causing the damage
-attacker entity that caused the inflictor to damage targ
- example: targ=monster, inflictor=rocket, attacker=player
-
-dir direction of the attack for knockback
-point point at which the damage is being inflicted, used for headshots
-damage amount of damage being inflicted
-knockback force to be applied against targ as a result of the damage
-
-inflictor, attacker, dir, and point can be NULL for environmental effects
-
-dflags these flags are used to control how T_Damage works
- DAMAGE_RADIUS damage was indirect (from a nearby explosion)
- DAMAGE_NO_ARMOR armor does not protect from this damage
- DAMAGE_NO_KNOCKBACK do not affect velocity, just view angles
- DAMAGE_NO_PROTECTION kills godmode, armor, everything
-============
-*/
-
-// team is the team that is immune to this damage
-void G_SelectiveDamage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
- vec3_t dir, vec3_t point, int damage, int dflags, int mod, int team )
-{
- if( targ->client && ( team != targ->client->ps.stats[ STAT_TEAM ] ) )
- G_Damage( targ, inflictor, attacker, dir, point, damage, dflags, mod );
-}
-
-void G_Damage( gentity_t *targ, gentity_t *inflictor, gentity_t *attacker,
- vec3_t dir, vec3_t point, int damage, int dflags, int mod )
-{
- gclient_t *client;
- int take;
- int asave = 0;
- int knockback;
-
- // Can't deal damage sometimes
- if( !targ->takedamage || targ->health <= 0 || level.intermissionQueued )
- return;
-
- if( !inflictor )
- inflictor = &g_entities[ ENTITYNUM_WORLD ];
-
- if( !attacker )
- attacker = &g_entities[ ENTITYNUM_WORLD ];
-
- // shootable doors / buttons don't actually have any health
- if( targ->s.eType == ET_MOVER )
- {
- if( targ->use && ( targ->moverState == MOVER_POS1 ||
- targ->moverState == ROTATOR_POS1 ) )
- targ->use( targ, inflictor, attacker );
-
- return;
- }
-
- client = targ->client;
- if( client && client->noclip )
- return;
-
- if( !dir )
- dflags |= DAMAGE_NO_KNOCKBACK;
- else
- VectorNormalize( dir );
-
- knockback = damage;
-
- if( inflictor->s.weapon != WP_NONE )
- {
- knockback = (int)( (float)knockback *
- BG_Weapon( inflictor->s.weapon )->knockbackScale );
- }
-
- if( targ->client )
- {
- knockback = (int)( (float)knockback *
- BG_Class( targ->client->ps.stats[ STAT_CLASS ] )->knockbackScale );
- }
-
- // Too much knockback from falling really far makes you "bounce" and
- // looks silly. However, none at all also looks bad. Cap it.
- if( mod == MOD_FALLING && knockback > 50 )
- knockback = 50;
-
- if( knockback > 200 )
- knockback = 200;
-
- if( targ->flags & FL_NO_KNOCKBACK )
- knockback = 0;
-
- if( dflags & DAMAGE_NO_KNOCKBACK )
- knockback = 0;
-
- // figure momentum add, even if the damage won't be taken
- if( knockback && targ->client )
- {
- vec3_t kvel;
- float mass;
-
- mass = 200;
-
- VectorScale( dir, g_knockback.value * (float)knockback / mass, kvel );
- VectorAdd( targ->client->ps.velocity, kvel, targ->client->ps.velocity );
-
- // set the timer so that the other client can't cancel
- // out the movement immediately
- if( !targ->client->ps.pm_time )
- {
- int t;
-
- t = knockback * 2;
- if( t < 50 )
- t = 50;
-
- if( t > 200 )
- t = 200;
-
- targ->client->ps.pm_time = t;
- targ->client->ps.pm_flags |= PMF_TIME_KNOCKBACK;
- }
- }
-
- // don't do friendly fire on movement attacks
- if( ( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
- mod == MOD_LEVEL4_CRUSH ) &&
- targ->s.eType == ET_BUILDABLE && targ->buildableTeam == TEAM_ALIENS )
- {
- return;
- }
-
- // check for completely getting out of the damage
- if( !( dflags & DAMAGE_NO_PROTECTION ) )
- {
-
- // if TF_NO_FRIENDLY_FIRE is set, don't do damage to the target
- // if the attacker was on the same team
- if( targ != attacker && OnSameTeam( targ, attacker ) )
- {
- // don't do friendly fire on movement attacks
- if( mod == MOD_LEVEL4_TRAMPLE || mod == MOD_LEVEL3_POUNCE ||
- mod == MOD_LEVEL4_CRUSH )
- return;
-
- // if dretchpunt is enabled and this is a dretch, do dretchpunt instead of damage
- if( g_dretchPunt.integer &&
- targ->client->ps.stats[ STAT_CLASS ] == PCL_ALIEN_LEVEL0 )
- {
- vec3_t dir, push;
-
- VectorSubtract( targ->r.currentOrigin, attacker->r.currentOrigin, dir );
- VectorNormalizeFast( dir );
- VectorScale( dir, ( damage * 10.0f ), push );
- push[2] = 64.0f;
- VectorAdd( targ->client->ps.velocity, push, targ->client->ps.velocity );
- return;
- }
-
- // check if friendly fire has been disabled
- if( !g_friendlyFire.integer )
- {
- return;
- }
- }
-
- if( targ->s.eType == ET_BUILDABLE && attacker->client &&
- mod != MOD_DECONSTRUCT && mod != MOD_SUICIDE &&
- mod != MOD_REPLACE && mod != MOD_NOCREEP )
- {
- if( targ->buildableTeam == attacker->client->pers.teamSelection &&
- !g_friendlyBuildableFire.integer )
- {
- return;
- }
-
- // base is under attack warning if DCC'd
- if( targ->buildableTeam == TEAM_HUMANS && G_FindDCC( targ ) &&
- level.time > level.humanBaseAttackTimer )
- {
- level.humanBaseAttackTimer = level.time + DC_ATTACK_PERIOD;
- G_BroadcastEvent( EV_DCC_ATTACK, 0 );
- }
- }
-
- // check for godmode
- if ( targ->flags & FL_GODMODE )
- return;
- }
-
- // add to the attacker's hit counter
- if( attacker->client && targ != attacker && targ->health > 0
- && targ->s.eType != ET_MISSILE
- && targ->s.eType != ET_GENERAL )
- {
- if( OnSameTeam( targ, attacker ) )
- attacker->client->ps.persistant[ PERS_HITS ]--;
- else
- attacker->client->ps.persistant[ PERS_HITS ]++;
- }
-
- take = damage;
-
- // add to the damage inflicted on a player this frame
- // the total will be turned into screen blends and view angle kicks
- // at the end of the frame
- if( client )
- {
- if( attacker )
- client->ps.persistant[ PERS_ATTACKER ] = attacker->s.number;
- else
- client->ps.persistant[ PERS_ATTACKER ] = ENTITYNUM_WORLD;
-
- client->damage_armor += asave;
- client->damage_blood += take;
- client->damage_knockback += knockback;
-
- if( dir )
- {
- VectorCopy ( dir, client->damage_from );
- client->damage_fromWorld = qfalse;
- }
- else
- {
- VectorCopy ( targ->r.currentOrigin, client->damage_from );
- client->damage_fromWorld = qtrue;
- }
-
- // set the last client who damaged the target
- targ->client->lasthurt_client = attacker->s.number;
- targ->client->lasthurt_mod = mod;
- take = (int)( take * G_CalcDamageModifier( point, targ, attacker,
- client->ps.stats[ STAT_CLASS ],
- dflags ) + 0.5f );
-
- //if boosted poison every attack
- if( attacker->client && attacker->client->ps.stats[ STAT_STATE ] & SS_BOOSTED )
- {
- if( (targ->client->ps.stats[ STAT_TEAM ] == TEAM_HUMANS ) &&
- mod != MOD_LEVEL2_ZAP && mod != MOD_POISON &&
- mod != MOD_LEVEL1_PCLOUD && mod != MOD_HSPAWN &&
- mod != MOD_ASPAWN && targ->client->poisonImmunityTime < level.time )
- {
- targ->client->ps.stats[ STAT_STATE ] |= SS_POISONED;
- targ->client->lastPoisonTime = level.time;
- targ->client->lastPoisonClient = attacker;
- }
- }
- }
-
- if( take < 1 )
- take = 1;
-
- if( g_debugDamage.integer )
- {
- G_Printf( "%i: client:%i health:%i damage:%i armor:%i\n", level.time, targ->s.number,
- targ->health, take, asave );
- }
-
- // do the damage
- if( take )
- {
- targ->health = targ->health - take;
-
- if( targ->client )
- targ->client->ps.stats[ STAT_HEALTH ] = targ->health;
-
- targ->lastDamageTime = level.time;
- targ->nextRegenTime = level.time + ALIEN_REGEN_DAMAGE_TIME;
-
- // add to the attackers "account" on the target
- if( attacker->client && attacker != targ )
- targ->credits[ attacker->client->ps.clientNum ] += take;
-
- if( targ->health <= 0 )
- {
- if( client )
- targ->flags |= FL_NO_KNOCKBACK;
-
- if( targ->health < -999 )
- targ->health = -999;
-
- targ->enemy = attacker;
- targ->die( targ, inflictor, attacker, take, mod );
- return;
- }
- else if( targ->pain )
- {
- //if(targ->s.eType==ET_BUILDABLE&&BG_Buildable(targ->s.modelindex,NULL)->cuboid)
- // VectorCopy(point,targ->s.angles2);
- targ->pain( targ, attacker, take );
- }
- }
-}
-
-
-/*
-============
-CanDamage
-
-Returns qtrue if the inflictor can directly damage the target. Used for
-explosions and melee attacks.
-============
-*/
-qboolean CanDamage( gentity_t *targ, vec3_t origin )
-{
- vec3_t dest;
- trace_t tr;
- vec3_t midpoint;
-
- // use the midpoint of the bounds instead of the origin, because
- // bmodels may have their origin is 0,0,0
- VectorAdd( targ->r.absmin, targ->r.absmax, midpoint );
- VectorScale( midpoint, 0.5, midpoint );
-
- VectorCopy( midpoint, dest );
- trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
- if( tr.fraction == 1.0 || tr.entityNum == targ->s.number )
- return qtrue;
-
- // this should probably check in the plane of projection,
- // rather than in world coordinate, and also include Z
- VectorCopy( midpoint, dest );
- dest[ 0 ] += 15.0;
- dest[ 1 ] += 15.0;
- trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
- if( tr.fraction == 1.0 )
- return qtrue;
-
- VectorCopy( midpoint, dest );
- dest[ 0 ] += 15.0;
- dest[ 1 ] -= 15.0;
- trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
- if( tr.fraction == 1.0 )
- return qtrue;
-
- VectorCopy( midpoint, dest );
- dest[ 0 ] -= 15.0;
- dest[ 1 ] += 15.0;
- trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
- if( tr.fraction == 1.0 )
- return qtrue;
-
- VectorCopy( midpoint, dest );
- dest[ 0 ] -= 15.0;
- dest[ 1 ] -= 15.0;
- trap_Trace( &tr, origin, vec3_origin, vec3_origin, dest, ENTITYNUM_NONE, MASK_SOLID );
- if( tr.fraction == 1.0 )
- return qtrue;
-
- return qfalse;
-}
-
-/*
-============
-G_SelectiveRadiusDamage
-============
-*/
-qboolean G_SelectiveRadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
- float radius, gentity_t *ignore, int mod, int team )
-{
- float points, dist;
- gentity_t *ent;
- int entityList[ MAX_GENTITIES ];
- int numListedEntities;
- vec3_t mins, maxs;
- vec3_t v;
- vec3_t dir;
- int i, e;
- qboolean hitClient = qfalse;
-
- if( radius < 1 )
- radius = 1;
-
- for( i = 0; i < 3; i++ )
- {
- mins[ i ] = origin[ i ] - radius;
- maxs[ i ] = origin[ i ] + radius;
- }
-
- numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- for( e = 0; e < numListedEntities; e++ )
- {
- ent = &g_entities[ entityList[ e ] ];
-
- if( ent == ignore )
- continue;
-
- if( !ent->takedamage )
- continue;
-
- if( ent->flags & FL_NOTARGET )
- continue;
-
- // find the distance from the edge of the bounding box
- for( i = 0 ; i < 3 ; i++ )
- {
- if( origin[ i ] < ent->r.absmin[ i ] )
- v[ i ] = ent->r.absmin[ i ] - origin[ i ];
- else if( origin[ i ] > ent->r.absmax[ i ] )
- v[ i ] = origin[ i ] - ent->r.absmax[ i ];
- else
- v[ i ] = 0;
- }
-
- dist = VectorLength( v );
- if( dist >= radius )
- continue;
-
- points = damage * ( 1.0 - dist / radius );
-
- if( CanDamage( ent, origin ) && ent->client &&
- ent->client->ps.stats[ STAT_TEAM ] != team )
- {
- VectorSubtract( ent->r.currentOrigin, origin, dir );
- // push the center of mass higher than the origin so players
- // get knocked into the air more
- dir[ 2 ] += 24;
- hitClient = qtrue;
- G_Damage( ent, NULL, attacker, dir, origin,
- (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod );
- }
- }
-
- return hitClient;
-}
-
-
-/*
-============
-G_RadiusDamage
-============
-*/
-qboolean G_RadiusDamage( vec3_t origin, gentity_t *attacker, float damage,
- float radius, gentity_t *ignore, int mod )
-{
- float points, dist;
- gentity_t *ent;
- int entityList[ MAX_GENTITIES ];
- int numListedEntities;
- vec3_t mins, maxs;
- vec3_t v;
- vec3_t dir;
- int i, e;
- qboolean hitClient = qfalse;
-
- if( radius < 1 )
- radius = 1;
-
- for( i = 0; i < 3; i++ )
- {
- mins[ i ] = origin[ i ] - radius;
- maxs[ i ] = origin[ i ] + radius;
- }
-
- numListedEntities = trap_EntitiesInBox( mins, maxs, entityList, MAX_GENTITIES );
-
- for( e = 0; e < numListedEntities; e++ )
- {
- ent = &g_entities[ entityList[ e ] ];
-
- if( ent == ignore )
- continue;
-
- if( !ent->takedamage )
- continue;
-
- // find the distance from the edge of the bounding box
- for( i = 0; i < 3; i++ )
- {
- if( origin[ i ] < ent->r.absmin[ i ] )
- v[ i ] = ent->r.absmin[ i ] - origin[ i ];
- else if( origin[ i ] > ent->r.absmax[ i ] )
- v[ i ] = origin[ i ] - ent->r.absmax[ i ];
- else
- v[ i ] = 0;
- }
-
- dist = VectorLength( v );
- if( dist >= radius )
- continue;
-
- points = damage * ( 1.0 - dist / radius );
-
- if( CanDamage( ent, origin ) )
- {
- VectorSubtract( ent->r.currentOrigin, origin, dir );
- // push the center of mass higher than the origin so players
- // get knocked into the air more
- dir[ 2 ] += 24;
- hitClient = qtrue;
- G_Damage( ent, NULL, attacker, dir, origin,
- (int)points, DAMAGE_RADIUS|DAMAGE_NO_LOCDAMAGE, mod );
- }
- }
-
- return hitClient;
-}
-
-/*
-================
-G_LogDestruction
-
-Log deconstruct/destroy events
-================
-*/
-void G_LogDestruction( gentity_t *self, gentity_t *actor, int mod )
-{
- buildFate_t fate;
-
- switch( mod )
- {
- case MOD_DECONSTRUCT:
- fate = BF_DECONSTRUCT;
- break;
- case MOD_REPLACE:
- fate = BF_REPLACE;
- break;
- case MOD_NOCREEP:
- fate = ( actor->client ) ? BF_UNPOWER : BF_AUTO;
- break;
- default:
- if( actor->client )
- {
- if( actor->client->pers.teamSelection ==
- BG_Buildable( self->s.modelindex, NULL )->team )
- {
- fate = BF_TEAMKILL;
- }
- else
- fate = BF_DESTROY;
- }
- else
- fate = BF_AUTO;
- break;
- }
- G_BuildLogAuto( actor, self, fate );
-
- // don't log when marked structures are removed
- if( mod == MOD_REPLACE )
- return;
-
- G_LogPrintf( S_COLOR_YELLOW "Deconstruct: %d %d %s %s: %s %s by %s\n",
- actor - g_entities,
- self - g_entities,
- BG_Buildable( self->s.modelindex, NULL )->name,
- modNames[ mod ],
- G_CuboidName(self->s.modelindex,self->cuboidSize,qtrue),
- mod == MOD_DECONSTRUCT ? "deconstructed" : "destroyed",
- actor->client ? actor->client->pers.netname : "<world>" );
-
- // No-power deaths for humans come after some minutes and it's confusing
- // when the messages appear attributed to the deconner. Just don't print them.
- if( mod == MOD_NOCREEP && actor->client &&
- actor->client->pers.teamSelection == TEAM_HUMANS )
- return;
-
- if( actor->client && actor->client->pers.teamSelection ==
- BG_Buildable( self->s.modelindex, NULL )->team )
- {
- G_TeamCommand( actor->client->ps.stats[ STAT_TEAM ],
- va( "print \"%s ^3%s^7 by %s\n\"",
- G_CuboidName(self->s.modelindex,self->cuboidSize,qfalse),
- mod == MOD_DECONSTRUCT ? "DECONSTRUCTED" : "DESTROYED",
- actor->client->pers.netname ) );
- }
-
-}
diff --git a/src/game/g_main.c.orig b/src/game/g_main.c.orig
deleted file mode 100644
index 33784c9..0000000
--- a/src/game/g_main.c.orig
+++ /dev/null
@@ -1,2548 +0,0 @@
-/*
-===========================================================================
-Copyright (C) 1999-2005 Id Software, Inc.
-Copyright (C) 2000-2009 Darklegion Development
-
-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,
-or (at your option) any later version.
-
-Tremulous is distributed in the hope that it will be
-useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
-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
-===========================================================================
-*/
-
-#include "g_local.h"
-
-level_locals_t level;
-
-typedef struct
-{
- vmCvar_t *vmCvar;
- char *cvarName;
- char *defaultString;
- int cvarFlags;
- int modificationCount; // for tracking changes
- qboolean trackChange; // track this variable, and announce if changed
- /* certain cvars can be set in worldspawn, but we don't want those values to
- persist, so keep track of non-worldspawn changes and restore that on map
- end. unfortunately, if the server crashes, the value set in worldspawn may
- persist */
- char *explicit;
-} cvarTable_t;
-
-gentity_t g_entities[ MAX_GENTITIES ];
-gclient_t g_clients[ MAX_CLIENTS ];
-
-vmCvar_t g_timelimit;
-vmCvar_t g_suddenDeathTime;
-vmCvar_t g_friendlyFire;
-vmCvar_t g_friendlyBuildableFire;
-vmCvar_t g_dretchPunt;
-vmCvar_t g_password;
-vmCvar_t g_needpass;
-vmCvar_t g_maxclients;
-vmCvar_t g_maxGameClients;
-vmCvar_t g_dedicated;
-vmCvar_t g_speed;
-vmCvar_t g_gravity;
-vmCvar_t g_cheats;
-vmCvar_t g_knockback;
-vmCvar_t g_inactivity;
-vmCvar_t g_debugMove;
-vmCvar_t g_debugDamage;
-vmCvar_t g_motd;
-vmCvar_t g_synchronousClients;
-vmCvar_t g_warmup;
-vmCvar_t g_doWarmup;
-vmCvar_t g_restarted;
-vmCvar_t g_lockTeamsAtStart;
-vmCvar_t g_logFile;
-vmCvar_t g_logFileSync;
-vmCvar_t g_allowVote;
-vmCvar_t g_voteLimit;
-vmCvar_t g_suddenDeathVotePercent;
-vmCvar_t g_suddenDeathVoteDelay;
-vmCvar_t g_teamForceBalance;
-vmCvar_t g_smoothClients;
-vmCvar_t pmove_fixed;
-vmCvar_t pmove_msec;
-vmCvar_t g_minNameChangePeriod;
-vmCvar_t g_maxNameChanges;
-
-vmCvar_t g_alienBuildPoints;
-vmCvar_t g_alienBuildQueueTime;
-vmCvar_t g_humanBuildPoints;
-vmCvar_t g_humanBuildQueueTime;
-vmCvar_t g_humanRepeaterBuildPoints;
-vmCvar_t g_humanRepeaterBuildQueueTime;
-vmCvar_t g_humanRepeaterMaxZones;
-vmCvar_t g_humanStage;
-vmCvar_t g_humanCredits;
-vmCvar_t g_humanMaxStage;
-vmCvar_t g_humanStage2Threshold;
-vmCvar_t g_humanStage3Threshold;
-vmCvar_t g_alienStage;
-vmCvar_t g_alienCredits;
-vmCvar_t g_alienMaxStage;
-vmCvar_t g_alienStage2Threshold;
-vmCvar_t g_alienStage3Threshold;
-vmCvar_t g_teamImbalanceWarnings;
-vmCvar_t g_freeFundPeriod;
-
-vmCvar_t g_unlagged;
-
-vmCvar_t g_disabledEquipment;
-vmCvar_t g_disabledClasses;
-vmCvar_t g_disabledBuildables;
-
-vmCvar_t g_markDeconstruct;
-
-vmCvar_t g_debugMapRotation;
-vmCvar_t g_currentMapRotation;
-vmCvar_t g_mapRotationNodes;
-vmCvar_t g_mapRotationStack;
-vmCvar_t g_nextMap;
-vmCvar_t g_initialMapRotation;
-
-vmCvar_t g_debugVoices;
-vmCvar_t g_voiceChats;
-
-vmCvar_t g_shove;
-
-vmCvar_t g_mapConfigs;
-vmCvar_t g_sayAreaRange;
-
-vmCvar_t g_floodMaxDemerits;
-vmCvar_t g_floodMinTime;
-
-vmCvar_t g_layouts;
-vmCvar_t g_layoutAuto;
-
-vmCvar_t g_emoticonsAllowedInNames;
-
-vmCvar_t g_admin;
-vmCvar_t g_adminTempBan;
-vmCvar_t g_adminMaxBan;
-
-vmCvar_t g_privateMessages;
-vmCvar_t g_specChat;
-vmCvar_t g_publicAdminMessages;
-vmCvar_t g_allowTeamOverlay;
-
-vmCvar_t g_censorship;
-
-vmCvar_t g_tag;
-
-vmCvar_t g_unlimited;
-vmCvar_t g_instantBuild;
-vmCvar_t g_cuboidSizeLimit;
-vmCvar_t g_cuboidMode;
-
-vmCvar_t g_buildableDensityLimit;
-vmCvar_t g_buildableDensityLimitRange;
-
-// copy cvars that can be set in worldspawn so they can be restored later
-static char cv_gravity[ MAX_CVAR_VALUE_STRING ];
-static char cv_humanMaxStage[ MAX_CVAR_VALUE_STRING ];
-static char cv_alienMaxStage[ MAX_CVAR_VALUE_STRING ];
-
-static cvarTable_t gameCvarTable[ ] =
-{
- // don't override the cheat state set by the system
- { &g_cheats, "sv_cheats", "", 0, 0, qfalse },
-
- // noset vars
- { NULL, "gamename", GAME_VERSION , CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
- { NULL, "gamedate", __DATE__ , CVAR_ROM, 0, qfalse },
- { &g_restarted, "g_restarted", "0", CVAR_ROM, 0, qfalse },
- { &g_lockTeamsAtStart, "g_lockTeamsAtStart", "0", CVAR_ROM, 0, qfalse },
- { NULL, "sv_mapname", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
- { NULL, "P", "", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
-
- // latched vars
-
- { &g_maxclients, "sv_maxclients", "8", CVAR_SERVERINFO | CVAR_LATCH | CVAR_ARCHIVE, 0, qfalse },
-
- // change anytime vars
- { &g_maxGameClients, "g_maxGameClients", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qfalse },
-
- { &g_timelimit, "timelimit", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
- { &g_suddenDeathTime, "g_suddenDeathTime", "0", CVAR_SERVERINFO | CVAR_ARCHIVE | CVAR_NORESTART, 0, qtrue },
-
- { &g_synchronousClients, "g_synchronousClients", "0", CVAR_SYSTEMINFO, 0, qfalse },
-
- { &g_friendlyFire, "g_friendlyFire", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
- { &g_friendlyBuildableFire, "g_friendlyBuildableFire", "0", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
- { &g_dretchPunt, "g_dretchPunt", "1", CVAR_ARCHIVE, 0, qtrue },
-
- { &g_teamForceBalance, "g_teamForceBalance", "0", CVAR_ARCHIVE, 0, qtrue },
-
- { &g_warmup, "g_warmup", "10", CVAR_ARCHIVE, 0, qtrue },
- { &g_doWarmup, "g_doWarmup", "0", CVAR_ARCHIVE, 0, qtrue },
- { &g_logFile, "g_logFile", "games.log", CVAR_ARCHIVE, 0, qfalse },
- { &g_logFileSync, "g_logFileSync", "0", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_password, "g_password", "", CVAR_USERINFO, 0, qfalse },
-
- { &g_needpass, "g_needpass", "0", CVAR_SERVERINFO | CVAR_ROM, 0, qfalse },
-
- { &g_dedicated, "dedicated", "0", 0, 0, qfalse },
-
- { &g_speed, "g_speed", "320", 0, 0, qtrue },
- { &g_gravity, "g_gravity", "800", 0, 0, qtrue, cv_gravity },
- { &g_knockback, "g_knockback", "1000", 0, 0, qtrue },
- { &g_inactivity, "g_inactivity", "0", 0, 0, qtrue },
- { &g_debugMove, "g_debugMove", "0", 0, 0, qfalse },
- { &g_debugDamage, "g_debugDamage", "0", 0, 0, qfalse },
- { &g_motd, "g_motd", "", 0, 0, qfalse },
-
- { &g_allowVote, "g_allowVote", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_voteLimit, "g_voteLimit", "5", CVAR_ARCHIVE, 0, qfalse },
- { &g_suddenDeathVotePercent, "g_suddenDeathVotePercent", "74", CVAR_ARCHIVE, 0, qfalse },
- { &g_suddenDeathVoteDelay, "g_suddenDeathVoteDelay", "180", CVAR_ARCHIVE, 0, qfalse },
- { &g_minNameChangePeriod, "g_minNameChangePeriod", "5", 0, 0, qfalse},
- { &g_maxNameChanges, "g_maxNameChanges", "5", 0, 0, qfalse},
-
- { &g_smoothClients, "g_smoothClients", "1", 0, 0, qfalse},
- { &pmove_fixed, "pmove_fixed", "0", CVAR_SYSTEMINFO, 0, qfalse},
- { &pmove_msec, "pmove_msec", "8", CVAR_SYSTEMINFO, 0, qfalse},
-
- { &g_alienBuildPoints, "g_alienBuildPoints", DEFAULT_ALIEN_BUILDPOINTS, 0, 0, qfalse },
- { &g_alienBuildQueueTime, "g_alienBuildQueueTime", DEFAULT_ALIEN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanBuildPoints, "g_humanBuildPoints", DEFAULT_HUMAN_BUILDPOINTS, 0, 0, qfalse },
- { &g_humanBuildQueueTime, "g_humanBuildQueueTime", DEFAULT_HUMAN_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterBuildPoints, "g_humanRepeaterBuildPoints", DEFAULT_HUMAN_REPEATER_BUILDPOINTS, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterMaxZones, "g_humanRepeaterMaxZones", DEFAULT_HUMAN_REPEATER_MAX_ZONES, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanRepeaterBuildQueueTime, "g_humanRepeaterBuildQueueTime", DEFAULT_HUMAN_REPEATER_QUEUE_TIME, CVAR_ARCHIVE, 0, qfalse },
- { &g_humanStage, "g_humanStage", "0", 0, 0, qfalse },
- { &g_humanCredits, "g_humanCredits", "0", 0, 0, qfalse },
- { &g_humanMaxStage, "g_humanMaxStage", DEFAULT_HUMAN_MAX_STAGE, 0, 0, qfalse, cv_humanMaxStage },
- { &g_humanStage2Threshold, "g_humanStage2Threshold", DEFAULT_HUMAN_STAGE2_THRESH, 0, 0, qfalse },
- { &g_humanStage3Threshold, "g_humanStage3Threshold", DEFAULT_HUMAN_STAGE3_THRESH, 0, 0, qfalse },
- { &g_alienStage, "g_alienStage", "0", 0, 0, qfalse },
- { &g_alienCredits, "g_alienCredits", "0", 0, 0, qfalse },
- { &g_alienMaxStage, "g_alienMaxStage", DEFAULT_ALIEN_MAX_STAGE, 0, 0, qfalse, cv_alienMaxStage },
- { &g_alienStage2Threshold, "g_alienStage2Threshold", DEFAULT_ALIEN_STAGE2_THRESH, 0, 0, qfalse },
- { &g_alienStage3Threshold, "g_alienStage3Threshold", DEFAULT_ALIEN_STAGE3_THRESH, 0, 0, qfalse },
- { &g_teamImbalanceWarnings, "g_teamImbalanceWarnings", "30", CVAR_ARCHIVE, 0, qfalse },
- { &g_freeFundPeriod, "g_freeFundPeriod", DEFAULT_FREEKILL_PERIOD, CVAR_ARCHIVE, 0, qtrue },
-
- { &g_unlagged, "g_unlagged", "1", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
-
- { &g_disabledEquipment, "g_disabledEquipment", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse },
- { &g_disabledClasses, "g_disabledClasses", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse },
- { &g_disabledBuildables, "g_disabledBuildables", "", CVAR_ROM | CVAR_SYSTEMINFO, 0, qfalse },
-
- { &g_sayAreaRange, "g_sayAreaRange", "1000", CVAR_ARCHIVE, 0, qtrue },
-
- { &g_floodMaxDemerits, "g_floodMaxDemerits", "5000", CVAR_ARCHIVE, 0, qfalse },
- { &g_floodMinTime, "g_floodMinTime", "2000", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_markDeconstruct, "g_markDeconstruct", "3", CVAR_SERVERINFO | CVAR_ARCHIVE, 0, qtrue },
-
- { &g_debugMapRotation, "g_debugMapRotation", "0", 0, 0, qfalse },
- { &g_currentMapRotation, "g_currentMapRotation", "-1", 0, 0, qfalse }, // -1 = NOT_ROTATING
- { &g_mapRotationNodes, "g_mapRotationNodes", "", CVAR_ROM, 0, qfalse },
- { &g_mapRotationStack, "g_mapRotationStack", "", CVAR_ROM, 0, qfalse },
- { &g_nextMap, "g_nextMap", "", 0 , 0, qtrue },
- { &g_initialMapRotation, "g_initialMapRotation", "", CVAR_ARCHIVE, 0, qfalse },
- { &g_debugVoices, "g_debugVoices", "0", 0, 0, qfalse },
- { &g_voiceChats, "g_voiceChats", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_shove, "g_shove", "0.0", CVAR_ARCHIVE, 0, qfalse },
- { &g_mapConfigs, "g_mapConfigs", "", CVAR_ARCHIVE, 0, qfalse },
- { NULL, "g_mapConfigsLoaded", "0", CVAR_ROM, 0, qfalse },
-
- { &g_layouts, "g_layouts", "", CVAR_LATCH, 0, qfalse },
- { &g_layoutAuto, "g_layoutAuto", "1", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_emoticonsAllowedInNames, "g_emoticonsAllowedInNames", "1", CVAR_LATCH|CVAR_ARCHIVE, 0, qfalse },
-
- { &g_admin, "g_admin", "admin.dat", CVAR_ARCHIVE, 0, qfalse },
- { &g_adminTempBan, "g_adminTempBan", "2m", CVAR_ARCHIVE, 0, qfalse },
- { &g_adminMaxBan, "g_adminMaxBan", "2w", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_privateMessages, "g_privateMessages", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_specChat, "g_specChat", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_publicAdminMessages, "g_publicAdminMessages", "1", CVAR_ARCHIVE, 0, qfalse },
- { &g_allowTeamOverlay, "g_allowTeamOverlay", "1", CVAR_ARCHIVE, 0, qtrue },
-
- { &g_censorship, "g_censorship", "", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_tag, "g_tag", "gpp", CVAR_INIT, 0, qfalse },
-
- { &g_unlimited, "g_unlimited", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
- { &g_instantBuild, "g_instantBuild", "0", CVAR_ARCHIVE | CVAR_SERVERINFO, 0, qfalse },
- { &g_cuboidSizeLimit, "g_cuboidSizeLimit", "0", CVAR_ARCHIVE, 0, qfalse },
- { &g_cuboidMode, "g_cuboidMode", "0", CVAR_ARCHIVE, 0, qfalse },
-
- { &g_buildableDensityLimit, "g_buildableDensityLimit", "0", CVAR_ARCHIVE, 0, qfalse },
- { &g_buildableDensityLimitRange, "g_buildableDensityLimitRange", "0", CVAR_ARCHIVE, 0, qfalse }
-};
-
-static int gameCvarTableSize = sizeof( gameCvarTable ) / sizeof( gameCvarTable[ 0 ] );
-
-
-void G_InitGame( int levelTime, int randomSeed, int restart );
-void G_RunFrame( int levelTime );
-void G_ShutdownGame( int restart );
-void CheckExitRules( void );
-
-void G_CountSpawns( void );
-void G_CalculateBuildPoints( void );
-
-/*
-================
-vmMain
-
-This is the only way control passes into the module.
-This must be the very first function compiled into the .q3vm file
-================
-*/
-Q_EXPORT intptr_t vmMain( int command, int arg0, int arg1, int arg2, int arg3, int arg4,
- int arg5, int arg6, int arg7, int arg8, int arg9,
- int arg10, int arg11 )
-{
- switch( command )
- {
- case GAME_INIT:
- G_InitGame( arg0, arg1, arg2 );
- return 0;
-
- case GAME_SHUTDOWN:
- G_ShutdownGame( arg0 );
- return 0;
-
- case GAME_CLIENT_CONNECT:
- return (intptr_t)ClientConnect( arg0, arg1 );
-
- case GAME_CLIENT_THINK:
- ClientThink( arg0 );
- return 0;
-
- case GAME_CLIENT_USERINFO_CHANGED:
- ClientUserinfoChanged( arg0, qfalse );
- return 0;
-
- case GAME_CLIENT_DISCONNECT:
- ClientDisconnect( arg0 );
- return 0;
-
- case GAME_CLIENT_BEGIN:
- ClientBegin( arg0 );
- return 0;
-
- case GAME_CLIENT_COMMAND:
- ClientCommand( arg0 );
- return 0;
-
- case GAME_RUN_FRAME:
- G_RunFrame( arg0 );
- return 0;
-
- case GAME_CONSOLE_COMMAND:
- return ConsoleCommand( );
- }
-
- return -1;
-}
-
-
-void QDECL G_Printf( const char *fmt, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, fmt );
- Q_vsnprintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
-
- trap_Print( text );
-}
-
-void QDECL G_Error( const char *fmt, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, fmt );
- Q_vsnprintf( text, sizeof( text ), fmt, argptr );
- va_end( argptr );
-
- trap_Error( text );
-}
-
-/*
-================
-G_FindTeams
-
-Chain together all entities with a matching team field.
-Entity teams are used for item groups and multi-entity mover groups.
-
-All but the first will have the FL_TEAMSLAVE flag set and teammaster field set
-All but the last will have the teamchain field set to the next one
-================
-*/
-void G_FindTeams( void )
-{
- gentity_t *e, *e2;
- int i, j;
- int c, c2;
-
- c = 0;
- c2 = 0;
-
- for( i = 1, e = g_entities+i; i < level.num_entities; i++, e++ )
- {
- if( !e->inuse )
- continue;
-
- if( !e->team )
- continue;
-
- if( e->flags & FL_TEAMSLAVE )
- continue;
-
- e->teammaster = e;
- c++;
- c2++;
-
- for( j = i + 1, e2 = e + 1; j < level.num_entities; j++, e2++ )
- {
- if( !e2->inuse )
- continue;
-
- if( !e2->team )
- continue;
-
- if( e2->flags & FL_TEAMSLAVE )
- continue;
-
- if( !strcmp( e->team, e2->team ) )
- {
- c2++;
- e2->teamchain = e->teamchain;
- e->teamchain = e2;
- e2->teammaster = e;
- e2->flags |= FL_TEAMSLAVE;
-
- // make sure that targets only point at the master
- if( e2->targetname )
- {
- e->targetname = e2->targetname;
- e2->targetname = NULL;
- }
- }
- }
- }
-
- G_Printf( "%i teams with %i entities\n", c, c2 );
-}
-
-
-/*
-=================
-G_RegisterCvars
-=================
-*/
-void G_RegisterCvars( void )
-{
- int i;
- cvarTable_t *cv;
-
- for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ )
- {
- trap_Cvar_Register( cv->vmCvar, cv->cvarName,
- cv->defaultString, cv->cvarFlags );
-
- if( cv->vmCvar )
- cv->modificationCount = cv->vmCvar->modificationCount;
-
- if( cv->explicit )
- strcpy( cv->explicit, cv->vmCvar->string );
- }
-}
-
-/*
-=================
-G_UpdateCvars
-=================
-*/
-void G_UpdateCvars( void )
-{
- int i;
- cvarTable_t *cv;
-
- for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ )
- {
- if( cv->vmCvar )
- {
- trap_Cvar_Update( cv->vmCvar );
-
- if( cv->modificationCount != cv->vmCvar->modificationCount )
- {
- cv->modificationCount = cv->vmCvar->modificationCount;
-
- if( cv->trackChange )
- trap_SendServerCommand( -1, va( "print \"Server: %s changed to %s\n\"",
- cv->cvarName, cv->vmCvar->string ) );
-
- if( !level.spawning && cv->explicit )
- strcpy( cv->explicit, cv->vmCvar->string );
- }
- }
- }
-}
-
-/*
-=================
-G_RestoreCvars
-=================
-*/
-void G_RestoreCvars( void )
-{
- int i;
- cvarTable_t *cv;
-
- for( i = 0, cv = gameCvarTable; i < gameCvarTableSize; i++, cv++ )
- {
- if( cv->vmCvar && cv->explicit )
- trap_Cvar_Set( cv->cvarName, cv->explicit );
- }
-}
-
-/*
-=================
-G_MapConfigs
-=================
-*/
-void G_MapConfigs( const char *mapname )
-{
-
- if( !g_mapConfigs.string[0] )
- return;
-
- if( trap_Cvar_VariableIntegerValue( "g_mapConfigsLoaded" ) )
- return;
-
- trap_SendConsoleCommand( EXEC_APPEND,
- va( "exec \"%s/default.cfg\"\n", g_mapConfigs.string ) );
-
- trap_SendConsoleCommand( EXEC_APPEND,
- va( "exec \"%s/%s.cfg\"\n", g_mapConfigs.string, mapname ) );
-
- trap_Cvar_Set( "g_mapConfigsLoaded", "1" );
-}
-
-/*
-================
-G_CheckCuboidConfig
-
-even tokens are map names
-odd tokens are cuboid modes
-================
-*/
-void G_CheckCuboidConfig(char* mapname)
-{
- char* token;
- char config[MAX_CVAR_VALUE_STRING]; //should be enough for few maps
- qboolean type=qfalse;
- qboolean found=qfalse;
- int mode;
-
- mode=trap_Cvar_VariableIntegerValue("g_cuboidDefaultMode");
- trap_Cvar_VariableStringBuffer("g_cuboidConfig",config,sizeof(config));
-
- while(1)
- {
- COM_Parse(&token);
-
- if(!token)
- break;
-
- if(!type)
- if(!Q_stricmp(token,mapname))
- found=qtrue;
- else
- if(found)
- {
- mode=atoi(token);
- break;
- }
- }
- Com_Printf("cuboids are %s%s^7 on %s\n",(mode==0?"^2ENABLED":(mode==1?"^1DISABLED ON S1":"^1DISABLED")),(found?"":" BY DEFAULT"),mapname);
-}
-
-/*
-============
-G_InitGame
-
-============
-*/
-void G_InitGame( int levelTime, int randomSeed, int restart )
-{
- int i;
- char map[ MAX_CVAR_VALUE_STRING ] = {""};
-
- srand( randomSeed );
-
- G_RegisterCvars( );
-
- G_Printf( "------- Game Initialization -------\n" );
- G_Printf( "gamename: %s\n", GAME_VERSION );
- G_Printf( "gamedate: %s\n", __DATE__ );
-
- BG_InitMemory( );
-
- // set some level globals
- memset( &level, 0, sizeof( level ) );
- level.time = levelTime;
- level.startTime = levelTime;
- level.alienStage2Time = level.alienStage3Time =
- level.humanStage2Time = level.humanStage3Time = level.startTime;
-
- level.snd_fry = G_SoundIndex( "sound/misc/fry.wav" ); // FIXME standing in lava / slime
-
- if( g_logFile.string[ 0 ] )
- {
- if( g_logFileSync.integer )
- trap_FS_FOpenFile( g_logFile.string, &level.logFile, FS_APPEND_SYNC );
- else
- trap_FS_FOpenFile( g_logFile.string, &level.logFile, FS_APPEND );
-
- if( !level.logFile )
- G_Printf( "WARNING: Couldn't open logfile: %s\n", g_logFile.string );
- else
- {
- char serverinfo[ MAX_INFO_STRING ];
- qtime_t qt;
- int t;
-
- trap_GetServerinfo( serverinfo, sizeof( serverinfo ) );
-
- G_LogPrintf( "------------------------------------------------------------\n" );
- G_LogPrintf( "InitGame: %s\n", serverinfo );
-
- t = trap_RealTime( &qt );
- G_LogPrintf("RealTime: %04i/%02i/%02i %02i:%02i:%02i\n",
- qt.tm_year+1900, qt.tm_mon+1, qt.tm_mday,
- qt.tm_hour, qt.tm_min, qt.tm_sec );
-
- }
- }
- else
- G_Printf( "Not logging to disk\n" );
-
- trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
- G_MapConfigs( map );
-
- // we're done with g_mapConfigs, so reset this for the next map
- trap_Cvar_Set( "g_mapConfigsLoaded", "0" );
-
- G_RegisterCommands( );
- G_admin_readconfig( NULL );
- G_LoadCensors( );
-
- // initialize all entities for this game
- memset( g_entities, 0, MAX_GENTITIES * sizeof( g_entities[ 0 ] ) );
- level.gentities = g_entities;
-
- // initialize all clients for this game
- level.maxclients = g_maxclients.integer;
- memset( g_clients, 0, MAX_CLIENTS * sizeof( g_clients[ 0 ] ) );
- level.clients = g_clients;
-
- // set client fields on player ents
- for( i = 0; i < level.maxclients; i++ )
- g_entities[ i ].client = level.clients + i;
-
- // always leave room for the max number of clients,
- // even if they aren't all used, so numbers inside that
- // range are NEVER anything but clients
- level.num_entities = MAX_CLIENTS;
-
- // let the server system know where the entites are
- trap_LocateGameData( level.gentities, level.num_entities, sizeof( gentity_t ),
- &level.clients[ 0 ].ps, sizeof( level.clients[ 0 ] ) );
-
- level.emoticonCount = BG_LoadEmoticons( level.emoticons, MAX_EMOTICONS );
-
- trap_SetConfigstring( CS_INTERMISSION, "0" );
-
- // test to see if a custom buildable layout will be loaded
- G_LayoutSelect( );
-
- // this has to be flipped after the first UpdateCvars
- level.spawning = qtrue;
- // parse the key/value pairs and spawn gentities
- G_SpawnEntitiesFromString( );
-
- // load up a custom building layout if there is one
- G_LayoutLoad( );
-
- // find out g_cuboidMode value
- G_CheckCuboidConfig( map );
-
- // the map might disable some things
- BG_InitAllowedGameElements( );
-
- // general initialization
- G_FindTeams( );
-
- BG_InitClassConfigs( );
- BG_InitBuildableConfigs( );
- G_InitDamageLocations( );
- G_InitMapRotations( );
- G_InitSpawnQueue( &level.alienSpawnQueue );
- G_InitSpawnQueue( &level.humanSpawnQueue );
-
- if( g_debugMapRotation.integer )
- G_PrintRotations( );
-
- level.voices = BG_VoiceInit( );
- BG_PrintVoices( level.voices, g_debugVoices.integer );
-
- //reset stages
- trap_Cvar_Set( "g_alienStage", va( "%d", S1 ) );
- trap_Cvar_Set( "g_humanStage", va( "%d", S1 ) );
- trap_Cvar_Set( "g_alienCredits", 0 );
- trap_Cvar_Set( "g_humanCredits", 0 );
- level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000;
-
- G_Printf( "-----------------------------------\n" );
-
- // So the server counts the spawns without a client attached
- G_CountSpawns( );
-
- G_UpdateTeamConfigStrings( );
-
- if( g_lockTeamsAtStart.integer )
- {
- level.alienTeamLocked = qtrue;
- level.humanTeamLocked = qtrue;
- trap_Cvar_Set( "g_lockTeamsAtStart", "0" );
- }
-}
-
-/*
-==================
-G_ClearVotes
-
-remove all currently active votes
-==================
-*/
-static void G_ClearVotes( void )
-{
- int i;
- memset( level.voteTime, 0, sizeof( level.voteTime ) );
- for( i = 0; i < NUM_TEAMS; i++ )
- {
- trap_SetConfigstring( CS_VOTE_TIME + i, "" );
- trap_SetConfigstring( CS_VOTE_STRING + i, "" );
- }
-}
-
-/*
-=================
-G_ShutdownGame
-=================
-*/
-void G_ShutdownGame( int restart )
-{
- // in case of a map_restart
- G_ClearVotes( );
-
- G_RestoreCvars( );
-
- G_Printf( "==== ShutdownGame ====\n" );
-
- if( level.logFile )
- {
- G_LogPrintf( "ShutdownGame:\n" );
- G_LogPrintf( "------------------------------------------------------------\n" );
- trap_FS_FCloseFile( level.logFile );
- level.logFile = 0;
- }
-
- // write all the client session data so we can get it back
- G_WriteSessionData( );
-
- G_admin_cleanup( );
- G_namelog_cleanup( );
- G_UnregisterCommands( );
-
- G_ShutdownMapRotations( );
-
- level.restarted = qfalse;
- level.surrenderTeam = TEAM_NONE;
- trap_SetConfigstring( CS_WINNER, "" );
-}
-
-
-
-//===================================================================
-
-void QDECL Com_Error( int level, const char *error, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, error );
- Q_vsnprintf( text, sizeof( text ), error, argptr );
- va_end( argptr );
-
- G_Error( "%s", text );
-}
-
-void QDECL Com_Printf( const char *msg, ... )
-{
- va_list argptr;
- char text[ 1024 ];
-
- va_start( argptr, msg );
- Q_vsnprintf( text, sizeof( text ), msg, argptr );
- va_end( argptr );
-
- G_Printf( "%s", text );
-}
-
-/*
-========================================================================
-
-PLAYER COUNTING / SCORE SORTING
-
-========================================================================
-*/
-
-
-/*
-=============
-SortRanks
-
-=============
-*/
-int QDECL SortRanks( const void *a, const void *b )
-{
- gclient_t *ca, *cb;
-
- ca = &level.clients[ *(int *)a ];
- cb = &level.clients[ *(int *)b ];
-
- // then sort by score
- if( ca->ps.persistant[ PERS_SCORE ] > cb->ps.persistant[ PERS_SCORE ] )
- return -1;
- if( ca->ps.persistant[ PERS_SCORE ] < cb->ps.persistant[ PERS_SCORE ] )
- return 1;
- else
- return 0;
-}
-
-/*
-============
-G_InitSpawnQueue
-
-Initialise a spawn queue
-============
-*/
-void G_InitSpawnQueue( spawnQueue_t *sq )
-{
- int i;
-
- sq->back = sq->front = 0;
- sq->back = QUEUE_MINUS1( sq->back );
-
- //0 is a valid clientNum, so use something else
- for( i = 0; i < MAX_CLIENTS; i++ )
- sq->clients[ i ] = -1;
-}
-
-/*
-============
-G_GetSpawnQueueLength
-
-Return the length of a spawn queue
-============
-*/
-int G_GetSpawnQueueLength( spawnQueue_t *sq )
-{
- int length = sq->back - sq->front + 1;
-
- while( length < 0 )
- length += MAX_CLIENTS;
-
- while( length >= MAX_CLIENTS )
- length -= MAX_CLIENTS;
-
- return length;
-}
-
-/*
-============
-G_PopSpawnQueue
-
-Remove from front element from a spawn queue
-============
-*/
-int G_PopSpawnQueue( spawnQueue_t *sq )
-{
- int clientNum = sq->clients[ sq->front ];
-
- if( G_GetSpawnQueueLength( sq ) > 0 )
- {
- sq->clients[ sq->front ] = -1;
- sq->front = QUEUE_PLUS1( sq->front );
- G_StopFollowing( g_entities + clientNum );
- g_entities[ clientNum ].client->ps.pm_flags &= ~PMF_QUEUED;
-
- return clientNum;
- }
- else
- return -1;
-}
-
-/*
-============
-G_PeekSpawnQueue
-
-Look at front element from a spawn queue
-============
-*/
-int G_PeekSpawnQueue( spawnQueue_t *sq )
-{
- return sq->clients[ sq->front ];
-}
-
-/*
-============
-G_SearchSpawnQueue
-
-Look to see if clientNum is already in the spawnQueue
-============
-*/
-qboolean G_SearchSpawnQueue( spawnQueue_t *sq, int clientNum )
-{
- int i;
-
- for( i = 0; i < MAX_CLIENTS; i++ )
- {
- if( sq->clients[ i ] == clientNum )
- return qtrue;
- }
-
- return qfalse;
-}
-
-/*
-============
-G_PushSpawnQueue
-
-Add an element to the back of the spawn queue
-============
-*/
-qboolean G_PushSpawnQueue( spawnQueue_t *sq, int clientNum )
-{
- // don't add the same client more than once
- if( G_SearchSpawnQueue( sq, clientNum ) )
- return qfalse;
-
- sq->back = QUEUE_PLUS1( sq->back );
- sq->clients[ sq->back ] = clientNum;
-
- g_entities[ clientNum ].client->ps.pm_flags |= PMF_QUEUED;
- return qtrue;
-}
-
-/*
-============
-G_RemoveFromSpawnQueue
-
-remove a specific client from a spawn queue
-============
-*/
-qboolean G_RemoveFromSpawnQueue( spawnQueue_t *sq, int clientNum )
-{
- int i = sq->front;
-
- if( G_GetSpawnQueueLength( sq ) )
- {
- do
- {
- if( sq->clients[ i ] == clientNum )
- {
- //and this kids is why it would have
- //been better to use an LL for internal
- //representation
- do
- {
- sq->clients[ i ] = sq->clients[ QUEUE_PLUS1( i ) ];
-
- i = QUEUE_PLUS1( i );
- } while( i != QUEUE_PLUS1( sq->back ) );
-
- sq->back = QUEUE_MINUS1( sq->back );
- g_entities[ clientNum ].client->ps.pm_flags &= ~PMF_QUEUED;
-
- return qtrue;
- }
-
- i = QUEUE_PLUS1( i );
- } while( i != QUEUE_PLUS1( sq->back ) );
- }
-
- return qfalse;
-}
-
-/*
-============
-G_GetPosInSpawnQueue
-
-Get the position of a client in a spawn queue
-============
-*/
-int G_GetPosInSpawnQueue( spawnQueue_t *sq, int clientNum )
-{
- int i = sq->front;
-
- if( G_GetSpawnQueueLength( sq ) )
- {
- do
- {
- if( sq->clients[ i ] == clientNum )
- {
- if( i < sq->front )
- return i + MAX_CLIENTS - sq->front;
- else
- return i - sq->front;
- }
-
- i = QUEUE_PLUS1( i );
- } while( i != QUEUE_PLUS1( sq->back ) );
- }
-
- return -1;
-}
-
-/*
-============
-G_PrintSpawnQueue
-
-Print the contents of a spawn queue
-============
-*/
-void G_PrintSpawnQueue( spawnQueue_t *sq )
-{
- int i = sq->front;
- int length = G_GetSpawnQueueLength( sq );
-
- G_Printf( "l:%d f:%d b:%d :", length, sq->front, sq->back );
-
- if( length > 0 )
- {
- do
- {
- if( sq->clients[ i ] == -1 )
- G_Printf( "*:" );
- else
- G_Printf( "%d:", sq->clients[ i ] );
-
- i = QUEUE_PLUS1( i );
- } while( i != QUEUE_PLUS1( sq->back ) );
- }
-
- G_Printf( "\n" );
-}
-
-/*
-============
-G_SpawnClients
-
-Spawn queued clients
-============
-*/
-void G_SpawnClients( team_t team )
-{
- int clientNum;
- gentity_t *ent, *spawn;
- vec3_t spawn_origin, spawn_angles;
- spawnQueue_t *sq = NULL;
- int numSpawns = 0;
-
- if( team == TEAM_ALIENS )
- {
- sq = &level.alienSpawnQueue;
- numSpawns = level.numAlienSpawns;
- }
- else if( team == TEAM_HUMANS )
- {
- sq = &level.humanSpawnQueue;
- numSpawns = level.numHumanSpawns;
- }
-
- if( G_GetSpawnQueueLength( sq ) > 0 && numSpawns > 0 )
- {
- clientNum = G_PeekSpawnQueue( sq );
- ent = &g_entities[ clientNum ];
-
- if( ( spawn = G_SelectTremulousSpawnPoint( team,
- ent->client->pers.lastDeathLocation,
- spawn_origin, spawn_angles ) ) )
- {
- clientNum = G_PopSpawnQueue( sq );
-
- if( clientNum < 0 )
- return;
-
- ent = &g_entities[ clientNum ];
-
- ent->client->sess.spectatorState = SPECTATOR_NOT;
- ClientUserinfoChanged( clientNum, qfalse );
- ClientSpawn( ent, spawn, spawn_origin, spawn_angles );
- }
- }
-}
-
-/*
-============
-G_CountSpawns
-
-Counts the number of spawns for each team
-============
-*/
-void G_CountSpawns( void )
-{
- int i;
- gentity_t *ent;
-
- level.numAlienSpawns = 0;
- level.numHumanSpawns = 0;
- for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ )
- {
- if( !ent->inuse || ent->s.eType != ET_BUILDABLE || ent->health <= 0 )
- continue;
-
- if( ent->s.modelindex == BA_A_SPAWN )
- level.numAlienSpawns++;
-
- if( ent->s.modelindex == BA_H_SPAWN )
- level.numHumanSpawns++;
- }
-}
-
-
-/*
-============
-G_TimeTilSuddenDeath
-============
-*/
-#define SUDDENDEATHWARNING 60000
-int G_TimeTilSuddenDeath( void )
-{
- if( ( !g_suddenDeathTime.integer && level.suddenDeathBeginTime == 0 ) ||
- ( level.suddenDeathBeginTime < 0 ) )
- return SUDDENDEATHWARNING + 1; // Always some time away
-
- return ( ( level.suddenDeathBeginTime ) - ( level.time - level.startTime ) );
-}
-
-
-#define PLAYER_COUNT_MOD 5.0f
-
-/*
-============
-G_CalculateBuildPoints
-
-Recalculate the quantity of building points available to the teams
-============
-*/
-void G_CalculateBuildPoints( void )
-{
- int i;
- buildable_t buildable;
- buildPointZone_t *zone;
-
- // BP queue updates
- while( level.alienBuildPointQueue > 0 &&
- level.alienNextQueueTime < level.time )
- {
- level.alienBuildPointQueue--;
- level.alienNextQueueTime += G_NextQueueTime( level.alienBuildPointQueue,
- g_alienBuildPoints.integer,
- g_alienBuildQueueTime.integer );
- }
-
- while( level.humanBuildPointQueue > 0 &&
- level.humanNextQueueTime < level.time )
- {
- level.humanBuildPointQueue--;
- level.humanNextQueueTime += G_NextQueueTime( level.humanBuildPointQueue,
- g_humanBuildPoints.integer,
- g_humanBuildQueueTime.integer );
- }
-
- // Sudden Death checks
- if( G_TimeTilSuddenDeath( ) <= 0 && level.suddenDeathWarning < TW_PASSED )
- {
- G_LogPrintf( "Beginning Sudden Death\n" );
- trap_SendServerCommand( -1, "cp \"Sudden Death!\"" );
- trap_SendServerCommand( -1, "print \"Beginning Sudden Death.\n\"" );
- level.suddenDeathWarning = TW_PASSED;
- G_ClearDeconMarks( );
-
- // Clear blueprints, or else structs that cost 0 BP can still be built after SD
- for( i = 0; i < level.maxclients; i++ )
- {
- if( g_entities[ i ].client->ps.stats[ STAT_BUILDABLE ] != BA_NONE )
- g_entities[ i ].client->ps.stats[ STAT_BUILDABLE ] = BA_NONE;
- }
- }
- else if( G_TimeTilSuddenDeath( ) <= SUDDENDEATHWARNING &&
- level.suddenDeathWarning < TW_IMMINENT )
- {
- trap_SendServerCommand( -1, va( "cp \"Sudden Death in %d seconds!\"",
- (int)( G_TimeTilSuddenDeath( ) / 1000 ) ) );
- trap_SendServerCommand( -1, va( "print \"Sudden Death will begin in %d seconds.\n\"",
- (int)( G_TimeTilSuddenDeath( ) / 1000 ) ) );
- level.suddenDeathWarning = TW_IMMINENT;
- }
-
- level.humanBuildPoints = g_humanBuildPoints.integer - level.humanBuildPointQueue;
- level.alienBuildPoints = g_alienBuildPoints.integer - level.alienBuildPointQueue;
-
- // Reset buildPointZones
- for( i = 0; i < g_humanRepeaterMaxZones.integer; i++ )
- {
- buildPointZone_t *zone = &level.buildPointZones[ i ];
-
- zone->active = qfalse;
- zone->totalBuildPoints = g_humanRepeaterBuildPoints.integer;
- }
-
- // Iterate through entities
- for( i = MAX_CLIENTS; i < level.num_entities; i++ )
- {
- gentity_t *ent = &g_entities[ i ];
- buildPointZone_t *zone;
- buildable_t buildable;
- int cost;
-
- if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD )
- continue;
-
- // mark a zone as active
- if( ent->usesBuildPointZone )
- {
- assert( ent->buildPointZone >= 0 && ent->buildPointZone < g_humanRepeaterMaxZones.integer );
-
- zone = &level.buildPointZones[ ent->buildPointZone ];
- zone->active = qtrue;
- }
-
- // Subtract the BP from the appropriate pool
- buildable = ent->s.modelindex;
- cost = BG_Buildable( buildable, ent->cuboidSize )->buildPoints;
-
- if( ent->buildableTeam == TEAM_ALIENS )
- level.alienBuildPoints -= cost;
- if( buildable == BA_H_REPEATER )
- level.humanBuildPoints -= cost;
- else if( buildable != BA_H_REACTOR )
- {
- gentity_t *power = G_PowerEntityForEntity( ent );
-
- if( power )
- {
- if( power->s.modelindex == BA_H_REACTOR )
- level.humanBuildPoints -= cost;
- else if( power->s.modelindex == BA_H_REPEATER && power->usesBuildPointZone )
- level.buildPointZones[ power->buildPointZone ].totalBuildPoints -= cost;
- }
- }
- }
-
- // Finally, update repeater zones and their queues
- // note that this has to be done after the used BP is calculated
- for( i = MAX_CLIENTS; i < level.num_entities; i++ )
- {
- gentity_t *ent = &g_entities[ i ];
-
- if( ent->s.eType != ET_BUILDABLE || ent->s.eFlags & EF_DEAD ||
- ent->buildableTeam != TEAM_HUMANS )
- continue;
-
- buildable = ent->s.modelindex;
-
- if( buildable != BA_H_REPEATER )
- continue;
-
- if( ent->usesBuildPointZone && level.buildPointZones[ ent->buildPointZone ].active )
- {
- zone = &level.buildPointZones[ ent->buildPointZone ];
-
- if( G_TimeTilSuddenDeath( ) > 0 )
- {
- // BP queue updates
- while( zone->queuedBuildPoints > 0 &&
- zone->nextQueueTime < level.time )
- {
- zone->queuedBuildPoints--;
- zone->nextQueueTime += G_NextQueueTime( zone->queuedBuildPoints,
- zone->totalBuildPoints,
- g_humanRepeaterBuildQueueTime.integer );
- }
- }
- else
- {
- zone->totalBuildPoints = zone->queuedBuildPoints = 0;
- }
- }
- }
-
- if( level.humanBuildPoints < 0 )
- level.humanBuildPoints = 0;
-
- if( level.alienBuildPoints < 0 )
- level.alienBuildPoints = 0;
-}
-
-/*
-============
-G_CalculateStages
-============
-*/
-void G_CalculateStages( void )
-{
- float alienPlayerCountMod = level.averageNumAlienClients / PLAYER_COUNT_MOD;
- float humanPlayerCountMod = level.averageNumHumanClients / PLAYER_COUNT_MOD;
- int alienNextStageThreshold, humanNextStageThreshold;
- static int lastAlienStageModCount = 1;
- static int lastHumanStageModCount = 1;
-
- if( alienPlayerCountMod < 0.1f )
- alienPlayerCountMod = 0.1f;
-
- if( humanPlayerCountMod < 0.1f )
- humanPlayerCountMod = 0.1f;
-
- if( g_alienCredits.integer >=
- (int)( ceil( (float)g_alienStage2Threshold.integer * alienPlayerCountMod ) ) &&
- g_alienStage.integer == S1 && g_alienMaxStage.integer > S1 )
- {
- trap_Cvar_Set( "g_alienStage", va( "%d", S2 ) );
- level.alienStage2Time = level.time;
- lastAlienStageModCount = g_alienStage.modificationCount;
- G_LogPrintf("Stage: A 2: Aliens reached Stage 2\n");
- }
-
- if( g_alienCredits.integer >=
- (int)( ceil( (float)g_alienStage3Threshold.integer * alienPlayerCountMod ) ) &&
- g_alienStage.integer == S2 && g_alienMaxStage.integer > S2 )
- {
- trap_Cvar_Set( "g_alienStage", va( "%d", S3 ) );
- level.alienStage3Time = level.time;
- lastAlienStageModCount = g_alienStage.modificationCount;
- G_LogPrintf("Stage: A 3: Aliens reached Stage 3\n");
- }
-
- if( g_humanCredits.integer >=
- (int)( ceil( (float)g_humanStage2Threshold.integer * humanPlayerCountMod ) ) &&
- g_humanStage.integer == S1 && g_humanMaxStage.integer > S1 )
- {
- trap_Cvar_Set( "g_humanStage", va( "%d", S2 ) );
- level.humanStage2Time = level.time;
- lastHumanStageModCount = g_humanStage.modificationCount;
- G_LogPrintf("Stage: H 2: Humans reached Stage 2\n");
- }
-
- if( g_humanCredits.integer >=
- (int)( ceil( (float)g_humanStage3Threshold.integer * humanPlayerCountMod ) ) &&
- g_humanStage.integer == S2 && g_humanMaxStage.integer > S2 )
- {
- trap_Cvar_Set( "g_humanStage", va( "%d", S3 ) );
- level.humanStage3Time = level.time;
- lastHumanStageModCount = g_humanStage.modificationCount;
- G_LogPrintf("Stage: H 3: Humans reached Stage 3\n");
- }
-
- if( g_alienStage.modificationCount > lastAlienStageModCount )
- {
- G_Checktrigger_stages( TEAM_ALIENS, g_alienStage.integer );
-
- if( g_alienStage.integer == S2 )
- level.alienStage2Time = level.time;
- else if( g_alienStage.integer == S3 )
- level.alienStage3Time = level.time;
-
- lastAlienStageModCount = g_alienStage.modificationCount;
- }
-
- if( g_humanStage.modificationCount > lastHumanStageModCount )
- {
- G_Checktrigger_stages( TEAM_HUMANS, g_humanStage.integer );
-
- if( g_humanStage.integer == S2 )
- level.humanStage2Time = level.time;
- else if( g_humanStage.integer == S3 )
- level.humanStage3Time = level.time;
-
- lastHumanStageModCount = g_humanStage.modificationCount;
- }
-
- if( g_alienStage.integer == S1 && g_alienMaxStage.integer > S1 )
- alienNextStageThreshold = (int)( ceil( (float)g_alienStage2Threshold.integer * alienPlayerCountMod ) );
- else if( g_alienStage.integer == S2 && g_alienMaxStage.integer > S2 )
- alienNextStageThreshold = (int)( ceil( (float)g_alienStage3Threshold.integer * alienPlayerCountMod ) );
- else
- alienNextStageThreshold = -1;
-
- if( g_humanStage.integer == S1 && g_humanMaxStage.integer > S1 )
- humanNextStageThreshold = (int)( ceil( (float)g_humanStage2Threshold.integer * humanPlayerCountMod ) );
- else if( g_humanStage.integer == S2 && g_humanMaxStage.integer > S2 )
- humanNextStageThreshold = (int)( ceil( (float)g_humanStage3Threshold.integer * humanPlayerCountMod ) );
- else
- humanNextStageThreshold = -1;
-
- // save a lot of bandwidth by rounding thresholds up to the nearest 100
- if( alienNextStageThreshold > 0 )
- alienNextStageThreshold = ceil( (float)alienNextStageThreshold / 100 ) * 100;
-
- if( humanNextStageThreshold > 0 )
- humanNextStageThreshold = ceil( (float)humanNextStageThreshold / 100 ) * 100;
-
- trap_SetConfigstring( CS_ALIEN_STAGES, va( "%d %d %d",
- g_alienStage.integer, g_alienCredits.integer,
- alienNextStageThreshold ) );
-
- trap_SetConfigstring( CS_HUMAN_STAGES, va( "%d %d %d",
- g_humanStage.integer, g_humanCredits.integer,
- humanNextStageThreshold ) );
-}
-
-/*
-============
-CalculateAvgPlayers
-
-Calculates the average number of players playing this game
-============
-*/
-void G_CalculateAvgPlayers( void )
-{
- //there are no clients or only spectators connected, so
- //reset the number of samples in order to avoid the situation
- //where the average tends to 0
- if( !level.numAlienClients )
- {
- level.numAlienSamples = 0;
- trap_Cvar_Set( "g_alienCredits", "0" );
- }
-
- if( !level.numHumanClients )
- {
- level.numHumanSamples = 0;
- trap_Cvar_Set( "g_humanCredits", "0" );
- }
-
- //calculate average number of clients for stats
- level.averageNumAlienClients =
- ( ( level.averageNumAlienClients * level.numAlienSamples )
- + level.numAlienClients ) /
- (float)( level.numAlienSamples + 1 );
- level.numAlienSamples++;
-
- level.averageNumHumanClients =
- ( ( level.averageNumHumanClients * level.numHumanSamples )
- + level.numHumanClients ) /
- (float)( level.numHumanSamples + 1 );
- level.numHumanSamples++;
-}
-
-/*
-============
-CalculateRanks
-
-Recalculates the score ranks of all players
-This will be called on every client connect, begin, disconnect, death,
-and team change.
-============
-*/
-void CalculateRanks( void )
-{
- int i;
- char P[ MAX_CLIENTS + 1 ] = {""};
-
- level.numConnectedClients = 0;
- level.numPlayingClients = 0;
- memset( level.numVotingClients, 0, sizeof( level.numVotingClients ) );
- level.numAlienClients = 0;
- level.numHumanClients = 0;
- level.numLiveAlienClients = 0;
- level.numLiveHumanClients = 0;
-
- for( i = 0; i < level.maxclients; i++ )
- {
- P[ i ] = '-';
- if ( level.clients[ i ].pers.connected != CON_DISCONNECTED )
- {
- level.sortedClients[ level.numConnectedClients ] = i;
- level.numConnectedClients++;
- P[ i ] = (char)'0' + level.clients[ i ].pers.teamSelection;
-
- level.numVotingClients[ TEAM_NONE ]++;
-
- if( level.clients[ i ].pers.connected != CON_CONNECTED )
- continue;
-
- if( level.clients[ i ].pers.teamSelection != TEAM_NONE )
- {
- level.numPlayingClients++;
- if( level.clients[ i ].pers.teamSelection == TEAM_ALIENS )
- {
- level.numAlienClients++;
- if( level.clients[ i ].sess.spectatorState == SPECTATOR_NOT )
- level.numLiveAlienClients++;
- }
- else if( level.clients[ i ].pers.teamSelection == TEAM_HUMANS )
- {
- level.numHumanClients++;
- if( level.clients[ i ].sess.spectatorState == SPECTATOR_NOT )
- level.numLiveHumanClients++;
- }
- }
- }
- }
- level.numNonSpectatorClients = level.numLiveAlienClients +
- level.numLiveHumanClients;
- level.numVotingClients[ TEAM_ALIENS ] = level.numAlienClients;
- level.numVotingClients[ TEAM_HUMANS ] = level.numHumanClients;
- P[ i ] = '\0';
- trap_Cvar_Set( "P", P );
-
- qsort( level.sortedClients, level.numConnectedClients,
- sizeof( level.sortedClients[ 0 ] ), SortRanks );
-
- // see if it is time to end the level
- CheckExitRules( );
-
- // if we are at the intermission, send the new info to everyone
- if( level.intermissiontime )
- SendScoreboardMessageToAllClients( );
-}
-
-
-/*
-========================================================================
-
-MAP CHANGING
-
-========================================================================
-*/
-
-/*
-========================
-SendScoreboardMessageToAllClients
-
-Do this at BeginIntermission time and whenever ranks are recalculated
-due to enters/exits/forced team changes
-========================
-*/
-void SendScoreboardMessageToAllClients( void )
-{
- int i;
-
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_CONNECTED )
- ScoreboardMessage( g_entities + i );
- }
-}
-
-/*
-========================
-MoveClientToIntermission
-
-When the intermission starts, this will be called for all players.
-If a new client connects, this will be called after the spawn function.
-========================
-*/
-void MoveClientToIntermission( gentity_t *ent )
-{
- // take out of follow mode if needed
- if( ent->client->sess.spectatorState == SPECTATOR_FOLLOW )
- G_StopFollowing( ent );
-
- // move to the spot
- VectorCopy( level.intermission_origin, ent->s.origin );
- VectorCopy( level.intermission_origin, ent->client->ps.origin );
- VectorCopy( level.intermission_angle, ent->client->ps.viewangles );
- ent->client->ps.pm_type = PM_INTERMISSION;
-
- // clean up powerup info
- memset( ent->client->ps.misc, 0, sizeof( ent->client->ps.misc ) );
-
- ent->client->ps.eFlags = 0;
- ent->s.eFlags = 0;
- ent->s.eType = ET_GENERAL;
- ent->s.modelindex = 0;
- ent->s.loopSound = 0;
- ent->s.event = 0;
- ent->r.contents = 0;
-}
-
-/*
-==================
-FindIntermissionPoint
-
-This is also used for spectator spawns
-==================
-*/
-void FindIntermissionPoint( void )
-{
- gentity_t *ent, *target;
- vec3_t dir;
-
- // find the intermission spot
- ent = G_Find( NULL, FOFS( classname ), "info_player_intermission" );
-
- if( !ent )
- { // the map creator forgot to put in an intermission point...
- G_SelectSpawnPoint( vec3_origin, level.intermission_origin, level.intermission_angle );
- }
- else
- {
- VectorCopy( ent->s.origin, level.intermission_origin );
- VectorCopy( ent->s.angles, level.intermission_angle );
- // if it has a target, look towards it
- if( ent->target )
- {
- target = G_PickTarget( ent->target );
-
- if( target )
- {
- VectorSubtract( target->s.origin, level.intermission_origin, dir );
- vectoangles( dir, level.intermission_angle );
- }
- }
- }
-
-}
-
-/*
-==================
-BeginIntermission
-==================
-*/
-void BeginIntermission( void )
-{
- int i;
- gentity_t *client;
-
- if( level.intermissiontime )
- return; // already active
-
- level.intermissiontime = level.time;
-
- G_ClearVotes( );
-
- G_UpdateTeamConfigStrings( );
-
- FindIntermissionPoint( );
-
- // move all clients to the intermission point
- for( i = 0; i < level.maxclients; i++ )
- {
- client = g_entities + i;
-
- if( !client->inuse )
- continue;
-
- // respawn if dead
- if( client->health <= 0 )
- respawn(client);
-
- MoveClientToIntermission( client );
- }
-
- // send the current scoring to all clients
- SendScoreboardMessageToAllClients( );
-}
-
-
-/*
-=============
-ExitLevel
-
-When the intermission has been exited, the server is either moved
-to a new map based on the map rotation or the current map restarted
-=============
-*/
-void ExitLevel( void )
-{
- int i;
- gclient_t *cl;
-
- if ( G_MapExists( g_nextMap.string ) )
- trap_SendConsoleCommand( EXEC_APPEND, va("map \"%s\"\n", g_nextMap.string ) );
- else if( G_MapRotationActive( ) )
- G_AdvanceMapRotation( 0 );
- else
- trap_SendConsoleCommand( EXEC_APPEND, "map_restart\n" );
-
- trap_Cvar_Set( "g_nextMap", "" );
-
- level.restarted = qtrue;
- level.changemap = NULL;
- level.intermissiontime = 0;
-
- // reset all the scores so we don't enter the intermission again
- for( i = 0; i < g_maxclients.integer; i++ )
- {
- cl = level.clients + i;
- if( cl->pers.connected != CON_CONNECTED )
- continue;
-
- cl->ps.persistant[ PERS_SCORE ] = 0;
- }
-
- // we need to do this here before chaning to CON_CONNECTING
- G_WriteSessionData( );
-
- // change all client states to connecting, so the early players into the
- // next level will know the others aren't done reconnecting
- for( i = 0; i < g_maxclients.integer; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_CONNECTED )
- level.clients[ i ].pers.connected = CON_CONNECTING;
- }
-
-}
-
-/*
-=================
-G_AdminMessage
-
-Print to all active server admins, and to the logfile, and to the server console
-=================
-*/
-void G_AdminMessage( gentity_t *ent, const char *msg )
-{
- char string[ 1024 ];
- int i;
-
- Com_sprintf( string, sizeof( string ), "chat %d %d \"%s\"",
- ent ? ent - g_entities : -1,
- G_admin_permission( ent, ADMF_ADMINCHAT ) ? SAY_ADMINS : SAY_ADMINS_PUBLIC,
- msg );
-
- // Send to all appropriate clients
- for( i = 0; i < level.maxclients; i++ )
- if( G_admin_permission( &g_entities[ i ], ADMF_ADMINCHAT ) )
- trap_SendServerCommand( i, string );
-
- // Send to the logfile and server console
- G_LogPrintf( "%s: %d \"%s" S_COLOR_WHITE "\": " S_COLOR_MAGENTA "%s\n",
- G_admin_permission( ent, ADMF_ADMINCHAT ) ? "AdminMsg" : "AdminMsgPublic",
- ent ? ent - g_entities : -1, ent ? ent->client->pers.netname : "console",
- msg );
-}
-
-
-/*
-=================
-G_LogPrintf
-
-Print to the logfile with a time stamp if it is open, and to the server console
-=================
-*/
-void QDECL G_LogPrintf( const char *fmt, ... )
-{
- va_list argptr;
- char string[ 1024 ], decolored[ 1024 ];
- int min, tens, sec;
-
- sec = ( level.time - level.startTime ) / 1000;
-
- min = sec / 60;
- sec -= min * 60;
- tens = sec / 10;
- sec -= tens * 10;
-
- Com_sprintf( string, sizeof( string ), "%3i:%i%i ", min, tens, sec );
-
- va_start( argptr, fmt );
- Q_vsnprintf( string + 7, sizeof( string ) - 7, fmt, argptr );
- va_end( argptr );
-
- if( g_dedicated.integer )
- {
- G_UnEscapeString( string, decolored, sizeof( decolored ) );
- G_Printf( "%s", decolored + 7 );
- }
-
- if( !level.logFile )
- return;
-
- G_DecolorString( string, decolored, sizeof( decolored ) );
- trap_FS_Write( decolored, strlen( decolored ), level.logFile );
-}
-
-/*
-=================
-G_SendGameStat
-=================
-*/
-void G_SendGameStat( team_t team )
-{
- char map[ MAX_STRING_CHARS ];
- char teamChar;
- char data[ BIG_INFO_STRING ];
- char entry[ MAX_STRING_CHARS ];
- int i, dataLength, entryLength;
- gclient_t *cl;
-
- // games with cheats enabled are not very good for balance statistics
- if( g_cheats.integer )
- return;
-
- trap_Cvar_VariableStringBuffer( "mapname", map, sizeof( map ) );
-
- switch( team )
- {
- case TEAM_ALIENS: teamChar = 'A'; break;
- case TEAM_HUMANS: teamChar = 'H'; break;
- case TEAM_NONE: teamChar = 'L'; break;
- default: return;
- }
-
- Com_sprintf( data, BIG_INFO_STRING,
- "%s %s T:%c A:%f H:%f M:%s D:%d SD:%d AS:%d AS2T:%d AS3T:%d HS:%d HS2T:%d HS3T:%d CL:%d",
- Q3_VERSION,
- g_tag.string,
- teamChar,
- level.averageNumAlienClients,
- level.averageNumHumanClients,
- map,
- level.time - level.startTime,
- G_TimeTilSuddenDeath( ),
- g_alienStage.integer,
- level.alienStage2Time - level.startTime,
- level.alienStage3Time - level.startTime,
- g_humanStage.integer,
- level.humanStage2Time - level.startTime,
- level.humanStage3Time - level.startTime,
- level.numConnectedClients );
-
- dataLength = strlen( data );
-
- for( i = 0; i < level.numConnectedClients; i++ )
- {
- int ping;
-
- cl = &level.clients[ level.sortedClients[ i ] ];
-
- if( cl->pers.connected == CON_CONNECTING )
- ping = -1;
- else
- ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
-
- switch( cl->ps.stats[ STAT_TEAM ] )
- {
- case TEAM_ALIENS: teamChar = 'A'; break;
- case TEAM_HUMANS: teamChar = 'H'; break;
- case TEAM_NONE: teamChar = 'S'; break;
- default: return;
- }
-
- Com_sprintf( entry, MAX_STRING_CHARS,
- " \"%s\" %c %d %d %d",
- cl->pers.netname,
- teamChar,
- cl->ps.persistant[ PERS_SCORE ],
- ping,
- ( level.time - cl->pers.enterTime ) / 60000 );
-
- entryLength = strlen( entry );
-
- if( dataLength + entryLength >= BIG_INFO_STRING )
- break;
-
- strcpy( data + dataLength, entry );
- dataLength += entryLength;
- }
-
- trap_SendGameStat( data );
-}
-
-/*
-================
-LogExit
-
-Append information about this game to the log file
-================
-*/
-void LogExit( const char *string )
-{
- int i, numSorted;
- gclient_t *cl;
- gentity_t *ent;
-
- G_LogPrintf( "Exit: %s\n", string );
-
- level.intermissionQueued = level.time;
-
- // this will keep the clients from playing any voice sounds
- // that will get cut off when the queued intermission starts
- trap_SetConfigstring( CS_INTERMISSION, "1" );
-
- // don't send more than 32 scores (FIXME?)
- numSorted = level.numConnectedClients;
- if( numSorted > 32 )
- numSorted = 32;
-
- for( i = 0; i < numSorted; i++ )
- {
- int ping;
-
- cl = &level.clients[ level.sortedClients[ i ] ];
-
- if( cl->ps.stats[ STAT_TEAM ] == TEAM_NONE )
- continue;
-
- if( cl->pers.connected == CON_CONNECTING )
- continue;
-
- ping = cl->ps.ping < 999 ? cl->ps.ping : 999;
-
- G_LogPrintf( "score: %i ping: %i client: %i %s\n",
- cl->ps.persistant[ PERS_SCORE ], ping, level.sortedClients[ i ],
- cl->pers.netname );
-
- }
-
- for( i = 1, ent = g_entities + i ; i < level.num_entities ; i++, ent++ )
- {
- if( !ent->inuse )
- continue;
-
- if( !Q_stricmp( ent->classname, "trigger_win" ) )
- {
- if( level.lastWin == ent->stageTeam )
- ent->use( ent, ent, ent );
- }
- }
-
- G_SendGameStat( level.lastWin );
-}
-
-
-/*
-=================
-CheckIntermissionExit
-
-The level will stay at the intermission for a minimum of 5 seconds
-If all players wish to continue, the level will then exit.
-If one or more players have not acknowledged the continue, the game will
-wait 10 seconds before going on.
-=================
-*/
-void CheckIntermissionExit( void )
-{
- int ready, notReady;
- int i;
- gclient_t *cl;
- clientList_t readyMasks;
-
- //if no clients are connected, just exit
- if( level.numConnectedClients == 0 )
- {
- ExitLevel( );
- return;
- }
-
- // see which players are ready
- ready = 0;
- notReady = 0;
- Com_Memset( &readyMasks, 0, sizeof( readyMasks ) );
- for( i = 0; i < g_maxclients.integer; i++ )
- {
- cl = level.clients + i;
-
- if( cl->pers.connected != CON_CONNECTED )
- continue;
-
- if( cl->ps.stats[ STAT_TEAM ] == TEAM_NONE )
- continue;
-
- if( cl->readyToExit )
- {
- ready++;
-
- Com_ClientListAdd( &readyMasks, i );
- }
- else
- notReady++;
- }
-
- trap_SetConfigstring( CS_CLIENTS_READY, Com_ClientListString( &readyMasks ) );
-
- // never exit in less than five seconds
- if( level.time < level.intermissiontime + 5000 )
- return;
-
- // never let intermission go on for over 1 minute
- if( level.time > level.intermissiontime + 60000 )
- {
- ExitLevel( );
- return;
- }
-
- // if nobody wants to go, clear timer
- if( ready == 0 && notReady > 0 )
- {
- level.readyToExit = qfalse;
- return;
- }
-
- // if everyone wants to go, go now
- if( notReady == 0 )
- {
- ExitLevel( );
- return;
- }
-
- // the first person to ready starts the thirty second timeout
- if( !level.readyToExit )
- {
- level.readyToExit = qtrue;
- level.exitTime = level.time;
- }
-
- // if we have waited thirty seconds since at least one player
- // wanted to exit, go ahead
- if( level.time < level.exitTime + 30000 )
- return;
-
- ExitLevel( );
-}
-
-/*
-=============
-ScoreIsTied
-=============
-*/
-qboolean ScoreIsTied( void )
-{
- int a, b;
-
- if( level.numPlayingClients < 2 )
- return qfalse;
-
- a = level.clients[ level.sortedClients[ 0 ] ].ps.persistant[ PERS_SCORE ];
- b = level.clients[ level.sortedClients[ 1 ] ].ps.persistant[ PERS_SCORE ];
-
- return a == b;
-}
-
-/*
-=================
-CheckExitRules
-
-There will be a delay between the time the exit is qualified for
-and the time everyone is moved to the intermission spot, so you
-can see the last frag.
-=================
-*/
-void CheckExitRules( void )
-{
- // if at the intermission, wait for all non-bots to
- // signal ready, then go to next level
- if( level.intermissiontime )
- {
- CheckIntermissionExit( );
- return;
- }
-
- if( level.intermissionQueued )
- {
- if( level.time - level.intermissionQueued >= INTERMISSION_DELAY_TIME )
- {
- level.intermissionQueued = 0;
- BeginIntermission( );
- }
-
- return;
- }
-
- if( g_timelimit.integer )
- {
- if( level.time - level.startTime >= g_timelimit.integer * 60000 )
- {
- level.lastWin = TEAM_NONE;
- trap_SendServerCommand( -1, "print \"Timelimit hit\n\"" );
- trap_SetConfigstring( CS_WINNER, "Stalemate" );
- LogExit( "Timelimit hit." );
- return;
- }
- else if( level.time - level.startTime >= ( g_timelimit.integer - 5 ) * 60000 &&
- level.timelimitWarning < TW_IMMINENT )
- {
- trap_SendServerCommand( -1, "cp \"5 minutes remaining!\"" );
- level.timelimitWarning = TW_IMMINENT;
- }
- else if( level.time - level.startTime >= ( g_timelimit.integer - 1 ) * 60000 &&
- level.timelimitWarning < TW_PASSED )
- {
- trap_SendServerCommand( -1, "cp \"1 minute remaining!\"" );
- level.timelimitWarning = TW_PASSED;
- }
- }
-
- if( level.uncondHumanWin ||
- ( !level.uncondAlienWin &&
- ( level.time > level.startTime + 1000 ) &&
- ( level.numAlienSpawns == 0 ) &&
- ( level.numLiveAlienClients == 0 ) ) )
- {
- //humans win
- level.lastWin = TEAM_HUMANS;
- trap_SendServerCommand( -1, "print \"Humans win\n\"");
- trap_SetConfigstring( CS_WINNER, "Humans Win" );
- LogExit( "Humans win." );
- }
- else if( level.uncondAlienWin ||
- ( ( level.time > level.startTime + 1000 ) &&
- ( level.numHumanSpawns == 0 ) &&
- ( level.numLiveHumanClients == 0 ) ) )
- {
- //aliens win
- level.lastWin = TEAM_ALIENS;
- trap_SendServerCommand( -1, "print \"Aliens win\n\"");
- trap_SetConfigstring( CS_WINNER, "Aliens Win" );
- LogExit( "Aliens win." );
- }
-}
-
-/*
-==================
-G_Vote
-==================
-*/
-void G_Vote( gentity_t *ent, team_t team, qboolean voting )
-{
- if( !level.voteTime[ team ] )
- return;
-
- if( voting && ent->client->pers.voted & ( 1 << team ) )
- return;
-
- if( !voting && !( ent->client->pers.voted & ( 1 << team ) ) )
- return;
-
- ent->client->pers.voted |= 1 << team;
-
- if( ent->client->pers.vote & ( 1 << team ) )
- {
- if( voting )
- level.voteYes[ team ]++;
- else
- level.voteYes[ team ]--;
-
- trap_SetConfigstring( CS_VOTE_YES + team,
- va( "%d", level.voteYes[ team ] ) );
- }
- else
- {
- if( voting )
- level.voteNo[ team ]++;
- else
- level.voteNo[ team ]--;
-
- trap_SetConfigstring( CS_VOTE_NO + team,
- va( "%d", level.voteNo[ team ] ) );
- }
-}
-
-
-/*
-========================================================================
-
-FUNCTIONS CALLED EVERY FRAME
-
-========================================================================
-*/
-
-
-void G_ExecuteVote( team_t team )
-{
- level.voteExecuteTime[ team ] = 0;
-
- trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n",
- level.voteString[ team ] ) );
-
- if( !Q_stricmpn( level.voteString[ team ], "map", 3 ) )
- level.restarted = qtrue;
-}
-
-/*
-==================
-G_CheckVote
-==================
-*/
-void G_CheckVote( team_t team )
-{
- float votePassThreshold = (float)level.voteThreshold[ team ] / 100.0f;
- qboolean pass = qfalse;
- char *msg;
- int i;
-
- if( level.voteExecuteTime[ team ] &&
- level.voteExecuteTime[ team ] < level.time )
- {
- G_ExecuteVote( team );
- }
-
- if( !level.voteTime[ team ] )
- return;
-
- if( ( level.time - level.voteTime[ team ] >= VOTE_TIME ) ||
- ( level.voteYes[ team ] + level.voteNo[ team ] == level.numVotingClients[ team ] ) )
- {
- pass = ( level.voteYes[ team ] &&
- (float)level.voteYes[ team ] / ( (float)level.voteYes[ team ] + (float)level.voteNo[ team ] ) > votePassThreshold );
- }
- else
- {
- if( (float)level.voteYes[ team ] >
- (float)level.numVotingClients[ team ] * votePassThreshold )
- {
- pass = qtrue;
- }
- else if( (float)level.voteNo[ team ] <=
- (float)level.numVotingClients[ team ] * ( 1.0f - votePassThreshold ) )
- {
- return;
- }
- }
-
- if( pass )
- level.voteExecuteTime[ team ] = level.time + level.voteDelay[ team ];
-
- G_LogPrintf( "EndVote: %s %s %d %d %d\n",
- team == TEAM_NONE ? "global" : BG_TeamName( team ),
- pass ? "pass" : "fail",
- level.voteYes[ team ], level.voteNo[ team ], level.numVotingClients[ team ] );
-
- msg = va( "print \"%sote %sed (%d - %d)\n\"",
- team == TEAM_NONE ? "V" : "Team v", pass ? "pass" : "fail",
- level.voteYes[ team ], level.voteNo[ team ] );
-
- if( team == TEAM_NONE )
- trap_SendServerCommand( -1, msg );
- else
- G_TeamCommand( team, msg );
-
- level.voteTime[ team ] = 0;
- level.voteYes[ team ] = 0;
- level.voteNo[ team ] = 0;
-
- for( i = 0; i < level.maxclients; i++ )
- level.clients[ i ].pers.voted &= ~( 1 << team );
-
- trap_SetConfigstring( CS_VOTE_TIME + team, "" );
- trap_SetConfigstring( CS_VOTE_STRING + team, "" );
- trap_SetConfigstring( CS_VOTE_YES + team, "0" );
- trap_SetConfigstring( CS_VOTE_NO + team, "0" );
-}
-
-
-/*
-==================
-CheckCvars
-==================
-*/
-void CheckCvars( void )
-{
- static int lastPasswordModCount = -1;
- static int lastMarkDeconModCount = -1;
- static int lastSDTimeModCount = -1;
- static int lastNumZones = 0;
-
- if( g_password.modificationCount != lastPasswordModCount )
- {
- lastPasswordModCount = g_password.modificationCount;
-
- if( *g_password.string && Q_stricmp( g_password.string, "none" ) )
- trap_Cvar_Set( "g_needpass", "1" );
- else
- trap_Cvar_Set( "g_needpass", "0" );
- }
-
- // Unmark any structures for deconstruction when
- // the server setting is changed
- if( g_markDeconstruct.modificationCount != lastMarkDeconModCount )
- {
- lastMarkDeconModCount = g_markDeconstruct.modificationCount;
- G_ClearDeconMarks( );
- }
-
- // If we change g_suddenDeathTime during a map, we need to update
- // when sd will begin
- if( g_suddenDeathTime.modificationCount != lastSDTimeModCount )
- {
- lastSDTimeModCount = g_suddenDeathTime.modificationCount;
- level.suddenDeathBeginTime = g_suddenDeathTime.integer * 60000;
- }
-
- // If the number of zones changes, we need a new array
- if( g_humanRepeaterMaxZones.integer != lastNumZones )
- {
- buildPointZone_t *newZones;
- size_t newsize = g_humanRepeaterMaxZones.integer * sizeof( buildPointZone_t );
- size_t oldsize = lastNumZones * sizeof( buildPointZone_t );
-
- newZones = BG_Alloc( newsize );
- if( level.buildPointZones )
- {
- Com_Memcpy( newZones, level.buildPointZones, MIN( oldsize, newsize ) );
- BG_Free( level.buildPointZones );
- }
-
- level.buildPointZones = newZones;
- lastNumZones = g_humanRepeaterMaxZones.integer;
- }
-
- level.frameMsec = trap_Milliseconds( );
-}
-
-/*
-=============
-G_RunThink
-
-Runs thinking code for this frame if necessary
-=============
-*/
-void G_RunThink( gentity_t *ent )
-{
- float thinktime;
-
- thinktime = ent->nextthink;
- if( thinktime <= 0 )
- return;
-
- if( thinktime > level.time )
- return;
-
- ent->nextthink = 0;
- if( !ent->think )
- G_Error( "NULL ent->think" );
-
- ent->think( ent );
-}
-
-/*
-=============
-G_EvaluateAcceleration
-
-Calculates the acceleration for an entity
-=============
-*/
-void G_EvaluateAcceleration( gentity_t *ent, int msec )
-{
- vec3_t deltaVelocity;
- vec3_t deltaAccel;
-
- VectorSubtract( ent->s.pos.trDelta, ent->oldVelocity, deltaVelocity );
- VectorScale( deltaVelocity, 1.0f / (float)msec, ent->acceleration );
-
- VectorSubtract( ent->acceleration, ent->oldAccel, deltaAccel );
- VectorScale( deltaAccel, 1.0f / (float)msec, ent->jerk );
-
- VectorCopy( ent->s.pos.trDelta, ent->oldVelocity );
- VectorCopy( ent->acceleration, ent->oldAccel );
-}
-
-/*
-================
-G_RunFrame
-
-Advances the non-player objects in the world
-================
-*/
-void G_RunFrame( int levelTime )
-{
- int i;
- gentity_t *ent;
- int msec;
- static int ptime3000 = 0;
-
- // if we are waiting for the level to restart, do nothing
- if( level.restarted )
- return;
-
- if( level.pausedTime )
- {
- msec = levelTime - level.time - level.pausedTime;
- level.pausedTime = levelTime - level.time;
-
- ptime3000 += msec;
- while( ptime3000 > 3000 )
- {
- ptime3000 -= 3000;
- trap_SendServerCommand( -1, "cp \"The game has been paused. Please wait.\"" );
-
- if( level.pausedTime >= 110000 && level.pausedTime <= 119000 )
- trap_SendServerCommand( -1, va( "print \"Server: Game will auto-unpause in %d seconds\n\"",
- (int) ( (float) ( 120000 - level.pausedTime ) / 1000.0f ) ) );
- }
-
- // Prevents clients from getting lagged-out messages
- for( i = 0; i < level.maxclients; i++ )
- {
- if( level.clients[ i ].pers.connected == CON_CONNECTED )
- level.clients[ i ].ps.commandTime = levelTime;
- }
-
- if( level.pausedTime > 120000 )
- {
- trap_SendServerCommand( -1, "print \"Server: The game has been unpaused automatically (2 minute max)\n\"" );
- trap_SendServerCommand( -1, "cp \"The game has been unpaused!\"" );
- level.pausedTime = 0;
- }
-
- return;
- }
-
- level.framenum++;
- level.previousTime = level.time;
- level.time = levelTime;
- msec = level.time - level.previousTime;
-
- // get any cvar changes
- G_UpdateCvars( );
- CheckCvars( );
- // now we are done spawning
- level.spawning = qfalse;
-
- //
- // go through all allocated objects
- //
- ent = &g_entities[ 0 ];
-
- for( i = 0; i < level.num_entities; i++, ent++ )
- {
- if( !ent->inuse )
- continue;
-
- // clear events that are too old
- if( level.time - ent->eventTime > EVENT_VALID_MSEC )
- {
- if( ent->s.event )
- {
- ent->s.event = 0; // &= EV_EVENT_BITS;
- if ( ent->client )
- {
- ent->client->ps.externalEvent = 0;
- //ent->client->ps.events[0] = 0;
- //ent->client->ps.events[1] = 0;
- }
- }
-
- if( ent->freeAfterEvent )
- {
- // tempEntities or dropped items completely go away after their event
- G_FreeEntity( ent );
- continue;
- }
- else if( ent->unlinkAfterEvent )
- {
- // items that will respawn will hide themselves after their pickup event
- ent->unlinkAfterEvent = qfalse;
- trap_UnlinkEntity( ent );
- }
- }
-
- // temporary entities don't think
- if( ent->freeAfterEvent )
- continue;
-
- // calculate the acceleration of this entity
- if( ent->evaluateAcceleration )
- G_EvaluateAcceleration( ent, msec );
-
- if( !ent->r.linked && ent->neverFree )
- continue;
-
- if( ent->s.eType == ET_MISSILE )
- {
- G_RunMissile( ent );
- continue;
- }
-
- if( ent->s.eType == ET_BUILDABLE )
- {
- G_BuildableThink( ent, msec );
- continue;
- }
-
- if( ent->s.eType == ET_CORPSE || ent->physicsObject )
- {
- G_Physics( ent, msec );
- continue;
- }
-
- if( ent->s.eType == ET_MOVER )
- {
- G_RunMover( ent );
- continue;
- }
-
- if( i < MAX_CLIENTS )
- {
- G_RunClient( ent );
- continue;
- }
-
- G_RunThink( ent );
- }
-
- // perform final fixups on the players
- ent = &g_entities[ 0 ];
-
- for( i = 0; i < level.maxclients; i++, ent++ )
- {
- if( ent->inuse )
- ClientEndFrame( ent );
- }
-
- // save position information for all active clients
- G_UnlaggedStore( );
-
- G_CountSpawns( );
- G_CalculateBuildPoints( );
- G_CalculateStages( );
-
- G_SpawnClients( TEAM_ALIENS );
- G_SpawnClients( TEAM_HUMANS );
- G_CalculateAvgPlayers( );
- G_UpdateZaps( msec );
-
- // see if it is time to end the level
- CheckExitRules( );
-
- // update to team status?
- CheckTeamStatus( );
-
- // cancel vote if timed out
- for( i = 0; i < NUM_TEAMS; i++ )
- G_CheckVote( i );
-
- level.frameMsec = trap_Milliseconds();
-}
-