Fix broken roaming spectator mode

* Added server cvar (mp_freespectatormode) which controls when a player can enter a free spectator mode (orbit cam, or free roaming). 0 = never, 1 = only when spectator, 2 = Always
* Free roaming will now be part of the cycle when pressing the jump key
* The overlay is now toggled with the crouch button
* Strafing in free roaming no longer snaps to different players
* Fixed bug that would break spectating completely if returning to the ready room while in free roam
* You can no longer enter free roaming using the "specmode" and "spec_mode" console commands if it's disabled by the server
This commit is contained in:
RGreenlees 2024-04-04 13:57:04 +01:00 committed by pierow
parent 019045c5c7
commit 4f32271532
7 changed files with 85 additions and 68 deletions

View file

@ -853,58 +853,41 @@ void CHudSpectator::HandleButtonsDown( int ButtonPressed )
int theNewMainMode = g_iUser1; int theNewMainMode = g_iUser1;
// Jump changes main window modes // Crouch toggles the overview mode
if (gHUD.GetIsNSMode() && ButtonPressed & IN_DUCK)
{
bool theInOverviewMode = gHUD.m_Spectator.IsInOverviewMode();
gHUD.m_Spectator.SetOverviewMode(!theInOverviewMode);
}
// Jump changes spectator modes
if ( ButtonPressed & IN_JUMP ) if ( ButtonPressed & IN_JUMP )
{ {
bool theFirstPerson = (g_iUser1 == OBS_IN_EYE); theNewMainMode++;
bool theInOverviewMode = gHUD.m_Spectator.IsInOverviewMode();
// NS AvHPlayMode CurrPlayMode = gHUD.GetPlayMode();
if(gHUD.GetIsNSMode())
float FreeSpecMode = gHUD.GetServerVariableFloat("mp_freespectatormode");
if (FreeSpecMode == 0 || (FreeSpecMode == 1 && gHUD.GetPlayMode() != PLAYMODE_OBSERVER))
{ {
// First-person full -> chase camera full -> firstperson with overview -> chase camera with overview while (theNewMainMode == OBS_CHASE_FREE || theNewMainMode == OBS_ROAMING)
if(theFirstPerson && !theInOverviewMode)
{ {
gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED); theNewMainMode++;
//gHUD.m_Spectator.SetOverviewMode(false);
}
else if(!theFirstPerson && !theInOverviewMode)
{
gHUD.m_Spectator.SetMode(OBS_IN_EYE);
gHUD.m_Spectator.SetOverviewMode(true);
}
else if(theFirstPerson && theInOverviewMode)
{
gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED);
//gHUD.m_Spectator.SetOverviewMode(true);
}
else if(!theFirstPerson && theInOverviewMode)
{
gHUD.m_Spectator.SetMode(OBS_IN_EYE);
gHUD.m_Spectator.SetOverviewMode(false);
} }
} }
// Combat
else if (theNewMainMode > OBS_IN_EYE)
{ {
// First-person full -> chase camera full theNewMainMode = OBS_CHASE_LOCKED;
if(theFirstPerson)
{
gHUD.m_Spectator.SetMode(OBS_CHASE_LOCKED);
gHUD.m_Spectator.SetOverviewMode(false);
}
else
{
gHUD.m_Spectator.SetMode(OBS_IN_EYE);
gHUD.m_Spectator.SetOverviewMode(false);
}
} }
gHUD.m_Spectator.SetMode(theNewMainMode);
} }
//g_iUser1 = theNewMainMode; //g_iUser1 = theNewMainMode;
// Attack moves to the next player // Attack moves to the next player
if ( ButtonPressed & (IN_MOVELEFT | IN_MOVERIGHT) ) if (g_iUser1 != OBS_ROAMING && ButtonPressed & (IN_MOVELEFT | IN_MOVERIGHT) )
{ {
FindNextPlayer( (ButtonPressed & IN_MOVELEFT) ? true:false ); FindNextPlayer( (ButtonPressed & IN_MOVELEFT) ? true:false );
@ -1037,25 +1020,43 @@ void CHudSpectator::HandleButtonsUp( int ButtonPressed )
void CHudSpectator::SetMode(int iNewMainMode) void CHudSpectator::SetMode(int iNewMainMode)
{ {
int NewMode = iNewMainMode;
if (NewMode == OBS_CHASE_FREE || NewMode == OBS_ROAMING)
{
float FreeSpecMode = gHUD.GetServerVariableFloat("mp_freespectatormode");
if (FreeSpecMode == 0 || (FreeSpecMode == 1 && gHUD.GetPlayMode() != PLAYMODE_OBSERVER))
{
if (g_iUser1 != OBS_CHASE_FREE && g_iUser1 != OBS_ROAMING)
{
NewMode = g_iUser1;
}
else
{
NewMode = OBS_IN_EYE;
}
}
}
// if value == -1 keep old value // if value == -1 keep old value
if ( iNewMainMode == -1 ) if (NewMode == -1 )
iNewMainMode = g_iUser1; NewMode = g_iUser1;
// main modes ettings will override inset window settings // main modes ettings will override inset window settings
if ( iNewMainMode != g_iUser1 ) if (NewMode != g_iUser1 )
{ {
// if we are NOT in HLTV mode, main spectator mode is set on server // if we are NOT in HLTV mode, main spectator mode is set on server
if ( !gEngfuncs.IsSpectateOnly() ) if ( !gEngfuncs.IsSpectateOnly() )
{ {
char cmdstring[32]; char cmdstring[32];
// forward command to server // forward command to server
sprintf(cmdstring,"specmode %i",iNewMainMode ); sprintf(cmdstring,"specmode %i", NewMode);
gEngfuncs.pfnServerCmd(cmdstring); gEngfuncs.pfnServerCmd(cmdstring);
return; return;
} }
else else
{ {
if ( !g_iUser2 && (iNewMainMode !=OBS_ROAMING ) ) // make sure we have a target if ( !g_iUser2 && (NewMode != OBS_ROAMING ) ) // make sure we have a target
{ {
// choose last Director object if still available // choose last Director object if still available
if ( IsActivePlayer( gEngfuncs.GetEntityByIndex( m_lastPrimaryObject ) ) ) if ( IsActivePlayer( gEngfuncs.GetEntityByIndex( m_lastPrimaryObject ) ) )
@ -1069,7 +1070,7 @@ void CHudSpectator::SetMode(int iNewMainMode)
} }
} }
switch ( iNewMainMode ) switch (NewMode)
{ {
case OBS_CHASE_LOCKED: case OBS_CHASE_LOCKED:
g_iUser1 = OBS_CHASE_LOCKED; g_iUser1 = OBS_CHASE_LOCKED;

View file

@ -670,7 +670,7 @@ void ClientCommand( edict_t *pEntity )
else if (FStrEq( pcmd, "follow")) // follow a specific player else if (FStrEq( pcmd, "follow")) // follow a specific player
{ {
CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev);
if ( pPlayer->IsObserver() ) if ( pPlayer->IsObserver() && pPlayer->pev->iuser1 != OBS_ROAMING )
{ {
pPlayer->Observer_SpectatePlayer(atoi( CMD_ARGV(1) )); pPlayer->Observer_SpectatePlayer(atoi( CMD_ARGV(1) ));
pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2);
@ -679,7 +679,7 @@ void ClientCommand( edict_t *pEntity )
else if ( FStrEq( pcmd, "follownext" ) ) // follow next player else if ( FStrEq( pcmd, "follownext" ) ) // follow next player
{ {
CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev); CBasePlayer * pPlayer = GetClassPtr((CBasePlayer *)pev);
if ( pPlayer->IsObserver() ) if ( pPlayer->IsObserver() && pPlayer->pev->iuser1 != OBS_ROAMING)
{ {
pPlayer->Observer_FindNextPlayer(atoi( CMD_ARGV(1) )); pPlayer->Observer_FindNextPlayer(atoi( CMD_ARGV(1) ));
pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2); pPlayer->SetDefaultSpectatingSettings(pPlayer->pev->iuser1, pPlayer->pev->iuser2);

View file

@ -125,6 +125,7 @@ cvar_t avh_jumpmode = {kvJumpMode, "1", FCVAR_SERVER};
cvar_t avh_version = {kvVersion, "330", FCVAR_SERVER}; cvar_t avh_version = {kvVersion, "330", FCVAR_SERVER};
cvar_t avh_widescreenclamp = {kvWidescreenClamp, "0", FCVAR_SERVER}; cvar_t avh_widescreenclamp = {kvWidescreenClamp, "0", FCVAR_SERVER};
cvar_t avh_randomrfk = {kvRandomRfk, "0", FCVAR_SERVER}; cvar_t avh_randomrfk = {kvRandomRfk, "0", FCVAR_SERVER};
cvar_t avh_freespectatormode = {kvFreeSpectatorMode, "1", FCVAR_SERVER };
// AI Player Settings // AI Player Settings
cvar_t avh_botsenabled = { kvBotsEnabled,"0", FCVAR_SERVER }; // Bots can be added to the server Y/N cvar_t avh_botsenabled = { kvBotsEnabled,"0", FCVAR_SERVER }; // Bots can be added to the server Y/N
@ -285,6 +286,7 @@ void GameDLLInit( void )
// TODO: Remove // TODO: Remove
CVAR_REGISTER (&avh_ironman); CVAR_REGISTER (&avh_ironman);
CVAR_REGISTER (&avh_ironmantime); CVAR_REGISTER (&avh_ironmantime);
CVAR_REGISTER (&avh_freespectatormode);
#ifdef DEBUG #ifdef DEBUG
CVAR_REGISTER (&avh_spawninvulnerabletime); CVAR_REGISTER (&avh_spawninvulnerabletime);

View file

@ -25,6 +25,8 @@
const float kNextTargetInterval = .2f; const float kNextTargetInterval = .2f;
extern cvar_t avh_freespectatormode;
// Find the next client in the game for this player to spectate // Find the next client in the game for this player to spectate
bool CBasePlayer::Observer_FindNextPlayer(bool inReverse) bool CBasePlayer::Observer_FindNextPlayer(bool inReverse)
{ {
@ -270,46 +272,55 @@ void CBasePlayer::Observer_SetMode( int iMode )
bool theIsObserving = (thePlayer->GetPlayMode() == PLAYMODE_OBSERVER); bool theIsObserving = (thePlayer->GetPlayMode() == PLAYMODE_OBSERVER);
if(!theIsObserving) int NewMode = iMode;
float FreeSpecMode = avh_freespectatormode.value;
if (FreeSpecMode == 0 || (FreeSpecMode == 1 && !theIsObserving))
{ {
if((iMode == OBS_ROAMING) /*|| (iMode == OBS_MAP_FREE) || (iMode == OBS_MAP_CHASE)*/) if (NewMode == OBS_CHASE_FREE || NewMode == OBS_ROAMING)
{ {
return; NewMode = (pev->iuser1 == OBS_CHASE_LOCKED || pev->iuser1 == OBS_IN_EYE) ? pev->iuser1 : OBS_IN_EYE;
} }
} }
// Just abort if we're changing to the mode we're already in // Just abort if we're changing to the mode we're already in
if ( iMode == pev->iuser1 ) if (NewMode == pev->iuser1 )
return; return;
// Removed by mmcguire. // Removed by mmcguire.
// is valid mode ? // is valid mode ?
if ( iMode < OBS_CHASE_LOCKED || iMode > OBS_IN_EYE ) if (NewMode < OBS_CHASE_LOCKED || NewMode > OBS_IN_EYE )
iMode = OBS_IN_EYE; // now it is NewMode = OBS_IN_EYE; // now it is
// if we are not roaming, we need a valid target to track // if we are not roaming, we need a valid target to track
if ( (iMode != OBS_ROAMING) && (m_hObserverTarget == NULL) ) if ( (NewMode != OBS_ROAMING) && (m_hObserverTarget == NULL) )
{ {
Observer_FindNextPlayer(); Observer_FindNextPlayer();
// if we didn't find a valid target switch to roaming // if we didn't find a valid target switch to roaming
if (m_hObserverTarget == NULL) if (m_hObserverTarget == NULL)
{ {
iMode = OBS_ROAMING; NewMode = OBS_ROAMING;
} }
} }
// set spectator mode // set spectator mode
pev->iuser1 = iMode; pev->iuser1 = NewMode;
// set target if not roaming // set target if not roaming
if (iMode == OBS_ROAMING) if (NewMode == OBS_ROAMING)
{
pev->iuser2 = 0; pev->iuser2 = 0;
}
else else
pev->iuser2 = ENTINDEX( m_hObserverTarget->edict() ); {
pev->iuser2 = ENTINDEX(m_hObserverTarget->edict());
// Make sure our target is valid (go backward then forward) // Make sure our target is valid (go backward then forward)
Observer_FindNextPlayer(true); Observer_FindNextPlayer(true);
Observer_FindNextPlayer(false); Observer_FindNextPlayer(false);
}
} }

View file

@ -1694,7 +1694,7 @@ void CBasePlayer::StartObserver( Vector vecPosition, Vector vecViewAngle )
pev->deadflag = DEAD_RESPAWNABLE; pev->deadflag = DEAD_RESPAWNABLE;
// Tell the physics code that this player's now in observer mode // Tell the physics code that this player's now in observer mode
Observer_SetMode(this->GetDefaultSpectatingMode()); Observer_SetMode(OBS_IN_EYE);
Observer_SpectatePlayer(this->GetDefaultSpectatingTarget()); Observer_SpectatePlayer(this->GetDefaultSpectatingTarget());
m_flNextObserverInput = 0; m_flNextObserverInput = 0;
} }

View file

@ -231,6 +231,7 @@ extern cvar_t avh_ironman;
extern cvar_t avh_mapvoteratio; extern cvar_t avh_mapvoteratio;
extern cvar_t avh_structurelimit; extern cvar_t avh_structurelimit;
extern cvar_t avh_version; extern cvar_t avh_version;
extern cvar_t avh_freespectatormode;
extern cvar_t avh_botsenabled; extern cvar_t avh_botsenabled;
extern cvar_t avh_botautomode; extern cvar_t avh_botautomode;
@ -348,6 +349,7 @@ AvHGamerules::AvHGamerules() : mTeamA(TEAM_ONE), mTeamB(TEAM_TWO)
RegisterServerVariable(&avh_structurelimit); RegisterServerVariable(&avh_structurelimit);
RegisterServerVariable(&avh_version); RegisterServerVariable(&avh_version);
RegisterServerVariable(&avh_widescreenclamp); RegisterServerVariable(&avh_widescreenclamp);
RegisterServerVariable(&avh_freespectatormode);
//playtest cvars //playtest cvars
RegisterServerVariable(&avh_fastjp); RegisterServerVariable(&avh_fastjp);

View file

@ -142,6 +142,7 @@ float ns_cvar_float(const cvar_t *cvar);
#define kvPerformance "mp_performance" #define kvPerformance "mp_performance"
#define kvIronMan "mp_ironman" #define kvIronMan "mp_ironman"
#define kvIronManTime "mp_ironmantime" #define kvIronManTime "mp_ironmantime"
#define kvFreeSpectatorMode "mp_freespectatormode"
#define kvBotsEnabled "mp_botsenabled" #define kvBotsEnabled "mp_botsenabled"
#define kvBotMinPlayers "mp_botminplayers" #define kvBotMinPlayers "mp_botminplayers"