diff options
Diffstat (limited to 'src/cgame/cg_servercmds.c')
-rw-r--r-- | src/cgame/cg_servercmds.c | 1016 |
1 files changed, 1016 insertions, 0 deletions
diff --git a/src/cgame/cg_servercmds.c b/src/cgame/cg_servercmds.c new file mode 100644 index 0000000..7fb3e06 --- /dev/null +++ b/src/cgame/cg_servercmds.c @@ -0,0 +1,1016 @@ +/* +=========================================================================== +Copyright (C) 1999-2005 Id Software, Inc. +Copyright (C) 2000-2006 Tim Angus + +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_servercmds.c -- reliably sequenced text commands sent by the server +// these are processed at snapshot transition time, so there will definately +// be a valid snapshot this frame + + +#include "cg_local.h" + +/* +================= +CG_ParseScores + +================= +*/ +static void CG_ParseScores( void ) +{ + int i; + + cg.numScores = atoi( CG_Argv( 1 ) ); + + if( cg.numScores > MAX_CLIENTS ) + cg.numScores = MAX_CLIENTS; + + cg.teamScores[ 0 ] = atoi( CG_Argv( 2 ) ); + cg.teamScores[ 1 ] = atoi( CG_Argv( 3 ) ); + + memset( cg.scores, 0, sizeof( cg.scores ) ); + + if( cg_debugRandom.integer ) + CG_Printf( "cg.numScores: %d\n", cg.numScores ); + + for( i = 0; i < cg.numScores; i++ ) + { + // + cg.scores[ i ].client = atoi( CG_Argv( i * 6 + 4 ) ); + cg.scores[ i ].score = atoi( CG_Argv( i * 6 + 5 ) ); + cg.scores[ i ].ping = atoi( CG_Argv( i * 6 + 6 ) ); + cg.scores[ i ].time = atoi( CG_Argv( i * 6 + 7 ) ); + cg.scores[ i ].weapon = atoi( CG_Argv( i * 6 + 8 ) ); + cg.scores[ i ].upgrade = atoi( CG_Argv( i * 6 + 9 ) ); + + if( cg.scores[ i ].client < 0 || cg.scores[ i ].client >= MAX_CLIENTS ) + cg.scores[ i ].client = 0; + + cgs.clientinfo[ cg.scores[ i ].client ].score = cg.scores[ i ].score; + cgs.clientinfo[ cg.scores[ i ].client ].powerups = 0; + + cg.scores[ i ].team = cgs.clientinfo[ cg.scores[ i ].client ].team; + } +} + +/* +================= +CG_ParseTeamInfo + +================= +*/ +static void CG_ParseTeamInfo( void ) +{ + int i; + int client; + + numSortedTeamPlayers = atoi( CG_Argv( 1 ) ); + + for( i = 0; i < numSortedTeamPlayers; i++ ) + { + client = atoi( CG_Argv( i * 6 + 2 ) ); + + sortedTeamPlayers[ i ] = client; + + cgs.clientinfo[ client ].location = atoi( CG_Argv( i * 6 + 3 ) ); + cgs.clientinfo[ client ].health = atoi( CG_Argv( i * 6 + 4 ) ); + cgs.clientinfo[ client ].armor = atoi( CG_Argv( i * 6 + 5 ) ); + cgs.clientinfo[ client ].curWeapon = atoi( CG_Argv( i * 6 + 6 ) ); + cgs.clientinfo[ client ].powerups = atoi( CG_Argv( i * 6 + 7 ) ); + } +} + + +/* +================ +CG_ParseServerinfo + +This is called explicitly when the gamestate is first received, +and whenever the server updates any serverinfo flagged cvars +================ +*/ +void CG_ParseServerinfo( void ) +{ + const char *info; + char *mapname; + + info = CG_ConfigString( CS_SERVERINFO ); + cgs.timelimit = atoi( Info_ValueForKey( info, "timelimit" ) ); + cgs.maxclients = atoi( Info_ValueForKey( info, "sv_maxclients" ) ); + cgs.markDeconstruct = atoi( Info_ValueForKey( info, "g_markDeconstruct" ) ); + mapname = Info_ValueForKey( info, "mapname" ); + Com_sprintf( cgs.mapname, sizeof( cgs.mapname ), "maps/%s.bsp", mapname ); +} + +/* +================== +CG_ParseWarmup +================== +*/ +static void CG_ParseWarmup( void ) +{ + const char *info; + int warmup; + + info = CG_ConfigString( CS_WARMUP ); + + warmup = atoi( info ); + cg.warmupCount = -1; + + if( warmup == 0 && cg.warmup ) + { + } + + cg.warmup = warmup; +} + +/* +================ +CG_SetConfigValues + +Called on load to set the initial values from configure strings +================ +*/ +void CG_SetConfigValues( void ) +{ + sscanf( CG_ConfigString( CS_BUILDPOINTS ), + "%d %d %d %d %d", &cgs.alienBuildPoints, + &cgs.alienBuildPointsTotal, + &cgs.humanBuildPoints, + &cgs.humanBuildPointsTotal, + &cgs.humanBuildPointsPowered ); + + sscanf( CG_ConfigString( CS_STAGES ), "%d %d %d %d %d %d", &cgs.alienStage, &cgs.humanStage, + &cgs.alienKills, &cgs.humanKills, &cgs.alienNextStageThreshold, &cgs.humanNextStageThreshold ); + sscanf( CG_ConfigString( CS_SPAWNS ), "%d %d", &cgs.numAlienSpawns, &cgs.numHumanSpawns ); + + cgs.levelStartTime = atoi( CG_ConfigString( CS_LEVEL_START_TIME ) ); + cg.warmup = atoi( CG_ConfigString( CS_WARMUP ) ); +} + + +/* +===================== +CG_ShaderStateChanged +===================== +*/ +void CG_ShaderStateChanged( void ) +{ + char originalShader[ MAX_QPATH ]; + char newShader[ MAX_QPATH ]; + char timeOffset[ 16 ]; + const char *o; + char *n, *t; + + o = CG_ConfigString( CS_SHADERSTATE ); + + while( o && *o ) + { + n = strstr( o, "=" ); + + if( n && *n ) + { + strncpy( originalShader, o, n - o ); + originalShader[ n - o ] = 0; + n++; + t = strstr( n, ":" ); + + if( t && *t ) + { + strncpy( newShader, n, t - n ); + newShader[ t - n ] = 0; + } + else + break; + + t++; + o = strstr( t, "@" ); + + if( o ) + { + strncpy( timeOffset, t, o - t ); + timeOffset[ o - t ] = 0; + o++; + trap_R_RemapShader( originalShader, newShader, timeOffset ); + } + } + else + break; + } +} + +/* +================ +CG_AnnounceAlienStageTransistion +================ +*/ +static void CG_AnnounceAlienStageTransistion( stage_t from, stage_t to ) +{ + if( cg.predictedPlayerState.stats[ STAT_PTEAM ] != PTE_ALIENS ) + return; + + trap_S_StartLocalSound( cgs.media.alienStageTransition, CHAN_ANNOUNCER ); + CG_CenterPrint( "We have evolved!", 200, GIANTCHAR_WIDTH * 4 ); +} + +/* +================ +CG_AnnounceHumanStageTransistion +================ +*/ +static void CG_AnnounceHumanStageTransistion( stage_t from, stage_t to ) +{ + if( cg.predictedPlayerState.stats[ STAT_PTEAM ] != PTE_HUMANS ) + return; + + trap_S_StartLocalSound( cgs.media.humanStageTransition, CHAN_ANNOUNCER ); + CG_CenterPrint( "Reinforcements have arrived!", 200, GIANTCHAR_WIDTH * 4 ); +} + +/* +================ +CG_ConfigStringModified + +================ +*/ +static void CG_ConfigStringModified( void ) +{ + const char *str; + int num; + + num = atoi( CG_Argv( 1 ) ); + + // get the gamestate from the client system, which will have the + // new configstring already integrated + trap_GetGameState( &cgs.gameState ); + + // look up the individual string that was modified + str = CG_ConfigString( num ); + + // do something with it if necessary + if( num == CS_MUSIC ) + CG_StartMusic( ); + else if( num == CS_SERVERINFO ) + CG_ParseServerinfo( ); + else if( num == CS_WARMUP ) + CG_ParseWarmup( ); + else if( num == CS_BUILDPOINTS ) + sscanf( str, "%d %d %d %d %d", &cgs.alienBuildPoints, + &cgs.alienBuildPointsTotal, + &cgs.humanBuildPoints, + &cgs.humanBuildPointsTotal, + &cgs.humanBuildPointsPowered ); + else if( num == CS_STAGES ) + { + stage_t oldAlienStage = cgs.alienStage; + stage_t oldHumanStage = cgs.humanStage; + + sscanf( str, "%d %d %d %d %d %d", + &cgs.alienStage, &cgs.humanStage, + &cgs.alienKills, &cgs.humanKills, + &cgs.alienNextStageThreshold, &cgs.humanNextStageThreshold ); + + if( cgs.alienStage != oldAlienStage ) + CG_AnnounceAlienStageTransistion( oldAlienStage, cgs.alienStage ); + + if( cgs.humanStage != oldHumanStage ) + CG_AnnounceHumanStageTransistion( oldHumanStage, cgs.humanStage ); + } + else if( num == CS_SPAWNS ) + sscanf( str, "%d %d", &cgs.numAlienSpawns, &cgs.numHumanSpawns ); + else if( num == CS_LEVEL_START_TIME ) + cgs.levelStartTime = atoi( str ); + else if( num == CS_VOTE_TIME ) + { + cgs.voteTime = atoi( str ); + cgs.voteModified = qtrue; + + if( cgs.voteTime ) + trap_Cvar_Set( "ui_voteActive", "1" ); + else + trap_Cvar_Set( "ui_voteActive", "0" ); + } + else if( num == CS_VOTE_YES ) + { + cgs.voteYes = atoi( str ); + cgs.voteModified = qtrue; + } + else if( num == CS_VOTE_NO ) + { + cgs.voteNo = atoi( str ); + cgs.voteModified = qtrue; + } + else if( num == CS_VOTE_STRING ) + Q_strncpyz( cgs.voteString, str, sizeof( cgs.voteString ) ); + else if( num >= CS_TEAMVOTE_TIME && num <= CS_TEAMVOTE_TIME + 1 ) + { + int cs_offset = num - CS_TEAMVOTE_TIME; + + cgs.teamVoteTime[ cs_offset ] = atoi( str ); + cgs.teamVoteModified[ cs_offset ] = qtrue; + + if( cs_offset == 0 ) + { + if( cgs.teamVoteTime[ cs_offset ] ) + trap_Cvar_Set( "ui_humanTeamVoteActive", "1" ); + else + trap_Cvar_Set( "ui_humanTeamVoteActive", "0" ); + } + else if( cs_offset == 1 ) + { + if( cgs.teamVoteTime[ cs_offset ] ) + trap_Cvar_Set( "ui_alienTeamVoteActive", "1" ); + else + trap_Cvar_Set( "ui_alienTeamVoteActive", "0" ); + } + } + else if( num >= CS_TEAMVOTE_YES && num <= CS_TEAMVOTE_YES + 1 ) + { + cgs.teamVoteYes[ num - CS_TEAMVOTE_YES ] = atoi( str ); + cgs.teamVoteModified[ num - CS_TEAMVOTE_YES ] = qtrue; + } + else if( num >= CS_TEAMVOTE_NO && num <= CS_TEAMVOTE_NO + 1 ) + { + cgs.teamVoteNo[ num - CS_TEAMVOTE_NO ] = atoi( str ); + cgs.teamVoteModified[ num - CS_TEAMVOTE_NO ] = qtrue; + } + else if( num >= CS_TEAMVOTE_STRING && num <= CS_TEAMVOTE_STRING + 1 ) + { + Q_strncpyz( cgs.teamVoteString[ num - CS_TEAMVOTE_STRING ], str, + sizeof( cgs.teamVoteString[ num - CS_TEAMVOTE_STRING ] ) ); + } + else if( num == CS_INTERMISSION ) + cg.intermissionStarted = atoi( str ); + else if( num >= CS_MODELS && num < CS_MODELS+MAX_MODELS ) + cgs.gameModels[ num - CS_MODELS ] = trap_R_RegisterModel( str ); + else if( num >= CS_SHADERS && num < CS_SHADERS+MAX_GAME_SHADERS ) + cgs.gameShaders[ num - CS_SHADERS ] = trap_R_RegisterShader( str ); + else if( num >= CS_PARTICLE_SYSTEMS && num < CS_PARTICLE_SYSTEMS+MAX_GAME_PARTICLE_SYSTEMS ) + cgs.gameParticleSystems[ num - CS_PARTICLE_SYSTEMS ] = CG_RegisterParticleSystem( (char *)str ); + else if( num >= CS_SOUNDS && num < CS_SOUNDS+MAX_SOUNDS ) + { + if( str[ 0 ] != '*' ) + { // player specific sounds don't register here + cgs.gameSounds[ num - CS_SOUNDS ] = trap_S_RegisterSound( str, qfalse ); + } + } + else if( num >= CS_PLAYERS && num < CS_PLAYERS+MAX_CLIENTS ) + { + CG_NewClientInfo( num - CS_PLAYERS ); + CG_BuildSpectatorString( ); + } + else if( num == CS_WINNER ) + { + trap_Cvar_Set( "ui_winner", str ); + } + else if( num == CS_SHADERSTATE ) + { + CG_ShaderStateChanged( ); + } +} + + +/* +=============== +CG_MapRestart + +The server has issued a map_restart, so the next snapshot +is completely new and should not be interpolated to. + +A tournement restart will clear everything, but doesn't +require a reload of all the media +=============== +*/ +static void CG_MapRestart( void ) +{ + if( cg_showmiss.integer ) + CG_Printf( "CG_MapRestart\n" ); + + CG_InitMarkPolys( ); + + // make sure the "3 frags left" warnings play again + cg.fraglimitWarnings = 0; + + cg.timelimitWarnings = 0; + + cg.intermissionStarted = qfalse; + + cgs.voteTime = 0; + + cg.mapRestart = qtrue; + + CG_StartMusic( ); + + trap_S_ClearLoopingSounds( qtrue ); + + // we really should clear more parts of cg here and stop sounds + + // play the "fight" sound if this is a restart without warmup + if( cg.warmup == 0 ) + CG_CenterPrint( "FIGHT!", 120, GIANTCHAR_WIDTH * 2 ); + + trap_Cvar_Set( "cg_thirdPerson", "0" ); +} + +/* +================= +CG_RemoveChatEscapeChar +================= +*/ +static void CG_RemoveChatEscapeChar( char *text ) +{ + int i, l; + + l = 0; + for( i = 0; text[ i ]; i++ ) + { + if( text[ i ] == '\x19' ) + continue; + + text[ l++ ] = text[ i ]; + } + + text[ l ] = '\0'; +} + +/* +=============== +CG_SetUIVars + +Set some cvars used by the UI +=============== +*/ +static void CG_SetUIVars( void ) +{ + int i; + char carriageCvar[ MAX_TOKEN_CHARS ]; + + *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_FindPurchasableForWeapon( i ) ) + 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_FindPurchasableForUpgrade( i ) ) + 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_Menu +============== +*/ +void CG_Menu( int menu ) +{ + const char *cmd = NULL; // command to send + const char *longMsg = NULL; // command parameter + const char *shortMsg = NULL; // non-modal version of message + CG_SetUIVars( ); + + // string literals have static storage duration, this is safe, + // cleaner and much more readable. + switch( menu ) + { + case MN_TEAM: + cmd = "menu tremulous_teamselect\n"; + break; + + case MN_A_CLASS: + cmd = "menu tremulous_alienclass\n"; + break; + + case MN_H_SPAWN: + cmd = "menu tremulous_humanitem\n"; + break; + + case MN_A_BUILD: + cmd = "menu tremulous_alienbuild\n"; + break; + + case MN_H_BUILD: + cmd = "menu tremulous_humanbuild\n"; + break; + + case MN_H_ARMOURY: + cmd = "menu tremulous_humanarmoury\n"; + break; + + case MN_A_TEAMFULL: + longMsg = "The alien team has too many players. Please wait until slots " + "become available or join the human team."; + shortMsg = "The alien team has too many players\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_H_TEAMFULL: + longMsg = "The human team has too many players. Please wait until slots " + "become available or join the alien team."; + shortMsg = "The human team has too many players\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_A_TEAMCHANGEBUILDTIMER: + longMsg = "You cannot leave the Alien team until your build timer " + "has expired."; + shortMsg = "You cannot change teams until your build timer expires.\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_H_TEAMCHANGEBUILDTIMER: + longMsg = "You cannot leave the Human team until your build timer " + "has expired."; + shortMsg = "You cannot change teams until your build timer expires.\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + //=============================== + + case MN_H_NOROOM: + longMsg = "There is no room to build here. Move until the buildable turns " + "translucent green indicating a valid build location."; + shortMsg = "There is no room to build here\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOPOWER: + longMsg = "There is no power remaining. Free up power by destroying " + "existing buildable objects."; + shortMsg = "There is no power remaining\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOTPOWERED: + longMsg = "This buildable is not powered. Build a Reactor and/or Repeater " + "in order to power it."; + shortMsg = "This buildable is not powered\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NORMAL: + longMsg = "Cannot build on this surface. The surface is too steep or " + "unsuitable to build on. Please choose another site for this " + "structure."; + shortMsg = "Cannot build on this surface\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_REACTOR: + longMsg = "There can only be one Reactor. Destroy the existing one if you " + "wish to move it."; + shortMsg = "There can only be one Reactor\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_REPEATER: + longMsg = "There is no power here. If available, a Repeater may be used to " + "transmit power to this location."; + shortMsg = "There is no power here\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NODCC: + longMsg = "There is no Defense Computer. A Defense Computer is needed to " + "build this."; + shortMsg = "There is no Defense Computer\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_TNODEWARN: + longMsg = "WARNING: This Telenode will not be powered. Build near a power " + "structure to prevent seeing this message again."; + shortMsg = "This Telenode will not be powered\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_RPTWARN: + longMsg = "WARNING: This Repeater will not be powered as there is no parent " + "Reactor providing power. Build a Reactor."; + shortMsg = "This Repeater will not be powered\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_RPTWARN2: + longMsg = "This area already has power. A Repeater is not required here."; + shortMsg = "This area already has power\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOSLOTS: + longMsg = "You have no room to carry this. Please sell any conflicting " + "upgrades before purchasing this item."; + shortMsg = "You have no room to carry this\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOFUNDS: + longMsg = "Insufficient funds. You do not have enough credits to perform " + "this action."; + shortMsg = "Insufficient funds\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_ITEMHELD: + longMsg = "You already hold this item. It is not possible to carry multiple " + "items of the same type."; + shortMsg = "You already hold this item\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOARMOURYHERE: + longMsg = "You must be near a powered Armoury in order to purchase " + "weapons, upgrades or non-energy ammunition."; + shortMsg = "You must be near a powered Armoury\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOENERGYAMMOHERE: + longMsg = "You must be near an Armoury, Reactor or Repeater in order " + "to purchase energy ammunition."; + shortMsg = "You must be near an Armoury, Reactor or Repeater\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOROOMBSUITON: + longMsg = "There is not enough room here to put on a Battle Suit. " + "Make sure you have enough head room to climb in."; + shortMsg = "Not enough room here to put on a Battle Suit\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_NOROOMBSUITOFF: + longMsg = "There is not enough room here to take off your Battle Suit. " + "Make sure you have enough head room to climb out."; + shortMsg = "Not enough room here to take off your Battle Suit\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + case MN_H_ARMOURYBUILDTIMER: + longMsg = "You are not allowed to buy or sell weapons until your " + "build timer has expired."; + shortMsg = "You can not buy or sell weapos until your build timer " + "expires\n"; + cmd = "menu tremulous_human_dialog\n"; + break; + + + //=============================== + + case MN_A_NOROOM: + longMsg = "There is no room to build here. Move until the structure turns " + "translucent green indicating a valid build location."; + shortMsg = "There is no room to build here\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NOCREEP: + longMsg = "There is no creep here. You must build near existing Eggs or " + "the Overmind. Alien structures will not support themselves."; + shortMsg = "There is no creep here\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NOOVMND: + longMsg = "There is no Overmind. An Overmind must be built to control " + "the structure you tried to place"; + shortMsg = "There is no Overmind\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_OVERMIND: + longMsg = "There can only be one Overmind. Destroy the existing one if you " + "wish to move it."; + shortMsg = "There can only be one Overmind\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_HOVEL: + longMsg = "There can only be one Hovel. Destroy the existing one if you " + "wish to move it."; + shortMsg = "There can only be one Hovel\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NOASSERT: + longMsg = "The Overmind cannot control any more structures. Destroy existing " + "structures to build more."; + shortMsg = "The Overmind cannot control any more structures\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_SPWNWARN: + longMsg = "WARNING: This spawn will not be controlled by an Overmind. " + "Build an Overmind to prevent seeing this message again."; + shortMsg = "This spawn will not be controlled by an Overmind\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NORMAL: + longMsg = "Cannot build on this surface. This surface is too steep or " + "unsuitable to build on. Please choose another site for this " + "structure."; + shortMsg = "Cannot build on this surface\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NOEROOM: + longMsg = "There is no room to evolve here. Move away from walls or other " + "nearby objects and try again."; + cmd = "menu tremulous_alien_dialog\n"; + shortMsg = "There is no room to evolve here\n"; + break; + + case MN_A_TOOCLOSE: + longMsg = "This location is too close to the enemy to evolve. Move away " + "until you are no longer aware of the enemy's presence and try " + "again."; + shortMsg = "This location is too close to the enemy to evolve\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_NOOVMND_EVOLVE: + longMsg = "There is no Overmind. An Overmind must be built to allow " + "you to upgrade."; + shortMsg = "There is no Overmind\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_EVOLVEBUILDTIMER: + longMsg = "You cannot Evolve until your build timer has expired."; + shortMsg = "You cannot Evolve until your build timer expires\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_HOVEL_OCCUPIED: + longMsg = "This Hovel is already occupied by another builder."; + shortMsg = "This Hovel is already occupied by another builder\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_HOVEL_BLOCKED: + longMsg = "The exit to this Hovel is currently blocked. Please wait until it " + "becomes clear then try again."; + shortMsg = "The exit to this Hovel is currently blocked\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_HOVEL_EXIT: + longMsg = "The exit to this Hovel would always be blocked. Please choose " + "a more suitable location."; + shortMsg = "The exit to this Hovel would always be blocked\n"; + cmd = "menu tremulous_alien_dialog\n"; + break; + + case MN_A_INFEST: + trap_Cvar_Set( "ui_currentClass", va( "%d %d", cg.snap->ps.stats[ STAT_PCLASS ], + cg.snap->ps.persistant[ PERS_CREDIT ] ) ); + cmd = "menu tremulous_alienupgrade\n"; + break; + + default: + Com_Printf( "cgame: debug: no such menu %d\n", menu ); + } + + if( !cg_disableWarningDialogs.integer || !shortMsg ) + { + // Player either wants dialog window or there's no short message + if( cmd ) + { + if( longMsg ) + trap_Cvar_Set( "ui_dialog", longMsg ); + + trap_SendConsoleCommand( cmd ); + } + } + else + { + // There is short message and player wants it + CG_Printf( shortMsg ); + } +} + +/* +================= +CG_ServerCommand + +The string has been tokenized and can be retrieved with +Cmd_Argc() / Cmd_Argv() +================= +*/ +static void CG_ServerCommand( void ) +{ + const char *cmd; + char text[ MAX_SAY_TEXT ]; + + cmd = CG_Argv( 0 ); + + if( !cmd[ 0 ] ) + { + // server claimed the command + return; + } + + if( !strcmp( cmd, "cp" ) ) + { + CG_CenterPrint( CG_Argv( 1 ), SCREEN_HEIGHT * 0.30, BIGCHAR_WIDTH ); + return; + } + + if( !strcmp( cmd, "cs" ) ) + { + CG_ConfigStringModified( ); + return; + } + + if( !strcmp( cmd, "print" ) ) + { + CG_Printf( "%s", CG_Argv( 1 ) ); + return; + } + + if( !strcmp( cmd, "chat" ) ) + { + if( !cg_teamChatsOnly.integer ) + { + Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); + if( Q_stricmpn( text, "[skipnotify]", 12 ) ) + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + CG_RemoveChatEscapeChar( text ); + CG_Printf( "%s\n", text ); + } + + return; + } + + if( !strcmp( cmd, "tchat" ) ) + { + Q_strncpyz( text, CG_Argv( 1 ), MAX_SAY_TEXT ); + if( Q_stricmpn( text, "[skipnotify]", 12 ) ) + { + if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_ALIENS ) + trap_S_StartLocalSound( cgs.media.alienTalkSound, CHAN_LOCAL_SOUND ); + else if( cg.snap->ps.stats[ STAT_PTEAM ] == PTE_HUMANS ) + trap_S_StartLocalSound( cgs.media.humanTalkSound, CHAN_LOCAL_SOUND ); + else + trap_S_StartLocalSound( cgs.media.talkSound, CHAN_LOCAL_SOUND ); + } + CG_RemoveChatEscapeChar( text ); + CG_Printf( "%s\n", text ); + return; + } + + if( !strcmp( cmd, "scores" ) ) + { + CG_ParseScores( ); + return; + } + + if( !strcmp( cmd, "tinfo" ) ) + { + CG_ParseTeamInfo( ); + return; + } + + if( !strcmp( cmd, "map_restart" ) ) + { + CG_MapRestart( ); + return; + } + + if( Q_stricmp( cmd, "remapShader" ) == 0 ) + { + if( trap_Argc( ) == 4 ) + trap_R_RemapShader( CG_Argv( 1 ), CG_Argv( 2 ), CG_Argv( 3 ) ); + } + + // clientLevelShot is sent before taking a special screenshot for + // the menu system during development + if( !strcmp( cmd, "clientLevelShot" ) ) + { + cg.levelShot = qtrue; + return; + } + + //the server has triggered a menu + if( !strcmp( cmd, "servermenu" ) ) + { + if( trap_Argc( ) == 2 && !cg.demoPlayback ) + CG_Menu( atoi( CG_Argv( 1 ) ) ); + + return; + } + + //the server thinks this client should close all menus + if( !strcmp( cmd, "serverclosemenus" ) ) + { + trap_SendConsoleCommand( "closemenus\n" ); + return; + } + + //poison cloud effect needs to be reliable + if( !strcmp( cmd, "poisoncloud" ) ) + { + cg.poisonedTime = cg.time; + + if( CG_IsParticleSystemValid( &cg.poisonCloudPS ) ) + { + cg.poisonCloudPS = CG_SpawnNewParticleSystem( cgs.media.poisonCloudPS ); + CG_SetAttachmentCent( &cg.poisonCloudPS->attachment, &cg.predictedPlayerEntity ); + CG_AttachToCent( &cg.poisonCloudPS->attachment ); + } + + return; + } + + if( !strcmp( cmd, "weaponswitch" ) ) + { + CG_Printf( "client weaponswitch\n" ); + if( trap_Argc( ) == 2 ) + { + cg.weaponSelect = atoi( CG_Argv( 1 ) ); + cg.weaponSelectTime = cg.time; + } + + return; + } + + // server requests a ptrc + if( !strcmp( cmd, "ptrcrequest" ) ) + { + int code = CG_ReadPTRCode( ); + + trap_SendClientCommand( va( "ptrcverify %d", code ) ); + return; + } + + // server issues a ptrc + if( !strcmp( cmd, "ptrcissue" ) ) + { + if( trap_Argc( ) == 2 ) + { + int code = atoi( CG_Argv( 1 ) ); + + CG_WritePTRCode( code ); + } + + return; + } + + // reply to ptrcverify + if( !strcmp( cmd, "ptrcconfirm" ) ) + { + trap_SendConsoleCommand( "menu ptrc_popmenu\n" ); + + return; + } + + CG_Printf( "Unknown client game command: %s\n", cmd ); +} + + +/* +==================== +CG_ExecuteNewServerCommands + +Execute all of the server commands that were received along +with this this snapshot. +==================== +*/ +void CG_ExecuteNewServerCommands( int latestSequence ) +{ + while( cgs.serverCommandSequence < latestSequence ) + { + if( trap_GetServerCommand( ++cgs.serverCommandSequence ) ) + CG_ServerCommand( ); + } +} |