/* =========================================================================== Wolfenstein: Enemy Territory GPL Source Code Copyright (C) 1999-2010 id Software LLC, a ZeniMax Media company. This file is part of the Wolfenstein: Enemy Territory GPL Source Code (“Wolf ET Source Code”). Wolf ET 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. Wolf ET 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 Wolf ET Source Code. If not, see . In addition, the Wolf: ET 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 Wolf ET 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" void BotDebug( int clientNum ); void GetBotAutonomies( int clientNum, int *weapAutonomy, int *moveAutonomy ); qboolean G_IsOnFireteam( int entityNum, fireteamData_t** teamNum ); /* ================== G_SendScore Sends current scoreboard information ================== */ void G_SendScore( gentity_t *ent ) { char entry[128]; int i; gclient_t *cl; int numSorted; int team, size, count; char buffer[1024]; char startbuffer[32]; // send the latest information on all clients numSorted = level.numConnectedClients; if ( numSorted > 64 ) { numSorted = 64; } i = 0; // Gordon: team doesnt actually mean team, ignore... for ( team = 0; team < 2; team++ ) { *buffer = '\0'; *startbuffer = '\0'; if ( team == 0 ) { Q_strncpyz( startbuffer, va( "sc0 %i %i", level.teamScores[TEAM_AXIS], level.teamScores[TEAM_ALLIES] ), 32 ); } else { Q_strncpyz( startbuffer, "sc1", 32 ); } size = strlen( startbuffer ) + 1; count = 0; for (; i < numSorted ; i++ ) { int ping, playerClass, respawnsLeft; cl = &level.clients[level.sortedClients[i]]; if ( g_entities[level.sortedClients[i]].r.svFlags & SVF_POW ) { continue; } // NERVE - SMF - if on same team, send across player class // Gordon: FIXME: remove/move elsewhere? if ( cl->ps.persistant[PERS_TEAM] == ent->client->ps.persistant[PERS_TEAM] || G_smvLocateEntityInMVList( ent, level.sortedClients[i], qfalse ) ) { playerClass = cl->ps.stats[STAT_PLAYER_CLASS]; } else { playerClass = 0; } // NERVE - SMF - number of respawns left respawnsLeft = cl->ps.persistant[PERS_RESPAWNS_LEFT]; if ( g_gametype.integer == GT_WOLF_LMS ) { if ( g_entities[level.sortedClients[i]].health <= 0 ) { respawnsLeft = -2; } } else { 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; } if ( g_gametype.integer == GT_WOLF_LMS ) { Com_sprintf( entry, sizeof( entry ), " %i %i %i %i %i %i %i", level.sortedClients[i], cl->ps.persistant[PERS_SCORE], ping, ( level.time - cl->pers.enterTime ) / 60000, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft ); } else { int j, totalXP; for ( totalXP = 0, j = 0; j < SK_NUM_SKILLS; j++ ) { totalXP += cl->sess.skillpoints[j]; } Com_sprintf( entry, sizeof( entry ), " %i %i %i %i %i %i %i", level.sortedClients[i], totalXP, ping, ( level.time - cl->pers.enterTime ) / 60000, g_entities[level.sortedClients[i]].s.powerups, playerClass, respawnsLeft ); } if ( size + strlen( entry ) > 1000 ) { i--; // we need to redo this client in the next buffer (if we can) break; } size += strlen( entry ); Q_strcat( buffer, 1024, entry ); if ( ++count >= 32 ) { i--; // we need to redo this client in the next buffer (if we can) break; } } if ( count > 0 || team == 0 ) { trap_SendServerCommand( ent - g_entities, va( "%s %i%s", startbuffer, count, buffer ) ); } } } /* ================== Cmd_Score_f Request current scoreboard information ================== */ void Cmd_Score_f( gentity_t *ent ) { ent->client->wantsscore = qtrue; // G_SendScore( 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, qboolean fToLower ) { while ( *in ) { if ( *in == 27 || *in == '^' ) { in++; // skip color code if ( *in ) { in++; } continue; } if ( *in < 32 ) { in++; continue; } *out++ = ( fToLower ) ? tolower( *in++ ) : *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]; qboolean fIsNumber = qtrue; // See if its a number or string for ( idnum = 0; idnum < strlen( s ) && s[idnum] != 0; idnum++ ) { if ( s[idnum] < '0' || s[idnum] > '9' ) { fIsNumber = qfalse; break; } } // check for a name match SanitizeString( s, s2, qtrue ); for ( idnum = 0, cl = level.clients; idnum < level.maxclients; idnum++, cl++ ) { if ( cl->pers.connected != CON_CONNECTED ) { continue; } SanitizeString( cl->pers.netname, n2, qtrue ); if ( !strcmp( n2, s2 ) ) { return( idnum ); } } // numeric values are just slot numbers if ( fIsNumber ) { idnum = atoi( s ); if ( idnum < 0 || idnum >= level.maxclients ) { CPx( to - g_entities, va( "print \"Bad client slot: [lof]%i\n\"", idnum ) ); return -1; } cl = &level.clients[idnum]; if ( cl->pers.connected != CON_CONNECTED ) { CPx( to - g_entities, va( "print \"Client[lof] %i [lon]is not active\n\"", idnum ) ); return -1; } return( idnum ); } CPx( to - g_entities, va( "print \"User [lof]%s [lon]is not on the server\n\"", s ) ); return( -1 ); } /* ================= Cmd_ListBotGoals_f ================= */ void Cmd_ListBotGoals_f( gentity_t* ent ) { int i; team_t t; if ( !CheatsOk( ent ) ) { return; } for ( t = TEAM_AXIS; t <= TEAM_ALLIES; t++ ) { gentity_t* list = g_entities, *targ; G_Printf( "\n%s bot goals\n=====================\n", ( t == TEAM_AXIS ? "Axis" : "Allies" ) ); for ( i = 0; i < level.num_entities; i++, list++ ) { if ( !list->inuse ) { continue; } if ( !( list->aiInactive & ( 1 << t ) ) ) { G_Printf( "%s (%s)", ( list->scriptName ? list->scriptName : ( list->targetname ? list->targetname : "NONE" ) ), list->classname ); if ( list->target_ent ) { targ = list->target_ent; G_Printf( " -> " ); G_Printf( "%s (%s)", ( targ->scriptName ? targ->scriptName : ( targ->targetname ? targ->targetname : "NONE" ) ), targ->classname ); } G_Printf( "\n" ); } } } } /* ================== 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; qboolean hasAmount = qfalse; if ( !CheatsOk( ent ) ) { return; } //----(SA) check for an amount (like "give health 30") amt = ConcatArgs( 2 ); if ( *amt ) { hasAmount = qtrue; } amount = atoi( amt ); //----(SA) end name = ConcatArgs( 1 ); if ( Q_stricmp( name, "all" ) == 0 ) { give_all = qtrue; } else { give_all = qfalse; } if ( Q_stricmpn( name, "skill", 5 ) == 0 ) { if ( hasAmount ) { if ( amount >= 0 && amount < SK_NUM_SKILLS ) { G_AddSkillPoints( ent, amount, 20 ); G_DebugAddSkillPoints( ent, amount, 20, "give skill" ); } } else { // bumps all skills with 1 level for ( i = 0; i < SK_NUM_SKILLS; i++ ) { G_AddSkillPoints( ent, i, 20 ); G_DebugAddSkillPoints( ent, i, 20, "give skill" ); } } return; } if ( Q_stricmpn( name, "medal", 5 ) == 0 ) { for ( i = 0; i < SK_NUM_SKILLS; i++ ) { if ( !ent->client->sess.medals[i] ) { ent->client->sess.medals[i] = 1; } } ClientUserinfoChanged( ent - g_entities ); return; } 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 ( Q_stricmpn( name, "damage", 6) == 0) { if(amount) { name = ConcatArgs( 3 ); if( *name ) { int client = ClientNumberFromString( ent, name ); if( client >= 0 ) { G_Damage( &g_entities[client], ent, ent, NULL, NULL, amount, DAMAGE_NO_PROTECTION, MOD_UNKNOWN ); } } else { G_Damage( ent, ent, ent, NULL, NULL, amount, DAMAGE_NO_PROTECTION, MOD_UNKNOWN ); } } 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_stricmpn( name, "ammo", 4 ) == 0 ) { if ( amount ) { if ( ent->client->ps.weapon && ent->client->ps.weapon != WP_SATCHEL && ent->client->ps.weapon != WP_SATCHEL_DET ) { Add_Ammo( ent, ent->client->ps.weapon, amount, qtrue ); } } else { for ( i = 1 ; i < WP_NUM_WEAPONS ; i++ ) { if ( COM_BitCheck( ent->client->ps.weapons, i ) && i != WP_SATCHEL && i != WP_SATCHEL_DET ) { 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_NUM_WEAPONS; i++ ) Add_Ammo( ent, i, amount, qtrue ); 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; char *name; qboolean godAll = qfalse; if ( !CheatsOk( ent ) ) { return; } name = ConcatArgs( 1 ); // are we supposed to make all our teammates gods too? if ( Q_stricmp( name, "all" ) == 0 ) { godAll = qtrue; } // can only use this cheat in single player if ( godAll && g_gametype.integer == GT_SINGLE_PLAYER ) { int j; qboolean settingFlag = qtrue; gentity_t *other; // are we turning it on or off? if ( ent->flags & FL_GODMODE ) { settingFlag = qfalse; } // loop through all players for ( j = 0; j < level.maxclients; j++ ) { other = &g_entities[j]; // if they're on the same team if ( OnSameTeam( other, ent ) ) { // set or clear the flag if ( settingFlag ) { other->flags |= FL_GODMODE; } else { other->flags &= ~FL_GODMODE; } } } if ( settingFlag ) { msg = "godmode all ON\n"; } else { msg = "godmode all OFF\n"; } } else { if ( !Q_stricmp( name, "on" ) || atoi( name ) ) { ent->flags |= FL_GODMODE; } else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) { ent->flags &= ~FL_GODMODE; } else { 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; char *name = ConcatArgs( 1 ); if ( !CheatsOk( ent ) ) { return; } if ( !Q_stricmp( name, "on" ) || atoi( name ) ) { ent->flags |= FL_NOFATIGUE; } else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) { ent->flags &= ~FL_NOFATIGUE; } else { 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; char *name = ConcatArgs( 1 ); if ( !CheatsOk( ent ) ) { return; } if ( !Q_stricmp( name, "on" ) || atoi( name ) ) { ent->client->noclip = qtrue; } else if ( !Q_stricmp( name, "off" ) || !Q_stricmp( name, "0" ) ) { ent->client->noclip = qfalse; } else { ent->client->noclip = !ent->client->noclip; } if ( ent->client->noclip ) { msg = "noclip ON\n"; } else { msg = "noclip OFF\n"; } trap_SendServerCommand( ent - g_entities, va( "print \"%s\"", msg ) ); } /* ================= Cmd_Kill_f ================= */ void Cmd_Kill_f( gentity_t *ent ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ( ent->client->ps.pm_flags & PMF_LIMBO ) || ent->health <= 0 || level.match_pause != PAUSE_NONE ) { return; } #ifdef SAVEGAME_SUPPORT if ( g_gametype.integer == GT_SINGLE_PLAYER && g_reloading.integer ) { return; } #endif // SAVEGAME_SUPPORT 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, ( g_gamestate.integer == GS_PLAYING ) ? 100000 : 135, MOD_SUICIDE ); } void BotRecordTeamChange( int client ); void G_TeamDataForString( const char* teamstr, int clientNum, team_t* team, spectatorState_t* sState, int* specClient ) { *sState = SPECTATOR_NOT; if ( !Q_stricmp( teamstr, "follow1" ) ) { *team = TEAM_SPECTATOR; *sState = SPECTATOR_FOLLOW; if ( specClient ) { *specClient = -1; } } else if ( !Q_stricmp( teamstr, "follow2" ) ) { *team = TEAM_SPECTATOR; *sState = SPECTATOR_FOLLOW; if ( specClient ) { *specClient = -2; } } else if ( !Q_stricmp( teamstr, "spectator" ) || !Q_stricmp( teamstr, "s" ) ) { *team = TEAM_SPECTATOR; *sState = SPECTATOR_FREE; } else if ( !Q_stricmp( teamstr, "red" ) || !Q_stricmp( teamstr, "r" ) || !Q_stricmp( teamstr, "axis" ) ) { *team = TEAM_AXIS; } else if ( !Q_stricmp( teamstr, "blue" ) || !Q_stricmp( teamstr, "b" ) || !Q_stricmp( teamstr, "allies" ) ) { *team = TEAM_ALLIES; } else { *team = PickTeam( clientNum ); if ( !G_teamJoinCheck( *team, &g_entities[clientNum] ) ) { *team = ( ( TEAM_AXIS | TEAM_ALLIES ) & ~*team ); } } } /* ================= SetTeam ================= */ qboolean SetTeam( gentity_t *ent, char *s, qboolean force, weapon_t w1, weapon_t w2, qboolean setweapons ) { team_t team, oldTeam; gclient_t *client; int clientNum; spectatorState_t specState; int specClient; int respawnsLeft; // // see what change is requested // client = ent->client; clientNum = client - level.clients; specClient = 0; // ydnar: preserve respawn count respawnsLeft = client->ps.persistant[ PERS_RESPAWNS_LEFT ]; G_TeamDataForString( s, client - level.clients, &team, &specState, &specClient ); if ( team != TEAM_SPECTATOR ) { // Ensure the player can join if ( !G_teamJoinCheck( team, ent ) ) { // Leave them where they were before the command was issued return( qfalse ); } if ( g_noTeamSwitching.integer && ( team != ent->client->sess.sessionTeam && ent->client->sess.sessionTeam != TEAM_SPECTATOR ) && g_gamestate.integer == GS_PLAYING && !force ) { trap_SendServerCommand( clientNum, "cp \"You cannot switch during a match, please wait until the round ends.\n\"" ); return qfalse; // ignore the request } if ( ( ( g_gametype.integer == GT_WOLF_LMS && g_lms_teamForceBalance.integer ) || g_teamForceBalance.integer ) && !force ) { int counts[TEAM_NUM_TEAMS]; counts[TEAM_ALLIES] = TeamCount( ent - g_entities, TEAM_ALLIES ); counts[TEAM_AXIS] = TeamCount( ent - g_entities, TEAM_AXIS ); // We allow a spread of one if ( team == TEAM_AXIS && counts[TEAM_AXIS] - counts[TEAM_ALLIES] >= 1 ) { CP( "cp \"The Axis has too many players.\n\"" ); return qfalse; // ignore the request } if ( team == TEAM_ALLIES && counts[TEAM_ALLIES] - counts[TEAM_AXIS] >= 1 ) { CP( "cp \"The Allies have too many players.\n\"" ); return qfalse; // ignore the request } // It's ok, the team we are switching to has less or same number of players } } 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 qfalse; } // NERVE - SMF - prevent players from switching to regain deployments if ( g_gametype.integer != GT_WOLF_LMS ) { if ( ( g_maxlives.integer > 0 || ( g_alliedmaxlives.integer > 0 && ent->client->sess.sessionTeam == TEAM_ALLIES ) || ( g_axismaxlives.integer > 0 && ent->client->sess.sessionTeam == TEAM_AXIS ) ) && ent->client->ps.persistant[PERS_RESPAWNS_LEFT] == 0 && oldTeam != TEAM_SPECTATOR ) { CP( "cp \"You can't switch teams because you are out of lives.\n\" 3" ); return qfalse; // ignore the request } } // DHM - Nerve :: Force players to wait 30 seconds before they can join a new team. // OSP - changed to 5 seconds // Gordon: disabling if in dev mode: cuz it sucks // Gordon: bleh, half of these variables don't mean what they used to, so this doesn't work /* if ( !g_cheats.integer ) { if ( team != oldTeam && level.warmupTime == 0 && !client->pers.initialSpawn && ( (level.time - client->pers.connectTime) > 10000 ) && ( (level.time - client->pers.enterTime) < 5000 ) && !force ) { CP(va("cp \"^3You must wait %i seconds before joining ^3a new team.\n\" 3", (int)(5 - ((level.time - client->pers.enterTime)/1000)))); return qfalse; } }*/ // dhm // // execute the team change // // DHM - Nerve // OSP if ( team != TEAM_SPECTATOR ) { client->pers.initialSpawn = qfalse; // no MV in-game if ( client->pers.mvCount > 0 ) { G_smvRemoveInvalidClients( ent, TEAM_AXIS ); G_smvRemoveInvalidClients( ent, TEAM_ALLIES ); } } // he starts at 'base' // RF, in single player, bots always use regular spawn points if ( !( ( g_gametype.integer == GT_SINGLE_PLAYER || g_gametype.integer == GT_COOP ) && ( ent->r.svFlags & SVF_BOT ) ) ) { 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_SWITCHTEAM ); } } // they go to the end of the line for tournements if ( team == TEAM_SPECTATOR ) { client->sess.spectatorTime = level.time; if ( !client->sess.referee ) { client->pers.invite = 0; } if ( team != oldTeam ) { G_smvAllRemoveSingleClient( ent - g_entities ); } } G_LeaveTank( ent, qfalse ); G_RemoveClientFromFireteams( clientNum, qtrue, qfalse ); if ( g_landminetimeout.integer ) { G_ExplodeMines( ent ); } G_FadeItems( ent, MOD_SATCHEL ); // remove ourself from teamlists { int i; mapEntityData_t *mEnt; mapEntityData_Team_t *teamList; for ( i = 0; i < 2; i++ ) { teamList = &mapEntityData[i]; if ( ( mEnt = G_FindMapEntityData( &mapEntityData[0], ent - g_entities ) ) != NULL ) { G_FreeMapEntityData( teamList, mEnt ); } mEnt = G_FindMapEntityDataSingleClient( teamList, NULL, ent->s.number, -1 ); while ( mEnt ) { mapEntityData_t *mEntFree = mEnt; mEnt = G_FindMapEntityDataSingleClient( teamList, mEnt, ent->s.number, -1 ); G_FreeMapEntityData( teamList, mEntFree ); } } } client->sess.spec_team = 0; client->sess.sessionTeam = team; client->sess.spectatorState = specState; client->sess.spectatorClient = specClient; client->pers.ready = qfalse; // (l)users will spam spec messages... honest! if ( team != oldTeam ) { gentity_t* tent = G_PopupMessage( PM_TEAM ); tent->s.effect2Time = team; tent->s.effect3Time = clientNum; tent->s.density = 0; } if ( setweapons ) { G_SetClientWeapons( ent, w1, w2, qfalse ); } // get and distribute relevent paramters G_UpdateCharacter( client ); // FIXME : doesn't ClientBegin take care of this already? ClientUserinfoChanged( clientNum ); ClientBegin( clientNum ); // ydnar: restore old respawn count (players cannot jump from team to team to regain lives) if ( respawnsLeft >= 0 && oldTeam != TEAM_SPECTATOR ) { client->ps.persistant[ PERS_RESPAWNS_LEFT ] = respawnsLeft; } G_verifyMatchState( oldTeam ); BotRecordTeamChange( clientNum ); // Reset stats when changing teams if ( team != oldTeam ) { G_deleteStats( clientNum ); } G_UpdateSpawnCounts(); if ( g_gamestate.integer == GS_PLAYING && ( client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES ) ) { if ( g_gametype.integer == GT_WOLF_LMS && level.numTeamClients[0] > 0 && level.numTeamClients[1] > 0 ) { trap_SendServerCommand( clientNum, "cp \"Will spawn next round, please wait.\n\"" ); limbo( ent, qfalse ); return( qfalse ); } else { int i; int x = client->sess.sessionTeam - TEAM_AXIS; for ( i = 0; i < MAX_COMMANDER_TEAM_SOUNDS; i++ ) { if ( level.commanderSounds[ x ][ i ].index ) { gentity_t* tent = G_TempEntity( client->ps.origin, EV_GLOBAL_CLIENT_SOUND ); tent->s.eventParm = level.commanderSounds[ x ][ i ].index - 1; tent->s.teamNum = clientNum; } } } } ent->client->pers.autofireteamCreateEndTime = 0; ent->client->pers.autofireteamJoinEndTime = 0; if ( client->sess.sessionTeam == TEAM_AXIS || client->sess.sessionTeam == TEAM_ALLIES ) { if ( g_autoFireteams.integer ) { fireteamData_t* ft = G_FindFreePublicFireteam( client->sess.sessionTeam ); if ( ft ) { trap_SendServerCommand( ent - g_entities, "aftj -1" ); ent->client->pers.autofireteamJoinEndTime = level.time + 20500; // G_AddClientToFireteam( ent-g_entities, ft->joinOrder[0] ); } else { trap_SendServerCommand( ent - g_entities, "aftc -1" ); ent->client->pers.autofireteamCreateEndTime = level.time + 20500; } } } return qtrue; } /* ================= 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 ) { // 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; gclient_t *client = ent->client; VectorCopy( client->ps.origin, pos ); // pos[2] += 16; // Gordon: removing for now VectorCopy( client->ps.viewangles, angle ); // Need this as it gets spec mode reset properly SetTeam( ent, "s", qtrue, -1, -1, qfalse ); VectorCopy( pos, client->ps.origin ); SetClientViewAngle( ent, angle ); } else { // legacy code, FIXME: useless? // Gordon: no this is for limbo i'd guess ent->client->sess.spectatorState = SPECTATOR_FREE; ent->client->ps.clientNum = ent - g_entities; } } int G_NumPlayersWithWeapon( weapon_t weap, team_t team ) { int i, j, cnt = 0; for ( i = 0; i < level.numConnectedClients; i++ ) { j = level.sortedClients[i]; if ( level.clients[j].sess.playerType != PC_SOLDIER ) { continue; } if ( level.clients[j].sess.sessionTeam != team ) { continue; } if ( level.clients[j].sess.latchPlayerWeapon != weap && level.clients[j].sess.playerWeapon != weap ) { continue; } cnt++; } return cnt; } int G_NumPlayersOnTeam( team_t team ) { int i, j, cnt = 0; for ( i = 0; i < level.numConnectedClients; i++ ) { j = level.sortedClients[i]; if ( level.clients[j].sess.sessionTeam != team ) { continue; } cnt++; } return cnt; } qboolean G_IsHeavyWeapon( weapon_t weap ) { int i; for ( i = 0; i < NUM_HEAVY_WEAPONS; i++ ) { if ( bg_heavyWeapons[i] == weap ) { return qtrue; } } return qfalse; } int G_TeamCount( gentity_t* ent, weapon_t weap ) { int i, j, cnt; if ( weap == -1 ) { // we aint checking for a weapon, so always include ourselves cnt = 1; } else { // we ARE checking for a weapon, so ignore ourselves cnt = 0; } for ( i = 0; i < level.numConnectedClients; i++ ) { j = level.sortedClients[i]; if ( j == ent - g_entities ) { continue; } if ( level.clients[j].sess.sessionTeam != ent->client->sess.sessionTeam ) { continue; } if ( weap != -1 ) { if ( level.clients[j].sess.playerWeapon != weap && level.clients[j].sess.latchPlayerWeapon != weap ) { continue; } } cnt++; } return cnt; } qboolean G_IsWeaponDisabled( gentity_t* ent, weapon_t weapon ) { int count, wcount; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return qtrue; } if ( !G_IsHeavyWeapon( weapon ) ) { return qfalse; } count = G_TeamCount( ent, -1 ); wcount = G_TeamCount( ent, weapon ); if ( wcount >= ceil( count * g_heavyWeaponRestriction.integer * 0.01f ) ) { return qtrue; } return qfalse; } void G_SetClientWeapons( gentity_t* ent, weapon_t w1, weapon_t w2, qboolean updateclient ) { qboolean changed = qfalse; if ( ent->client->sess.latchPlayerWeapon2 != w2 ) { ent->client->sess.latchPlayerWeapon2 = w2; changed = qtrue; } if ( !G_IsWeaponDisabled( ent, w1 ) ) { if ( ent->client->sess.latchPlayerWeapon != w1 ) { ent->client->sess.latchPlayerWeapon = w1; changed = qtrue; } } else { if ( ent->client->sess.latchPlayerWeapon != 0 ) { ent->client->sess.latchPlayerWeapon = 0; changed = qtrue; } } if ( updateclient && changed ) { ClientUserinfoChanged( ent - g_entities ); } } /* ================= Cmd_Team_f ================= */ void Cmd_Team_f( gentity_t *ent, unsigned int dwCommand, qboolean fValue ) { char s[MAX_TOKEN_CHARS]; char ptype[4]; char weap[4], weap2[4]; weapon_t w, w2; if ( trap_Argc() < 2 ) { char *pszTeamName; switch ( ent->client->sess.sessionTeam ) { case TEAM_ALLIES: pszTeamName = "Allies"; break; case TEAM_AXIS: pszTeamName = "Axis"; break; case TEAM_SPECTATOR: pszTeamName = "Spectator"; break; case TEAM_FREE: default: pszTeamName = "Free"; break; } CP( va( "print \"%s team\n\"", pszTeamName ) ); return; } trap_Argv( 1, s, sizeof( s ) ); trap_Argv( 2, ptype, sizeof( ptype ) ); trap_Argv( 3, weap, sizeof( weap ) ); trap_Argv( 4, weap2, sizeof( weap2 ) ); w = atoi( weap ); w2 = atoi( weap2 ); ent->client->sess.latchPlayerType = atoi( ptype ); if ( ent->client->sess.latchPlayerType < PC_SOLDIER || ent->client->sess.latchPlayerType > PC_COVERTOPS ) { ent->client->sess.latchPlayerType = PC_SOLDIER; } if ( ent->client->sess.latchPlayerType < PC_SOLDIER || ent->client->sess.latchPlayerType > PC_COVERTOPS ) { ent->client->sess.latchPlayerType = PC_SOLDIER; } if ( !SetTeam( ent, s, qfalse, w, w2, qtrue ) ) { G_SetClientWeapons( ent, w, w2, qtrue ); } } void Cmd_ResetSetup_f( gentity_t* ent ) { qboolean changed = qfalse; if ( !ent || !ent->client ) { return; } ent->client->sess.latchPlayerType = ent->client->sess.playerType; if ( ent->client->sess.latchPlayerWeapon != ent->client->sess.playerWeapon ) { ent->client->sess.latchPlayerWeapon = ent->client->sess.playerWeapon; changed = qtrue; } if ( ent->client->sess.latchPlayerWeapon2 != ent->client->sess.playerWeapon2 ) { ent->client->sess.latchPlayerWeapon2 = ent->client->sess.playerWeapon2; changed = qtrue; } if ( changed ) { ClientUserinfoChanged( ent - g_entities ); } } void Cmd_SetClass_f( gentity_t* ent, unsigned int dwCommand, qboolean fValue ) { } void Cmd_SetWeapons_f( gentity_t* ent, unsigned int dwCommand, qboolean fValue ) { } // START Mad Doc - TDF /* ================= Cmd_TeamBot_f ================= */ void Cmd_TeamBot_f( gentity_t *foo ) { char ptype[4], weap[4], fireteam[4]; char entNumStr[4]; int entNum; char *weapon; char weaponBuf[MAX_INFO_STRING]; char userinfo[MAX_INFO_STRING]; gentity_t *ent; trap_Argv( 1, entNumStr, sizeof( entNumStr ) ); entNum = atoi( entNumStr ); ent = g_entities + entNum; trap_Argv( 3, ptype, sizeof( ptype ) ); trap_Argv( 4, weap, sizeof( weap ) ); trap_Argv( 5, fireteam, sizeof( fireteam ) ); ent->client->sess.latchPlayerType = atoi( ptype ); ent->client->sess.latchPlayerWeapon = atoi( weap ); ent->client->sess.latchPlayerWeapon2 = 0; ent->client->sess.playerType = atoi( ptype ); ent->client->sess.playerWeapon = atoi( weap ); // remove any weapon info from the userinfo, so SetWolfSpawnWeapons() doesn't reset the weapon as that trap_GetUserinfo( entNum, userinfo, sizeof( userinfo ) ); weapon = Info_ValueForKey( userinfo, "pWeapon" ); if ( weapon[0] ) { Q_strncpyz( weaponBuf, weapon, sizeof( weaponBuf ) ); Info_RemoveKey( userinfo, "pWeapon" ); trap_SetUserinfo( entNum, userinfo ); } SetWolfSpawnWeapons( ent->client ); } // END Mad Doc - TDF /* ================= Cmd_Follow_f ================= */ void Cmd_Follow_f( gentity_t *ent, unsigned int dwCommand, qboolean fValue ) { int i; char arg[MAX_TOKEN_CHARS]; if ( trap_Argc() != 2 ) { if ( ent->client->sess.spectatorState == SPECTATOR_FOLLOW ) { StopFollowing( ent ); } return; } if ( ent->client->ps.pm_flags & PMF_LIMBO ) { CP( "cpm \"Can't issue a follow command while in limbo.\n\"" ); CP( "cpm \"Hit FIRE to switch between teammates.\n\"" ); return; } trap_Argv( 1, arg, sizeof( arg ) ); i = ClientNumberFromString( ent, arg ); if ( i == -1 ) { if ( !Q_stricmp( arg, "allies" ) ) { i = TEAM_ALLIES; } else if ( !Q_stricmp( arg, "axis" ) ) { i = TEAM_AXIS; } else { return;} if ( !TeamCount( ent - g_entities, i ) ) { CP( va( "print \"The %s team %s empty! Follow command ignored.\n\"", aTeams[i], ( ( ent->client->sess.sessionTeam != i ) ? "is" : "would be" ) ) ); return; } // Allow for simple toggle if ( ent->client->sess.spec_team != i ) { if ( teamInfo[i].spec_lock && !( ent->client->sess.spec_invite & i ) ) { CP( va( "print \"Sorry, the %s team is locked from spectators.\n\"", aTeams[i] ) ); } else { ent->client->sess.spec_team = i; CP( va( "print \"Spectator follow is now locked on the %s team.\n\"", aTeams[i] ) ); Cmd_FollowCycle_f( ent, 1 ); } } else { ent->client->sess.spec_team = 0; CP( va( "print \"%s team spectating is now disabled.\n\"", aTeams[i] ) ); } 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 ( level.clients[ i ].ps.pm_flags & PMF_LIMBO ) { return; } // OSP - can't follow a player on a speclocked team, unless allowed if ( !G_allowFollow( ent, level.clients[i].sess.sessionTeam ) ) { CP( va( "print \"Sorry, the %s team is locked from spectators.\n\"", aTeams[level.clients[i].sess.sessionTeam] ) ); return; } // first set them to spectator if ( ent->client->sess.sessionTeam != TEAM_SPECTATOR ) { SetTeam( ent, "spectator", qfalse, -1, -1, qfalse ); } 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; // 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", qfalse, -1, -1, qfalse ); } 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; } } if ( level.clients[clientnum].ps.pm_flags & PMF_LIMBO ) { continue; } // OSP if ( !G_desiredFollow( ent, level.clients[clientnum].sess.sessionTeam ) ) { 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_EntitySound Mad Doc xkan, 11/06/2002 - Plays a sound (wav file or sound script) on this entity Note that calling G_AddEvent(..., EV_GENERAL_SOUND, ...) has the danger of the event never getting through to the client because the entity might not be visible (unless it has the SVF_BROADCAST flag), so if you want to make sure the sound is heard, call this function instead. ======================*/ void G_EntitySound( gentity_t *ent, // entity to play the sound on const char *soundId, // sound file name or sound script ID int volume ) { // sound volume, only applies to sound file name call // for sound script, volume is currently always 127. trap_SendServerCommand( -1, va( "entitySound %d %s %d %i %i %i normal", ent->s.number, soundId, volume, (int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) ); } /*====================== G_EntitySoundNoCut Mad Doc xkan, 1/16/2003 - Similar to G_EntitySound, but do not cut this sound off ======================*/ void G_EntitySoundNoCut( gentity_t *ent, // entity to play the sound on const char *soundId, // sound file name or sound script ID int volume ) { // sound volume, only applies to sound file name call // for sound script, volume is currently always 127. trap_SendServerCommand( -1, va( "entitySound %d %s %d %i %i %i noCut", ent->s.number, soundId, volume, (int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) ); } /* ================== G_Say ================== */ #define MAX_SAY_TEXT 150 void G_SayTo( gentity_t *ent, gentity_t *other, int mode, int color, const char *name, const char *message, qboolean localize ) { if ( !other || !other->inuse || !other->client ) { return; } if ( ( mode == SAY_TEAM || mode == SAY_TEAMNL ) && !OnSameTeam( ent, other ) ) { return; } // NERVE - SMF - if spectator, no chatting to players in WolfMP if ( match_mutespecs.integer > 0 && ent->client->sess.referee == 0 && // OSP ( ( 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; } else { if ( mode == SAY_BUDDY ) { // send only to people who have the sender on their buddy list if ( ent->s.clientNum != other->s.clientNum ) { fireteamData_t *ft1, *ft2; if ( !G_IsOnFireteam( other - g_entities, &ft1 ) ) { return; } if ( !G_IsOnFireteam( ent - g_entities, &ft2 ) ) { return; } if ( ft1 != ft2 ) { return; } } } trap_SendServerCommand( other - g_entities, va( "%s \"%s%c%c%s\" %i %i", mode == SAY_TEAM || mode == SAY_BUDDY ? "tchat" : "chat", name, Q_COLOR_ESCAPE, color, message, ent - g_entities, 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]; qboolean localize = qfalse; char *loc; 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_BUDDY: localize = qtrue; G_LogPrintf( "saybuddy: %s: %s\n", ent->client->pers.netname, chatText ); loc = BG_GetLocationString( ent->r.currentOrigin ); Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, loc ); color = COLOR_YELLOW; break; case SAY_TEAM: localize = qtrue; G_LogPrintf( "sayteam: %s: %s\n", ent->client->pers.netname, chatText ); loc = BG_GetLocationString( ent->r.currentOrigin ); Com_sprintf( name, sizeof( name ), "[lof](%s%c%c) (%s): ", ent->client->pers.netname, Q_COLOR_ESCAPE, COLOR_WHITE, loc ); color = COLOR_CYAN; break; case SAY_TEAMNL: G_LogPrintf( "sayteamnl: %s: %s\n", ent->client->pers.netname, chatText ); Com_sprintf( name, sizeof( name ), "(%s^7): ", ent->client->pers.netname ); color = COLOR_CYAN; break; } Q_strncpyz( text, chatText, sizeof( text ) ); if ( target ) { if ( !COM_BitCheck( target->client->sess.ignoreClients, ent - g_entities ) ) { 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.numConnectedClients; j++ ) { other = &g_entities[level.sortedClients[j]]; if ( !COM_BitCheck( other->client->sess.ignoreClients, ent - g_entities ) ) { G_SayTo( ent, other, mode, color, name, text, localize ); } } } /* ================== Cmd_Say_f ================== */ void Cmd_Say_f( gentity_t *ent, int mode, qboolean arg0 ) { if ( trap_Argc() < 2 && !arg0 ) { return; } G_Say( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 0 : 1 ) ) ); } extern void BotRecordVoiceChat( int client, int destclient, const char *id, int mode, qboolean noResponse ); // NERVE - SMF 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; } // OSP - spec vchat rules follow the same as normal chatting rules if ( match_mutespecs.integer > 0 && ent->client->sess.referee == 0 && ent->client->sess.sessionTeam == TEAM_SPECTATOR && other->client->sess.sessionTeam != TEAM_SPECTATOR ) { return; } // send only to people who have the sender on their buddy list if ( mode == SAY_BUDDY ) { if ( ent->s.clientNum != other->s.clientNum ) { fireteamData_t *ft1, *ft2; if ( !G_IsOnFireteam( other - g_entities, &ft1 ) ) { return; } if ( !G_IsOnFireteam( ent - g_entities, &ft2 ) ) { return; } if ( ft1 != ft2 ) { return; } } } if ( mode == SAY_TEAM ) { color = COLOR_CYAN; cmd = "vtchat"; } else if ( mode == SAY_BUDDY ) { color = COLOR_YELLOW; cmd = "vbchat"; } else { color = COLOR_GREEN; cmd = "vchat"; } // RF, record this chat so bots can parse them // bots respond with voiceonly, so we check for this so they dont keep responding to responses BotRecordVoiceChat( ent->s.number, other->s.number, id, mode, voiceonly == 2 ); if ( voiceonly == 2 ) { voiceonly = qfalse; } if ( mode == SAY_TEAM || mode == SAY_BUDDY ) { CPx( other - g_entities, va( "%s %d %d %d %s %i %i %i", cmd, voiceonly, ent - g_entities, color, id, (int)ent->s.pos.trBase[0], (int)ent->s.pos.trBase[1], (int)ent->s.pos.trBase[2] ) ); } else { CPx( other - g_entities, va( "%s %d %d %d %s", cmd, voiceonly, ent - g_entities, color, id ) ); } } void G_Voice( gentity_t *ent, gentity_t *target, int mode, const char *id, qboolean voiceonly ) { int j; // 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; } // Only do the spam check for MP if ( ent->voiceChatSquelch >= 30000 ) { trap_SendServerCommand( ent - g_entities, "cpm \"^1Spam Protection^7: VoiceChat ignored\n\"" ); return; } if ( g_voiceChatsAllowed.integer ) { ent->voiceChatSquelch += ( 34000 / g_voiceChatsAllowed.integer ); } else { return; } // dhm // OSP - Charge for the lame spam! /*if(mode == SAY_ALL && (!Q_stricmp(id, "DynamiteDefused") || !Q_stricmp(id, "DynamitePlanted"))) { return; }*/ 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 ); } if ( mode == SAY_BUDDY ) { char buffer[32]; int cls = -1, i, cnt, num; qboolean allowclients[MAX_CLIENTS]; memset( allowclients, 0, sizeof( allowclients ) ); trap_Argv( 1, buffer, 32 ); cls = atoi( buffer ); trap_Argv( 2, buffer, 32 ); cnt = atoi( buffer ); if ( cnt > MAX_CLIENTS ) { cnt = MAX_CLIENTS; } for ( i = 0; i < cnt; i++ ) { trap_Argv( 3 + i, buffer, 32 ); num = atoi( buffer ); if ( num < 0 ) { continue; } if ( num >= MAX_CLIENTS ) { continue; } allowclients[ num ] = qtrue; } for ( j = 0; j < level.numConnectedClients; j++ ) { if ( level.sortedClients[j] != ent->s.clientNum ) { if ( cls != -1 && cls != level.clients[level.sortedClients[j]].sess.playerType ) { continue; } } if ( cnt ) { if ( !allowclients[ level.sortedClients[j] ] ) { continue; } } G_VoiceTo( ent, &g_entities[level.sortedClients[j]], mode, id, voiceonly ); } } else { // send it to all the apropriate clients for ( j = 0; j < level.numConnectedClients; j++ ) { G_VoiceTo( ent, &g_entities[level.sortedClients[j]], mode, id, voiceonly ); } } } /* ================== Cmd_Voice_f ================== */ static void Cmd_Voice_f( gentity_t *ent, int mode, qboolean arg0, qboolean voiceonly ) { if ( mode != SAY_BUDDY ) { if ( trap_Argc() < 2 && !arg0 ) { return; } G_Voice( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 0 : 1 ) ), voiceonly ); } else { char buffer[16]; int index; trap_Argv( 2, buffer, sizeof( buffer ) ); index = atoi( buffer ); if ( index < 0 ) { index = 0; } if ( trap_Argc() < 3 + index && !arg0 ) { return; } G_Voice( ent, NULL, mode, ConcatArgs( ( ( arg0 ) ? 2 + index : 3 + index ) ), 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 /* ================== Cmd_Where_f ================== */ void Cmd_Where_f( gentity_t *ent ) { trap_SendServerCommand( ent - g_entities, va( "print \"%s\n\"", vtos( ent->s.origin ) ) ); } /* ================== Cmd_CallVote_f ================== */ qboolean Cmd_CallVote_f( gentity_t *ent, unsigned int dwCommand, qboolean fRefCommand ) { int i; char arg1[MAX_STRING_TOKENS]; char arg2[MAX_STRING_TOKENS]; // Normal checks, if its not being issued as a referee command if ( !fRefCommand ) { if ( level.voteInfo.voteTime ) { CP( "cpm \"A vote is already in progress.\n\"" ); return qfalse; } else if ( level.intermissiontime ) { CP( "cpm \"Cannot callvote during intermission.\n\"" ); return qfalse; } else if ( !ent->client->sess.referee ) { if ( voteFlags.integer == VOTING_DISABLED ) { CP( "cpm \"Voting not enabled on this server.\n\"" ); return qfalse; } else if ( vote_limit.integer > 0 && ent->client->pers.voteCount >= vote_limit.integer ) { CP( va( "cpm \"You have already called the maximum number of votes (%d).\n\"", vote_limit.integer ) ); return qfalse; } else if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { CP( "cpm \"Not allowed to call a vote as a spectator.\n\"" ); return qfalse; } } } // 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, ';' ) ) { char *strCmdBase = ( !fRefCommand ) ? "vote" : "ref command"; G_refPrintf( ent, "Invalid %s string.", strCmdBase ); return( qfalse ); } if ( trap_Argc() > 1 && ( i = G_voteCmdCheck( ent, arg1, arg2, fRefCommand ) ) != G_NOTFOUND ) { // --OSP if ( i != G_OK ) { if ( i == G_NOTFOUND ) { return( qfalse ); // Command error } else { return( qtrue );} } } else { if ( !fRefCommand ) { CP( va( "print \"\n^3>>> Unknown vote command: ^7%s %s\n\"", arg1, arg2 ) ); G_voteHelp( ent, qtrue ); } return( qfalse ); } Com_sprintf( level.voteInfo.voteString, sizeof( level.voteInfo.voteString ), "%s %s", arg1, arg2 ); // start the voting, the caller automatically votes yes // If a referee, vote automatically passes. // OSP if ( fRefCommand ) { // level.voteInfo.voteYes = level.voteInfo.numVotingClients + 10; // JIC :) // Don't announce some votes, as in comp mode, it is generally a ref // who is policing people who shouldn't be joining and players don't want // this sort of spam in the console if ( level.voteInfo.vote_fn != G_Kick_v && level.voteInfo.vote_fn != G_Mute_v ) { AP( "cp \"^1** Referee Server Setting Change **\n\"" ); } // Gordon: just call the stupid thing.... don't bother with the voting faff level.voteInfo.vote_fn( NULL, 0, NULL, NULL, qfalse ); G_globalSound( "sound/misc/referee.wav" ); } else { level.voteInfo.voteYes = 1; AP( va( "print \"[lof]%s^7 [lon]called a vote.[lof] Voting for: %s\n\"", ent->client->pers.netname, level.voteInfo.voteString ) ); AP( va( "cp \"[lof]%s\n^7[lon]called a vote.\n\"", ent->client->pers.netname ) ); G_globalSound( "sound/misc/vote.wav" ); } level.voteInfo.voteTime = level.time; level.voteInfo.voteNo = 0; // Don't send the vote info if a ref initiates (as it will automatically pass) if ( !fRefCommand ) { for ( i = 0; i < level.numConnectedClients; i++ ) { level.clients[level.sortedClients[i]].ps.eFlags &= ~EF_VOTED; } ent->client->pers.voteCount++; ent->client->ps.eFlags |= EF_VOTED; trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteInfo.voteYes ) ); trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteInfo.voteNo ) ); trap_SetConfigstring( CS_VOTE_STRING, level.voteInfo.voteString ); trap_SetConfigstring( CS_VOTE_TIME, va( "%i", level.voteInfo.voteTime ) ); } return( qtrue ); } qboolean StringToFilter( const char *s, ipFilter_t *f ); qboolean G_FindFreeComplainIP( gclient_t* cl, ipFilter_t* ip ) { int i = 0; if ( !g_ipcomplaintlimit.integer ) { return qtrue; } for ( i = 0; i < MAX_COMPLAINTIPS && i < g_ipcomplaintlimit.integer; i++ ) { if ( !cl->pers.complaintips[i].compare && !cl->pers.complaintips[i].mask ) { cl->pers.complaintips[i].compare = ip->compare; cl->pers.complaintips[i].mask = ip->mask; return qtrue; } if ( ( cl->pers.complaintips[i].compare & cl->pers.complaintips[i].mask ) == ( ip->compare & ip->mask ) ) { return qtrue; } } return qfalse; } /* ================== 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 && g_gamestate.integer == GS_PLAYING && g_complaintlimit.integer ) { gentity_t* other = &g_entities[ ent->client->pers.complaintClient ]; gclient_t *cl = other->client; if ( !cl ) { return; } if ( cl->pers.connected != CON_CONNECTED ) { return; } if ( cl->pers.localClient ) { trap_SendServerCommand( ent - g_entities, "complaint -3" ); return; } 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 ( !cl->pers.localClient ) { const char* value; char userinfo[MAX_INFO_STRING]; ipFilter_t ip; trap_GetUserinfo( ent - g_entities, userinfo, sizeof( userinfo ) ); value = Info_ValueForKey( userinfo, "ip" ); StringToFilter( value, &ip ); if ( num <= 0 || !G_FindFreeComplainIP( cl, &ip ) ) { trap_DropClient( cl - level.clients, "kicked after too many complaints.", cl->sess.referee ? 0 : 300 ); trap_SendServerCommand( ent - g_entities, "complaint -1" ); return; } } trap_SendServerCommand( ent->client->pers.complaintClient, va( "cpm \"^1Warning^7: Complaint filed against you by %s^* You have Lost XP.\n\"", ent->client->pers.netname ) ); trap_SendServerCommand( ent - g_entities, "complaint -1" ); AddScore( other, WOLF_FRIENDLY_PENALTY ); G_LoseKillSkillPoints( other, ent->sound2to1, ent->sound1to2, ent->sound2to3 ? qtrue : qfalse ); } else { trap_SendServerCommand( ent->client->pers.complaintClient, "cpm \"No complaint filed against you.\n\"" ); trap_SendServerCommand( ent - g_entities, "complaint -2" ); } // Reset this ent's complainEndTime so they can't send multiple complaints ent->client->pers.complaintEndTime = -1; ent->client->pers.complaintClient = -1; return; } // dhm if ( ent->client->pers.applicationEndTime > level.time ) { gclient_t *cl = g_entities[ ent->client->pers.applicationClient ].client; if ( !cl ) { return; } if ( cl->pers.connected != CON_CONNECTED ) { return; } trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { trap_SendServerCommand( ent - g_entities, "application -4" ); trap_SendServerCommand( ent->client->pers.applicationClient, "application -3" ); G_AddClientToFireteam( ent->client->pers.applicationClient, ent - g_entities ); } else { trap_SendServerCommand( ent - g_entities, "application -4" ); trap_SendServerCommand( ent->client->pers.applicationClient, "application -2" ); } ent->client->pers.applicationEndTime = 0; ent->client->pers.applicationClient = -1; return; } ent->client->pers.applicationEndTime = 0; ent->client->pers.applicationClient = -1; if ( ent->client->pers.invitationEndTime > level.time ) { gclient_t *cl = g_entities[ ent->client->pers.invitationClient ].client; if ( !cl ) { return; } if ( cl->pers.connected != CON_CONNECTED ) { return; } trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { trap_SendServerCommand( ent - g_entities, "invitation -4" ); trap_SendServerCommand( ent->client->pers.invitationClient, "invitation -3" ); G_AddClientToFireteam( ent - g_entities, ent->client->pers.invitationClient ); } else { trap_SendServerCommand( ent - g_entities, "invitation -4" ); trap_SendServerCommand( ent->client->pers.invitationClient, "invitation -2" ); } ent->client->pers.invitationEndTime = 0; ent->client->pers.invitationClient = -1; return; } ent->client->pers.invitationEndTime = 0; ent->client->pers.invitationClient = -1; if ( ent->client->pers.propositionEndTime > level.time ) { gclient_t *cl = g_entities[ ent->client->pers.propositionClient ].client; if ( !cl ) { return; } if ( cl->pers.connected != CON_CONNECTED ) { return; } trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { trap_SendServerCommand( ent - g_entities, "proposition -4" ); trap_SendServerCommand( ent->client->pers.propositionClient2, "proposition -3" ); G_InviteToFireTeam( ent - g_entities, ent->client->pers.propositionClient ); } else { trap_SendServerCommand( ent - g_entities, "proposition -4" ); trap_SendServerCommand( ent->client->pers.propositionClient2, "proposition -2" ); } ent->client->pers.propositionEndTime = 0; ent->client->pers.propositionClient = -1; ent->client->pers.propositionClient2 = -1; return; } if ( ent->client->pers.autofireteamEndTime > level.time ) { fireteamData_t* ft; trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { trap_SendServerCommand( ent - g_entities, "aft -2" ); if ( G_IsFireteamLeader( ent - g_entities, &ft ) ) { ft->priv = qtrue; } } else { trap_SendServerCommand( ent - g_entities, "aft -2" ); } ent->client->pers.autofireteamEndTime = 0; return; } if ( ent->client->pers.autofireteamCreateEndTime > level.time ) { trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { trap_SendServerCommand( ent - g_entities, "aftc -2" ); G_RegisterFireteam( ent - g_entities ); } else { trap_SendServerCommand( ent - g_entities, "aftc -2" ); } ent->client->pers.autofireteamCreateEndTime = 0; return; } if ( ent->client->pers.autofireteamJoinEndTime > level.time ) { trap_Argv( 1, msg, sizeof( msg ) ); if ( msg[0] == 'y' || msg[1] == 'Y' || msg[1] == '1' ) { fireteamData_t* ft; trap_SendServerCommand( ent - g_entities, "aftj -2" ); ft = G_FindFreePublicFireteam( ent->client->sess.sessionTeam ); if ( ft ) { G_AddClientToFireteam( ent - g_entities, ft->joinOrder[0] ); } } else { trap_SendServerCommand( ent - g_entities, "aftj -2" ); } ent->client->pers.autofireteamCreateEndTime = 0; return; } ent->client->pers.propositionEndTime = 0; ent->client->pers.propositionClient = -1; ent->client->pers.propositionClient2 = -1; // 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.voteInfo.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; } if ( level.voteInfo.vote_fn == G_Kick_v ) { int pid = atoi( level.voteInfo.vote_value ); if ( !g_entities[ pid ].client ) { return; } if ( g_entities[ pid ].client->sess.sessionTeam != TEAM_SPECTATOR && ent->client->sess.sessionTeam != g_entities[ pid ].client->sess.sessionTeam ) { trap_SendServerCommand( ent - g_entities, "print \"Cannot vote to kick player on opposing team.\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.voteInfo.voteYes++; trap_SetConfigstring( CS_VOTE_YES, va( "%i", level.voteInfo.voteYes ) ); } else { level.voteInfo.voteNo++; trap_SetConfigstring( CS_VOTE_NO, va( "%i", level.voteInfo.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 return qfalse; } // jpw /* ================= 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 ( ent->client->cameraPortal ) { G_FreeEntity( ent->client->cameraPortal ); } ent->client->cameraPortal = G_Spawn(); ent->client->cameraPortal->s.eType = ET_CAMERA; ent->client->cameraPortal->s.apos.trType = TR_STATIONARY; ent->client->cameraPortal->s.apos.trTime = 0; ent->client->cameraPortal->s.apos.trDuration = 0; VectorClear( ent->client->cameraPortal->s.angles ); VectorClear( ent->client->cameraPortal->s.apos.trDelta ); G_SetOrigin( ent->client->cameraPortal, ent->r.currentOrigin ); VectorCopy( ent->r.currentOrigin, ent->client->cameraPortal->s.origin2 ); ent->client->cameraPortal->s.frame = 0; ent->client->cameraPortal->r.svFlags |= ( SVF_PORTAL | SVF_SINGLECLIENT ); ent->client->cameraPortal->r.singleClient = ent->client->ps.clientNum; ent->client->ps.eFlags |= EF_VIEWING_CAMERA; ent->s.eFlags |= EF_VIEWING_CAMERA; VectorCopy( ent->r.currentOrigin, ent->client->cameraOrigin ); // backup our origin // (SA) trying this in client to avoid 1 frame of player drawing // ent->client->ps.eFlags |= EF_NODRAW; // ent->s.eFlags |= EF_NODRAW; } /* ================= Cmd_StopCamera_f ================= */ void Cmd_StopCamera_f( gentity_t *ent ) { // gentity_t *sp; if ( ent->client->cameraPortal && ( ent->client->ps.eFlags & EF_VIEWING_CAMERA ) ) { // send a script event // G_Script_ScriptEvent( ent->client->cameraPortal, "stopcam", "" ); // go back into noclient mode G_FreeEntity( ent->client->cameraPortal ); ent->client->cameraPortal = NULL; ent->s.eFlags &= ~EF_VIEWING_CAMERA; ent->client->ps.eFlags &= ~EF_VIEWING_CAMERA; //G_SetOrigin( ent, ent->client->cameraOrigin ); // restore our origin //VectorCopy( ent->client->cameraOrigin, ent->client->ps.origin ); // (SA) trying this in client to avoid 1 frame of player drawing // ent->s.eFlags &= ~EF_NODRAW; // ent->client->ps.eFlags &= ~EF_NODRAW; // RF, if we are near the spawn point, save the "current" game, for reloading after death // sp = NULL; // gcc: suggests () around assignment used as truth value // while ((sp = G_Find( sp, FOFS(classname), "info_player_deathmatch" ))) { // info_player_start becomes info_player_deathmatch in it's spawn functon // if (Distance( ent->s.pos.trBase, sp->s.origin ) < 256 && trap_InPVS( ent->s.pos.trBase, sp->s.origin )) { // G_SaveGame( NULL ); // break; // } // } } } /* ================= Cmd_SetCameraOrigin_f ================= */ void Cmd_SetCameraOrigin_f( gentity_t *ent ) { char buffer[MAX_TOKEN_CHARS]; int i; vec3_t origin; if ( trap_Argc() != 4 ) { return; } for ( i = 0 ; i < 3 ; i++ ) { trap_Argv( i + 1, buffer, sizeof( buffer ) ); origin[i] = atof( buffer ); } if ( ent->client->cameraPortal ) { //G_SetOrigin( ent->client->cameraPortal, origin ); // set our origin VectorCopy( origin, ent->client->cameraPortal->s.origin2 ); trap_LinkEntity( ent->client->cameraPortal ); // G_SetOrigin( ent, origin ); // set our origin // VectorCopy( origin, ent->client->ps.origin ); } } extern gentity_t *BotFindEntityForName( char *name ); /* ============== Cmd_InterruptCamera_f ============== */ void Cmd_InterruptCamera_f( gentity_t *ent ) { gentity_t *player; if ( g_gametype.integer != GT_SINGLE_PLAYER && g_gametype.integer != GT_COOP ) { return; } player = BotFindEntityForName( "player" ); if ( !player ) { return; } G_Script_ScriptEvent( player, "trigger", "cameraInterrupt" ); } extern vec3_t playerMins; extern vec3_t playerMaxs; qboolean G_TankIsOccupied( gentity_t* ent ) { if ( !ent->tankLink ) { return qfalse; } return qtrue; } qboolean G_TankIsMountable( gentity_t* ent, gentity_t* other ) { if ( !( ent->spawnflags & 128 ) ) { return qfalse; } if ( level.disableTankEnter ) { return qfalse; } if ( G_TankIsOccupied( ent ) ) { return qfalse; } if ( ent->health <= 0 ) { return qfalse; } if ( other->client->ps.weaponDelay ) { return qfalse; } return qtrue; } // Rafael /* ================== Cmd_Activate_f ================== */ qboolean Do_Activate2_f( gentity_t *ent, gentity_t *traceEnt ) { qboolean found = qfalse; if ( ent->client->sess.playerType == PC_COVERTOPS && !ent->client->ps.powerups[PW_OPS_DISGUISED] && ent->health > 0 ) { if ( !ent->client->ps.powerups[PW_BLUEFLAG] && !ent->client->ps.powerups[PW_REDFLAG] ) { if ( traceEnt->s.eType == ET_CORPSE ) { if ( BODY_TEAM( traceEnt ) < 4 && BODY_TEAM( traceEnt ) != ent->client->sess.sessionTeam ) { found = qtrue; if ( BODY_VALUE( traceEnt ) >= 250 ) { traceEnt->nextthink = traceEnt->timestamp + BODY_TIME( BODY_TEAM( traceEnt ) ); // BG_AnimScriptEvent( &ent->client->ps, ent->client->pers.character->animModelInfo, ANIM_ET_PICKUPGRENADE, qfalse, qtrue ); // ent->client->ps.pm_flags |= PMF_TIME_LOCKPLAYER; // ent->client->ps.pm_time = 2100; ent->client->ps.powerups[PW_OPS_DISGUISED] = 1; ent->client->ps.powerups[PW_OPS_CLASS_1] = BODY_CLASS( traceEnt ) & 1; ent->client->ps.powerups[PW_OPS_CLASS_2] = BODY_CLASS( traceEnt ) & 2; ent->client->ps.powerups[PW_OPS_CLASS_3] = BODY_CLASS( traceEnt ) & 4; BODY_TEAM( traceEnt ) += 4; traceEnt->activator = ent; traceEnt->s.time2 = 1; // sound effect G_AddEvent( ent, EV_DISGUISE_SOUND, 0 ); G_AddSkillPoints( ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 5.f ); G_DebugAddSkillPoints( ent, SK_MILITARY_INTELLIGENCE_AND_SCOPED_WEAPONS, 5, "stealing uniform" ); Q_strncpyz( ent->client->disguiseNetname, g_entities[traceEnt->s.clientNum].client->pers.netname, sizeof( ent->client->disguiseNetname ) ); ent->client->disguiseRank = g_entities[traceEnt->s.clientNum].client ? g_entities[traceEnt->s.clientNum].client->sess.rank : 0; ClientUserinfoChanged( ent->s.clientNum ); } else { BODY_VALUE( traceEnt ) += 5; } } } } } return found; } // TAT 1/14/2003 - extracted out the functionality of Cmd_Activate_f from finding the object to use // so we can force bots to use items, without worrying that they are looking EXACTLY at the target qboolean Do_Activate_f( gentity_t *ent, gentity_t *traceEnt ) { qboolean found = qfalse; qboolean walking = qfalse; vec3_t forward; //, offset, end; //trace_t tr; // Arnout: invisible entities can't be used if ( traceEnt->entstate == STATE_INVISIBLE || traceEnt->entstate == STATE_UNDERCONSTRUCTION ) { return qfalse; } if ( ent->client->pers.cmd.buttons & BUTTON_WALKING ) { walking = qtrue; } 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 qfalse; } memset( &trace, 0, sizeof( trace ) ); if ( traceEnt->use ) { G_UseEntity( traceEnt, ent, 0 ); } found = qtrue; } else if ( traceEnt->s.eType == ET_ITEM ) { trace_t trace; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { return qfalse; } 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 ); } found = qtrue; } else if ( traceEnt->s.eType == ET_MOVER && G_TankIsMountable( traceEnt, ent ) ) { G_Script_ScriptEvent( traceEnt, "mg42", "mount" ); ent->tagParent = traceEnt->nextTrain; Q_strncpyz( ent->tagName, "tag_player", MAX_QPATH ); ent->backupWeaponTime = ent->client->ps.weaponTime; ent->client->ps.weaponTime = traceEnt->backupWeaponTime; ent->client->ps.weapHeat[WP_DUMMY_MG42] = traceEnt->mg42weapHeat; ent->tankLink = traceEnt; traceEnt->tankLink = ent; G_ProcessTagConnect( ent, qtrue ); found = qtrue; } else if ( G_EmplacedGunIsMountable( traceEnt, ent ) ) { gclient_t* cl = &level.clients[ ent->s.clientNum ]; 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] ); ent->backupWeaponTime = ent->client->ps.weaponTime; ent->client->ps.weaponTime = traceEnt->backupWeaponTime; ent->client->ps.weapHeat[WP_DUMMY_MG42] = traceEnt->mg42weapHeat; 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) found = qtrue; } 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) found = qtrue; } else if ( ( Q_stricmp( traceEnt->classname, "team_WOLF_checkpoint" ) == 0 ) ) { if ( traceEnt->count != ent->client->sess.sessionTeam ) { traceEnt->health++; } found = qtrue; } 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; found = qtrue; } else if ( !Q_stricmp( traceEnt->classname, "func_invisible_user" ) ) { if ( walking ) { traceEnt->flags |= FL_SOFTACTIVATE; // no noise } G_UseEntity( traceEnt, ent, ent ); found = qtrue; } else if ( !Q_stricmp( traceEnt->classname, "props_footlocker" ) ) { G_UseEntity( traceEnt, ent, ent ); found = qtrue; } } return found; } void G_LeaveTank( gentity_t* ent, qboolean position ) { gentity_t* tank; // found our tank (or whatever) vec3_t axis[3]; vec3_t pos; trace_t tr; tank = ent->tankLink; if ( !tank ) { return; } if ( position ) { AnglesToAxis( tank->s.angles, axis ); VectorMA( ent->client->ps.origin, 128, axis[1], pos ); trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID ); if ( tr.startsolid ) { // try right VectorMA( ent->client->ps.origin, -128, axis[1], pos ); trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID ); if ( tr.startsolid ) { // try back VectorMA( ent->client->ps.origin, -224, axis[0], pos ); trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID ); if ( tr.startsolid ) { // try front VectorMA( ent->client->ps.origin, 224, axis[0], pos ); trap_Trace( &tr, pos, playerMins, playerMaxs, pos, -1, CONTENTS_SOLID ); if ( tr.startsolid ) { // give up return; } } } } VectorClear( ent->client->ps.velocity ); // Gordon: dont want them to fly away ;D TeleportPlayer( ent, pos, ent->client->ps.viewangles ); } tank->mg42weapHeat = ent->client->ps.weapHeat[WP_DUMMY_MG42]; tank->backupWeaponTime = ent->client->ps.weaponTime; ent->client->ps.weaponTime = ent->backupWeaponTime; G_Script_ScriptEvent( tank, "mg42", "unmount" ); ent->tagParent = NULL; *ent->tagName = '\0'; ent->s.eFlags &= ~EF_MOUNTEDTANK; ent->client->ps.eFlags &= ~EF_MOUNTEDTANK; tank->s.powerups = -1; tank->tankLink = NULL; ent->tankLink = NULL; } void Cmd_Activate_f( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *traceEnt; vec3_t forward, right, up, offset; // int activatetime = level.time; qboolean found = qfalse; qboolean pass2 = qfalse; int i; if ( ent->health <= 0 ) { return; } if ( ent->s.weapon == WP_MORTAR_SET || ent->s.weapon == WP_MOBILE_MG42_SET ) { return; } 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.eFlags &= ~EF_AAGUN_ACTIVE; ent->client->ps.persistant[PERS_HWEAPON_USE] = 0; ent->active = qfalse; for ( i = 0; i < level.num_entities; i++ ) { if ( g_entities[i].s.eType == ET_MG42_BARREL && g_entities[i].r.ownerNum == ent->s.number ) { g_entities[i].mg42weapHeat = ent->client->ps.weapHeat[WP_DUMMY_MG42]; g_entities[i].backupWeaponTime = ent->client->ps.weaponTime; break; } } ent->client->ps.weaponTime = ent->backupWeaponTime; } else { ent->active = qfalse; } return; } else if ( ent->client->ps.eFlags & EF_MOUNTEDTANK && ent->s.eFlags & EF_MOUNTEDTANK && !level.disableTankExit ) { G_LeaveTank( ent, qtrue ); return; } AngleVectors( ent->client->ps.viewangles, forward, right, up ); VectorCopy( ent->client->ps.origin, offset ); offset[2] += ent->client->ps.viewheight; // lean if ( ent->client->ps.leanf ) { VectorMA( offset, ent->client->ps.leanf, right, offset ); } //VectorMA( offset, 256, forward, end ); VectorMA( offset, 96, forward, end ); trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_MISSILECLIP | CONTENTS_BODY | CONTENTS_CORPSE ) ); if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) { trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_MISSILECLIP | CONTENTS_TRIGGER ) ); pass2 = qtrue; } tryagain: if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) { return; } traceEnt = &g_entities[ tr.entityNum ]; found = Do_Activate_f( ent, traceEnt ); if ( !found && !pass2 ) { pass2 = qtrue; trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_MISSILECLIP | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) ); goto tryagain; } } void Cmd_Activate2_f( gentity_t *ent ) { trace_t tr; vec3_t end; gentity_t *traceEnt; vec3_t forward, right, up, offset; // int activatetime = level.time; qboolean found = qfalse; qboolean pass2 = qfalse; if ( ent->client->sess.playerType != PC_COVERTOPS ) { return; } 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 ) ); if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) { trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) ); pass2 = qtrue; } tryagain: if ( tr.surfaceFlags & SURF_NOIMPACT || tr.entityNum == ENTITYNUM_WORLD ) { return; } traceEnt = &g_entities[ tr.entityNum ]; found = Do_Activate2_f( ent, traceEnt ); if ( !found && !pass2 ) { pass2 = qtrue; trap_Trace( &tr, offset, NULL, NULL, end, ent->s.number, ( CONTENTS_SOLID | CONTENTS_BODY | CONTENTS_CORPSE | CONTENTS_TRIGGER ) ); goto tryagain; } } /* ============================ 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); } */ void G_UpdateSpawnCounts( void ) { int i, j; char cs[MAX_STRING_CHARS]; int current, count, team; for ( i = 0; i < level.numspawntargets; i++ ) { trap_GetConfigstring( CS_MULTI_SPAWNTARGETS + i, cs, sizeof( cs ) ); current = atoi( Info_ValueForKey( cs, "c" ) ); team = atoi( Info_ValueForKey( cs, "t" ) ) & ~256; count = 0; for ( j = 0; j < level.numConnectedClients; j++ ) { gclient_t* client = &level.clients[ level.sortedClients[ j ] ]; if ( client->sess.sessionTeam != TEAM_AXIS && client->sess.sessionTeam != TEAM_ALLIES ) { continue; } if ( client->sess.sessionTeam == team && client->sess.spawnObjectiveIndex == i + 1 ) { count++; continue; } if ( client->sess.spawnObjectiveIndex == 0 ) { if ( client->sess.sessionTeam == TEAM_AXIS ) { if ( level.axisAutoSpawn == i ) { count++; continue; } } else { if ( level.alliesAutoSpawn == i ) { count++; continue; } } } } if ( count == current ) { continue; } Info_SetValueForKey( cs, "c", va( "%i", count ) ); trap_SetConfigstring( CS_MULTI_SPAWNTARGETS + i, cs ); } } /* ============ Cmd_SetSpawnPoint_f ============ */ void SetPlayerSpawn( gentity_t* ent, int spawn, qboolean update ) { ent->client->sess.spawnObjectiveIndex = spawn; if ( ent->client->sess.spawnObjectiveIndex >= MAX_MULTI_SPAWNTARGETS || ent->client->sess.spawnObjectiveIndex < 0 ) { ent->client->sess.spawnObjectiveIndex = 0; } if ( update ) { G_UpdateSpawnCounts(); } } void Cmd_SetSpawnPoint_f( gentity_t* ent ) { char arg[MAX_TOKEN_CHARS]; int val, i; if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); val = atoi( arg ); if ( ent->client ) { SetPlayerSpawn( ent, val, qtrue ); } // if( ent->client->sess.sessionTeam != TEAM_SPECTATOR && !(ent->client->ps.pm_flags & PMF_LIMBO) ) { // return; // } for ( i = 0; i < level.numLimboCams; i++ ) { int x = ( g_entities[level.limboCams[i].targetEnt].count - CS_MULTI_SPAWNTARGETS ) + 1; if ( level.limboCams[i].spawn && x == val ) { VectorCopy( level.limboCams[i].origin, ent->s.origin2 ); ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE; trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) ); break; } } } /* ============ Cmd_SetSniperSpot_f ============ */ void Cmd_SetSniperSpot_f( gentity_t *clent ) { gentity_t *spot; vmCvar_t cvar_mapname; char filename[MAX_QPATH]; fileHandle_t f; char buf[1024]; if ( !g_cheats.integer ) { return; } if ( !trap_Cvar_VariableIntegerValue( "cl_running" ) ) { return; // only allow locally playing client } if ( clent->s.number != 0 ) { return; // only allow locally playing client } // drop a sniper spot here spot = G_Spawn(); spot->classname = "bot_sniper_spot"; VectorCopy( clent->r.currentOrigin, spot->s.origin ); VectorCopy( clent->client->ps.viewangles, spot->s.angles ); spot->aiTeam = clent->client->sess.sessionTeam; // output to text file trap_Cvar_Register( &cvar_mapname, "mapname", "", CVAR_SERVERINFO | CVAR_ROM ); Com_sprintf( filename, sizeof( filename ), "maps/%s.botents", cvar_mapname.string ); if ( trap_FS_FOpenFile( filename, &f, FS_APPEND ) < 0 ) { G_Error( "Cmd_SetSniperSpot_f: cannot open %s for writing", filename ); } Com_sprintf( buf, sizeof( buf ), "{\n\"classname\" \"%s\"\n\"origin\" \"%.3f %.3f %.3f\"\n\"angles\" \"%.2f %.2f %.2f\"\n\"aiTeam\" \"%i\"\n}\n\n", spot->classname, spot->s.origin[0], spot->s.origin[1], spot->s.origin[2], spot->s.angles[0], spot->s.angles[1], spot->s.angles[2], spot->aiTeam ); trap_FS_Write( buf, strlen( buf ), f ); trap_FS_FCloseFile( f ); G_Printf( "dropped sniper spot\n" ); return; } void G_PrintAccuracyLog( gentity_t *ent ); /* ============ Cmd_SetWayPoint_f ============ */ /*void Cmd_SetWayPoint_f( gentity_t *ent ) { char arg[MAX_TOKEN_CHARS]; vec3_t forward, muzzlePoint, end, loc; trace_t trace; if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to set waypoints as spectator.\n\"" ); return; } if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, arg, sizeof( arg ) ); AngleVectors( ent->client->ps.viewangles, forward, NULL, NULL ); VectorCopy( ent->r.currentOrigin, muzzlePoint ); muzzlePoint[2] += ent->client->ps.viewheight; VectorMA( muzzlePoint, 8192, forward, end ); trap_Trace( &trace, muzzlePoint, NULL, NULL, end, ent->s.number, MASK_SHOT ); if( trace.surfaceFlags & SURF_NOIMPACT ) return; VectorCopy( trace.endpos, loc ); G_SetWayPoint( ent, atoi(arg), loc ); }*/ /* ============ Cmd_ClearWayPoint_f ============ */ /*void Cmd_ClearWayPoint_f( gentity_t *ent ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR ) { trap_SendServerCommand( ent-g_entities, "print \"Not allowed to clear waypoints as spectator.\n\"" ); return; } G_RemoveWayPoint( ent->client ); }*/ void Cmd_WeaponStat_f( gentity_t* ent ) { char buffer[16]; extWeaponStats_t stat; if ( !ent || !ent->client ) { return; } if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, buffer, 16 ); stat = atoi( buffer ); trap_SendServerCommand( ent - g_entities, va( "rws %i %i", ent->client->sess.aWeaponStats[stat].atts, ent->client->sess.aWeaponStats[stat].hits ) ); } void Cmd_IntermissionWeaponStats_f( gentity_t* ent ) { char buffer[1024]; int i, clientNum; if ( !ent || !ent->client ) { return; } trap_Argv( 1, buffer, sizeof( buffer ) ); clientNum = atoi( buffer ); if ( clientNum < 0 || clientNum > MAX_CLIENTS ) { return; } Q_strncpyz( buffer, "imws ", sizeof( buffer ) ); for ( i = 0; i < WS_MAX; i++ ) { Q_strcat( buffer, sizeof( buffer ), va( "%i %i %i ", level.clients[clientNum].sess.aWeaponStats[i].atts, level.clients[clientNum].sess.aWeaponStats[i].hits, level.clients[clientNum].sess.aWeaponStats[i].kills ) ); } trap_SendServerCommand( ent - g_entities, buffer ); } void G_MakeReady( gentity_t* ent ) { ent->client->ps.eFlags |= EF_READY; ent->s.eFlags |= EF_READY; // rain - #105 - moved this set here ent->client->pers.ready = qtrue; } void G_MakeUnready( gentity_t* ent ) { ent->client->ps.eFlags &= ~EF_READY; ent->s.eFlags &= ~EF_READY; // rain - #105 - moved this set here ent->client->pers.ready = qfalse; } void Cmd_IntermissionReady_f( gentity_t* ent ) { if ( !ent || !ent->client ) { return; } G_MakeReady( ent ); } void Cmd_IntermissionPlayerKillsDeaths_f( gentity_t* ent ) { char buffer[1024]; int i; if ( !ent || !ent->client ) { return; } Q_strncpyz( buffer, "impkd ", sizeof( buffer ) ); for ( i = 0; i < MAX_CLIENTS; i++ ) { if ( g_entities[i].inuse ) { Q_strcat( buffer, sizeof( buffer ), va( "%i %i ", level.clients[i].sess.kills, level.clients[i].sess.deaths ) ); } else { Q_strcat( buffer, sizeof( buffer ), "0 0 " ); } } trap_SendServerCommand( ent - g_entities, buffer ); } void G_CalcClientAccuracies( void ) { int i, j; int shots, hits; for ( i = 0; i < MAX_CLIENTS; i++ ) { shots = 0; hits = 0; if ( g_entities[i].inuse ) { for ( j = 0; j < WS_MAX; j++ ) { shots += level.clients[i].sess.aWeaponStats[j].atts; hits += level.clients[i].sess.aWeaponStats[j].hits; } level.clients[ i ].acc = shots ? ( 100 * hits ) / (float)shots : 0; } else { level.clients[ i ].acc = 0; } } } void Cmd_IntermissionWeaponAccuracies_f( gentity_t* ent ) { char buffer[1024]; int i; if ( !ent || !ent->client ) { return; } G_CalcClientAccuracies(); Q_strncpyz( buffer, "imwa ", sizeof( buffer ) ); for ( i = 0; i < MAX_CLIENTS; i++ ) { Q_strcat( buffer, sizeof( buffer ), va( "%i ", (int)level.clients[ i ].acc ) ); } trap_SendServerCommand( ent - g_entities, buffer ); } void Cmd_SelectedObjective_f( gentity_t* ent ) { int i, val; char buffer[16]; vec_t dist, neardist = 0; int nearest = -1; if ( !ent || !ent->client ) { return; } // if( ent->client->sess.sessionTeam != TEAM_SPECTATOR && !(ent->client->ps.pm_flags & PMF_LIMBO) ) { // return; // } if ( trap_Argc() != 2 ) { return; } trap_Argv( 1, buffer, 16 ); val = atoi( buffer ) + 1; for ( i = 0; i < level.numLimboCams; i++ ) { if ( !level.limboCams[i].spawn && level.limboCams[i].info == val ) { if ( !level.limboCams[i].hasEnt ) { VectorCopy( level.limboCams[i].origin, ent->s.origin2 ); ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE; trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) ); break; } else { dist = VectorDistanceSquared( level.limboCams[i].origin, g_entities[level.limboCams[i].targetEnt].r.currentOrigin ); if ( nearest == -1 || dist < neardist ) { nearest = i; neardist = dist; } } } } if ( nearest != -1 ) { i = nearest; VectorCopy( level.limboCams[i].origin, ent->s.origin2 ); ent->r.svFlags |= SVF_SELF_PORTAL_EXCLUSIVE; trap_SendServerCommand( ent - g_entities, va( "portalcampos %i %i %i %i %i %i %i %i", val - 1, (int)level.limboCams[i].origin[0], (int)level.limboCams[i].origin[1], (int)level.limboCams[i].origin[2], (int)level.limboCams[i].angles[0], (int)level.limboCams[i].angles[1], (int)level.limboCams[i].angles[2], level.limboCams[i].hasEnt ? level.limboCams[i].targetEnt : -1 ) ); } } void Cmd_Ignore_f( gentity_t* ent ) { char cmd[MAX_TOKEN_CHARS]; int cnum; trap_Argv( 1, cmd, sizeof( cmd ) ); if ( !*cmd ) { trap_SendServerCommand( ent - g_entities, "print \"usage: Ignore .\n\"\n" ); return; } cnum = G_refClientnumForName( ent, cmd ); if ( cnum != MAX_CLIENTS ) { COM_BitSet( ent->client->sess.ignoreClients, cnum ); } } void Cmd_TicketTape_f( void ) { /* char cmd[MAX_TOKEN_CHARS]; trap_Argv( 1, cmd, sizeof( cmd ) ); trap_SendServerCommand( -1, va( "tt \"LANDMINES SPOTTED CHECK COMMAND MAP FOR DETAILS \"\n", cmd ));*/ } void Cmd_UnIgnore_f( gentity_t* ent ) { char cmd[MAX_TOKEN_CHARS]; int cnum; trap_Argv( 1, cmd, sizeof( cmd ) ); if ( !*cmd ) { trap_SendServerCommand( ent - g_entities, "print \"usage: Unignore .\n\"\n" ); return; } cnum = G_refClientnumForName( ent, cmd ); if ( cnum != MAX_CLIENTS ) { COM_BitClear( ent->client->sess.ignoreClients, cnum ); } } /* ================= Cmd_SwapPlacesWithBot_f ================= */ void Cmd_SwapPlacesWithBot_f( gentity_t *ent, int botNum ) { gentity_t *botent; gclient_t cl, *client; clientPersistant_t saved; clientSession_t sess; int persistant[MAX_PERSISTANT]; // client = ent->client; // botent = &g_entities[botNum]; if ( !botent->client ) { return; } // if this bot is dead if ( botent->health <= 0 && ( botent->client->ps.pm_flags & PMF_LIMBO ) ) { trap_SendServerCommand( ent - g_entities, "print \"Bot is in limbo mode, cannot swap places.\n\"" ); return; } // if ( client->sess.sessionTeam != botent->client->sess.sessionTeam ) { trap_SendServerCommand( ent - g_entities, "print \"Bot is on different team, cannot swap places.\n\"" ); return; } // // copy the client information cl = *botent->client; // G_DPrintf( "Swapping places: %s in for %s\n", ent->client->pers.netname, botent->client->pers.netname ); // kill the bot botent->flags &= ~FL_GODMODE; botent->client->ps.stats[STAT_HEALTH] = botent->health = 0; player_die( botent, ent, ent, 100000, MOD_SWAP_PLACES ); // make sure they go into limbo mode right away, and dont show a corpse limbo( botent, qfalse ); // respawn the player ent->client->ps.pm_flags &= ~PMF_LIMBO; // JPW NERVE turns off limbo // copy the location VectorCopy( cl.ps.origin, ent->s.origin ); VectorCopy( cl.ps.viewangles, ent->s.angles ); // copy session data, so we spawn in as the same class // save items saved = client->pers; sess = client->sess; memcpy( persistant, ent->client->ps.persistant, sizeof( persistant ) ); // give them the right weapons/etc *client = cl; client->sess = sess; client->sess.playerType = ent->client->sess.latchPlayerType = cl.sess.playerType; client->sess.playerWeapon = ent->client->sess.latchPlayerWeapon = cl.sess.playerWeapon; client->sess.playerWeapon2 = ent->client->sess.latchPlayerWeapon2 = cl.sess.playerWeapon2; // spawn them in ClientSpawn( ent, qtrue ); // restore items client->pers = saved; memcpy( ent->client->ps.persistant, persistant, sizeof( persistant ) ); client->ps = cl.ps; client->ps.clientNum = ent->s.number; ent->health = client->ps.stats[STAT_HEALTH]; SetClientViewAngle( ent, cl.ps.viewangles ); // make sure they dont respawn immediately after they die client->pers.lastReinforceTime = 0; } /* ================= 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 ) { if ( !ent->client->sess.muted ) { Cmd_Say_f( ent, SAY_ALL, qfalse ); } return; } if ( Q_stricmp( cmd, "say_team" ) == 0 ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE ) { trap_SendServerCommand( ent - g_entities, "print \"Can't team chat as spectator\n\"\n" ); return; } if ( !ent->client->sess.muted ) { Cmd_Say_f( ent, SAY_TEAM, qfalse ); } return; } else if ( Q_stricmp( cmd, "vsay" ) == 0 ) { if ( !ent->client->sess.muted ) { Cmd_Voice_f( ent, SAY_ALL, qfalse, qfalse ); } return; } else if ( Q_stricmp( cmd, "vsay_team" ) == 0 ) { if ( ent->client->sess.sessionTeam == TEAM_SPECTATOR || ent->client->sess.sessionTeam == TEAM_FREE ) { trap_SendServerCommand( ent - g_entities, "print \"Can't team chat as spectator\n\"\n" ); return; } if ( !ent->client->sess.muted ) { Cmd_Voice_f( ent, SAY_TEAM, qfalse, qfalse ); } return; } else if ( Q_stricmp( cmd, "say_buddy" ) == 0 ) { if ( !ent->client->sess.muted ) { Cmd_Say_f( ent, SAY_BUDDY, qfalse ); } return; } else if ( Q_stricmp( cmd, "vsay_buddy" ) == 0 ) { if ( !ent->client->sess.muted ) { Cmd_Voice_f( ent, SAY_BUDDY, qfalse, qfalse ); } return; } else if ( Q_stricmp( cmd, "score" ) == 0 ) { Cmd_Score_f( ent ); return; } else if ( Q_stricmp( cmd, "vote" ) == 0 ) { Cmd_Vote_f( ent ); return; } else if ( Q_stricmp( cmd, "fireteam" ) == 0 ) { Cmd_FireTeam_MP_f( ent ); return; } else if ( Q_stricmp( cmd, "showstats" ) == 0 ) { G_PrintAccuracyLog( ent ); return; } else if ( Q_stricmp( cmd, "rconAuth" ) == 0 ) { Cmd_AuthRcon_f( ent ); return; } else if ( Q_stricmp( cmd, "ignore" ) == 0 ) { Cmd_Ignore_f( ent ); return; } else if ( Q_stricmp( cmd, "unignore" ) == 0 ) { Cmd_UnIgnore_f( ent ); return; } else if ( Q_stricmp( cmd, "obj" ) == 0 ) { Cmd_SelectedObjective_f( ent ); return; } else if ( !Q_stricmp( cmd, "impkd" ) ) { Cmd_IntermissionPlayerKillsDeaths_f( ent ); return; } else if ( !Q_stricmp( cmd, "imwa" ) ) { Cmd_IntermissionWeaponAccuracies_f( ent ); return; } else if ( !Q_stricmp( cmd, "imws" ) ) { Cmd_IntermissionWeaponStats_f( ent ); return; } else if ( !Q_stricmp( cmd, "imready" ) ) { Cmd_IntermissionReady_f( ent ); return; } else if ( Q_stricmp( cmd, "ws" ) == 0 ) { Cmd_WeaponStat_f( ent ); return; } else if ( !Q_stricmp( cmd, "forcetapout" ) ) { if ( !ent || !ent->client ) { return; } if ( ent->client->ps.stats[STAT_HEALTH] <= 0 && ( ent->client->sess.sessionTeam == TEAM_AXIS || ent->client->sess.sessionTeam == TEAM_ALLIES ) ) { limbo( ent, qtrue ); } return; } // OSP // Do these outside as we don't want to advertise it in the help screen if ( !Q_stricmp( cmd, "wstats" ) ) { G_statsPrint( ent, 1 ); return; } if ( !Q_stricmp( cmd, "sgstats" ) ) { // Player game stats G_statsPrint( ent, 2 ); return; } if ( !Q_stricmp( cmd, "stshots" ) ) { // "Topshots" accuracy rankings G_weaponStatsLeaders_cmd( ent, qtrue, qtrue ); return; } if ( !Q_stricmp( cmd, "rs" ) ) { Cmd_ResetSetup_f( ent ); return; } if ( G_commandCheck( ent, cmd, qtrue ) ) { return; } // OSP // 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. CPx( clientNum, va( "print \"^3%s^7 not allowed during intermission.\n\"", cmd ) ); return; } if ( Q_stricmp( cmd, "give" ) == 0 ) { Cmd_Give_f( ent ); } else if ( Q_stricmp( cmd, "listbotgoals" ) == 0 ) { Cmd_ListBotGoals_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, "follownext" ) == 0 ) { Cmd_FollowCycle_f( ent, 1 ); } else if ( Q_stricmp( cmd, "followprev" ) == 0 ) { Cmd_FollowCycle_f( ent, -1 ); } else if ( Q_stricmp( cmd, "where" ) == 0 ) { Cmd_Where_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, "cameraInterrupt" ) == 0 ) { Cmd_InterruptCamera_f( ent ); } else if ( Q_stricmp( cmd, "setviewpos" ) == 0 ) { Cmd_SetViewpos_f( ent ); } else if ( Q_stricmp( cmd, "setspawnpt" ) == 0 ) { Cmd_SetSpawnPoint_f( ent ); } else if ( Q_stricmp( cmd, "setsniperspot" ) == 0 ) { Cmd_SetSniperSpot_f( ent ); // } else if (Q_stricmp (cmd, "waypoint") == 0) { // Cmd_SetWayPoint_f( ent ); // } else if (Q_stricmp (cmd, "clearwaypoint") == 0) { // Cmd_ClearWayPoint_f( ent ); // OSP } else if ( G_commandCheck( ent, cmd, qfalse ) ) { return; } else { trap_SendServerCommand( clientNum, va( "print \"unknown cmd[lof] %s\n\"", cmd ) ); } }