//========= Copyright Valve Corporation, All rights reserved. ============// // // Purpose: // // $NoKeywords: $ // //=============================================================================// /* ===== client.cpp ======================================================== client/server game specific stuff */ #include "cbase.h" #include "player.h" #include "client.h" #include "soundent.h" #include "gamerules.h" #include "game.h" #include "physics.h" #include "entitylist.h" #include "shake.h" #include "globalstate.h" #include "event_tempentity_tester.h" #include "ndebugoverlay.h" #include "engine/IEngineSound.h" #include #include "tier1/strtools.h" #include "te_effect_dispatch.h" #include "globals.h" #include "nav_mesh.h" #include "team.h" #include "datacache/imdlcache.h" #include "basemultiplayerplayer.h" #include "voice_gamemgr.h" #ifdef TF_DLL #include "tf_player.h" #include "tf_gamerules.h" #endif #ifdef HL2_DLL #include "weapon_physcannon.h" #endif // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" extern int giPrecacheGrunt; // For not just using one big ai net extern CBaseEntity* FindPickerEntity( CBasePlayer* pPlayer ); extern bool IsInCommentaryMode( void ); ConVar *sv_cheats = NULL; enum eAllowPointServerCommand { eAllowNever, eAllowOfficial, eAllowAlways }; #ifdef TF_DLL // The default value here should match the default of the convar eAllowPointServerCommand sAllowPointServerCommand = eAllowOfficial; #else eAllowPointServerCommand sAllowPointServerCommand = eAllowAlways; #endif // TF_DLL void sv_allow_point_servercommand_changed( IConVar *pConVar, const char *pOldString, float flOldValue ) { ConVarRef var( pConVar ); if ( !var.IsValid() ) { return; } const char *pNewValue = var.GetString(); if ( V_strcasecmp ( pNewValue, "always" ) == 0 ) { sAllowPointServerCommand = eAllowAlways; } #ifdef TF_DLL else if ( V_strcasecmp ( pNewValue, "official" ) == 0 ) { sAllowPointServerCommand = eAllowOfficial; } #endif // TF_DLL else { sAllowPointServerCommand = eAllowNever; } } ConVar sv_allow_point_servercommand ( "sv_allow_point_servercommand", #ifdef TF_DLL // The default value here should match the default of the convar "official", #else // Other games may use this in their official maps, and only TF exposes IsValveMap() currently "always", #endif // TF_DLL FCVAR_NONE, "Allow use of point_servercommand entities in map. Potentially dangerous for untrusted maps.\n" " disallow : Always disallow\n" #ifdef TF_DLL " official : Allowed for valve maps only\n" #endif // TF_DLL " always : Allow for all maps", sv_allow_point_servercommand_changed ); void ClientKill( edict_t *pEdict, const Vector &vecForce, bool bExplode = false ) { CBasePlayer *pPlayer = static_cast( GetContainingEntity( pEdict ) ); pPlayer->CommitSuicide( vecForce, bExplode ); } char * CheckChatText( CBasePlayer *pPlayer, char *text ) { char *p = text; // invalid if NULL or empty if ( !text || !text[0] ) return NULL; int length = Q_strlen( text ); // remove quotes (leading & trailing) if present if (*p == '"') { p++; length -=2; p[length] = 0; } // cut off after 127 chars if ( length > 127 ) text[127] = 0; GameRules()->CheckChatText( pPlayer, p ); return p; } //// HOST_SAY // String comes in as // say blah blah blah // or as // blah blah blah // void Host_Say( edict_t *pEdict, const CCommand &args, bool teamonly ) { CBasePlayer *client; int j; char *p; char text[256]; char szTemp[256]; const char *cpSay = "say"; const char *cpSayTeam = "say_team"; const char *pcmd = args[0]; bool bSenderDead = false; // We can get a raw string now, without the "say " prepended if ( args.ArgC() == 0 ) return; if ( !stricmp( pcmd, cpSay) || !stricmp( pcmd, cpSayTeam ) ) { if ( args.ArgC() >= 2 ) { p = (char *)args.ArgS(); } else { // say with a blank message, nothing to do return; } } else // Raw text, need to prepend argv[0] { if ( args.ArgC() >= 2 ) { Q_snprintf( szTemp,sizeof(szTemp), "%s %s", ( char * )pcmd, (char *)args.ArgS() ); } else { // Just a one word command, use the first word...sigh Q_snprintf( szTemp,sizeof(szTemp), "%s", ( char * )pcmd ); } p = szTemp; } CBasePlayer *pPlayer = NULL; if ( pEdict ) { pPlayer = ((CBasePlayer *)CBaseEntity::Instance( pEdict )); Assert( pPlayer ); // make sure the text has valid content p = CheckChatText( pPlayer, p ); } if ( !p ) return; if ( pEdict ) { if ( !pPlayer->CanSpeak() ) return; // See if the player wants to modify of check the text pPlayer->CheckChatText( p, 127 ); // though the buffer szTemp that p points to is 256, // chat text is capped to 127 in CheckChatText above Assert( strlen( pPlayer->GetPlayerName() ) > 0 ); bSenderDead = ( pPlayer->m_lifeState != LIFE_ALIVE ); } else { bSenderDead = false; } const char *pszFormat = NULL; const char *pszPrefix = NULL; const char *pszLocation = NULL; if ( g_pGameRules ) { pszFormat = g_pGameRules->GetChatFormat( teamonly, pPlayer ); pszPrefix = g_pGameRules->GetChatPrefix( teamonly, pPlayer ); pszLocation = g_pGameRules->GetChatLocation( teamonly, pPlayer ); } const char *pszPlayerName = pPlayer ? pPlayer->GetPlayerName():"Console"; if ( pszPrefix && strlen( pszPrefix ) > 0 ) { if ( pszLocation && strlen( pszLocation ) ) { Q_snprintf( text, sizeof(text), "%s %s @ %s: ", pszPrefix, pszPlayerName, pszLocation ); } else { Q_snprintf( text, sizeof(text), "%s %s: ", pszPrefix, pszPlayerName ); } } else { Q_snprintf( text, sizeof(text), "%s: ", pszPlayerName ); } j = sizeof(text) - 2 - strlen(text); // -2 for /n and null terminator if ( (int)strlen(p) > j ) p[j] = 0; Q_strncat( text, p, sizeof( text ), COPY_ALL_CHARACTERS ); Q_strncat( text, "\n", sizeof( text ), COPY_ALL_CHARACTERS ); // loop through all players // Start with the first player. // This may return the world in single player if the client types something between levels or during spawn // so check it, or it will infinite loop client = NULL; for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { client = ToBaseMultiplayerPlayer( UTIL_PlayerByIndex( i ) ); if ( !client || !client->edict() ) continue; if ( client->edict() == pEdict ) continue; if ( !(client->IsNetClient()) ) // Not a client ? (should never be true) continue; if ( teamonly && g_pGameRules->PlayerCanHearChat( client, pPlayer ) != GR_TEAMMATE ) continue; if ( pPlayer && !client->CanHearAndReadChatFrom( pPlayer ) ) continue; if ( pPlayer && GetVoiceGameMgr() && GetVoiceGameMgr()->IsPlayerIgnoringPlayer( pPlayer->entindex(), i ) ) continue; CSingleUserRecipientFilter user( client ); user.MakeReliable(); if ( pszFormat ) { UTIL_SayText2Filter( user, pPlayer, true, pszFormat, pszPlayerName, p, pszLocation ); } else { UTIL_SayTextFilter( user, text, pPlayer, true ); } } if ( pPlayer ) { // print to the sending client CSingleUserRecipientFilter user( pPlayer ); user.MakeReliable(); if ( pszFormat ) { UTIL_SayText2Filter( user, pPlayer, true, pszFormat, pszPlayerName, p, pszLocation ); } else { UTIL_SayTextFilter( user, text, pPlayer, true ); } } // echo to server console // Adrian: Only do this if we're running a dedicated server since we already print to console on the client. if ( engine->IsDedicatedServer() ) Msg( "%s", text ); Assert( p ); int userid = 0; const char *networkID = "Console"; const char *playerName = "Console"; const char *playerTeam = "Console"; if ( pPlayer ) { userid = pPlayer->GetUserID(); networkID = pPlayer->GetNetworkIDString(); playerName = pPlayer->GetPlayerName(); CTeam *team = pPlayer->GetTeam(); if ( team ) { playerTeam = team->GetName(); } } if ( teamonly ) UTIL_LogPrintf( "\"%s<%i><%s><%s>\" say_team \"%s\"\n", playerName, userid, networkID, playerTeam, p ); else UTIL_LogPrintf( "\"%s<%i><%s><%s>\" say \"%s\"\n", playerName, userid, networkID, playerTeam, p ); IGameEvent * event = gameeventmanager->CreateEvent( "player_say", true ); if ( event ) { event->SetInt("userid", userid ); event->SetString("text", p ); event->SetInt("priority", 1 ); // HLTV event priority, not transmitted gameeventmanager->FireEvent( event, true ); } } void ClientPrecache( void ) { // Precache cable textures. CBaseEntity::PrecacheModel( "cable/cable.vmt" ); CBaseEntity::PrecacheModel( "cable/cable_lit.vmt" ); CBaseEntity::PrecacheModel( "cable/chain.vmt" ); CBaseEntity::PrecacheModel( "cable/rope.vmt" ); CBaseEntity::PrecacheModel( "sprites/blueglow1.vmt" ); CBaseEntity::PrecacheModel( "sprites/purpleglow1.vmt" ); CBaseEntity::PrecacheModel( "sprites/purplelaser1.vmt" ); #ifndef HL2MP CBaseEntity::PrecacheScriptSound( "Hud.Hint" ); #endif // HL2MP CBaseEntity::PrecacheScriptSound( "Player.FallDamage" ); CBaseEntity::PrecacheScriptSound( "Player.Swim" ); // General HUD sounds CBaseEntity::PrecacheScriptSound( "Player.PickupWeapon" ); CBaseEntity::PrecacheScriptSound( "Player.DenyWeaponSelection" ); CBaseEntity::PrecacheScriptSound( "Player.WeaponSelected" ); CBaseEntity::PrecacheScriptSound( "Player.WeaponSelectionClose" ); CBaseEntity::PrecacheScriptSound( "Player.WeaponSelectionMoveSlot" ); // General legacy temp ents sounds CBaseEntity::PrecacheScriptSound( "Bounce.Glass" ); CBaseEntity::PrecacheScriptSound( "Bounce.Metal" ); CBaseEntity::PrecacheScriptSound( "Bounce.Flesh" ); CBaseEntity::PrecacheScriptSound( "Bounce.Wood" ); CBaseEntity::PrecacheScriptSound( "Bounce.Shrapnel" ); CBaseEntity::PrecacheScriptSound( "Bounce.ShotgunShell" ); CBaseEntity::PrecacheScriptSound( "Bounce.Shell" ); CBaseEntity::PrecacheScriptSound( "Bounce.Concrete" ); ClientGamePrecache(); } CON_COMMAND_F( cast_ray, "Tests collision detection", FCVAR_CHEAT ) { CBasePlayer *pPlayer = UTIL_GetCommandClient(); Vector forward; trace_t tr; pPlayer->EyeVectors( &forward ); Vector start = pPlayer->EyePosition(); UTIL_TraceLine(start, start + forward * MAX_COORD_RANGE, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.DidHit() ) { DevMsg(1, "Hit %s\nposition %.2f, %.2f, %.2f\nangles %.2f, %.2f, %.2f\n", tr.m_pEnt->GetClassname(), tr.m_pEnt->GetAbsOrigin().x, tr.m_pEnt->GetAbsOrigin().y, tr.m_pEnt->GetAbsOrigin().z, tr.m_pEnt->GetAbsAngles().x, tr.m_pEnt->GetAbsAngles().y, tr.m_pEnt->GetAbsAngles().z ); DevMsg(1, "Hit: hitbox %d, hitgroup %d, physics bone %d, solid %d, surface %s, surfaceprop %s, contents %08x\n", tr.hitbox, tr.hitgroup, tr.physicsbone, tr.m_pEnt->GetSolid(), tr.surface.name, physprops->GetPropName( tr.surface.surfaceProps ), tr.contents ); NDebugOverlay::Line( start, tr.endpos, 0, 255, 0, false, 10 ); NDebugOverlay::Line( tr.endpos, tr.endpos + tr.plane.normal * 12, 255, 255, 0, false, 10 ); } } CON_COMMAND_F( cast_hull, "Tests hull collision detection", FCVAR_CHEAT ) { CBasePlayer *pPlayer = UTIL_GetCommandClient(); Vector forward; trace_t tr; Vector extents; extents.Init(16,16,16); pPlayer->EyeVectors( &forward ); Vector start = pPlayer->EyePosition(); UTIL_TraceHull(start, start + forward * MAX_COORD_RANGE, -extents, extents, MASK_SOLID, pPlayer, COLLISION_GROUP_NONE, &tr ); if ( tr.DidHit() ) { DevMsg(1, "Hit %s\nposition %.2f, %.2f, %.2f\nangles %.2f, %.2f, %.2f\n", tr.m_pEnt->GetClassname(), tr.m_pEnt->GetAbsOrigin().x, tr.m_pEnt->GetAbsOrigin().y, tr.m_pEnt->GetAbsOrigin().z, tr.m_pEnt->GetAbsAngles().x, tr.m_pEnt->GetAbsAngles().y, tr.m_pEnt->GetAbsAngles().z ); DevMsg(1, "Hit: hitbox %d, hitgroup %d, physics bone %d, solid %d, surface %s, surfaceprop %s\n", tr.hitbox, tr.hitgroup, tr.physicsbone, tr.m_pEnt->GetSolid(), tr.surface.name, physprops->GetPropName( tr.surface.surfaceProps ) ); NDebugOverlay::SweptBox( start, tr.endpos, -extents, extents, vec3_angle, 0, 0, 255, 0, 10 ); Vector end = tr.endpos;// - tr.plane.normal * DotProductAbs( tr.plane.normal, extents ); NDebugOverlay::Line( end, end + tr.plane.normal * 24, 255, 255, 64, false, 10 ); } } //----------------------------------------------------------------------------- // Purpose: Used to find targets for ent_* commands // Without a name, returns the entity under the player's crosshair. // With a name it finds entities via name/classname/index //----------------------------------------------------------------------------- CBaseEntity *GetNextCommandEntity( CBasePlayer *pPlayer, const char *name, CBaseEntity *ent ) { if ( !pPlayer ) return NULL; // If no name was given set bits based on the picked if (FStrEq(name,"")) { // If we've already found an entity, return NULL. // Makes it easier to write code using this func. if ( ent ) return NULL; return FindPickerEntity( pPlayer ); } int index = atoi( name ); if ( index ) { // If we've already found an entity, return NULL. // Makes it easier to write code using this func. if ( ent ) return NULL; return CBaseEntity::Instance( index ); } // Loop through all entities matching, starting from the specified previous while ( (ent = gEntList.NextEnt(ent)) != NULL ) { if ( (ent->GetEntityName() != NULL_STRING && ent->NameMatches(name)) || (ent->m_iClassname != NULL_STRING && ent->ClassMatches(name)) ) { return ent; } } return NULL; } //----------------------------------------------------------------------------- // Purpose: called each time a player uses a "cmd" command // Input : pPlayer - the player who issued the command //----------------------------------------------------------------------------- void SetDebugBits( CBasePlayer* pPlayer, const char *name, int bit ) { if ( !pPlayer ) return; CBaseEntity *pEntity = NULL; while ( (pEntity = GetNextCommandEntity( pPlayer, name, pEntity )) != NULL ) { if (pEntity->m_debugOverlays & bit) { pEntity->m_debugOverlays &= ~bit; } else { pEntity->m_debugOverlays |= bit; #ifdef AI_MONITOR_FOR_OSCILLATION if( pEntity->IsNPC() ) { pEntity->MyNPCPointer()->m_ScheduleHistory.RemoveAll(); } #endif//AI_MONITOR_FOR_OSCILLATION } } } //----------------------------------------------------------------------------- // Purpose: // Input : pKillTargetName - //----------------------------------------------------------------------------- void KillTargets( const char *pKillTargetName ) { CBaseEntity *pentKillTarget = NULL; DevMsg( 2, "KillTarget: %s\n", pKillTargetName ); pentKillTarget = gEntList.FindEntityByName( NULL, pKillTargetName ); while ( pentKillTarget ) { UTIL_Remove( pentKillTarget ); DevMsg( 2, "killing %s\n", STRING( pentKillTarget->m_iClassname ) ); pentKillTarget = gEntList.FindEntityByName( pentKillTarget, pKillTargetName ); } } //------------------------------------------------------------------------------ // Purpose: //------------------------------------------------------------------------------ void ConsoleKillTarget( CBasePlayer *pPlayer, const char *name ) { // If no name was given use the picker if (FStrEq(name,"")) { CBaseEntity *pEntity = FindPickerEntity( pPlayer ); if ( pEntity ) { UTIL_Remove( pEntity ); Msg( "killing %s\n", pEntity->GetDebugName() ); return; } } // Otherwise use name or classname KillTargets( name ); } //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CPointClientCommand : public CPointEntity { public: DECLARE_CLASS( CPointClientCommand, CPointEntity ); DECLARE_DATADESC(); void InputCommand( inputdata_t& inputdata ); }; void CPointClientCommand::InputCommand( inputdata_t& inputdata ) { if ( !inputdata.value.String()[0] ) return; edict_t *pClient = NULL; if ( gpGlobals->maxClients == 1 ) { pClient = engine->PEntityOfEntIndex( 1 ); } else { // In multiplayer, send it back to the activator CBasePlayer *player = dynamic_cast< CBasePlayer * >( inputdata.pActivator ); if ( player ) { pClient = player->edict(); } if ( IsInCommentaryMode() && !pClient ) { // Commentary is stuffing a command in. We'll pretend it came from the first player. pClient = engine->PEntityOfEntIndex( 1 ); } } if ( !pClient || !pClient->GetUnknown() ) return; engine->ClientCommand( pClient, "%s\n", inputdata.value.String() ); } BEGIN_DATADESC( CPointClientCommand ) DEFINE_INPUTFUNC( FIELD_STRING, "Command", InputCommand ), END_DATADESC() LINK_ENTITY_TO_CLASS( point_clientcommand, CPointClientCommand ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- class CPointServerCommand : public CPointEntity { public: DECLARE_CLASS( CPointServerCommand, CPointEntity ); DECLARE_DATADESC(); void InputCommand( inputdata_t& inputdata ); }; //----------------------------------------------------------------------------- // Purpose: // Input : inputdata - //----------------------------------------------------------------------------- void CPointServerCommand::InputCommand( inputdata_t& inputdata ) { if ( !inputdata.value.String()[0] ) return; bool bAllowed = ( sAllowPointServerCommand == eAllowAlways ); #ifdef TF_DLL if ( sAllowPointServerCommand == eAllowOfficial ) { bAllowed = TFGameRules() && TFGameRules()->IsValveMap(); } #endif // TF_DLL if ( bAllowed ) { engine->ServerCommand( UTIL_VarArgs( "%s\n", inputdata.value.String() ) ); } else { Warning( "point_servercommand usage blocked by sv_allow_point_servercommand setting\n" ); } } BEGIN_DATADESC( CPointServerCommand ) DEFINE_INPUTFUNC( FIELD_STRING, "Command", InputCommand ), END_DATADESC() LINK_ENTITY_TO_CLASS( point_servercommand, CPointServerCommand ); //------------------------------------------------------------------------------ // Purpose : Draw a line betwen two points. White if no world collisions, red if collisions // Input : // Output : //------------------------------------------------------------------------------ void CC_DrawLine( const CCommand &args ) { Vector startPos; Vector endPos; startPos.x = atof(args[1]); startPos.y = atof(args[2]); startPos.z = atof(args[3]); endPos.x = atof(args[4]); endPos.y = atof(args[5]); endPos.z = atof(args[6]); UTIL_AddDebugLine(startPos,endPos,true,true); } static ConCommand drawline("drawline", CC_DrawLine, "Draws line between two 3D Points.\n\tGreen if no collision\n\tRed is collides with something\n\tArguments: x1 y1 z1 x2 y2 z2", FCVAR_CHEAT); //------------------------------------------------------------------------------ // Purpose : Draw a cross at a points. // Input : // Output : //------------------------------------------------------------------------------ void CC_DrawCross( const CCommand &args ) { Vector vPosition; vPosition.x = atof(args[1]); vPosition.y = atof(args[2]); vPosition.z = atof(args[3]); // Offset since min and max z in not about center Vector mins = Vector(-5,-5,-5); Vector maxs = Vector(5,5,5); Vector start = mins + vPosition; Vector end = maxs + vPosition; UTIL_AddDebugLine(start,end,true,true); start.x += (maxs.x - mins.x); end.x -= (maxs.x - mins.x); UTIL_AddDebugLine(start,end,true,true); start.y += (maxs.y - mins.y); end.y -= (maxs.y - mins.y); UTIL_AddDebugLine(start,end,true,true); start.x -= (maxs.x - mins.x); end.x += (maxs.x - mins.x); UTIL_AddDebugLine(start,end,true,true); } static ConCommand drawcross("drawcross", CC_DrawCross, "Draws a cross at the given location\n\tArguments: x y z", FCVAR_CHEAT); //------------------------------------------------------------------------------ // helper function for kill and explode //------------------------------------------------------------------------------ void kill_helper( const CCommand &args, bool bExplode ) { if ( args.ArgC() > 1 && sv_cheats->GetBool() ) { // Find the matching netname for ( int i = 1; i <= gpGlobals->maxClients; i++ ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex(i) ); if ( pPlayer ) { if ( Q_strstr( pPlayer->GetPlayerName(), args[1] ) ) { pPlayer->CommitSuicide( bExplode ); } } } } else { CBasePlayer *pPlayer = UTIL_GetCommandClient(); if ( pPlayer ) { pPlayer->CommitSuicide( bExplode ); } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( kill, "Kills the player with generic damage" ) { kill_helper( args, false ); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( explode, "Kills the player with explosive damage" ) { kill_helper( args, true ); } //------------------------------------------------------------------------------ // helper function for killvector and explodevector //------------------------------------------------------------------------------ void killvector_helper( const CCommand &args, bool bExplode ) { CBasePlayer *pPlayer = UTIL_GetCommandClient(); if ( pPlayer && args.ArgC() == 5 ) { // Find the matching netname. for ( int iClient = 1; iClient <= gpGlobals->maxClients; iClient++ ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_PlayerByIndex( iClient ) ); if ( pPlayer ) { if ( Q_strstr( pPlayer->GetPlayerName(), args[1] ) ) { // Build world-space force vector. Vector vecForce; vecForce.x = atof( args[2] ); vecForce.y = atof( args[3] ); vecForce.z = atof( args[4] ); ClientKill( pPlayer->edict(), vecForce, bExplode ); } } } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND_F( killvector, "Kills a player applying force. Usage: killvector ", FCVAR_CHEAT ) { killvector_helper( args, false ); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND_F( explodevector, "Kills a player applying an explosive force. Usage: explodevector ", FCVAR_CHEAT ) { killvector_helper( args, false ); } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND_F( buddha, "Toggle. Player takes damage but won't die. (Shows red cross when health is zero)", FCVAR_CHEAT ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer ) { if (pPlayer->m_debugOverlays & OVERLAY_BUDDHA_MODE) { pPlayer->m_debugOverlays &= ~OVERLAY_BUDDHA_MODE; Msg("Buddha Mode off...\n"); } else { pPlayer->m_debugOverlays |= OVERLAY_BUDDHA_MODE; Msg("Buddha Mode on...\n"); } } } #define TALK_INTERVAL 0.66 // min time between say commands from a client //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( say, "Display player message" ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer ) { if (( pPlayer->LastTimePlayerTalked() + TALK_INTERVAL ) < gpGlobals->curtime) { Host_Say( pPlayer->edict(), args, 0 ); pPlayer->NotePlayerTalked(); } } // This will result in a "console" say. Ignore anything from // an index greater than 0 when we don't have a player pointer, // as would be the case when a client that's connecting generates // text via a script. This can be exploited to flood everyone off. else if ( UTIL_GetCommandClientIndex() == 0 ) { Host_Say( NULL, args, 0 ); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( say_team, "Display player message to team" ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if (pPlayer) { if (( pPlayer->LastTimePlayerTalked() + TALK_INTERVAL ) < gpGlobals->curtime) { Host_Say( pPlayer->edict(), args, 1 ); pPlayer->NotePlayerTalked(); } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( give, "Give item to player.\n\tArguments: " ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer && (gpGlobals->maxClients == 1 || sv_cheats->GetBool()) && args.ArgC() >= 2 ) { char item_to_give[ 256 ]; Q_strncpy( item_to_give, args[1], sizeof( item_to_give ) ); Q_strlower( item_to_give ); // Don't allow regular users to create point_servercommand entities for the same reason as blocking ent_fire if ( !Q_stricmp( item_to_give, "point_servercommand" ) ) { if ( engine->IsDedicatedServer() ) { // We allow people with disabled autokick to do it, because they already have rcon. if ( pPlayer->IsAutoKickDisabled() == false ) return; } else if ( gpGlobals->maxClients > 1 ) { // On listen servers with more than 1 player, only allow the host to create point_servercommand. CBasePlayer *pHostPlayer = UTIL_GetListenServerHost(); if ( pPlayer != pHostPlayer ) return; } } // Dirty hack to avoid suit playing it's pickup sound if ( !Q_stricmp( item_to_give, "item_suit" ) ) { pPlayer->EquipSuit( false ); return; } string_t iszItem = AllocPooledString( item_to_give ); // Make a copy of the classname pPlayer->GiveNamedItem( STRING(iszItem) ); } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ CON_COMMAND( fov, "Change players FOV" ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer && sv_cheats->GetBool() ) { if ( args.ArgC() > 1 ) { int nFOV = atoi( args[1] ); pPlayer->SetDefaultFOV( nFOV ); } else { ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs( "\"fov\" is \"%d\"\n", pPlayer->GetFOV() ) ); } } } //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void CC_Player_SetModel( const CCommand &args ) { if ( gpGlobals->deathmatch ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer && args.ArgC() == 2) { static char szName[256]; Q_snprintf( szName, sizeof( szName ), "models/%s.mdl", args[1] ); pPlayer->SetModel( szName ); UTIL_SetSize(pPlayer, VEC_HULL_MIN, VEC_HULL_MAX); } } static ConCommand setmodel("setmodel", CC_Player_SetModel, "Changes's player's model", FCVAR_CHEAT ); //----------------------------------------------------------------------------- // Purpose: //----------------------------------------------------------------------------- void CC_Player_TestDispatchEffect( const CCommand &args ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer) return; if ( args.ArgC() < 2 ) { Msg(" Usage: test_dispatcheffect \n " ); Msg(" defaults are: \n" ); return; } // Optional distance float flDistance = 1024; if ( args.ArgC() >= 3 ) { flDistance = atoi( args[ 2 ] ); } // Optional flags float flags = 0; if ( args.ArgC() >= 4 ) { flags = atoi( args[ 3 ] ); } // Optional magnitude float magnitude = 0; if ( args.ArgC() >= 5 ) { magnitude = atof( args[ 4 ] ); } // Optional scale float scale = 0; if ( args.ArgC() >= 6 ) { scale = atof( args[ 5 ] ); } Vector vecForward; QAngle vecAngles = pPlayer->EyeAngles(); AngleVectors( vecAngles, &vecForward ); // Trace forward trace_t tr; Vector vecSrc = pPlayer->EyePosition(); Vector vecEnd = vecSrc + (vecForward * flDistance); UTIL_TraceLine( vecSrc, vecEnd, MASK_ALL, pPlayer, COLLISION_GROUP_NONE, &tr ); // Fill out the generic data CEffectData data; // If we hit something, use that data if ( tr.fraction < 1.0 ) { data.m_vOrigin = tr.endpos; VectorAngles( tr.plane.normal, data.m_vAngles ); data.m_vNormal = tr.plane.normal; } else { data.m_vOrigin = vecEnd; data.m_vAngles = vecAngles; AngleVectors( vecAngles, &data.m_vNormal ); } data.m_nEntIndex = pPlayer->entindex(); data.m_fFlags = flags; data.m_flMagnitude = magnitude; data.m_flScale = scale; DispatchEffect( (char *)args[1], data ); } static ConCommand test_dispatcheffect("test_dispatcheffect", CC_Player_TestDispatchEffect, "Test a clientside dispatch effect.\n\tUsage: test_dispatcheffect \n\tDefaults are: \n", FCVAR_CHEAT); #ifdef HL2_DLL //----------------------------------------------------------------------------- // Purpose: Quickly switch to the physics cannon, or back to previous item //----------------------------------------------------------------------------- void CC_Player_PhysSwap( void ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer ) { CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); if ( pWeapon ) { // Tell the client to stop selecting weapons engine->ClientCommand( UTIL_GetCommandClient()->edict(), "cancelselect" ); const char *strWeaponName = pWeapon->GetName(); if ( !Q_stricmp( strWeaponName, "weapon_physcannon" ) ) { PhysCannonForceDrop( pWeapon, NULL ); pPlayer->SelectLastItem(); } else { pPlayer->SelectItem( "weapon_physcannon" ); } } } } static ConCommand physswap("phys_swap", CC_Player_PhysSwap, "Automatically swaps the current weapon for the physcannon and back again." ); #endif //----------------------------------------------------------------------------- // Purpose: Quickly switch to the bug bait, or back to previous item //----------------------------------------------------------------------------- void CC_Player_BugBaitSwap( void ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer ) { CBaseCombatWeapon *pWeapon = pPlayer->GetActiveWeapon(); if ( pWeapon ) { // Tell the client to stop selecting weapons engine->ClientCommand( UTIL_GetCommandClient()->edict(), "cancelselect" ); const char *strWeaponName = pWeapon->GetName(); if ( !Q_stricmp( strWeaponName, "weapon_bugbait" ) ) { pPlayer->SelectLastItem(); } else { pPlayer->SelectItem( "weapon_bugbait" ); } } } } static ConCommand bugswap("bug_swap", CC_Player_BugBaitSwap, "Automatically swaps the current weapon for the bug bait and back again.", FCVAR_CHEAT ); //------------------------------------------------------------------------------ //------------------------------------------------------------------------------ void CC_Player_Use( const CCommand &args ) { CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( pPlayer) { pPlayer->SelectItem((char *)args[1]); } } static ConCommand use("use", CC_Player_Use, "Use a particular weapon\t\nArguments: "); //------------------------------------------------------------------------------ // A small wrapper around SV_Move that never clips against the supplied entity. //------------------------------------------------------------------------------ static bool TestEntityPosition ( CBasePlayer *pPlayer ) { trace_t trace; UTIL_TraceEntity( pPlayer, pPlayer->GetAbsOrigin(), pPlayer->GetAbsOrigin(), MASK_PLAYERSOLID, &trace ); return (trace.startsolid == 0); } //------------------------------------------------------------------------------ // Searches along the direction ray in steps of "step" to see if // the entity position is passible. // Used for putting the player in valid space when toggling off noclip mode. //------------------------------------------------------------------------------ static int FindPassableSpace( CBasePlayer *pPlayer, const Vector& direction, float step, Vector& oldorigin ) { int i; for ( i = 0; i < 100; i++ ) { Vector origin = pPlayer->GetAbsOrigin(); VectorMA( origin, step, direction, origin ); pPlayer->SetAbsOrigin( origin ); if ( TestEntityPosition( pPlayer ) ) { VectorCopy( pPlayer->GetAbsOrigin(), oldorigin ); return 1; } } return 0; } //------------------------------------------------------------------------------ // Noclip //------------------------------------------------------------------------------ void EnableNoClip( CBasePlayer *pPlayer ) { // Disengage from hierarchy pPlayer->SetParent( NULL ); pPlayer->SetMoveType( MOVETYPE_NOCLIP ); ClientPrint( pPlayer, HUD_PRINTCONSOLE, "noclip ON\n"); pPlayer->AddEFlags( EFL_NOCLIP_ACTIVE ); } void CC_Player_NoClip( void ) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; CPlayerState *pl = pPlayer->PlayerData(); Assert( pl ); if (pPlayer->GetMoveType() != MOVETYPE_NOCLIP) { EnableNoClip( pPlayer ); return; } pPlayer->RemoveEFlags( EFL_NOCLIP_ACTIVE ); pPlayer->SetMoveType( MOVETYPE_WALK ); Vector oldorigin = pPlayer->GetAbsOrigin(); ClientPrint( pPlayer, HUD_PRINTCONSOLE, "noclip OFF\n"); if ( !TestEntityPosition( pPlayer ) ) { Vector forward, right, up; AngleVectors ( pl->v_angle, &forward, &right, &up); // Try to move into the world if ( !FindPassableSpace( pPlayer, forward, 1, oldorigin ) ) { if ( !FindPassableSpace( pPlayer, right, 1, oldorigin ) ) { if ( !FindPassableSpace( pPlayer, right, -1, oldorigin ) ) // left { if ( !FindPassableSpace( pPlayer, up, 1, oldorigin ) ) // up { if ( !FindPassableSpace( pPlayer, up, -1, oldorigin ) ) // down { if ( !FindPassableSpace( pPlayer, forward, -1, oldorigin ) ) // back { Msg( "Can't find the world\n" ); } } } } } } pPlayer->SetAbsOrigin( oldorigin ); } } static ConCommand noclip("noclip", CC_Player_NoClip, "Toggle. Player becomes non-solid and flies.", FCVAR_CHEAT); //------------------------------------------------------------------------------ // Sets client to godmode //------------------------------------------------------------------------------ void CC_God_f (void) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; #ifdef TF_DLL if ( TFGameRules() && ( TFGameRules()->IsPVEModeActive() == false ) ) { if ( gpGlobals->deathmatch ) return; } #else if ( gpGlobals->deathmatch ) return; #endif pPlayer->ToggleFlag( FL_GODMODE ); if (!(pPlayer->GetFlags() & FL_GODMODE ) ) ClientPrint( pPlayer, HUD_PRINTCONSOLE, "godmode OFF\n"); else ClientPrint( pPlayer, HUD_PRINTCONSOLE, "godmode ON\n"); } static ConCommand god("god", CC_God_f, "Toggle. Player becomes invulnerable.", FCVAR_CHEAT ); //------------------------------------------------------------------------------ // Sets client to godmode //------------------------------------------------------------------------------ CON_COMMAND_F( setpos, "Move player to specified origin (must have sv_cheats).", FCVAR_CHEAT ) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; if ( args.ArgC() < 3 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage: setpos x y \n"); return; } Vector oldorigin = pPlayer->GetAbsOrigin(); Vector newpos; newpos.x = atof( args[1] ); newpos.y = atof( args[2] ); newpos.z = args.ArgC() == 4 ? atof( args[3] ) : oldorigin.z; pPlayer->SetAbsOrigin( newpos ); if ( !TestEntityPosition( pPlayer ) ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "setpos into world, use noclip to unstick yourself!\n"); } } //------------------------------------------------------------------------------ // Sets client to godmode //------------------------------------------------------------------------------ void CC_setang_f (const CCommand &args) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; if ( args.ArgC() < 3 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage: setang pitch yaw \n"); return; } QAngle oldang = pPlayer->GetAbsAngles(); QAngle newang; newang.x = atof( args[1] ); newang.y = atof( args[2] ); newang.z = args.ArgC() == 4 ? atof( args[3] ) : oldang.z; pPlayer->SnapEyeAngles( newang ); } static ConCommand setang("setang", CC_setang_f, "Snap player eyes to specified pitch yaw (must have sv_cheats).", FCVAR_CHEAT ); static float GetHexFloat( const char *pStr ) { if ( ( pStr[0] == '0' ) && ( pStr[1] == 'x' ) ) { uint32 f = (uint32)V_atoi64( pStr ); return *reinterpret_cast< const float * >( &f ); } return atof( pStr ); } //------------------------------------------------------------------------------ // Move position //------------------------------------------------------------------------------ CON_COMMAND_F( setpos_exact, "Move player to an exact specified origin (must have sv_cheats).", FCVAR_CHEAT ) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; if ( args.ArgC() < 3 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage: setpos_exact x y \n"); return; } Vector oldorigin = pPlayer->GetAbsOrigin(); Vector newpos; newpos.x = GetHexFloat( args[1] ); newpos.y = GetHexFloat( args[2] ); newpos.z = args.ArgC() == 4 ? GetHexFloat( args[3] ) : oldorigin.z; pPlayer->Teleport( &newpos, NULL, NULL ); if ( !TestEntityPosition( pPlayer ) ) { if ( pPlayer->GetMoveType() != MOVETYPE_NOCLIP ) { EnableNoClip( pPlayer ); return; } } } CON_COMMAND_F( setang_exact, "Snap player eyes and orientation to specified pitch yaw (must have sv_cheats).", FCVAR_CHEAT ) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; if ( args.ArgC() < 3 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Usage: setang_exact pitch yaw \n"); return; } QAngle oldang = pPlayer->GetAbsAngles(); QAngle newang; newang.x = GetHexFloat( args[1] ); newang.y = GetHexFloat( args[2] ); newang.z = args.ArgC() == 4 ? GetHexFloat( args[3] ) : oldang.z; pPlayer->Teleport( NULL, &newang, NULL ); pPlayer->SnapEyeAngles( newang ); #ifdef TF_DLL static_cast( pPlayer )->DoAnimationEvent( PLAYERANIMEVENT_SNAP_YAW ); #endif } //------------------------------------------------------------------------------ // Sets client to notarget mode. //------------------------------------------------------------------------------ void CC_Notarget_f (void) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; if ( gpGlobals->deathmatch ) return; pPlayer->ToggleFlag( FL_NOTARGET ); if ( !(pPlayer->GetFlags() & FL_NOTARGET ) ) ClientPrint( pPlayer, HUD_PRINTCONSOLE, "notarget OFF\n"); else ClientPrint( pPlayer, HUD_PRINTCONSOLE, "notarget ON\n"); } ConCommand notarget("notarget", CC_Notarget_f, "Toggle. Player becomes hidden to NPCs.", FCVAR_CHEAT); //------------------------------------------------------------------------------ // Damage the client the specified amount //------------------------------------------------------------------------------ void CC_HurtMe_f(const CCommand &args) { if ( !sv_cheats->GetBool() ) return; CBasePlayer *pPlayer = ToBasePlayer( UTIL_GetCommandClient() ); if ( !pPlayer ) return; int iDamage = 10; if ( args.ArgC() >= 2 ) { iDamage = atoi( args[ 1 ] ); } pPlayer->TakeDamage( CTakeDamageInfo( pPlayer, pPlayer, iDamage, DMG_PREVENT_PHYSICS_FORCE ) ); } static ConCommand hurtme("hurtme", CC_HurtMe_f, "Hurts the player.\n\tArguments: ", FCVAR_CHEAT); #ifdef DBGFLAG_ASSERT static bool IsInGroundList( CBaseEntity *ent, CBaseEntity *ground ) { if ( !ground || !ent ) return false; groundlink_t *root = ( groundlink_t * )ground->GetDataObject( GROUNDLINK ); if ( root ) { groundlink_t *link = root->nextLink; while ( link != root ) { CBaseEntity *other = link->entity; if ( other == ent ) return true; link = link->nextLink; } } return false; } #endif static int DescribeGroundList( CBaseEntity *ent ) { if ( !ent ) return 0; int c = 1; Msg( "%i : %s (ground %i %s)\n", ent->entindex(), ent->GetClassname(), ent->GetGroundEntity() ? ent->GetGroundEntity()->entindex() : -1, ent->GetGroundEntity() ? ent->GetGroundEntity()->GetClassname() : "NULL" ); groundlink_t *root = ( groundlink_t * )ent->GetDataObject( GROUNDLINK ); if ( root ) { groundlink_t *link = root->nextLink; while ( link != root ) { CBaseEntity *other = link->entity; if ( other ) { Msg( " %02i: %i %s\n", c++, other->entindex(), other->GetClassname() ); if ( other->GetGroundEntity() != ent ) { Assert( 0 ); Msg( " mismatched!!!\n" ); } } else { Assert( 0 ); Msg( " %02i: NULL link\n", c++ ); } link = link->nextLink; } } if ( ent->GetGroundEntity() != NULL ) { Assert( IsInGroundList( ent, ent->GetGroundEntity() ) ); } return c - 1; } void CC_GroundList_f(const CCommand &args) { if ( !UTIL_IsCommandIssuedByServerAdmin() ) return; if ( args.ArgC() == 2 ) { int idx = atoi( args[1] ); CBaseEntity *ground = CBaseEntity::Instance( idx ); if ( ground ) { DescribeGroundList( ground ); } } else { CBaseEntity *ent = NULL; int linkCount = 0; while ( (ent = gEntList.NextEnt(ent)) != NULL ) { linkCount += DescribeGroundList( ent ); } extern int groundlinksallocated; Assert( linkCount == groundlinksallocated ); Msg( "--- %i links\n", groundlinksallocated ); } } static ConCommand groundlist("groundlist", CC_GroundList_f, "Display ground entity list " ); //----------------------------------------------------------------------------- // Purpose: called each time a player uses a "cmd" command // Input : *pEdict - the player who issued the command //----------------------------------------------------------------------------- void ClientCommand( CBasePlayer *pPlayer, const CCommand &args ) { const char *pCmd = args[0]; // Is the client spawned yet? if ( !pPlayer ) return; MDLCACHE_CRITICAL_SECTION(); /* const char *pstr; if (((pstr = strstr(pcmd, "weapon_")) != NULL) && (pstr == pcmd)) { // Subtype may be specified if ( args.ArgC() == 2 ) { pPlayer->SelectItem( pcmd, atoi( args[1] ) ); } else { pPlayer->SelectItem(pcmd); } } */ if ( FStrEq( pCmd, "killtarget" ) ) { if ( g_pDeveloper->GetBool() && sv_cheats->GetBool() && UTIL_IsCommandIssuedByServerAdmin() ) { ConsoleKillTarget( pPlayer, args[1] ); } } else if ( FStrEq( pCmd, "demorestart" ) ) { pPlayer->ForceClientDllUpdate(); } else if ( FStrEq( pCmd, "fade" ) ) { color32 black = {32,63,100,200}; UTIL_ScreenFade( pPlayer, black, 3, 3, FFADE_OUT ); } else if ( FStrEq( pCmd, "te" ) ) { if ( sv_cheats->GetBool() && UTIL_IsCommandIssuedByServerAdmin() ) { if ( FStrEq( args[1], "stop" ) ) { // Destroy it // CBaseEntity *ent = gEntList.FindEntityByClassname( NULL, "te_tester" ); while ( ent ) { CBaseEntity *next = gEntList.FindEntityByClassname( ent, "te_tester" ); UTIL_Remove( ent ); ent = next; } } else { CTempEntTester::Create( pPlayer->WorldSpaceCenter(), pPlayer->EyeAngles(), args[1], args[2] ); } } } else { if ( !g_pGameRules->ClientCommand( pPlayer, args ) ) { if ( Q_strlen( pCmd ) > 128 ) { ClientPrint( pPlayer, HUD_PRINTCONSOLE, "Console command too long.\n" ); } else { // tell the user they entered an unknown command ClientPrint( pPlayer, HUD_PRINTCONSOLE, UTIL_VarArgs( "Unknown command: %s\n", pCmd ) ); } } } }