diff --git a/src/game/client/c_baseplayer.cpp b/src/game/client/c_baseplayer.cpp index 05cb0e8ba..d7ecb837d 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 5d971071b..7be152dfe 100644 --- a/src/game/client/clientmode_shared.cpp +++ b/src/game/client/clientmode_shared.cpp @@ -823,6 +823,47 @@ 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) + { + 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 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 d61fe6568..9635e2830 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 aa4c57e00..17dffd1e4 100644 --- a/src/game/server/player.cpp +++ b/src/game/server/player.cpp @@ -6951,6 +6951,22 @@ bool CBasePlayer::ClientCommand( const CCommand &args ) return true; } +#ifdef NEO + else if (stricmp(cmd, "spec_player_entity_number") == 0) + { + if (GetObserverMode() > OBS_MODE_FIXED && args.ArgC() == 2) + { + int targetEntIndex = atoi( args[1] ); + CBasePlayer* target = UTIL_PlayerByIndex(targetEntIndex); + + if (SetObserverTarget( target )) { + m_bForcedObserverMode = false; + SetObserverMode(OBS_MODE_IN_EYE); + } + } + return true; + } +#endif // NEO else if ( stricmp( cmd, "spec_player" ) == 0 ) // chase next player { if ( GetObserverMode() > OBS_MODE_FIXED && args.ArgC() == 2 )