diff --git a/src/game/client/c_baseanimating.cpp b/src/game/client/c_baseanimating.cpp index e4297c0e0f..0c7cb8a9c1 100644 --- a/src/game/client/c_baseanimating.cpp +++ b/src/game/client/c_baseanimating.cpp @@ -3619,16 +3619,19 @@ int C_BaseAnimating::InternalDrawModel( int flags ) #ifdef NEO if (IsViewModel()) { // view models become dark when standing close to and facing a wall, change lighting origin - auto pOwner = UTIL_PlayerByIndex(GetLocalPlayerIndex()); - if (pOwner) + if (!engine->IsHLTV()) { - static Vector ownerOrigin; - ownerOrigin = pOwner->EyePosition(); - pInfo->pLightingOrigin = &ownerOrigin; + auto pOwner = UTIL_PlayerByIndex(GetLocalPlayerIndex()); + if (pOwner) + { + static Vector ownerOrigin; + ownerOrigin = pOwner->EyePosition(); + pInfo->pLightingOrigin = &ownerOrigin; + } } } - else if (IsBaseCombatWeapon()) - { + else if (IsBaseCombatWeapon() && !GetMoveParent()) + { // dropped weapons can become dark when they rotate such that their origin falls through the floor static Vector worldSpaceCenter; worldSpaceCenter = WorldSpaceCenter(); pInfo->pLightingOrigin = &worldSpaceCenter; diff --git a/src/game/client/game_controls/SpectatorGUI.cpp b/src/game/client/game_controls/SpectatorGUI.cpp index 29414422f4..cb9c7af4b3 100644 --- a/src/game/client/game_controls/SpectatorGUI.cpp +++ b/src/game/client/game_controls/SpectatorGUI.cpp @@ -50,6 +50,8 @@ void AddSubKeyNamed( KeyValues *pKeys, const char *pszName ); #include "c_team.h" #include "neo_gamerules.h" #include "c_neo_player.h" +#include "view.h" +#include "hltvcamera.h" #endif // memdbgon must be the last include file in a .cpp file!!! @@ -986,48 +988,77 @@ CON_COMMAND_F( spec_player, "Spectate player by partial name, steamid, or userid } } -#ifdef NEO +#ifdef NEO CON_COMMAND_F( spec_player_under_mouse, "Spectate player by partial name, steamid, or userid", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + C_NEO_Player *pNeoPlayer = C_NEO_Player::GetLocalNEOPlayer(); if ( !pNeoPlayer || !pNeoPlayer->IsObserver() ) return; - if (!engine->IsHLTV() || !HLTVCamera()->IsPVSLocked()) + C_BaseEntity* currentTarget = pNeoPlayer->GetObserverTarget(); + C_NEO_Player *target = nullptr; + float targetDotProduct = -1; + for (int i = 1; i <= gpGlobals->maxClients; i++) { - C_BaseEntity* currentTarget = pNeoPlayer->GetObserverTarget(); - C_NEO_Player *target = nullptr; - float targetDotProduct = -1; - for (int i = 1; i < gpGlobals->maxClients; i++) + C_NEO_Player* pPlayer = ToNEOPlayer(UTIL_PlayerByIndex(i)); + if (currentTarget != pPlayer && pNeoPlayer->IsValidObserverTarget(pPlayer) && pPlayer->IsAlive()) { - C_NEO_Player* pPlayer = ToNEOPlayer(UTIL_PlayerByIndex(i)); - if (currentTarget != pPlayer && pNeoPlayer->IsValidObserverTarget(pPlayer) && pPlayer->IsAlive()) + Vector vecToTarget = pPlayer->WorldSpaceCenter() - MainViewOrigin(); + vecToTarget.NormalizeInPlace(); + float dotProduct = DotProduct(MainViewForward(), vecToTarget); + if (dotProduct > targetDotProduct && dotProduct > 0.5) { - Vector vecForward; - AngleVectors( pNeoPlayer->EyeAngles(), &vecForward ); - - Vector vecToTarget = pPlayer->WorldSpaceCenter() - pNeoPlayer->EyePosition(); - vecToTarget.NormalizeInPlace(); - float dotProduct = DotProduct(vecForward, vecToTarget); - if (dotProduct > targetDotProduct && dotProduct > 0.5) - { - targetDotProduct = dotProduct; - target = pPlayer; - } + targetDotProduct = dotProduct; + target = pPlayer; } } + } + + if (target) + { + engine->IsHLTV() ? HLTVCamera()->SetPrimaryTarget(target->entindex()) : engine->ClientCmd(VarArgs("spec_player_entity_number %d", target->entindex())); + } +} - if (target) +CON_COMMAND_F( spec_fastest_player, "Spectate the fastest player", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + C_NEO_Player *pNeoPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if ( !pNeoPlayer || !pNeoPlayer->IsObserver() ) + return; + + if (engine->IsHLTV()) + { + if (HLTVCamera()->IsPVSLocked()) { - if (engine->IsHLTV()) - { - HLTVCamera()->SetPrimaryTarget(target->entindex()); - } - else + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + + // We have up to date information on all the players, just do it here + float fastestSpeedSquared = 0; + CBasePlayer* pFastestEntity = nullptr; + for (int i = 1; i <= gpGlobals->maxClients; i++) + { + CBasePlayer* pPlayer = UTIL_PlayerByIndex(i); + if (pPlayer && !pPlayer->IsObserver() && pPlayer->GetAbsVelocity().LengthSqr() > fastestSpeedSquared) { - engine->ClientCmd( VarArgs("spec_player_entity_number %d", target->entindex()) ); + fastestSpeedSquared = pPlayer->GetAbsVelocity().LengthSqr(); + pFastestEntity = pPlayer; } } + + if (pFastestEntity) + HLTVCamera()->SetPrimaryTarget(pFastestEntity->entindex()); + } + else + { + engine->ClientCmd(VarArgs("spectate_fastest_player")); } } #endif // NEO diff --git a/src/game/client/hltvcamera.cpp b/src/game/client/hltvcamera.cpp index 7fab97c4e9..4bec9e5038 100644 --- a/src/game/client/hltvcamera.cpp +++ b/src/game/client/hltvcamera.cpp @@ -22,6 +22,12 @@ #include "c_cs_player.h" #endif +#ifdef NEO +#include "neo_gamerules.h" +#include "c_neo_player.h" +#include "shareddefs.h" +#endif // NEO + ConVar spec_autodirector( "spec_autodirector", "1", FCVAR_CLIENTDLL | FCVAR_CLIENTCMD_CAN_EXECUTE, "Auto-director chooses best view modes while spectating" ); // memdbgon must be the last include file in a .cpp file!!! @@ -74,6 +80,9 @@ void C_HLTVCamera::Init() ListenForGameEvent( "hltv_message" ); ListenForGameEvent( "hltv_title" ); ListenForGameEvent( "hltv_status" ); +#ifdef NEO + ListenForGameEvent( "player_death" ); +#endif // NEO Reset(); @@ -306,7 +315,11 @@ C_BaseEntity *C_HLTVCamera::GetCameraMan() void C_HLTVCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float& fov ) { +#ifdef NEO + C_NEO_Player *pPlayer = static_cast(UTIL_PlayerByIndex( m_iTraget1 )); +#else C_BasePlayer *pPlayer = UTIL_PlayerByIndex( m_iTraget1 ); +#endif // NEO if ( !pPlayer ) return; @@ -324,11 +337,19 @@ void C_HLTVCamera::CalcInEyeCamView( Vector& eyeOrigin, QAngle& eyeAngles, float if ( pPlayer->GetFlags() & FL_DUCKING ) { +#ifdef NEO + m_vCamOrigin += VEC_DUCK_VIEW_NEOSCALE(pPlayer); +#else m_vCamOrigin += VEC_DUCK_VIEW; +#endif // NEO } else { +#ifdef NEO + m_vCamOrigin += VEC_VIEW_NEOSCALE(pPlayer); +#else m_vCamOrigin += VEC_VIEW; +#endif // NEO } eyeOrigin = m_vCamOrigin; @@ -375,6 +396,9 @@ void C_HLTVCamera::Accelerate( Vector& wishdir, float wishspeed, float accel ) } +#ifdef NEO +extern ConVar neo_fov; +#endif // NEO // movement code is a copy of CGameMovement::FullNoClipMove() void C_HLTVCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov) { @@ -399,7 +423,22 @@ void C_HLTVCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& // Copy movement amounts float fmove = m_LastCmd.forwardmove * factor; float smove = m_LastCmd.sidemove * factor; - + +#ifdef NEO + const bool bDroneMove = m_LastCmd.buttons & IN_WALK; + if (bDroneMove) + { + forward.z = 0; + if (fmove && smove) + { + const float absFMove = fabs(fmove); + const float absSMove = fabs(smove); + const float moveMagnitude = FastSqrt((absFMove * absFMove) + (absSMove * absSMove)); + fmove *= absFMove / moveMagnitude; + smove *= absSMove / moveMagnitude; + } + } +#endif // NEO VectorNormalize (forward); // Normalize remainder of vectors VectorNormalize (right); // @@ -470,7 +509,11 @@ void C_HLTVCamera::CalcRoamingView(Vector& eyeOrigin, QAngle& eyeAngles, float& eyeOrigin = m_vCamOrigin; eyeAngles = m_aCamAngle; +#ifdef NEO + fov = neo_fov.GetFloat(); +#else fov = m_flFOV; +#endif // NEO } void C_HLTVCamera::CalcFixedView(Vector& eyeOrigin, QAngle& eyeAngles, float& fov) @@ -551,6 +594,10 @@ void C_HLTVCamera::CalcView(Vector& origin, QAngle& angles, float& fov) } } +#ifdef NEO +ConVar cl_neo_hltvcamera_spectate_next_target_on_set_mode("cl_neo_hltvcamera_spectate_next_target_on_set_mode", "1", FCVAR_ARCHIVE, "Spectate next target when changing to in-eye or chase and don't have a valid target", true, 0, true, 1); +static bool bAllowChangingModeWhenSettingPrimaryTarget = true; +#endif // NEO void C_HLTVCamera::SetMode(int iMode) { if ( m_nCameraMode == iMode ) @@ -561,6 +608,14 @@ void C_HLTVCamera::SetMode(int iMode) int iOldMode = m_nCameraMode; m_nCameraMode = iMode; +#ifdef NEO + if (cl_neo_hltvcamera_spectate_next_target_on_set_mode.GetBool() && (iMode == OBS_MODE_IN_EYE || iMode == OBS_MODE_CHASE) && !GetPrimaryTarget()) + { + bAllowChangingModeWhenSettingPrimaryTarget = false; + SpecNextPlayer(false); + bAllowChangingModeWhenSettingPrimaryTarget = true; + } +#endif // NEO IGameEvent *event = gameeventmanager->CreateEvent( "hltv_changed_mode" ); if ( event ) { @@ -571,6 +626,17 @@ void C_HLTVCamera::SetMode(int iMode) } } +#ifdef NEO +enum +{ + NEO_HLTV_ON_TARGET_MODE_NOTHING = 0, + NEO_HLTV_ON_TARGET_MODE_IN_EYE, + NEO_HLTV_ON_TARGET_MODE_CHASE, + + NEO_HLTV_ON_TARGET_MODE__TOTAL = NEO_HLTV_ON_TARGET_MODE_CHASE +}; +ConVar cl_neo_hltvcamera_default_mode_when_setting_primary_target("cl_neo_hltvcamera_default_mode_when_setting_primary_target", "1", FCVAR_ARCHIVE, "What to do if changing primary targets and not in in-eye or chase. 0 = do nothing, 1 = switch to in-eye, 2 = switch to chase", true, 0, true, NEO_HLTV_ON_TARGET_MODE__TOTAL); +#endif // NEO void C_HLTVCamera::SetPrimaryTarget( int nEntity ) { if ( m_iTraget1 == nEntity ) @@ -579,6 +645,24 @@ void C_HLTVCamera::SetPrimaryTarget( int nEntity ) int iOldTarget = m_iTraget1; m_iTraget1 = nEntity; +#ifdef NEO + if (GetMode() != OBS_MODE_IN_EYE && GetMode() != OBS_MODE_CHASE && bAllowChangingModeWhenSettingPrimaryTarget) + { + switch (cl_neo_hltvcamera_default_mode_when_setting_primary_target.GetInt()) + { + case NEO_HLTV_ON_TARGET_MODE_IN_EYE: + SetMode(OBS_MODE_IN_EYE); + break; + case NEO_HLTV_ON_TARGET_MODE_CHASE: + SetMode(OBS_MODE_CHASE); + break; + case NEO_HLTV_ON_TARGET_MODE_NOTHING: + default: + break; + } + } + +#endif // NEO if ( GetMode() == OBS_MODE_ROAMING ) { Vector vOrigin; @@ -609,6 +693,37 @@ void C_HLTVCamera::SetPrimaryTarget( int nEntity ) gameeventmanager->FireEventClientSide( event ); } } +#ifdef NEO +void C_HLTVCamera::SpectateEvent(NeoSpectateEvent event) +{ + int entIndexLastPlayerMatchingEvent = -1; + switch (event) + { + case NEO_SPECTATE_EVENT_LAST_HURT: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastHurt(); + break; + case NEO_SPECTATE_EVENT_LAST_SHOOTER: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastShooter(); + break; + case NEO_SPECTATE_EVENT_LAST_ATTACKER: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastAttacker(); + break; + case NEO_SPECTATE_EVENT_LAST_KILLER: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastKiller(); + break; + case NEO_SPECTATE_EVENT_LAST_GHOSTER: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastGhoster(); + break; + case NEO_SPECTATE_EVENT_LAST_EVENT: + default: + entIndexLastPlayerMatchingEvent = NEORules()->GetLastEvent(); + break; + } + + if (entIndexLastPlayerMatchingEvent > 0) + SetPrimaryTarget(entIndexLastPlayerMatchingEvent); +} +#endif // NEO void C_HLTVCamera::SpecNextPlayer( bool bInverse ) { @@ -668,6 +783,9 @@ void C_HLTVCamera::SpecPlayerByPredicate( const char *szSearch ) return; } +#ifdef NEO +ConVar cl_neo_hltvcamera_auto_observe_killer_if_observing_victim("cl_neo_hltvcamera_auto_observe_killer_if_observing_victim", "1", FCVAR_ARCHIVE, "If the current observer target is killed when in eye or following, switch observer target to the killer", true, 0, true, 1); +#endif // NEO void C_HLTVCamera::FireGameEvent( IGameEvent * event) { if ( !engine->IsHLTV() ) @@ -683,6 +801,7 @@ void C_HLTVCamera::FireGameEvent( IGameEvent * event) if ( !gViewPortInterface ) return; +#ifndef NEO if ( engine->IsPlayingDemo() ) { // for demo playback show full menu @@ -691,6 +810,7 @@ void C_HLTVCamera::FireGameEvent( IGameEvent * event) SetMode( OBS_MODE_ROAMING ); } else +#endif // NEO { // during live broadcast only show black bars gViewPortInterface->ShowPanel( PANEL_SPECGUI, true ); @@ -735,6 +855,22 @@ void C_HLTVCamera::FireGameEvent( IGameEvent * event) return; } +#ifdef NEO + if (Q_strcmp("player_death", type) == 0) + { + if (!cl_neo_hltvcamera_auto_observe_killer_if_observing_victim.GetBool() || (m_nCameraMode != OBS_MODE_IN_EYE && m_nCameraMode != OBS_MODE_CHASE)) + return; + + const int victimIndex = engine->GetPlayerForUserID(event->GetInt("userid")); + const int killerIndex = engine->GetPlayerForUserID(event->GetInt("attacker")); + if (victimIndex && m_iTraget1 == victimIndex && killerIndex) + { + SetPrimaryTarget( killerIndex ); + } + return; + } + +#endif // NEO // after this only auto-director commands follow // don't execute them if autodirector is off and PVS is unlocked if ( !spec_autodirector.GetBool() && !IsPVSLocked() ) diff --git a/src/game/client/hltvcamera.h b/src/game/client/hltvcamera.h index a6a90a45c9..5cfe354b13 100644 --- a/src/game/client/hltvcamera.h +++ b/src/game/client/hltvcamera.h @@ -11,6 +11,9 @@ #endif #include "GameEventListener.h" +#ifdef NEO +#include "neo_gamerules.h" +#endif // NEO class C_HLTVCamera : CGameEventListener { @@ -36,6 +39,9 @@ class C_HLTVCamera : CGameEventListener int GetMode(); // returns current camera mode C_BaseEntity *GetPrimaryTarget(); // return primary target void SetPrimaryTarget( int nEntity); // set the primary obs target +#ifdef NEO + void SpectateEvent(NeoSpectateEvent event); +#endif // NEO C_BaseEntity *GetCameraMan(); // return camera entity if any void CreateMove(CUserCmd *cmd); diff --git a/src/game/client/hud_crosshair.cpp b/src/game/client/hud_crosshair.cpp index 5bf3ce8c1f..0f326436dd 100644 --- a/src/game/client/hud_crosshair.cpp +++ b/src/game/client/hud_crosshair.cpp @@ -168,9 +168,14 @@ bool CHudCrosshair::ShouldDraw( void ) { case OBS_MODE_IN_EYE: player = ToNEOPlayer(player->GetObserverTarget()); + if (engine->IsHLTV() && player && player->IsObserver()) + { // HLTV can spectate other spectators (and doesn't switch away from dead players by default) + return false; + } break; - case OBS_MODE_ROAMING: - return cl_observercrosshair.GetBool(); + // NEO NOTE (Adam) technically cl_observercrosshair should allow us to see a crosshair when roaming, but we're early returning in the draw function anyway if we dont have a valid target so + // I removed the OBS_MODE_ROAMING clause here so stv demo watchers, who still have an observer target even when roaming, dont see a crosshair when normal spectators watching live dont. + // NEO TODO Fix cl_observercrosshair for both default: return false; } diff --git a/src/game/client/neo/c_neo_player.cpp b/src/game/client/neo/c_neo_player.cpp index 4faa485307..6e97c7421b 100644 --- a/src/game/client/neo/c_neo_player.cpp +++ b/src/game/client/neo/c_neo_player.cpp @@ -1444,7 +1444,7 @@ void C_NEO_Player::UpdateGlowEffects(int iNewTeam) }; if (IsLocalPlayer()) { - for (int i = 1; i < gpGlobals->maxClients; i++) { + for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer *pPlayer = UTIL_PlayerByIndex( i ); if (!pPlayer || pPlayer == this) { continue; diff --git a/src/game/client/neo/ui/neo_hud_ghost_beacons.cpp b/src/game/client/neo/ui/neo_hud_ghost_beacons.cpp index d17c8d3bec..c3a4c3058b 100644 --- a/src/game/client/neo/ui/neo_hud_ghost_beacons.cpp +++ b/src/game/client/neo/ui/neo_hud_ghost_beacons.cpp @@ -102,7 +102,8 @@ void CNEOHud_GhostBeacons::DrawNeoHudElement() if (!ghoster || !ghoster->m_bCarryingGhost || ghoster->GetTeamNumber() < FIRST_GAME_TEAM || - !ghoster->IsAlive() || NEORules()->IsRoundOver()) + !ghoster->IsAlive() || NEORules()->IsRoundOver() || + (engine->IsHLTV() && (localPlayer->GetObserverMode() != OBS_MODE_IN_EYE && localPlayer->GetObserverMode() != OBS_MODE_CHASE))) { return; } diff --git a/src/game/client/neo/ui/neo_hud_round_state.cpp b/src/game/client/neo/ui/neo_hud_round_state.cpp index aebb5b5bae..c539c1d5b9 100644 --- a/src/game/client/neo/ui/neo_hud_round_state.cpp +++ b/src/game/client/neo/ui/neo_hud_round_state.cpp @@ -21,6 +21,8 @@ #include "vgui_avatarimage.h" #include "neo_scoreboard.h" +#include "hltvcamera.h" + // memdbgon must be the last include file in a .cpp file!!! #include "tier0/memdbgon.h" @@ -1388,6 +1390,12 @@ int CNEOHud_RoundState::GetSelectedPlayerInHud() CON_COMMAND_F( spec_player_by_hud_position, "Spectate player by position in the top hud", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if ( args.ArgC() != 2 ) { ConMsg( "Usage: spec_player_by_hud_position { player position in top hud, 0 indexed }\n" ); @@ -1411,12 +1419,18 @@ CON_COMMAND_F( spec_player_by_hud_position, "Spectate player by position in the const int entityIndex = g_pNeoHudRoundState->GetEntityIndexAtPositionInHud(positionInHud, true); if (entityIndex) { - engine->ClientCmd( VarArgs("spec_player_entity_number %d", entityIndex) ); + engine->IsHLTV() ? HLTVCamera()->SetPrimaryTarget(entityIndex) : engine->ClientCmd(VarArgs("spec_player_entity_number %d", entityIndex)); } } CON_COMMAND_F( spec_next_entity_in_hud, "Spectate next valid player to the right of the current spectate target", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if (!g_pNeoHudRoundState) return; @@ -1434,12 +1448,18 @@ CON_COMMAND_F( spec_next_entity_in_hud, "Spectate next valid player to the right const int playerIndex = g_pNeoHudRoundState->GetEntityIndexAtPositionInHud(g_pNeoHudRoundState->GetNextAlivePlayerInHud(spectateTargetMinusIndexedPositionInHud, false)); if (playerIndex) { - engine->ClientCmd(VarArgs("spec_player_entity_number %d", playerIndex)); + engine->IsHLTV() ? HLTVCamera()->SetPrimaryTarget(playerIndex) : engine->ClientCmd(VarArgs("spec_player_entity_number %d", playerIndex)); } } CON_COMMAND_F( spec_previous_entity_in_hud, "Spectate next valid player to the left of the current spectate target", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if (!g_pNeoHudRoundState) return; @@ -1457,12 +1477,18 @@ CON_COMMAND_F( spec_previous_entity_in_hud, "Spectate next valid player to the l const int playerIndex = g_pNeoHudRoundState->GetEntityIndexAtPositionInHud(g_pNeoHudRoundState->GetNextAlivePlayerInHud(spectateTargetMinusIndexedPositionInHud, true)); if (playerIndex) { - engine->ClientCmd(VarArgs("spec_player_entity_number %d", playerIndex)); + engine->IsHLTV() ? HLTVCamera()->SetPrimaryTarget(playerIndex) : engine->ClientCmd(VarArgs("spec_player_entity_number %d", playerIndex)); } } CON_COMMAND_F( select_next_alive_player_in_hud, "Select the next alive player in the top hud", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: Selection is used to switch observer target in spectate_player_selected_in_hud, but HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if (!g_pNeoHudRoundState) return; @@ -1475,6 +1501,12 @@ CON_COMMAND_F( select_next_alive_player_in_hud, "Select the next alive player in CON_COMMAND_F( select_previous_alive_player_in_hud, "Select the previous alive player in the top hud", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: Selection is used to switch observer target in spectate_player_selected_in_hud, but HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if (!g_pNeoHudRoundState) return; @@ -1487,6 +1519,12 @@ CON_COMMAND_F( select_previous_alive_player_in_hud, "Select the previous alive p CON_COMMAND_F( spectate_player_selected_in_hud, "Spectate entity selected in the top hud", FCVAR_CLIENTCMD_CAN_EXECUTE ) { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg( "%s: HLTV Camera is PVS locked\n", __FUNCTION__ ); + return; + } + if (!g_pNeoHudRoundState) return; @@ -1497,6 +1535,6 @@ CON_COMMAND_F( spectate_player_selected_in_hud, "Spectate entity selected in the const int entityIndex = g_pNeoHudRoundState->GetSelectedPlayerInHud(); if (entityIndex) { - engine->ClientCmd( VarArgs("spec_player_entity_number %d", entityIndex) ); + engine->IsHLTV() ? HLTVCamera()->SetPrimaryTarget(entityIndex) : engine->ClientCmd(VarArgs("spec_player_entity_number %d", entityIndex)); } } \ No newline at end of file diff --git a/src/game/server/neo/bot/neo_bot.cpp b/src/game/server/neo/bot/neo_bot.cpp index 696b3c953d..c3a1d8199f 100644 --- a/src/game/server/neo/bot/neo_bot.cpp +++ b/src/game/server/neo/bot/neo_bot.cpp @@ -550,36 +550,6 @@ CNEOBot::CNEOBot() SetAutoJump(0.f, 0.f); V_memcpy(&m_profile, &FIXED_DEFAULT_PROFILE, sizeof(CNEOBotProfile)); - - // set default values for convars only present on the client - edict_t* edict = GetEntity()->edict(); - if (edict) - { - { - char szCrhSerial[NEO_XHAIR_SEQMAX] = {}; - DefaultCrosshairSerial(szCrhSerial); - engine->SetFakeClientConVarValue(edict, "cl_neo_crosshair", szCrhSerial); - } - - constexpr struct { - const char* name, *value; - } convars[] = { - { "cl_neo_pvs_cull_roaming_observer", "0" }, - { "cl_neo_streamermode", "0" }, - { "cl_neo_tachi_prefer_auto", "1" }, - { "cl_neo_taking_damage_sounds", "0" }, - { "cl_onlysteamnick", "0" }, - { "hap_HasDevice", "0" }, - { "neo_clantag", "" }, - { "neo_fov", "90" }, - { "neo_name", "" }, - }; - - for (const auto& convar : convars) - { - engine->SetFakeClientConVarValue(edict, convar.name, convar.value); - } - } } diff --git a/src/game/server/neo/bot/neo_bot_manager.cpp b/src/game/server/neo/bot/neo_bot_manager.cpp index 6b5fdaeffa..2bd6ecc8c4 100644 --- a/src/game/server/neo/bot/neo_bot_manager.cpp +++ b/src/game/server/neo/bot/neo_bot_manager.cpp @@ -316,7 +316,7 @@ void CNEOBotManager::MaintainBotQuota() nNonNEOBotsOnGameTeams++; naCountClasses[iPlayerTeam][iClass]++; } - else if ( iPlayerTeam == TEAM_SPECTATOR ) + else if ( iPlayerTeam == TEAM_SPECTATOR && !pPlayer->IsHLTV() ) { nSpectators++; } diff --git a/src/game/server/neo/neo_player.cpp b/src/game/server/neo/neo_player.cpp index cf2660fef9..d1dc54d1d0 100644 --- a/src/game/server/neo/neo_player.cpp +++ b/src/game/server/neo/neo_player.cpp @@ -141,13 +141,6 @@ END_SCRIPTDESC(); static constexpr int SHOWMENU_STRLIMIT = 512; -int CNEO_Player::m_iLastHurt = -1; -int CNEO_Player::m_iLastShooter = -1; -int CNEO_Player::m_iLastEvent = -1; -int CNEO_Player::m_iLastAttacker = -1; -int CNEO_Player::m_iLastKiller = -1; -int CNEO_Player::m_iLastGhoster = -1; - const Vector CNEO_Player::VECTOR_INVALID_WAYPOINT = vec3_invalid; CBaseEntity *g_pLastJinraiSpawn, *g_pLastNSFSpawn; @@ -582,6 +575,36 @@ CNEO_Player::CNEO_Player() m_flNextPingTime = 0; ResetBotCommandState(); + + // set default values for convars only present on the client and read by the server + edict_t* pEdict = edict(); + if (pEdict) + { + { + char szCrhSerial[NEO_XHAIR_SEQMAX] = {}; + DefaultCrosshairSerial(szCrhSerial); + engine->SetFakeClientConVarValue(pEdict, "cl_neo_crosshair", szCrhSerial); + } + + constexpr struct { + const char* name, *value; + } convars[] = { + { "cl_neo_pvs_cull_roaming_observer", "0" }, + { "cl_neo_streamermode", "0" }, + { "cl_neo_tachi_prefer_auto", "1" }, + { "cl_neo_taking_damage_sounds", "0" }, + { "cl_onlysteamnick", "0" }, + { "hap_HasDevice", "0" }, + { "neo_clantag", "" }, + { "neo_fov", "90" }, + { "neo_name", "" }, + }; + + for (const auto& convar : convars) + { + engine->SetFakeClientConVarValue(pEdict, convar.name, convar.value); + } + } } CNEO_Player::~CNEO_Player( void ) @@ -1942,12 +1965,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) } return true; } - else if ( FStrEq(args[0], "spec_fastest_player" )) + else if ( FStrEq(args[0], "spectate_fastest_player" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - int fastestSpeedSquared = 0; + float fastestSpeedSquared = 0; CBaseEntity* pFastestEntity = nullptr; for (int i = 1; i <= gpGlobals->maxClients; i++) { @@ -1970,12 +1993,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_hurt" )) + else if ( FStrEq(args[0], "spectate_last_hurt" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastHurt); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastHurt()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -1987,12 +2010,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_shooter" )) + else if ( FStrEq(args[0], "spectate_last_shooter" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastShooter); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastShooter()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -2004,12 +2027,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_event" )) + else if ( FStrEq(args[0], "spectate_last_event" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastEvent); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastEvent()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -2021,12 +2044,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_attacker" )) + else if ( FStrEq(args[0], "spectate_last_attacker" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastAttacker); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastAttacker()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -2038,12 +2061,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_killer" )) + else if ( FStrEq(args[0], "spectate_last_killer" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastKiller); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastKiller()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -2055,12 +2078,12 @@ bool CNEO_Player::ClientCommand( const CCommand &args ) return true; } - else if ( FStrEq(args[0], "spec_last_ghoster" )) + else if ( FStrEq(args[0], "spectate_last_ghoster" )) { int observerMode = GetObserverMode(); if ( observerMode > OBS_MODE_FIXED ) { - CBaseEntity* pPlayer = UTIL_EntityByIndex(m_iLastGhoster); + CBaseEntity* pPlayer = UTIL_EntityByIndex(NEORules()->GetLastGhoster()); if (SetObserverTarget( pPlayer )) { m_bForcedObserverMode = false; if (observerMode != OBS_MODE_IN_EYE && observerMode != OBS_MODE_CHASE) @@ -2240,7 +2263,7 @@ void CNEO_Player::Event_Killed( const CTakeDamageInfo &info ) CBaseEntity* pAttacker = info.GetAttacker(); if (pAttacker && pAttacker->IsPlayer()) // we can only have players as a spectate target atm { - m_iLastKiller = m_iLastEvent = pAttacker->entindex(); + NEORules()->SetLastKiller(pAttacker->entindex()); for (int i = 1; i <= gpGlobals->maxClients; i++) { CBasePlayer* pObserver = dynamic_cast(UTIL_EntityByIndex(i)); @@ -2519,6 +2542,8 @@ void CNEO_Player::FireBullets ( const FireBulletsInfo_t &info ) // effect lasts 0.5 seconds, but allow 200-300ms leeway with GetFogObscuredRatio cache window m_botThermOpticCamoDisruptedTimer.Start(0.2f); } + + NEORules()->SetLastShooter(entindex()); } void CNEO_Player::Weapon_Equip(CBaseCombatWeapon* pWeapon) @@ -3154,7 +3179,7 @@ AttackersTotals CNEO_Player::GetAttackersTotals() const int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) { - m_iLastHurt = entindex(); + NEORules()->SetLastHurt(entindex()); if (m_takedamage != DAMAGE_EVENTS_ONLY) { if (sv_neo_warmup_godmode.GetBool()) @@ -3175,7 +3200,7 @@ int CNEO_Player::OnTakeDamage_Alive(const CTakeDamageInfo& info) if (auto *attacker = ToNEOPlayer(info.GetAttacker())) { const int attackerIdx = attacker->entindex(); - m_iLastAttacker = m_iLastEvent = attackerIdx; // NEO TODO (Adam) Once we can spectate non-players, let last attacker be non-neoplayer (Jeff) + NEORules()->SetLastAttacker(entindex()); // NEO TODO (Adam) Once we can spectate non-players, let last attacker be non-neoplayer (Jeff) // Separate the fractional amount of damage from the whole const float flFractionalDamage = info.GetDamage() - floor(info.GetDamage()); diff --git a/src/game/server/neo/neo_player.h b/src/game/server/neo/neo_player.h index 6e9a15f7b4..ab80573be2 100644 --- a/src/game/server/neo/neo_player.h +++ b/src/game/server/neo/neo_player.h @@ -339,19 +339,6 @@ class CNEO_Player : public CHL2MP_Player // Cache for GetFogObscuredRatio for each player mutable CNEO_Player_FogCacheEntry m_playerFogCache[MAX_PLAYERS_ARRAY_SAFE]; - - static int m_iLastHurt; - static int m_iLastShooter; -public: - void SetLastShooter() { m_iLastShooter = entindex(); }; - -private: - static int m_iLastEvent; - static int m_iLastAttacker; - static int m_iLastKiller; - static int m_iLastGhoster; -public: - void SetLastGhoster() { m_iLastGhoster = m_iLastEvent = entindex(); }; private: CNEO_Player(const CNEO_Player&); diff --git a/src/game/shared/neo/neo_gamerules.cpp b/src/game/shared/neo/neo_gamerules.cpp index b30c44b1db..67a1e6c34c 100644 --- a/src/game/shared/neo/neo_gamerules.cpp +++ b/src/game/shared/neo/neo_gamerules.cpp @@ -15,6 +15,7 @@ #include "engine/SndInfo.h" #include "engine/IEngineSound.h" #include "filesystem.h" +#include "hltvcamera.h" #else #include "neo_player.h" #include "team.h" @@ -283,6 +284,12 @@ BEGIN_NETWORK_TABLE_NOBASE( CNEORules, DT_NEORules ) RecvPropInt(RECVINFO(m_iJuggernautPlayerIndex)), RecvPropBool(RECVINFO(m_bJuggernautItemExists)), RecvPropEHandle(RECVINFO(m_hJuggernaut)), + RecvPropInt(RECVINFO(m_iLastHurt)), + RecvPropInt(RECVINFO(m_iLastShooter)), + RecvPropInt(RECVINFO(m_iLastEvent)), + RecvPropInt(RECVINFO(m_iLastAttacker)), + RecvPropInt(RECVINFO(m_iLastKiller)), + RecvPropInt(RECVINFO(m_iLastGhoster)), #else SendPropTime(SENDINFO(m_flNeoNextRoundStartTime)), SendPropTime(SENDINFO(m_flNeoRoundStartTime)), @@ -311,6 +318,12 @@ BEGIN_NETWORK_TABLE_NOBASE( CNEORules, DT_NEORules ) SendPropInt(SENDINFO(m_iJuggernautPlayerIndex), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), SendPropBool(SENDINFO(m_bJuggernautItemExists)), SendPropEHandle(SENDINFO(m_hJuggernaut)), + SendPropInt(SENDINFO(m_iLastHurt), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), + SendPropInt(SENDINFO(m_iLastShooter), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), + SendPropInt(SENDINFO(m_iLastEvent), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), + SendPropInt(SENDINFO(m_iLastAttacker), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), + SendPropInt(SENDINFO(m_iLastKiller), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), + SendPropInt(SENDINFO(m_iLastGhoster), NumBitsForCount(MAX_PLAYERS_ARRAY_SAFE), SPROP_UNSIGNED), #endif END_NETWORK_TABLE() @@ -370,8 +383,8 @@ const NeoGameTypeSettings NEO_GAME_TYPE_SETTINGS[NEO_GAME_TYPE__TOTAL] = { } BEGIN_RECV_TABLE( CNEOGameRulesProxy, DT_NEOGameRulesProxy ) - RecvPropDataTable( "neo_gamerules_data", 0, 0, - &REFERENCE_RECV_TABLE( DT_NEORules ), + RecvPropDataTable( "neo_gamerules_data", 0, 0, + &REFERENCE_RECV_TABLE( DT_NEORules ), RecvProxy_NEORules ) END_RECV_TABLE() #else @@ -4803,3 +4816,68 @@ void CNEORules::InitDefaultAIRelationships( void ) CBaseCombatCharacter::SetDefaultRelationship(CLASS_EARTH_FAUNA, CLASS_PLAYER_ALLY_VITAL,D_HT, 0); } #endif + +#ifdef CLIENT_DLL +auto spectateChecks = []() { + if (engine->IsHLTV() && HLTVCamera()->IsPVSLocked()) + { + ConMsg("%s: HLTV Camera is PVS locked\n", __FUNCTION__); + return false; + } + + C_NEO_Player* pNeoPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if (!pNeoPlayer || !pNeoPlayer->IsObserver()) + return false; + + return true; + +}; + +CON_COMMAND_F( spec_last_hurt, "Spectate the last hurt player", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_HURT) : engine->ClientCmd(VarArgs("spectate_last_hurt")); +} + +CON_COMMAND_F( spec_last_shooter, "Spectate the last shooter", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_SHOOTER) : engine->ClientCmd(VarArgs("spectate_last_shooter")); +} + +CON_COMMAND_F( spec_last_event, "Spectate the last attacker, killer or ghoster", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_EVENT) : engine->ClientCmd(VarArgs("spectate_last_event")); +} + +CON_COMMAND_F( spec_last_attacker, "Spectate the last attacker", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_ATTACKER) : engine->ClientCmd(VarArgs("spectate_last_attacker")); +} + +CON_COMMAND_F( spec_last_killer, "Spectate the last killer", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_KILLER) : engine->ClientCmd(VarArgs("spectate_last_killer")); +} + +CON_COMMAND_F( spec_last_ghoster, "Spectate the last ghoster", FCVAR_CLIENTCMD_CAN_EXECUTE ) +{ + if (!spectateChecks()) + return; + + engine->IsHLTV() ? HLTVCamera()->SpectateEvent(NEO_SPECTATE_EVENT_LAST_GHOSTER) : engine->ClientCmd(VarArgs("spectate_last_ghoster")); +} +#endif // CLIENT_DLL diff --git a/src/game/shared/neo/neo_gamerules.h b/src/game/shared/neo/neo_gamerules.h index c8cbfd2d3e..c146ce8d7f 100644 --- a/src/game/shared/neo/neo_gamerules.h +++ b/src/game/shared/neo/neo_gamerules.h @@ -160,6 +160,15 @@ enum NeoHudElements : NEO_HUD_BITS_UNDERLYING_TYPE { NEO_HUD_ELEMENT_WORLDPOS_MARKER_ENT = (static_cast(1) << 16), }; +enum NeoSpectateEvent { + NEO_SPECTATE_EVENT_LAST_HURT = 0, + NEO_SPECTATE_EVENT_LAST_SHOOTER, + NEO_SPECTATE_EVENT_LAST_EVENT, + NEO_SPECTATE_EVENT_LAST_ATTACKER, + NEO_SPECTATE_EVENT_LAST_KILLER, + NEO_SPECTATE_EVENT_LAST_GHOSTER, +}; + class CNEORules : public CHL2MPRules, public CGameEventListener { public: @@ -435,6 +444,21 @@ class CNEORules : public CHL2MPRules, public CGameEventListener void JuggernautActivated(CNEO_Player *pPlayer); void JuggernautDeactivated(CNEO_Juggernaut *pJuggernaut); void JuggernautTotalRemoval(CNEO_Juggernaut *pJuggernaut); + + void SetLastHurt(const int index) { m_iLastHurt = index; } + void SetLastShooter(const int index) { m_iLastShooter = index; } + void SetLastAttacker(const int index) { m_iLastAttacker = m_iLastEvent = index; } + void SetLastKiller(const int index) { m_iLastKiller = m_iLastEvent = index; } + void SetLastGhoster(const int index) { m_iLastGhoster = m_iLastEvent = index; } +#endif // GAME_DLL +public: + const int GetLastHurt() const { return m_iLastHurt; } + const int GetLastShooter() const { return m_iLastShooter; } + const int GetLastEvent() const { return m_iLastEvent; } + const int GetLastAttacker() const { return m_iLastAttacker; } + const int GetLastKiller() const { return m_iLastKiller; } + const int GetLastGhoster() const { return m_iLastGhoster; } +#ifdef GAME_DLL private: CNEO_Juggernaut *m_pJuggernautItem = nullptr; CNEO_Player *m_pJuggernautPlayer = nullptr; @@ -498,6 +522,14 @@ class CNEORules : public CHL2MPRules, public CGameEventListener CNetworkVar(float, m_flNeoRoundStartTime); CNetworkVar(float, m_flNeoNextRoundStartTime); + // For spectator commands. Networked so can be saved in demos for hltv + CNetworkVar(int, m_iLastHurt); + CNetworkVar(int, m_iLastShooter); + CNetworkVar(int, m_iLastEvent); + CNetworkVar(int, m_iLastAttacker); + CNetworkVar(int, m_iLastKiller); + CNetworkVar(int, m_iLastGhoster); + public: // VIP networked variables CNetworkVar(int, m_iEscortingTeam); diff --git a/src/game/shared/neo/weapons/weapon_ghost.cpp b/src/game/shared/neo/weapons/weapon_ghost.cpp index 027260a212..7f99233e33 100644 --- a/src/game/shared/neo/weapons/weapon_ghost.cpp +++ b/src/game/shared/neo/weapons/weapon_ghost.cpp @@ -227,7 +227,7 @@ void CWeaponGhost::Equip(CBaseCombatCharacter *pNewOwner) auto neoOwner = assert_cast(pNewOwner); #ifdef GAME_DLL - neoOwner->SetLastGhoster(); + NEORules()->SetLastGhoster(neoOwner->entindex()); #endif // GAME_DLL // Prevent ghoster from sprinting diff --git a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp index 4f459615ab..d2b4b22187 100644 --- a/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp +++ b/src/game/shared/neo/weapons/weapon_neobasecombatweapon.cpp @@ -956,9 +956,6 @@ void CNEOBaseCombatWeapon::PrimaryAttack(void) return; } -#ifdef GAME_DLL - pPlayer->SetLastShooter(); -#endif // GAME_DLL if (!(GetNeoWepBits() & NEO_WEP_SUPPRESSED)) { pPlayer->DoMuzzleFlash(); diff --git a/src/utils/serverplugin_sample/serverplugin_empty.cpp b/src/utils/serverplugin_sample/serverplugin_empty.cpp index 42e4c1d3bf..aa70191546 100644 --- a/src/utils/serverplugin_sample/serverplugin_empty.cpp +++ b/src/utils/serverplugin_sample/serverplugin_empty.cpp @@ -337,6 +337,9 @@ CON_COMMAND( DoAskConnect, "Server plugin example of using the ask connect dialo kv->SetString( "title", pServerIP ); // The IP address of the server to connect to goes in the "title" field. kv->SetInt( "time", 3 ); +#ifdef NEO + // If using this as an example consider the below will miss the client with entindex gpGlobals->maxClients +#endif // NEO for ( int i=1; i < gpGlobals->maxClients; i++ ) { edict_t *pEdict = engine->PEntityOfEntIndex( i );