From c6bf72b500c42a1f95fc84326b387de268883abf Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 15 Jan 2026 10:23:22 +0000 Subject: [PATCH 1/2] init --- src/game/client/clientmode_shared.cpp | 7 ++++++ src/game/client/in_main.cpp | 7 ++++++ src/game/server/player.cpp | 34 +++++++++++++++++++++++++++ 3 files changed, 48 insertions(+) diff --git a/src/game/client/clientmode_shared.cpp b/src/game/client/clientmode_shared.cpp index 5d971071b5..23e96d1810 100644 --- a/src/game/client/clientmode_shared.cpp +++ b/src/game/client/clientmode_shared.cpp @@ -823,6 +823,13 @@ int ClientModeShared::HandleSpectatorKeyInput( int down, ButtonCode_t keynum, co engine->ClientCmd( "spec_prev" ); return 0; } +#ifdef NEO + else if (down && pszCurrentBinding && Q_strcmp(pszCurrentBinding, "+specmouseplayer") == 0) + { + engine->ClientCmd( "spec_mouse_player" ); + return 0; + } +#endif // NEO else if ( down && pszCurrentBinding && Q_strcmp( pszCurrentBinding, "+jump" ) == 0 ) { engine->ClientCmd( "spec_mode" ); diff --git a/src/game/client/in_main.cpp b/src/game/client/in_main.cpp index d61fe65688..9635e28301 100644 --- a/src/game/client/in_main.cpp +++ b/src/game/client/in_main.cpp @@ -157,6 +157,7 @@ static kbutton_t in_thermoptic; static kbutton_t in_vision; static kbutton_t in_spec_next; static kbutton_t in_spec_prev; +static kbutton_t in_spec_mouse_player; #endif /* @@ -637,6 +638,9 @@ void IN_SpecNextDown(const CCommand &args) { KeyDown(&in_spec_next, args[1]); } void IN_SpecPrevUp(const CCommand &args) { KeyUp(&in_spec_prev, args[1]); } void IN_SpecPrevDown(const CCommand &args) { KeyDown(&in_spec_prev, args[1]); } +void IN_SpecMousePlayerDown(const CCommand &args) { KeyUp(&in_spec_mouse_player, args[1]); } +void IN_SpecMousePlayerUp(const CCommand &args) { KeyDown(&in_spec_mouse_player, args[1]); } + void IN_AimToggle(const CCommand& args) { if (::input->KeyState(&in_aim)) @@ -1887,6 +1891,9 @@ static ConCommand endspecnextplayer("-specnextplayer", IN_SpecNextUp); static ConCommand startspecprevplayer("+specprevplayer", IN_SpecPrevDown); static ConCommand endspecprevplayer("-specprevplayer", IN_SpecPrevUp); + +static ConCommand startspecmouseplayer("+specmouseplayer", IN_SpecMousePlayerDown); +static ConCommand endspecmouseplayer("-specmouseplayer", IN_SpecMousePlayerUp); #endif /* diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index aa4c57e005..3ce757473e 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -6951,6 +6951,40 @@ bool CBasePlayer::ClientCommand( const CCommand &args ) return true; } +#ifdef NEO + else if (stricmp(cmd, "spec_mouse_player") == 0) + { + if (GetObserverMode() > OBS_MODE_FIXED) + { + CNEO_Player *target = nullptr; + float targetDotProduct = -1; + for (int i = 1; i < gpGlobals->maxClients; i++) + { + CNEO_Player* pPlayer = ToNEOPlayer(UTIL_PlayerByIndex(i)); + if (IsValidObserverTarget(pPlayer)) + { + Vector vecForward; + AngleVectors( EyeAngles(), &vecForward ); + + Vector vecToTarget = pPlayer->WorldSpaceCenter() - EyePosition(); + vecToTarget.NormalizeInPlace(); + float dotProduct = DotProduct(vecForward, vecToTarget); + if (dotProduct > targetDotProduct) + { + targetDotProduct = dotProduct; + target = pPlayer; + } + } + } + + if (target) { + SetObserverMode(OBS_MODE_IN_EYE); + SetObserverTarget( target ); + } + } + return true; + } +#endif // NEO else if ( stricmp( cmd, "spec_player" ) == 0 ) // chase next player { if ( GetObserverMode() > OBS_MODE_FIXED && args.ArgC() == 2 ) From c8b3ce7fd74d739f54ba203704a4ff14d2a7dc22 Mon Sep 17 00:00:00 2001 From: AdamTadeusz Date: Thu, 15 Jan 2026 12:40:04 +0000 Subject: [PATCH 2/2] find player client side --- src/game/client/c_baseplayer.cpp | 5 ++++ src/game/client/clientmode_shared.cpp | 36 ++++++++++++++++++++++++++- src/game/server/player.cpp | 30 +++++----------------- 3 files changed, 46 insertions(+), 25 deletions(-) diff --git a/src/game/client/c_baseplayer.cpp b/src/game/client/c_baseplayer.cpp index 05cb0e8ba9..d7ecb837d9 100644 --- a/src/game/client/c_baseplayer.cpp +++ b/src/game/client/c_baseplayer.cpp @@ -736,7 +736,12 @@ bool C_BasePlayer::IsValidObserverTarget(CBaseEntity* target) if (player->m_lifeState == LIFE_DEAD || player->m_lifeState == LIFE_DYING) { +#ifdef NEO + constexpr int DEATH_SPEC_TIME = 3.0f; // OGNT switches spectator targets much faster than the DEATH_ANIMATION_TIME + if ((player->m_flDeathTime + DEATH_SPEC_TIME) < gpGlobals->curtime) +#else if ((player->m_flDeathTime + DEATH_ANIMATION_TIME) < gpGlobals->curtime) +#endif // NEO { return false; // allow watching until 3 seconds after death to see death animation } diff --git a/src/game/client/clientmode_shared.cpp b/src/game/client/clientmode_shared.cpp index 23e96d1810..7be152dfe5 100644 --- a/src/game/client/clientmode_shared.cpp +++ b/src/game/client/clientmode_shared.cpp @@ -826,7 +826,41 @@ int ClientModeShared::HandleSpectatorKeyInput( int down, ButtonCode_t keynum, co #ifdef NEO else if (down && pszCurrentBinding && Q_strcmp(pszCurrentBinding, "+specmouseplayer") == 0) { - engine->ClientCmd( "spec_mouse_player" ); + C_NEO_Player *pNeoPlayer = C_NEO_Player::GetLocalNEOPlayer(); + if (!pNeoPlayer) + { + Assert(false); + return 0; + } + + 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()) + { + Vector vecForward; + AngleVectors( pNeoPlayer->EyeAngles(), &vecForward ); + + Vector vecToTarget = pPlayer->WorldSpaceCenter() - pNeoPlayer->EyePosition(); + vecToTarget.NormalizeInPlace(); + float dotProduct = DotProduct(vecForward, vecToTarget); + if (dotProduct > targetDotProduct && dotProduct > 0) + { + targetDotProduct = dotProduct; + target = pPlayer; + } + } + } + + if (target) + { + engine->ClientCmd( VarArgs("spec_player_entity_number %d", target->entindex()) ); + } + return 0; } #endif // NEO diff --git a/src/game/server/player.cpp b/src/game/server/player.cpp index 3ce757473e..17dffd1e41 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -6952,34 +6952,16 @@ bool CBasePlayer::ClientCommand( const CCommand &args ) return true; } #ifdef NEO - else if (stricmp(cmd, "spec_mouse_player") == 0) + else if (stricmp(cmd, "spec_player_entity_number") == 0) { - if (GetObserverMode() > OBS_MODE_FIXED) + if (GetObserverMode() > OBS_MODE_FIXED && args.ArgC() == 2) { - CNEO_Player *target = nullptr; - float targetDotProduct = -1; - for (int i = 1; i < gpGlobals->maxClients; i++) - { - CNEO_Player* pPlayer = ToNEOPlayer(UTIL_PlayerByIndex(i)); - if (IsValidObserverTarget(pPlayer)) - { - Vector vecForward; - AngleVectors( EyeAngles(), &vecForward ); - - Vector vecToTarget = pPlayer->WorldSpaceCenter() - EyePosition(); - vecToTarget.NormalizeInPlace(); - float dotProduct = DotProduct(vecForward, vecToTarget); - if (dotProduct > targetDotProduct) - { - targetDotProduct = dotProduct; - target = pPlayer; - } - } - } + int targetEntIndex = atoi( args[1] ); + CBasePlayer* target = UTIL_PlayerByIndex(targetEntIndex); - if (target) { + if (SetObserverTarget( target )) { + m_bForcedObserverMode = false; SetObserverMode(OBS_MODE_IN_EYE); - SetObserverTarget( target ); } } return true;