NS/main/source/dlls/observer.cpp
pierow b5177e8e47 Additional spectator fixes
- fixed issues with piemenu cursor code being enabled when spectating
- the cursor is now enabled when in an observer mode that locks the view.
- fixed bugs/exploits where dead players could first person spectate the enemy team or travel around in freelook if all spectatable players on the team are dead.
2024-05-04 05:56:52 -04:00

331 lines
No EOL
8.1 KiB
C++

//=========== (C) Copyright 1996-2002, Valve, L.L.C. All rights reserved. ===========
//
// The copyright to the contents herein is the property of Valve, L.L.C.
// The contents may be used and/or copied only with the written permission of
// Valve, L.L.C., or in accordance with the terms and conditions stipulated in
// the agreement/contract under which the contents have been supplied.
//
// Purpose: Functionality for the observer chase camera
//
// $Workfile: $
// $Date: $
//
//-----------------------------------------------------------------------------
// $Log: $
//
// $NoKeywords: $
//=============================================================================
#include "extdll.h"
#include "util.h"
#include "cbase.h"
#include "player.h"
#include "pm_shared/pm_shared.h"
#include "../mod/AvHPlayer.h"
#include "../mod/AvHGamerules.h"
const float kNextTargetInterval = .2f;
extern cvar_t avh_freespectatormode;
// Find the next client in the game for this player to spectate
bool CBasePlayer::Observer_FindNextPlayer(bool inReverse)
{
// MOD AUTHORS: Modify the logic of this function if you want to restrict the observer to watching
// only a subset of the players. e.g. Make it check the target's team.
bool theSuccess = false;
int iStart;
if ( m_hObserverTarget )
iStart = ENTINDEX( m_hObserverTarget->edict() );
else
iStart = ENTINDEX( edict() );
int iCurrent = iStart;
m_hObserverTarget = NULL;
if(this->entindex() == 1)
{
int a = 0;
}
int iDir = inReverse ? -1 : 1;
AvHPlayer* theThisAvHPlayer = dynamic_cast<AvHPlayer*>(this);
do
{
iCurrent += iDir;
// Loop through the clients
if (iCurrent > gpGlobals->maxClients)
iCurrent = 1;
if (iCurrent < 1)
iCurrent = gpGlobals->maxClients;
CBaseEntity *pEnt = UTIL_PlayerByIndex( iCurrent );
if ( !pEnt )
continue;
if ( pEnt == this )
continue;
// Don't spec observers or invisible players
if ( ((CBasePlayer*)pEnt)->IsObserver() || (pEnt->pev->effects & EF_NODRAW))
continue;
// MOD AUTHORS: Add checks on target here.
AvHPlayer* thePotentialTargetPlayer = dynamic_cast<AvHPlayer*>(pEnt);
if(theThisAvHPlayer && thePotentialTargetPlayer)
{
if(!thePotentialTargetPlayer->GetIsRelevant())
{
continue;
}
// Don't allow spectating of other team when we could come back in
if(((theThisAvHPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (theThisAvHPlayer->GetPlayMode() == PLAYMODE_REINFORCING)) && (thePotentialTargetPlayer->pev->team != this->pev->team))
{
continue;
}
if(theThisAvHPlayer->GetPlayMode() != PLAYMODE_OBSERVER)
{
if(GetGameRules()->GetIsTournamentMode() && (thePotentialTargetPlayer->pev->team != this->pev->team) && (this->pev->team != TEAM_IND))
{
continue;
}
}
// Don't allow spectating of players in top down mode
if(thePotentialTargetPlayer->GetIsInTopDownMode())
{
continue;
}
// Don't allow spectating opposite teams in tournament mode
//if(GetGameRules()->GetIsTournamentMode())
//{
//}
m_hObserverTarget = pEnt;
}
break;
} while ( iCurrent != iStart );
// Did we find a target?
if ( m_hObserverTarget )
{
// Store the target in pev so the physics DLL can get to it
if (pev->iuser1 != OBS_ROAMING)
pev->iuser2 = ENTINDEX( m_hObserverTarget->edict() );
// Move to the target
UTIL_SetOrigin( pev, m_hObserverTarget->pev->origin );
//ALERT( at_console, "Now Tracking %s\n", STRING( m_hObserverTarget->pev->classname ) );
m_flNextObserverInput = gpGlobals->time + kNextTargetInterval;
theSuccess = true;
}
else
{
//ALERT( at_console, "No observer targets.\n" );
}
return theSuccess;
}
void CBasePlayer::Observer_SpectatePlayer(int index)
{
CBaseEntity* pEnt = UTIL_PlayerByIndex(index);
AvHPlayer* thePotentialTargetPlayer = dynamic_cast<AvHPlayer*>(pEnt);
AvHPlayer* theThisAvHPlayer = dynamic_cast<AvHPlayer*>(this);
if (theThisAvHPlayer == NULL || thePotentialTargetPlayer == NULL)
{
return;
}
if(!thePotentialTargetPlayer->GetIsRelevant())
{
return;
}
// Don't allow spectating of other team when we could come back in
if(((theThisAvHPlayer->GetPlayMode() == PLAYMODE_AWAITINGREINFORCEMENT) || (theThisAvHPlayer->GetPlayMode() == PLAYMODE_REINFORCING)) && (thePotentialTargetPlayer->pev->team != this->pev->team))
{
return;
}
if(theThisAvHPlayer->GetPlayMode() != PLAYMODE_OBSERVER)
{
if(GetGameRules()->GetIsTournamentMode() && (thePotentialTargetPlayer->pev->team != this->pev->team) && (this->pev->team != TEAM_IND))
{
return;
}
}
// Don't allow spectating of players in top down mode
if(thePotentialTargetPlayer->GetIsInTopDownMode())
{
return;
}
m_hObserverTarget = pEnt;
pev->iuser2 = index;
}
// Handle buttons in observer mode
void CBasePlayer::Observer_HandleButtons()
{
// Removed by mmcguire.
// This is all handled through the client side UI.
/*
// Slow down mouse clicks
if ( m_flNextObserverInput > gpGlobals->time )
return;
// Only do this if spectating
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this);
ASSERT(thePlayer);
bool theIsObserving = (thePlayer->GetPlayMode() == PLAYMODE_OBSERVER);
// Jump changes from modes: Chase to Roaming
if ( m_afButtonPressed & IN_JUMP )
{
if ( pev->iuser1 == OBS_CHASE_LOCKED )
{
Observer_SetMode( OBS_CHASE_FREE );
}
else if ( pev->iuser1 == OBS_CHASE_FREE )
{
if(theIsObserving)
{
Observer_SetMode(OBS_ROAMING);
}
else
{
Observer_SetMode(OBS_IN_EYE);
}
}
else if ( pev->iuser1 == OBS_ROAMING )
{
Observer_SetMode( OBS_IN_EYE );
}
else if ( pev->iuser1 == OBS_IN_EYE )
{
if(theIsObserving)
{
Observer_SetMode(OBS_MAP_FREE);
}
else
{
Observer_SetMode(OBS_CHASE_LOCKED);
}
}
else if ( pev->iuser1 == OBS_MAP_FREE )
{
Observer_SetMode( OBS_MAP_CHASE );
}
else
{
Observer_SetMode( OBS_CHASE_FREE ); // don't use OBS_CHASE_LOCKED anymore
}
m_flNextObserverInput = gpGlobals->time + kNextTargetInterval;
}
// Attack moves to the next player
if ( (m_afButtonPressed & IN_ATTACK) || ((m_afButtonPressed & IN_MOVERIGHT) && (pev->iuser1 != OBS_ROAMING)) )
{
if(Observer_FindNextPlayer( false ))
{
m_flNextObserverInput = gpGlobals->time + kNextTargetInterval;
}
else
{
Observer_SetMode( OBS_CHASE_FREE );
}
}
// Attack2 moves to the prev player
if ( (m_afButtonPressed & IN_ATTACK2) || ((m_afButtonPressed & IN_MOVELEFT) && (pev->iuser1 != OBS_ROAMING)) )
{
if(Observer_FindNextPlayer( true ))
{
m_flNextObserverInput = gpGlobals->time + kNextTargetInterval;
}
else
{
Observer_SetMode( OBS_CHASE_FREE );
}
}
*/
}
// Attempt to change the observer mode
void CBasePlayer::Observer_SetMode( int iMode )
{
// Limit some modes if we're not observing
AvHPlayer* thePlayer = dynamic_cast<AvHPlayer*>(this);
ASSERT(thePlayer);
bool theIsObserving = (thePlayer->GetPlayMode() == PLAYMODE_OBSERVER);
int NewMode = iMode;
float FreeSpecMode = avh_freespectatormode.value;
if (FreeSpecMode == 0 || (FreeSpecMode == 1 && !theIsObserving))
{
if (NewMode == OBS_CHASE_FREE || NewMode == OBS_ROAMING)
{
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
if (NewMode == pev->iuser1 )
return;
// Removed by mmcguire.
// is valid mode ?
if (NewMode < OBS_CHASE_LOCKED || NewMode > OBS_IN_EYE )
NewMode = OBS_IN_EYE; // now it is
// if we are not roaming, we need a valid target to track
if (NewMode != OBS_ROAMING)
{
Observer_FindNextPlayer();
// if we didn't find a valid target switch to roaming
if (m_hObserverTarget == NULL)
{
NewMode = OBS_ROAMING;
}
}
// set spectator mode
pev->iuser1 = NewMode;
// set target if not roaming
if (NewMode == OBS_ROAMING)
{
pev->iuser2 = 0;
if (!theIsObserving)
{
pev->health = 0; // Set dead status and block free look movement in pmove.
}
}
else
{
pev->iuser2 = ENTINDEX(m_hObserverTarget->edict());
// Make sure our target is valid (go backward then forward)
Observer_FindNextPlayer(true);
Observer_FindNextPlayer(false);
}
}