/* =========================================================================== Return to Castle Wolfenstein multiplayer GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Return to Castle Wolfenstein multiplayer GPL Source Code (“RTCW MP Source Code”). RTCW MP Source Code 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 3 of the License, or (at your option) any later version. RTCW MP Source Code 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 RTCW MP Source Code. If not, see . In addition, the RTCW MP Source Code is also subject to certain additional terms. You should have received a copy of these additional terms immediately following the terms and conditions of the GNU General Public License which accompanied the RTCW MP Source Code. If not, please request a copy in writing from id Software at the address below. If you have questions concerning this license or the applicable additional terms, you may contact in writing id Software LLC, c/o ZeniMax Media Inc., Suite 120, Rockville, Maryland 20850 USA. =========================================================================== */ #include "g_local.h" /* ================== DeathmatchScoreboardMessage ================== */ void DeathmatchScoreboardMessage( gentity_t *ent ) { char entry[1024]; char string[1400]; int stringlength; int i, j; gclient_t *cl; int numSorted; int scoreFlags; // send the latest information on all clients string[0] = 0; stringlength = 0; scoreFlags = 0; // don't send more than 32 scores (FIXME?) numSorted = level.numConnectedClients; if ( numSorted > 32 ) { numSorted = 32; } for ( i = 0 ; i < numSorted ; i++ ) { int ping; int playerClass; int respawnsLeft; cl = &level.clients[level.sortedClients[i]]; // NERVE - SMF - if on same team, send across player class if ( cl->ps.persistant[PERS_TEAM] == ent->client->ps.persistant[PERS_TEAM] ) { playerClass = cl->ps.stats[STAT_PLAYER_CLASS]; } else { playerClass = 0; } // NERVE - SMF - number of respawns left respawnsLeft = cl->ps.persistant[PERS_RESPAWNS_LEFT]; if ( respawnsLeft == 0 && ( ( cl->ps.pm_flags & PMF_LIMBO ) || ( level.intermissiontime && g_entities[level.sortedClients[i]].health <= 0 ) ) ) { respawnsLeft = -2; } if ( cl->pers.connected == CON_CONNECTING ) { ping = -1; } else { ping = cl->ps.ping < 999 ? cl->ps.ping : 999; } Com_sprintf( entry, sizeof( entry ), " %i %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, ( level.time - cl->pers.enterTime ) / 60000, scoreFlags, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft ); j = strlen( entry ); if ( stringlength + j > 1024 ) { break; } strcpy( string + stringlength, entry ); stringlength += j; } trap_SendServerCommand( ent - g_entities, va( "scores %i %i %i%s", i, level.teamScores[TEAM_RED], level.teamScores[TEAM_BLUE], string ) ); } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f( gentity_t *ent ) { DeathmatchScoreboardMessage( ent ); } /* ================== CheatsOk ================== */ qboolean CheatsOk( gentity_t *ent ) { if ( !g_cheats.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Cheats are not enabled on this server.\n\"" ) ); return qfalse; } if ( ent->health <= 0 ) { trap_SendServerCommand( ent - g_entities, va( "print \"You must be alive to use this command.\n\"" ) ); return qfalse; } return qtrue; } /* ================== 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 ( i != c - 1 ) { line[len] = ' '; len++; } } line[len] = 0; return line; } /* ================== SanitizeString Remove case and control characters ================== */ void SanitizeString( char *in, char *out ) { while ( *in ) { if ( *in == 27 ) { in += 2; // skip color code continue; } if ( *in < 32 ) { in++; continue; } *out++ = tolower( *in++ ); } *out = 0; } /* ================== ClientNumberFromString Returns a player number for either a number or name string Returns -1 if invalid ================== */ int ClientNumberFromString( gentity_t *to, char *s ) { gclient_t *cl; int idnum; char s2[MAX_STRING_CHARS]; char n2[MAX_STRING_CHARS]; // numeric values are just slot numbers if ( s[0] >= '0' && s[0] <= '9' ) { idnum = atoi( s ); if ( idnum < 0 || idnum >= level.maxclients ) { trap_SendServerCommand( to - g_entities, va( "print \"Bad client slot: [lof]%i\n\"", idnum ) ); return -1; } cl = &level.clients[idnum]; if ( cl->pers.connected != CON_CONNECTED ) { trap_SendServerCommand( to - g_entities, va( "print \"Client[lof] %i [lon]is not active\n\"", idnum ) ); return -1; } return idnum; } // check for a name match SanitizeString( s, s2 ); for ( idnum = 0,cl = level.clients ; idnum < level.maxclients ; idnum++,cl++ ) { if ( cl->pers.connected != CON_CONNECTED ) { continue; } SanitizeString( cl->pers.netname, n2 ); if ( !strcmp( n2, s2 ) ) { return idnum; } } trap_SendServerCommand( to - g_entities, va( "print \"User [lof]%s [lon]is not on the server\n\"", s ) ); return -1; } /* ================== Cmd_Give_f Give items to a client ================== */ void Cmd_Give_f( gentity_t *ent ) { char *name, *amt; gitem_t *it; int i; qboolean give_all; gentity_t *it_ent; trace_t trace; int amount; if ( !CheatsOk( ent ) ) { return; } //----(SA) check for an amount (like "give health 30") amt = ConcatArgs( 2 ); amount = atoi( amt ); //----(SA) end name = ConcatArgs( 1 ); if ( Q_stricmp( name, "all" ) == 0 ) { give_all = qtrue; } else { give_all = qfalse; } if ( give_all || Q_stricmpn( name, "health", 6 ) == 0 ) { //----(SA) modified if ( amount ) { ent->health += amount; } else { ent->health = ent->client->ps.stats[STAT_MAX_HEALTH]; } if ( !give_all ) { return; } } if ( give_all || Q_stricmp( name, "weapons" ) == 0 ) { for ( i = 0; i < WP_NUM_WEAPONS; i++ ) { if ( BG_WeaponInWolfMP( i ) ) { COM_BitSet( ent->client->ps.weapons, i ); } } if ( !give_all ) { return; } } if ( give_all || Q_stricmp( name, "holdable" ) == 0 ) { ent->client->ps.stats[STAT_HOLDABLE_ITEM] = ( 1 << ( HI_BOOK3 - 1 ) ) - 1 - ( 1 << HI_NONE ); for ( i = 1 ; i <= HI_BOOK3 ; i++ ) { ent->client->ps.holdable[i] = 10; } if ( !give_all ) { return; } } if ( give_all || Q_stricmpn( name, "ammo", 4 ) == 0 ) { if ( amount ) { if ( ent->client->ps.weapon ) { Add_Ammo( ent, ent->client->ps.weapon, amount, qtrue ); } } else { for ( i = 1 ; i < WP_MONSTER_ATTACK1 ; i++ ) Add_Ammo( ent, i, 9999, qtrue ); } if ( !give_all ) { return; } } // "give allammo " allows you to give a specific amount of ammo to /all/ weapons while // allowing "give ammo " to only give to the selected weap. if ( Q_stricmpn( name, "allammo", 7 ) == 0 && amount ) { for ( i = 1 ; i < WP_MONSTER_ATTACK1 ; i++ ) Add_Ammo( ent, i, amount, qtrue ); if ( !give_all ) { return; } } if ( give_all || Q_stricmpn( name, "armor", 5 ) == 0 ) { if ( g_gametype.integer == GT_SINGLE_PLAYER ) { // JPW NERVE -- no armor in multiplayer //----(SA) modified if ( amount ) { ent->client->ps.stats[STAT_ARMOR] += amount; } else { ent->client->ps.stats[STAT_ARMOR] = 200; } } // jpw if ( !give_all ) { return; } } //---- (SA) Wolf keys if ( give_all || Q_stricmp( name, "keys" ) == 0 ) { ent->client->ps.stats[STAT_KEYS] = ( 1 << KEY_NUM_KEYS ) - 2; if ( !give_all ) { return; } } //---- (SA) end // spawn a specific item right on the player if ( !give_all ) { it = BG_FindItem( name ); if ( !it ) { return; } it_ent = G_Spawn(); VectorCopy( ent->r.currentOrigin, it_ent->s.origin ); it_ent->classname = it->classname; G_SpawnItem( it_ent, it ); FinishSpawningItem( it_ent ); memset( &trace, 0, sizeof( trace ) ); it_ent->active = qtrue; Touch_Item( it_ent, ent, &trace ); it_ent->active = qfalse; if ( it_ent->inuse ) { G_FreeEntity( it_ent ); } } } /* ================== Cmd_God_f Sets client to godmode argv(0) god ================== */ void Cmd_God_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } 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_Nofatigue_f Sets client to nofatigue argv(0) nofatigue ================== */ void Cmd_Nofatigue_f( gentity_t *ent ) { char *msg; if ( !CheatsOk( ent ) ) { return; } ent->flags ^= FL_NOFATIGUE; if ( !( ent->flags & FL_NOFATIGUE ) ) { msg = "nofatigue OFF\n"; } else { msg = "nofatigue 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; if ( !CheatsOk( ent ) ) { return; } 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 ( !CheatsOk( ent ) ) { return; } 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 ) { if ( !CheatsOk( ent ) ) { return; } // doesn't work in single player if ( g_gametype.integer != 0 ) { trap_SendServerCommand( ent - g_entities, "print \"Must be in g_gametype 0 for levelshot\n\"" ); return; } BeginIntermission(); trap_SendServerCommand( ent - g_entities, "clientLevelShot" ); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f( gentity_t *ent ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return; } if ( g_gamestate.integer != GS_PLAYING ) { return; } if ( g_gametype.integer >= GT_WOLF && ent->client->ps.pm_flags & PMF_LIMBO ) { return; } ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; ent->client->ps.persistant[PERS_HWEAPON_USE] = 0; // TTimo - if using /kill while at MG42 player_die( ent, ent, ent, 100000, MOD_SUICIDE ); } /* ================= SetTeam ================= */ void SetTeam( gentity_t *ent, char *s ) { int team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; // // see what change is requested // client = ent->client; clientNum = client - level.clients; specClient = 0; specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "scoreboard" ) || !Q_stricmp( s, "score" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_SCOREBOARD; } else if ( !Q_stricmp( s, "follow1" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -1; } else if ( !Q_stricmp( s, "follow2" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FOLLOW; specClient = -2; } else if ( !Q_stricmp( s, "spectator" ) || !Q_stricmp( s, "s" ) ) { team = TEAM_SPECTATOR; specState = SPECTATOR_FREE; } else if ( g_gametype.integer >= GT_TEAM ) { // if running a team game, assign player to one of the teams specState = SPECTATOR_NOT; if ( !Q_stricmp( s, "red" ) || !Q_stricmp( s, "r" ) ) { team = TEAM_RED; } else if ( !Q_stricmp( s, "blue" ) || !Q_stricmp( s, "b" ) ) { team = TEAM_BLUE; } else { // pick the team with the least number of players team = PickTeam( clientNum ); } // NERVE - SMF if ( g_noTeamSwitching.integer && team != ent->client->sess.sessionTeam && g_gamestate.integer == GS_PLAYING ) { trap_SendServerCommand( clientNum, "cp \"You cannot switch during a match, please wait until the round ends.\n\"" ); return; // ignore the request } // NERVE - SMF - merge from team arena if ( g_teamForceBalance.integer ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_BLUE] = TeamCount( ent - g_entities, TEAM_BLUE ); counts[TEAM_RED] = TeamCount( ent - g_entities, TEAM_RED ); // We allow a spread of one if ( team == TEAM_RED && counts[TEAM_RED] - counts[TEAM_BLUE] >= 1 ) { trap_SendServerCommand( clientNum, "cp \"The Axis has too many players.\n\"" ); return; // ignore the request } if ( team == TEAM_BLUE && counts[TEAM_BLUE] - counts[TEAM_RED] >= 1 ) { trap_SendServerCommand( clientNum, "cp \"The Allies have too many players.\n\"" ); return; // ignore the request } // It's ok, the team we are switching to has less or same number of players } // -NERVE - SMF } else { // force them to spectators if there aren't any spots free team = TEAM_FREE; } // override decision if limiting the players if ( g_gametype.integer == GT_TOURNAMENT && level.numNonSpectatorClients >= 2 ) { team = TEAM_SPECTATOR; } else if ( g_maxGameClients.integer > 0 && level.numNonSpectatorClients >= g_maxGameClients.integer ) { team = TEAM_SPECTATOR; } // // decide if we will allow the change // oldTeam = client->sess.sessionTeam; if ( team == oldTeam && team != TEAM_SPECTATOR ) { return; } // NERVE - SMF - prevent players from switching to regain deployments if ( g_maxlives.integer > 0 && ent->client->ps.persistant[PERS_RESPAWNS_LEFT] == 0 && oldTeam != TEAM_SPECTATOR ) { trap_SendServerCommand( clientNum, "cp \"You can't switch teams because you are out of lives.\n\" 3" ); return; // ignore the request } // DHM - Nerve :: Force players to wait 30 seconds before they can join a new team. if ( g_gametype.integer >= GT_WOLF && team != oldTeam && level.warmupTime == 0 && !client->pers.initialSpawn && ( ( level.time - client->pers.connectTime ) > 10000 ) && ( ( level.time - client->pers.enterTime ) < 30000 ) ) { trap_SendServerCommand( ent - g_entities, va( "cp \"^3You must wait %i seconds before joining ^3a new team.\n\" 3", (int)( 30 - ( ( level.time - client->pers.enterTime ) / 1000 ) ) ) ); return; } // dhm // // execute the team change // // DHM - Nerve if ( client->pers.initialSpawn && team != TEAM_SPECTATOR ) { client->pers.initialSpawn = qfalse; } // he starts at 'base' client->pers.teamState.state = TEAM_BEGIN; if ( oldTeam != TEAM_SPECTATOR ) { if ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) { // Kill him (makes sure he loses flags, etc) ent->flags &= ~FL_GODMODE; ent->client->ps.stats[STAT_HEALTH] = ent->health = 0; player_die( ent, ent, ent, 100000, MOD_SUICIDE ); } } // they go to the end of the line for tournements if ( team == TEAM_SPECTATOR ) { client->sess.spectatorTime = level.time; } client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; if ( team == TEAM_RED ) { trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the Axis team.\n\"", client->pers.netname ) ); } else if ( team == TEAM_BLUE ) { trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the Allied team.\n\"", client->pers.netname ) ); } else if ( team == TEAM_SPECTATOR && oldTeam != TEAM_SPECTATOR ) { trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the spectators.\n\"", client->pers.netname ) ); } else if ( team == TEAM_FREE ) { trap_SendServerCommand( -1, va( "cp \"[lof]%s" S_COLOR_WHITE " [lon]joined the battle.\n\"", client->pers.netname ) ); } // get and distribute relevent paramters ClientUserinfoChanged( clientNum ); ClientBegin( clientNum ); } // DHM - Nerve /* ================= SetWolfData ================= */ void SetWolfData( gentity_t *ent, char *ptype, char *weap, char *grenade, char *skinnum ) { // DHM - Nerve gclient_t *client; client = ent->client; client->sess.latchPlayerType = atoi( ptype ); client->sess.latchPlayerWeapon = atoi( weap ); client->sess.latchPlayerItem = atoi( grenade ); client->sess.latchPlayerSkin = atoi( skinnum ); } // dhm - end /* ================= StopFollowing If the client being followed leaves the game, or you just want to drop to free floating spectator mode ================= */ void StopFollowing( gentity_t *ent ) { if ( g_gametype.integer < GT_WOLF ) { // NERVE - SMF - don't forcibly set this for multiplayer ent->client->sess.sessionTeam = TEAM_SPECTATOR; ent->client->ps.persistant[ PERS_TEAM ] = TEAM_SPECTATOR; } // ATVI Wolfenstein Misc #474 // divert behaviour if TEAM_SPECTATOR, moved the code from SpectatorThink to put back into free fly correctly // (I am not sure this can be called in non-TEAM_SPECTATOR situation, better be safe) if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { // drop to free floating, somewhere above the current position (that's the client you were following) vec3_t pos, angle; int enterTime; gclient_t *client = ent->client; VectorCopy( client->ps.origin, pos ); pos[2] += 16; VectorCopy( client->ps.viewangles, angle ); // ATVI Wolfenstein Misc #414, backup enterTime enterTime = client->pers.enterTime; SetTeam( ent, "spectator" ); client->pers.enterTime = enterTime; VectorCopy( pos, client->ps.origin ); SetClientViewAngle( ent, angle ); } else { // legacy code, FIXME: useless? ent->client->sess.spectatorState = SPECTATOR_FREE; ent->r.svFlags &= ~SVF_BOT; ent->client->ps.clientNum = ent - g_entities; } } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent ) { int oldTeam; char s[MAX_TOKEN_CHARS]; char ptype[4], weap[4], pistol[4], grenade[4], skinnum[4]; if ( trap_Argc() < 2 ) { oldTeam = ent->client->sess.sessionTeam; switch ( oldTeam ) { case TEAM_BLUE: trap_SendServerCommand( ent - g_entities, "print \"Blue team\n\"" ); break; case TEAM_RED: trap_SendServerCommand( ent - g_entities, "print \"Red team\n\"" ); break; case TEAM_FREE: trap_SendServerCommand( ent - g_entities, "print \"Free team\n\"" ); break; case TEAM_SPECTATOR: trap_SendServerCommand( ent - g_entities, "print \"Spectator team\n\"" ); break; } return; } // if they are playing a tournement game, count as a loss if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // DHM - Nerve if ( g_gametype.integer >= GT_WOLF ) { trap_Argv( 2, ptype, sizeof( ptype ) ); trap_Argv( 3, weap, sizeof( weap ) ); trap_Argv( 4, pistol, sizeof( pistol ) ); trap_Argv( 5, grenade, sizeof( grenade ) ); trap_Argv( 6, skinnum, sizeof( skinnum ) ); SetWolfData( ent, ptype, weap, grenade, skinnum ); } // dhm - end trap_Argv( 1, s, sizeof( s ) ); SetTeam( ent, s ); } /* ================= Cmd_Follow_f ================= */ void Cmd_Follow_f( gentity_t *ent ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { return; } // can't follow self if ( &level.clients[ i ] == ent->client ) { return; } // can't follow another spectator if ( level.clients[ i ].sess.sessionTeam == TEAM_SPECTATOR ) { return; } if ( g_gametype.integer >= GT_WOLF ) { if ( level.clients[ i ].ps.pm_flags & PMF_LIMBO ) { return; } } // if they are playing a tournement game, count as a loss if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator" ); } ent->client->sess.spectatorState = SPECTATOR_FOLLOW; ent->client->sess.spectatorClient = i; } /* ================= Cmd_FollowCycle_f ================= */ void Cmd_FollowCycle_f( gentity_t *ent, int dir ) { int clientnum; int original; // if they are playing a tournement game, count as a loss if ( g_gametype.integer == GT_TOURNAMENT && ent->client->sess.sessionTeam == TEAM_FREE ) { ent->client->sess.losses++; } // first set them to spectator if ( ( ent->client->sess.spectatorState == SPECTATOR_NOT ) && ( !( ent->client->ps.pm_flags & PMF_LIMBO ) ) ) { // JPW NERVE for limbo state SetTeam( ent, "spectator" ); } if ( dir != 1 && dir != -1 ) { G_Error( "Cmd_FollowCycle_f: bad dir %i", dir ); } clientnum = ent->client->sess.spectatorClient; original = clientnum; do { clientnum += dir; if ( clientnum >= level.maxclients ) { clientnum = 0; } if ( clientnum < 0 ) { clientnum = level.maxclients - 1; } // can only follow connected clients if ( level.clients[ clientnum ].pers.connected != CON_CONNECTED ) { continue; } // can't follow another spectator if ( level.clients[ clientnum ].sess.sessionTeam == TEAM_SPECTATOR ) { continue; } // JPW NERVE -- couple extra checks for limbo mode if ( ent->client->ps.pm_flags & PMF_LIMBO ) { if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) { continue; } if ( level.clients[clientnum].sess.sessionTeam != ent->client->sess.sessionTeam ) { continue; } } // jpw if ( g_gametype.integer >= GT_WOLF ) { if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) { continue; } } // this is good, we can use it ent->client->sess.spectatorClient = clientnum; ent->client->sess.spectatorState = SPECTATOR_FOLLOW; return; } while ( clientnum != original ); // leave it where it was } /* ================== G_Say ================== */ #define MAX_SAY_TEXT 150 #define SAY_ALL 0 #define SAY_TEAM 1 #define SAY_TELL 2 #define SAY_LIMBO 3 // NERVE - SMF void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, qboolean localize ) { // removed static so it would link if ( !other ) { return; } if ( !other->inuse ) { return; } if ( !other->client ) { return; } if ( mode == SAY_TEAM && !OnSameTeam( ent, other ) ) { return; } // no chatting to players in tournements if ( g_gametype.integer == GT_TOURNAMENT && other->client->sess.sessionTeam == TEAM_FREE && ent->client->sess.sessionTeam != TEAM_FREE ) { return; } // NERVE - SMF - if spectator, no chatting to players in WolfMP if ( g_gametype.integer >= GT_WOLF && ( ( ent->client->sess.sessionTeam == TEAM_FREE && other->client->sess.sessionTeam != TEAM_FREE ) || ( ent->client->sess.sessionTeam == TEAM_SPECTATOR && other->client->sess.sessionTeam != TEAM_SPECTATOR ) ) ) { return; } // NERVE - SMF if ( mode == SAY_LIMBO ) { trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\"", "lchat", name, Q_COLOR_ESCAPE, color, message ) ); } // -NERVE - SMF else { trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\" %i", mode == SAY_TEAM ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message, localize ) ); } } void G_Say( gentity_t *ent, gentity_t *target, int mode, const char *chatText ) { int j; gentity_t *other; int color; char name[64]; // don't let text be too long for malicious reasons char text[MAX_SAY_TEXT]; char location[64]; qboolean localize = qfalse; if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } switch ( mode ) { default: case SAY_ALL: G_LogPrintf( "say: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf( name, sizeof( name ), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; case SAY_TEAM: localize = qtrue; G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); if ( Team_GetLocationMsg( ent, location, sizeof( location ) ) ) { Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); } else { Com_sprintf( name, sizeof( name ), "(%s%c%c): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); } color = COLOR_CYAN; break; case SAY_TELL: if ( target && g_gametype.integer >= GT_TEAM && target->client->sess.sessionTeam == ent->client->sess.sessionTeam && Team_GetLocationMsg( ent, location, sizeof( location ) ) ) { Com_sprintf( name, sizeof( name ), "[%s%c%c] (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, location ); } else { Com_sprintf( name, sizeof( name ), "[%s%c%c]: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); } color = COLOR_MAGENTA; break; // NERVE - SMF case SAY_LIMBO: G_LogPrintf( "say_limbo: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf( name, sizeof( name ), "%s%c%c: ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE ); color = COLOR_GREEN; break; // -NERVE - SMF } Q_strncpyz( text, chatText, sizeof( text ) ); if ( target ) { G_SayTo( ent, target, mode, color, name, text, localize ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "%s%s\n", name, text ); } // send it to all the apropriate clients for ( j = 0; j < level.maxclients; j++ ) { other = &g_entities[j]; G_SayTo( ent, other, mode, color, name, text, localize ); } } /* ================== Cmd_Say_f ================== */ static void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { char *p; if ( trap_Argc() < 2 && !arg0 ) { return; } if ( arg0 ) { p = ConcatArgs( 0 ); } else { p = ConcatArgs( 1 ); } G_Say( ent, NULL, mode, p ); } /* ================== Cmd_Tell_f ================== */ static void Cmd_Tell_f( gentity_t *ent ) { int targetNum; gentity_t *target; char *p; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() < 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } p = ConcatArgs( 2 ); G_LogPrintf( "tell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, p ); G_Say( ent, target, SAY_TELL, p ); G_Say( ent, ent, SAY_TELL, p ); } // NERVE - SMF static void G_VoiceTo( gentity_t *ent, gentity_t *other, int mode, const char *id, qboolean voiceonly ) { int color; char *cmd; if ( !other ) { return; } if ( !other->inuse ) { return; } if ( !other->client ) { return; } if ( mode == SAY_TEAM && !OnSameTeam( ent, other ) ) { return; } // no chatting to players in tournements if ( ( g_gametype.integer == GT_TOURNAMENT ) ) { return; } if ( mode == SAY_TEAM ) { color = COLOR_CYAN; cmd = "vtchat"; } else if ( mode == SAY_TELL ) { color = COLOR_MAGENTA; cmd = "vtell"; } else { color = COLOR_GREEN; cmd = "vchat"; } trap_SendServerCommand( other - g_entities, va( "%s %d %d %d %s %i %i %i", cmd, voiceonly, ent->s.number, color, id, (int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) ); } void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) { int j; gentity_t *other; if ( g_gametype.integer < GT_TEAM && mode == SAY_TEAM ) { mode = SAY_ALL; } // DHM - Nerve :: Don't allow excessive spamming of voice chats ent->voiceChatSquelch -= ( level.time - ent->voiceChatPreviousTime ); ent->voiceChatPreviousTime = level.time; if ( ent->voiceChatSquelch < 0 ) { ent->voiceChatSquelch = 0; } if ( ent->voiceChatSquelch >= 30000 ) { trap_SendServerCommand( ent - g_entities, "print \"^1Spam Protection^7: VoiceChat ignored\n\"" ); return; } if ( g_voiceChatsAllowed.integer ) { ent->voiceChatSquelch += ( 34000 / g_voiceChatsAllowed.integer ); } else { return; } // dhm if ( target ) { G_VoiceTo( ent, target, mode, id, voiceonly ); return; } // echo the text to the console if ( g_dedicated.integer ) { G_Printf( "voice: %s %s\n", ent->client->pers.netname, id ); } // send it to all the apropriate clients for ( j = 0; j < level.maxclients; j++ ) { other = &g_entities[j]; G_VoiceTo( ent, other, mode, id, voiceonly ); } } /* ================== Cmd_Voice_f ================== */ static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) { char *p; if ( trap_Argc() < 2 && !arg0 ) { return; } if ( arg0 ) { p = ConcatArgs( 0 ); } else { p = ConcatArgs( 1 ); } G_Voice( ent, NULL, mode, p, voiceonly ); } // TTimo gcc: defined but not used #if 0 /* ================== Cmd_VoiceTell_f ================== */ static void Cmd_VoiceTell_f( gentity_t *ent, qboolean voiceonly ) { int targetNum; gentity_t *target; char *id; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() < 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); targetNum = atoi( arg ); if ( targetNum < 0 || targetNum >= level.maxclients ) { return; } target = &g_entities[targetNum]; if ( !target || !target->inuse || !target->client ) { return; } id = ConcatArgs( 2 ); G_LogPrintf( "vtell: %s to %s: %s\n", ent->client->pers.netname, target->client->pers.netname, id ); G_Voice( ent, target, SAY_TELL, id, voiceonly ); // don't tell to the player self if it was already directed to this player // also don't send the chat back to a bot if ( ent != target && !( ent->r.svFlags & SVF_BOT ) ) { G_Voice( ent, ent, SAY_TELL, id, voiceonly ); } } #endif // TTimo gcc: defined but not used #if 0 /* ================== Cmd_VoiceTaunt_f ================== */ static void Cmd_VoiceTaunt_f( gentity_t *ent ) { gentity_t *who; int i; if ( !ent->client ) { return; } // insult someone who just killed you if ( ent->enemy && ent->enemy->client && ent->enemy->client->lastkilled_client == ent->s.number ) { // i am a dead corpse if ( !( ent->enemy->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, ent->enemy, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } if ( !( ent->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, ent, SAY_TELL, VOICECHAT_DEATHINSULT, qfalse ); } ent->enemy = NULL; return; } // insult someone you just killed if ( ent->client->lastkilled_client >= 0 && ent->client->lastkilled_client != ent->s.number ) { who = g_entities + ent->client->lastkilled_client; if ( who->client ) { // who is the person I just killed if ( who->client->lasthurt_mod == MOD_GAUNTLET ) { if ( !( who->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); // and I killed them with a gauntlet } if ( !( ent->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLGAUNTLET, qfalse ); } } else { if ( !( who->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, who, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); // and I killed them with something else } if ( !( ent->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, ent, SAY_TELL, VOICECHAT_KILLINSULT, qfalse ); } } ent->client->lastkilled_client = -1; return; } } if ( g_gametype.integer >= GT_TEAM ) { // praise a team mate who just got a reward for ( i = 0; i < MAX_CLIENTS; i++ ) { who = g_entities + i; if ( who->client && who != ent && who->client->sess.sessionTeam == ent->client->sess.sessionTeam ) { if ( who->client->rewardTime > level.time ) { if ( !( who->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, who, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } if ( !( ent->r.svFlags & SVF_BOT ) ) { // G_Voice( ent, ent, SAY_TELL, VOICECHAT_PRAISE, qfalse ); } return; } } } } // just say something // G_Voice( ent, NULL, SAY_ALL, VOICECHAT_TAUNT, qfalse ); } // -NERVE - SMF #endif static char *gc_orders[] = { "hold your position", "hold this position", "come here", "cover me", "guard location", "search and destroy", "report" }; void Cmd_GameCommand_f( gentity_t *ent ) { int player; int order; char str[MAX_TOKEN_CHARS]; trap_Argv( 1, str, sizeof( str ) ); player = atoi( str ); trap_Argv( 2, str, sizeof( str ) ); order = atoi( str ); if ( player < 0 || player >= MAX_CLIENTS ) { return; } if ( order < 0 || order > sizeof( gc_orders ) / sizeof( char * ) ) { return; } G_Say( ent, &g_entities[player], SAY_TELL, gc_orders[order] ); G_Say( ent, ent, SAY_TELL, gc_orders[order] ); } /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) ); } static const char *gameNames[] = { "Free For All", "Tournament", "Single Player", "Team Deathmatch", "Capture the Flag", "Wolf Multiplayer", "Wolf Stopwatch", "Wolf Checkpoint" }; /* ================== Cmd_CallVote_f ================== */ void Cmd_CallVote_f( gentity_t *ent ) { int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; char cleanName[64]; // JPW NERVE int mask = 0; if ( !g_voteFlags.integer ) { trap_SendServerCommand( ent - g_entities, "print \"Voting not enabled on this server.\n\"" ); return; } if ( level.voteTime ) { trap_SendServerCommand( ent - g_entities, "print \"A vote is already in progress.\n\"" ); return; } if ( ent->client->pers.voteCount >= MAX_VOTE_COUNT ) { trap_SendServerCommand( ent - g_entities, "print \"You have called the maximum number of votes.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent - g_entities, "print \"Not allowed to call a vote as spectator.\n\"" ); return; } // make sure it is a valid command to vote on trap_Argv( 1, arg1, sizeof( arg1 ) ); trap_Argv( 2, arg2, sizeof( arg2 ) ); if ( strchr( arg1, ';' ) || strchr( arg2, ';' ) ) { trap_SendServerCommand( ent - g_entities, "print \"Invalid vote string.\n\"" ); return; } if ( !Q_stricmp( arg1, "map_restart" ) ) { mask = VOTEFLAGS_RESTART; } else if ( !Q_stricmp( arg1, "nextmap" ) ) { mask = VOTEFLAGS_NEXTMAP; } else if ( !Q_stricmp( arg1, "map" ) ) { mask = VOTEFLAGS_MAP; } else if ( !Q_stricmp( arg1, "g_gametype" ) ) { mask = VOTEFLAGS_TYPE; } else if ( !Q_stricmp( arg1, "kick" ) ) { mask = VOTEFLAGS_KICK; } else if ( !Q_stricmp( arg1, "clientkick" ) ) { mask = VOTEFLAGS_KICK; } else if ( !Q_stricmp( arg1, "start_match" ) ) { // NERVE - SMF mask = VOTEFLAGS_STARTMATCH; } else if ( !Q_stricmp( arg1, "reset_match" ) ) { // NERVE - SMF mask = VOTEFLAGS_RESETMATCH; } else if ( !Q_stricmp( arg1, "swap_teams" ) ) { // NERVE - SMF mask = VOTEFLAGS_SWAP; // JPW NERVE #ifndef PRE_RELEASE_DEMO } else if ( !Q_stricmp( arg1, testid1 ) ) { } else if ( !Q_stricmp( arg1, testid2 ) ) { } else if ( !Q_stricmp( arg1, testid3 ) ) { #endif // jpw } else { trap_SendServerCommand( ent - g_entities, "print \"Invalid vote string.\n\"" ); trap_SendServerCommand( ent - g_entities, "print \"Vote commands are: map_restart, nextmap, start_match, swap_teams, reset_match, map , g_gametype , kick , clientkick \n\"" ); return; } if ( !( g_voteFlags.integer & mask ) ) { trap_SendServerCommand( ent - g_entities, va( "print \"Voting for %s disabled on this server\n\"", arg1 ) ); return; } // if there is still a vote to be executed if ( level.voteExecuteTime ) { level.voteExecuteTime = 0; trap_SendConsoleCommand( EXEC_APPEND, va( "%s\n", level.voteString ) ); } // special case for g_gametype, check for bad values if ( !Q_stricmp( arg1, "g_gametype" ) ) { i = atoi( arg2 ); if ( i < GT_WOLF || i >= GT_MAX_GAME_TYPE ) { trap_SendServerCommand( ent - g_entities, "print \"Invalid gametype.\n\"" ); return; } Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %d", arg1, i ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s %s", arg1, gameNames[i] ); } else if ( !Q_stricmp( arg1, "map_restart" ) ) { // NERVE - SMF - do a warmup when we restart maps if ( strlen( arg2 ) ) { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 ); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s", arg1, arg2 ); } Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } else if ( !Q_stricmp( arg1, "map" ) ) { // special case for map changes, we want to reset the nextmap setting // this allows a player to change maps, but not upset the map rotation char s[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof( s ) ); if ( *s ) { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s; set nextmap \"%s\"", arg1, arg2, s ); } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s %s", arg1, arg2 ); } Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } else if ( !Q_stricmp( arg1, "nextmap" ) ) { char s[MAX_STRING_CHARS]; trap_Cvar_VariableStringBuffer( "nextmap", s, sizeof( s ) ); if ( !*s ) { trap_SendServerCommand( ent - g_entities, "print \"nextmap not set.\n\"" ); return; } Com_sprintf( level.voteString, sizeof( level.voteString ), "vstr nextmap" ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); // JPW NERVE } else if ( !Q_stricmp( arg1,"kick" ) ) { int i,kicknum = MAX_CLIENTS; for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( level.clients[i].pers.connected != CON_CONNECTED ) { continue; } // strip the color crap out Q_strncpyz( cleanName, level.clients[i].pers.netname, sizeof( cleanName ) ); Q_CleanStr( cleanName ); if ( !Q_stricmp( cleanName, arg2 ) ) { kicknum = i; } } Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "kick %s", level.clients[kicknum].pers.netname ); if ( kicknum != MAX_CLIENTS ) { // found a client # to kick, so override votestring with better one Com_sprintf( level.voteString, sizeof( level.voteString ),"clientkick \"%d\"",kicknum ); } else { // if it can't do a name match, don't allow kick (to prevent votekick text spam wars) trap_SendServerCommand( ent - g_entities, "print \"Client not on server.\n\"" ); return; } // jpw } else { Com_sprintf( level.voteString, sizeof( level.voteString ), "%s \"%s\"", arg1, arg2 ); Com_sprintf( level.voteDisplayString, sizeof( level.voteDisplayString ), "%s", level.voteString ); } trap_SendServerCommand( -1, va( "print \"[lof]%s [lon]called a vote.\n\"", ent->client->pers.netname ) ); // start the voting, the caller autoamtically votes yes level.voteTime = level.time; level.voteYes = 1; level.voteNo = 0; for ( i = 0 ; i < level.maxclients ; i++ ) { level.clients[i].ps.eFlags &= ~EF_VOTED; } ent->client->ps.eFlags |= EF_VOTED; trap_SetConfigstring( CS_VOTE_TIME, va( "%i", level.voteTime ) ); trap_SetConfigstring( CS_VOTE_STRING, level.voteDisplayString ); trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteYes ) ); trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteNo ) ); } /* ================== Cmd_Vote_f ================== */ void Cmd_Vote_f( gentity_t *ent ) { char msg[64]; int num; // DHM - Nerve :: Complaints supercede voting (and share command) if ( ent->client->pers.complaintEndTime > level.time ) { gclient_t *cl = g_entities[ ent->client->pers.complaintClient ].client; if ( !cl ) { return; } if ( cl->pers.connected != CON_CONNECTED ) { return; } if ( cl->pers.localClient ) { trap_SendServerCommand( ent - g_entities, "complaint -3" ); return; } // Reset this ent's complainEndTime so they can't send multiple complaints ent->client->pers.complaintEndTime = -1; ent->client->pers.complaintClient = -1; trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { // Increase their complaint counter cl->pers.complaints++; num = g_complaintlimit.integer - cl->pers.complaints; if ( num <= 0 && !cl->pers.localClient ) { trap_DropClient( cl - level.clients, "kicked after too many complaints." ); trap_SendServerCommand( ent - g_entities, "complaint -1" ); return; } trap_SendServerCommand( cl->ps.clientNum, va( "print \"^1Warning^7: Complaint filed against you. [lof](%d [lon]until kicked[lof])\n\"", num ) ); trap_SendServerCommand( ent - g_entities, "complaint -1" ); } else { trap_SendServerCommand( ent - g_entities, "complaint -2" ); } return; } // dhm // Reset this ent's complainEndTime so they can't send multiple complaints ent->client->pers.complaintEndTime = -1; ent->client->pers.complaintClient = -1; if ( !level.voteTime ) { trap_SendServerCommand( ent - g_entities, "print \"No vote in progress.\n\"" ); return; } if ( ent->client->ps.eFlags & EF_VOTED ) { trap_SendServerCommand( ent - g_entities, "print \"Vote already cast.\n\"" ); return; } if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent - g_entities, "print \"Not allowed to vote as spectator.\n\"" ); return; } trap_SendServerCommand( ent - g_entities, "print \"Vote cast.\n\"" ); ent->client->ps.eFlags |= EF_VOTED; trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { level.voteYes++; trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteYes ) ); } else { level.voteNo++; trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteNo ) ); } // a majority will be determined in G_CheckVote, which will also account // for players entering or leaving } qboolean G_canPickupMelee( gentity_t *ent ) { // JPW NERVE -- no "melee" weapons in net play if ( g_gametype.integer >= GT_WOLF ) { return qfalse; } // jpw if ( !( ent->client ) ) { return qfalse; // hmm, shouldn't be too likely... } if ( !( ent->s.weapon ) ) { // no weap, go ahead return qtrue; } if ( ent->client->ps.weaponstate == WEAPON_RELOADING ) { return qfalse; } if ( WEAPS_ONE_HANDED & ( 1 << ( ent->s.weapon ) ) ) { return qtrue; } return qfalse; } /* ================= Cmd_SetViewpos_f ================= */ void Cmd_SetViewpos_f( gentity_t *ent ) { vec3_t origin, angles; char buffer[MAX_TOKEN_CHARS]; int i; if ( !g_cheats.integer ) { trap_SendServerCommand( ent - g_entities, va( "print \"Cheats are not enabled on this server.\n\"" ) ); return; } if ( trap_Argc() != 5 ) { trap_SendServerCommand( ent - g_entities, va( "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 ); } /* ================= Cmd_StartCamera_f ================= */ void Cmd_StartCamera_f( gentity_t *ent ) { if ( !CheatsOk( ent ) ) { return; } g_camEnt->r.svFlags |= SVF_PORTAL; g_camEnt->r.svFlags &= ~SVF_NOCLIENT; ent->client->cameraPortal = g_camEnt; ent->client->ps.eFlags |= EF_VIEWING_CAMERA; ent->s.eFlags |= EF_VIEWING_CAMERA; } /* ================= Cmd_StopCamera_f ================= */ void Cmd_StopCamera_f( gentity_t *ent ) { if ( !CheatsOk( ent ) ) { return; } if ( ent->client->cameraPortal ) { // send a script event G_Script_ScriptEvent( ent->client->cameraPortal, "stopcam", "" ); // go back into noclient mode ent->client->cameraPortal->r.svFlags |= SVF_NOCLIENT; ent->client->cameraPortal = NULL; ent->s.eFlags &= ~EF_VIEWING_CAMERA; ent->client->ps.eFlags &= ~EF_VIEWING_CAMERA; } } /* ================= Cmd_SetCameraOrigin_f ================= */ void Cmd_SetCameraOrigin_f( gentity_t *ent ) { char buffer[MAX_TOKEN_CHARS]; int i; if ( trap_Argc() != 4 ) { return; } VectorClear( ent->client->cameraOrigin ); for ( i = 0 ; i < 3 ; i++ ) { trap_Argv( i + 1, buffer, sizeof( buffer ) ); ent->client->cameraOrigin[i] = atof( buffer ); } } // Rafael /* ================== Cmd_Activate_f ================== */ void Cmd_Activate_f( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *traceEnt; vec3_t forward, right, up, offset; static int oldactivatetime = 0; int activatetime = level.time; qboolean walking = qfalse; if ( ent->active ) { if ( ent->client->ps.persistant[PERS_HWEAPON_USE] ) { // DHM - Nerve :: Restore original position if current position is bad trap_Trace( &tr, ent->r.currentOrigin, ent->r.mins, ent->r.maxs, ent->r.currentOrigin, ent->s.number, MASK_PLAYERSOLID ); if ( tr.startsolid ) { VectorCopy( ent->TargetAngles, ent->client->ps.origin ); VectorCopy( ent->TargetAngles, ent->r.currentOrigin ); ent->r.contents = CONTENTS_CORPSE; // DHM - this will correct itself in ClientEndFrame } ent->client->ps.eFlags &= ~EF_MG42_ACTIVE; // DHM - Nerve :: unset flag ent->client->ps.persistant[PERS_HWEAPON_USE] = 0; ent->active = qfalse; return; } else { ent->active = qfalse; } } if ( ent->client->pers.cmd.buttons & BUTTON_WALKING ) { walking = qtrue; } AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePointForActivate( ent, forward, right, up, offset ); VectorMA( offset, 96, forward, end ); trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) ); if ( tr.surfaceFlags & SURF_NOIMPACT ) { return; } if ( tr.entityNum == ENTITYNUM_WORLD ) { return; } traceEnt = &g_entities[ tr.entityNum ]; if ( traceEnt->classname ) { traceEnt->flags &= ~FL_SOFTACTIVATE; // FL_SOFTACTIVATE will be set if the user is holding 'walk' key if ( traceEnt->s.eType == ET_ALARMBOX ) { trace_t trace; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return; } memset( &trace, 0, sizeof( trace ) ); if ( traceEnt->use ) { traceEnt->use( traceEnt, ent, 0 ); } } else if ( traceEnt->s.eType == ET_ITEM ) { trace_t trace; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return; } memset( &trace, 0, sizeof( trace ) ); if ( traceEnt->touch ) { if ( ent->client->pers.autoActivate == PICKUP_ACTIVATE ) { ent->client->pers.autoActivate = PICKUP_FORCE; //----(SA) force pickup } traceEnt->active = qtrue; traceEnt->touch( traceEnt, ent, &trace ); } } else if ( ( Q_stricmp( traceEnt->classname, "misc_mg42" ) == 0 ) && traceEnt->active == qfalse ) { if ( ( traceEnt->r.currentOrigin[2] - ent->r.currentOrigin[2] < 40 ) && ( traceEnt->r.currentOrigin[2] - ent->r.currentOrigin[2] > 0 ) && !infront( traceEnt, ent ) ) { //----(SA) make sure the client isn't holding a hot potato gclient_t *cl; cl = &level.clients[ ent->s.clientNum ]; if ( !( cl->ps.grenadeTimeLeft ) && !( cl->ps.pm_flags & PMF_DUCKED ) && !( traceEnt->s.eFlags & EF_SMOKING ) && ( cl->ps.weapon != WP_SNIPERRIFLE ) ) { // JPW NERVE no mg42 while scoped in // DHM - Remember initial gun position to restore later vec3_t point; AngleVectors( traceEnt->s.apos.trBase, forward, NULL, NULL ); VectorMA( traceEnt->r.currentOrigin, -36, forward, point ); point[2] = ent->r.currentOrigin[2]; // Save initial position VectorCopy( point, ent->TargetAngles ); // Zero out velocity VectorCopy( vec3_origin, ent->client->ps.velocity ); VectorCopy( vec3_origin, ent->s.pos.trDelta ); traceEnt->active = qtrue; ent->active = qtrue; traceEnt->r.ownerNum = ent->s.number; VectorCopy( traceEnt->s.angles, traceEnt->TargetAngles ); traceEnt->s.otherEntityNum = ent->s.number; cl->pmext.harc = traceEnt->harc; cl->pmext.varc = traceEnt->varc; VectorCopy( traceEnt->s.angles, cl->pmext.centerangles ); cl->pmext.centerangles[PITCH] = AngleNormalize180( cl->pmext.centerangles[PITCH] ); cl->pmext.centerangles[YAW] = AngleNormalize180( cl->pmext.centerangles[YAW] ); cl->pmext.centerangles[ROLL] = AngleNormalize180( cl->pmext.centerangles[ROLL] ); if ( !( ent->r.svFlags & SVF_CASTAI ) ) { G_UseTargets( traceEnt, ent ); //----(SA) added for Mike so mounting an MG42 can be a trigger event (let me know if there's any issues with this) } } } } else if ( ( ( Q_stricmp( traceEnt->classname, "func_door" ) == 0 ) || ( Q_stricmp( traceEnt->classname, "func_door_rotating" ) == 0 ) ) ) { if ( walking ) { traceEnt->flags |= FL_SOFTACTIVATE; // no noise } G_TryDoor( traceEnt, ent, ent ); // (door,other,activator) } else if ( ( Q_stricmp( traceEnt->classname, "team_WOLF_checkpoint" ) == 0 ) ) { if ( traceEnt->count != ent->client->sess.sessionTeam ) { traceEnt->health++; } } else if ( ( Q_stricmp( traceEnt->classname, "func_button" ) == 0 ) && ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY ) && traceEnt->active == qfalse ) { Use_BinaryMover( traceEnt, ent, ent ); traceEnt->active = qtrue; } else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) ) { if ( walking ) { traceEnt->flags |= FL_SOFTACTIVATE; // no noise } traceEnt->use( traceEnt, ent, ent ); } else if ( !Q_stricmp( traceEnt->classname, "script_mover" ) ) { G_Script_ScriptEvent( traceEnt, "activate", ent->aiName ); } else if ( !Q_stricmp( traceEnt->classname, "props_footlocker" ) ) { traceEnt->use( traceEnt, ent, ent ); } } if ( activatetime > oldactivatetime + 1000 ) { oldactivatetime = activatetime; } } // Rafael WolfKick //=================== // Cmd_WolfKick //=================== #define WOLFKICKDISTANCE 96 int Cmd_WolfKick_f( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *traceEnt; vec3_t forward, right, up, offset; gentity_t *tent; static int oldkicktime = 0; int kicktime = level.time; qboolean solidKick = qfalse; // don't play "hit" sound on a trigger unless it's an func_invisible_user int damage = 15; // DHM - Nerve :: No kick in wolf multiplayer if ( g_gametype.integer >= GT_WOLF ) { return 0; } if ( ent->client->ps.leanf ) { return 0; // no kick when leaning } if ( oldkicktime > kicktime ) { return ( 0 ); } else { oldkicktime = kicktime + 1000; } // play the anim BG_AnimScriptEvent( &ent->client->ps, ANIM_ET_KICK, qfalse, qtrue ); ent->client->ps.persistant[PERS_WOLFKICK] = 1; AngleVectors( ent->client->ps.viewangles, forward, right, up ); CalcMuzzlePointForActivate( ent, forward, right, up, offset ); // note to self: we need to determine the usable distance for wolf VectorMA( offset, WOLFKICKDISTANCE, forward, end ); trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) ); if ( tr.surfaceFlags & SURF_NOIMPACT || tr.fraction == 1.0 ) { tent = G_TempEntity( tr.endpos, EV_WOLFKICK_MISS ); tent->s.eventParm = ent->s.number; return ( 1 ); } traceEnt = &g_entities[ tr.entityNum ]; if ( !ent->melee ) { // because we dont want you to open a door with a prop if ( ( Q_stricmp( traceEnt->classname, "func_door_rotating" ) == 0 ) && ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY ) && traceEnt->active == qfalse ) { if ( traceEnt->key < 0 ) { // door force locked //----(SA) play kick "hit" sound tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL ); tent->s.otherEntityNum = ent->s.number; \ //----(SA) end AICast_AudibleEvent( ent->s.clientNum, tr.endpos, HEAR_RANGE_DOOR_KICKLOCKED ); // "someone kicked a locked door near me!" if ( traceEnt->soundPos3 ) { G_AddEvent( traceEnt, EV_GENERAL_SOUND, traceEnt->soundPos3 ); } else { G_AddEvent( traceEnt, EV_GENERAL_SOUND, G_SoundIndex( "sound/movers/doors/default_door_locked.wav" ) ); } return 1; //----(SA) changed. shows boot for locked doors } if ( traceEnt->key > 0 ) { // door requires key gitem_t *item = BG_FindItemForKey( traceEnt->key, 0 ); if ( !( ent->client->ps.stats[STAT_KEYS] & ( 1 << item->giTag ) ) ) { //----(SA) play kick "hit" sound tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL ); tent->s.otherEntityNum = ent->s.number; \ //----(SA) end AICast_AudibleEvent( ent->s.clientNum, tr.endpos, HEAR_RANGE_DOOR_KICKLOCKED ); // "someone kicked a locked door near me!" // player does not have key if ( traceEnt->soundPos3 ) { G_AddEvent( traceEnt, EV_GENERAL_SOUND, traceEnt->soundPos3 ); } else { G_AddEvent( traceEnt, EV_GENERAL_SOUND, G_SoundIndex( "sound/movers/doors/default_door_locked.wav" ) ); } return 1; //----(SA) changed. shows boot animation for locked doors } } if ( traceEnt->teammaster && traceEnt->team && traceEnt != traceEnt->teammaster ) { traceEnt->teammaster->active = qtrue; traceEnt->teammaster->flags |= FL_KICKACTIVATE; Use_BinaryMover( traceEnt->teammaster, ent, ent ); G_UseTargets( traceEnt->teammaster, ent ); } else { traceEnt->active = qtrue; traceEnt->flags |= FL_KICKACTIVATE; Use_BinaryMover( traceEnt, ent, ent ); G_UseTargets( traceEnt, ent ); } } else if ( ( Q_stricmp( traceEnt->classname, "func_button" ) == 0 ) && ( traceEnt->s.apos.trType == TR_STATIONARY && traceEnt->s.pos.trType == TR_STATIONARY ) && traceEnt->active == qfalse ) { Use_BinaryMover( traceEnt, ent, ent ); traceEnt->active = qtrue; } else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) ) { traceEnt->flags |= FL_KICKACTIVATE; // so cell doors know they were kicked // It doesn't hurt to pass this along since only ent use() funcs who care about it will check. // However, it may become handy to put a "KICKABLE" or "NOTKICKABLE" flag on the invisible_user traceEnt->use( traceEnt, ent, ent ); traceEnt->flags &= ~FL_KICKACTIVATE; // reset solidKick = qtrue; //----(SA) } else if ( !Q_stricmp( traceEnt->classname, "props_flippy_table" ) && traceEnt->use ) { traceEnt->use( traceEnt, ent, ent ); } } // snap the endpos to integers, but nudged towards the line SnapVectorTowards( tr.endpos, offset ); // send bullet impact if ( traceEnt->takedamage && traceEnt->client ) { tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_FLESH ); tent->s.eventParm = traceEnt->s.number; if ( LogAccuracyHit( traceEnt, ent ) ) { ent->client->ps.persistant[PERS_ACCURACY_HITS]++; } } else { // Ridah, bullet impact should reflect off surface vec3_t reflect; float dot; if ( traceEnt->r.contents >= 0 && ( traceEnt->r.contents & CONTENTS_TRIGGER ) && !solidKick ) { tent = G_TempEntity( tr.endpos, EV_WOLFKICK_MISS ); // (SA) don't play the "hit" sound if you kick most triggers } else { tent = G_TempEntity( tr.endpos, EV_WOLFKICK_HIT_WALL ); } dot = DotProduct( forward, tr.plane.normal ); VectorMA( forward, -2 * dot, tr.plane.normal, reflect ); VectorNormalize( reflect ); tent->s.eventParm = DirToByte( reflect ); // done. if ( ent->melee ) { ent->active = qfalse; ent->melee->health = 0; } } tent->s.otherEntityNum = ent->s.number; // try to swing chair if ( traceEnt->takedamage ) { if ( ent->melee ) { ent->active = qfalse; ent->melee->health = 0; ent->client->ps.eFlags &= ~EF_MELEE_ACTIVE; } G_Damage( traceEnt, ent, ent, forward, tr.endpos, damage, 0, MOD_KICKED ); //----(SA) modified } return ( 1 ); } // done /* ============================ Cmd_ClientMonsterSlickAngle ============================ */ /* void Cmd_ClientMonsterSlickAngle (gentity_t *clent) { char s[MAX_STRING_CHARS]; int entnum; int angle; gentity_t *ent; vec3_t dir, kvel; vec3_t forward; if (trap_Argc() != 3) { G_Printf( "ClientDamage command issued with incorrect number of args\n" ); } trap_Argv( 1, s, sizeof( s ) ); entnum = atoi(s); ent = &g_entities[entnum]; trap_Argv( 2, s, sizeof( s ) ); angle = atoi(s); // sanity check (also protect from cheaters) if (g_gametype.integer != GT_SINGLE_PLAYER && entnum != clent->s.number) { trap_DropClient( clent->s.number, "Dropped due to illegal ClientMonsterSlick command\n" ); return; } VectorClear (dir); dir[YAW] = angle; AngleVectors (dir, forward, NULL, NULL); VectorScale (forward, 32, kvel); VectorAdd (ent->client->ps.velocity, kvel, ent->client->ps.velocity); } */ // NERVE - SMF /* ============ ClientDamage ============ */ void ClientDamage( gentity_t *clent, int entnum, int enemynum, int id ) { gentity_t *enemy, *ent; vec3_t vec; ent = &g_entities[entnum]; enemy = &g_entities[enemynum]; // NERVE - SMF - took this out, this is causing more problems than its helping // Either a new way has to be found, or this check needs to change. // sanity check (also protect from cheaters) // if (g_gametype.integer != GT_SINGLE_PLAYER && entnum != clent->s.number) { // trap_DropClient( clent->s.number, "Dropped due to illegal ClientDamage command\n" ); // return; // } // -NERVE - SMF // if the attacker can't see the target, then don't allow damage if ( g_gametype.integer != GT_SINGLE_PLAYER ) { // TTimo it can happen that enemy->client == NULL // see Changelog 09/22/2001 if ( ( enemy->client ) && ( !CanDamage( ent, enemy->client->ps.origin ) ) ) { return; // don't allow damage } } switch ( id ) { case CLDMG_SPIRIT: if ( g_gametype.integer == GT_SINGLE_PLAYER ) { G_Damage( ent, enemy, enemy, vec3_origin, vec3_origin, 3, DAMAGE_NO_KNOCKBACK, MOD_ZOMBIESPIRIT ); } break; case CLDMG_BOSS1LIGHTNING: if ( g_gametype.integer != GT_SINGLE_PLAYER ) { break; } if ( ent->takedamage ) { VectorSubtract( ent->r.currentOrigin, enemy->r.currentOrigin, vec ); VectorNormalize( vec ); G_Damage( ent, enemy, enemy, vec, ent->r.currentOrigin, 6 + rand() % 3, 0, MOD_LIGHTNING ); } break; case CLDMG_TESLA: // do some cheat protection if ( g_gametype.integer != GT_SINGLE_PLAYER ) { if ( enemy->s.weapon != WP_TESLA ) { break; } if ( !( enemy->client->buttons & BUTTON_ATTACK ) ) { break; } //if ( AICast_GetCastState( enemy->s.number )->lastWeaponFiredWeaponNum != WP_TESLA ) // break; //if ( AICast_GetCastState( enemy->s.number )->lastWeaponFired < level.time - 400 ) // break; } if ( ( ent->aiCharacter == AICHAR_PROTOSOLDIER ) || ( ent->aiCharacter == AICHAR_SUPERSOLDIER ) || ( ent->aiCharacter == AICHAR_LOPER ) || ( ent->aiCharacter >= AICHAR_STIMSOLDIER1 && ent->aiCharacter <= AICHAR_STIMSOLDIER3 ) ) { break; } if ( ent->takedamage /*&& !AICast_NoFlameDamage(ent->s.number)*/ ) { VectorSubtract( ent->r.currentOrigin, enemy->r.currentOrigin, vec ); VectorNormalize( vec ); G_Damage( ent, enemy, enemy, vec, ent->r.currentOrigin, 3, 0, MOD_LIGHTNING ); } break; case CLDMG_FLAMETHROWER: // do some cheat protection /* JPW NERVE pulled flamethrower client damage completely if (g_gametype.integer != GT_SINGLE_PLAYER) { if ( enemy->s.weapon != WP_FLAMETHROWER ) break; // if ( !(enemy->client->buttons & BUTTON_ATTACK) ) // JPW NERVE flames should be able to damage while puffs are active // break; } else { // this is required for Zombie flame attack //if ((enemy->aiCharacter == AICHAR_ZOMBIE) && !AICast_VisibleFromPos( enemy->r.currentOrigin, enemy->s.number, ent->r.currentOrigin, ent->s.number, qfalse )) // break; } if ( ent->takedamage && !AICast_NoFlameDamage(ent->s.number) ) { #define FLAME_THRESHOLD 50 int damage = 5; // RF, only do damage once they start burning //if (ent->health > 0) // don't explode from flamethrower // G_Damage( traceEnt, ent, ent, forward, tr.endpos, 1, 0, MOD_LIGHTNING); // now check the damageQuota to see if we should play a pain animation // first reduce the current damageQuota with time if (ent->flameQuotaTime && ent->flameQuota > 0) { ent->flameQuota -= (int)(((float)(level.time - ent->flameQuotaTime)/1000) * (float)damage/2.0); if (ent->flameQuota < 0) ent->flameQuota = 0; } // add the new damage ent->flameQuota += damage; ent->flameQuotaTime = level.time; // Ridah, make em burn if (ent->client && ( !(ent->r.svFlags & SVF_CASTAI) || ent->health <= 0 || ent->flameQuota > FLAME_THRESHOLD)) { if (ent->s.onFireEnd < level.time) ent->s.onFireStart = level.time; if (ent->health <= 0 || !(ent->r.svFlags & SVF_CASTAI) || (g_gametype.integer != GT_SINGLE_PLAYER)) { if (ent->r.svFlags & SVF_CASTAI) { ent->s.onFireEnd = level.time + 6000; } else { ent->s.onFireEnd = level.time + FIRE_FLASH_TIME; } } else { ent->s.onFireEnd = level.time + 99999; // make sure it goes for longer than they need to die } ent->flameBurnEnt = enemy->s.number; // add to playerState for client-side effect ent->client->ps.onFireStart = level.time; } } */ break; } } // -NERVE - SMF /* ============ Cmd_ClientDamage_f ============ */ void Cmd_ClientDamage_f( gentity_t *clent ) { char s[MAX_STRING_CHARS]; int entnum, id, enemynum; if ( trap_Argc() != 4 ) { G_Printf( "ClientDamage command issued with incorrect number of args\n" ); } trap_Argv( 1, s, sizeof( s ) ); entnum = atoi( s ); trap_Argv( 2, s, sizeof( s ) ); enemynum = atoi( s ); trap_Argv( 3, s, sizeof( s ) ); id = atoi( s ); ClientDamage( clent, entnum, enemynum, id ); } /* ============== Cmd_EntityCount_f ============== */ #define AITEAM_NAZI 0 #define AITEAM_ALLIES 1 #define AITEAM_MONSTER 2 void Cmd_EntityCount_f( gentity_t *ent ) { if ( !g_cheats.integer ) { return; } G_Printf( "entity count = %i\n", level.num_entities ); { int kills[2]; int nazis[2]; int monsters[2]; int i; gentity_t *ent; // count kills kills[0] = kills[1] = 0; nazis[0] = nazis[1] = 0; monsters[0] = monsters[1] = 0; for ( i = 0; i < MAX_CLIENTS; i++ ) { ent = &g_entities[i]; if ( !ent->inuse ) { continue; } if ( !( ent->r.svFlags & SVF_CASTAI ) ) { continue; } if ( ent->aiTeam == AITEAM_ALLIES ) { continue; } kills[1]++; if ( ent->health <= 0 ) { kills[0]++; } if ( ent->aiTeam == AITEAM_NAZI ) { nazis[1]++; if ( ent->health <= 0 ) { nazis[0]++; } } else { monsters[1]++; if ( ent->health <= 0 ) { monsters[0]++; } } } G_Printf( "kills %i/%i nazis %i/%i monsters %i/%i \n",kills[0], kills[1], nazis[0], nazis[1], monsters[0], monsters[1] ); } } // NERVE - SMF /* ============ Cmd_SetSpawnPoint_f ============ */ void Cmd_SetSpawnPoint_f( gentity_t *clent ) { char arg[MAX_TOKEN_CHARS]; // int spawnIndex; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); if ( clent->client ) { // JPW NERVE clent->client->sess.spawnObjectiveIndex = atoi( arg ); // JPW NERVE } } // -NERVE - SMF /* ================= ClientCommand ================= */ void ClientCommand( int clientNum ) { gentity_t *ent; char cmd[MAX_TOKEN_CHARS]; ent = g_entities + clientNum; if ( !ent->client ) { return; // not fully in game yet } trap_Argv( 0, cmd, sizeof( cmd ) ); if ( Q_stricmp( cmd, "say" ) == 0 ) { Cmd_Say_f( ent, SAY_ALL, qfalse ); return; } if ( Q_stricmp( cmd, "say_team" ) == 0 ) { Cmd_Say_f( ent, SAY_TEAM, qfalse ); return; } // NERVE - SMF if ( Q_stricmp( cmd, "say_limbo" ) == 0 ) { Cmd_Say_f( ent, SAY_LIMBO, qfalse ); return; } if ( Q_stricmp( cmd, "vsay" ) == 0 ) { Cmd_Voice_f( ent, SAY_ALL, qfalse, qfalse ); return; } if ( Q_stricmp( cmd, "vsay_team" ) == 0 ) { Cmd_Voice_f( ent, SAY_TEAM, qfalse, qfalse ); return; } // -NERVE - SMF if ( Q_stricmp( cmd, "tell" ) == 0 ) { Cmd_Tell_f( ent ); return; } if ( Q_stricmp( cmd, "score" ) == 0 ) { Cmd_Score_f( ent ); return; } // NERVE - SMF - moved this here so current/new players can set team during scoreboard win if ( Q_stricmp( cmd, "team" ) == 0 ) { Cmd_Team_f( ent ); return; } // ignore all other commands when at intermission if ( level.intermissiontime ) { // Cmd_Say_f (ent, qfalse, qtrue); // NERVE - SMF - we don't want to spam the clients with this. return; } if ( Q_stricmp( cmd, "give" ) == 0 ) { Cmd_Give_f( ent ); } else if ( Q_stricmp( cmd, "god" ) == 0 ) { Cmd_God_f( ent ); } else if ( Q_stricmp( cmd, "nofatigue" ) == 0 ) { Cmd_Nofatigue_f( ent ); } else if ( Q_stricmp( cmd, "notarget" ) == 0 ) { Cmd_Notarget_f( ent ); } else if ( Q_stricmp( cmd, "noclip" ) == 0 ) { Cmd_Noclip_f( ent ); } else if ( Q_stricmp( cmd, "kill" ) == 0 ) { Cmd_Kill_f( ent ); } else if ( Q_stricmp( cmd, "levelshot" ) == 0 ) { Cmd_LevelShot_f( ent ); } else if ( Q_stricmp( cmd, "follow" ) == 0 ) { Cmd_Follow_f( ent ); } else if ( Q_stricmp( cmd, "follownext" ) == 0 ) { Cmd_FollowCycle_f( ent, 1 ); } else if ( Q_stricmp( cmd, "followprev" ) == 0 ) { Cmd_FollowCycle_f( ent, -1 ); } // else if (Q_stricmp (cmd, "team") == 0) // NERVE - SMF - moved above intermission check // Cmd_Team_f (ent); else if ( Q_stricmp( cmd, "where" ) == 0 ) { Cmd_Where_f( ent ); } else if ( Q_stricmp( cmd, "callvote" ) == 0 ) { Cmd_CallVote_f( ent ); } else if ( Q_stricmp( cmd, "vote" ) == 0 ) { Cmd_Vote_f( ent ); } else if ( Q_stricmp( cmd, "gc" ) == 0 ) { Cmd_GameCommand_f( ent ); } // else if (Q_stricmp (cmd, "startCamera") == 0) // Cmd_StartCamera_f( ent ); // else if (Q_stricmp (cmd, "stopCamera") == 0) // Cmd_StopCamera_f( ent ); // else if (Q_stricmp (cmd, "setCameraOrigin") == 0) // Cmd_SetCameraOrigin_f( ent ); else if ( Q_stricmp( cmd, "setviewpos" ) == 0 ) { Cmd_SetViewpos_f( ent ); } else if ( Q_stricmp( cmd, "entitycount" ) == 0 ) { Cmd_EntityCount_f( ent ); } else if ( Q_stricmp( cmd, "setspawnpt" ) == 0 ) { Cmd_SetSpawnPoint_f( ent ); } else { trap_SendServerCommand( clientNum, va( "print \"unknown cmd[lof] %s\n\"", cmd ) ); } }