source-engine-2018-hl2_src/game/server/tf2/controlzone.cpp
FluorescentCIAAfricanAmerican 3bf9df6b27 1
2020-04-22 12:56:21 -04:00

332 lines
10 KiB
C++

//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Complete definition of the ControlZone behavioral entity
//
// $NoKeywords: $
//=============================================================================//
#include "tf_shareddefs.h"
#include "cbase.h"
#include "EntityOutput.h"
#include "tf_player.h"
#include "controlzone.h"
#include "team.h"
//-----------------------------------------------------------------------------
// Purpose: Since the control zone is a data only class, force it to always be sent ( shouldn't change often so )
// bandwidth usage should be small.
// Input : **ppSendTable -
// *recipient -
// *pvs -
// clientArea -
// Output : Returns true on success, false on failure.
//-----------------------------------------------------------------------------
int CControlZone::UpdateTransmitState()
{
if ( IsEffectActive( EF_NODRAW ) )
{
return SetTransmitState( FL_EDICT_DONTSEND );
}
else
{
return SetTransmitState( FL_EDICT_ALWAYS );
}
}
IMPLEMENT_SERVERCLASS_ST(CControlZone, DT_ControlZone)
SendPropInt( SENDINFO(m_nZoneNumber), 8, SPROP_UNSIGNED ),
END_SEND_TABLE()
LINK_ENTITY_TO_CLASS( trigger_controlzone, CControlZone);
BEGIN_DATADESC( CControlZone )
// outputs
DEFINE_OUTPUT( m_ControllingTeam, "ControllingTeam" ),
// inputs
DEFINE_INPUTFUNC( FIELD_VOID, "SetTeam", InputSetTeam ),
DEFINE_INPUTFUNC( FIELD_VOID, "LockTeam", InputLockControllingTeam ),
// keys
DEFINE_KEYFIELD_NOT_SAVED( m_iLockAfterChange, FIELD_INTEGER, "LockAfterChange" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillCaptured, FIELD_FLOAT, "UncontestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_flTimeTillContested, FIELD_FLOAT, "ContestedTime" ),
DEFINE_KEYFIELD_NOT_SAVED( m_nZoneNumber, FIELD_INTEGER, "ZoneNumber" ),
END_DATADESC()
// Control Zone Ent Flags
#define CZF_DONT_USE_TOUCHES 1
//-----------------------------------------------------------------------------
// Purpose: Initializes the control zone
// Records who was the original controlling team (for control locking)
//-----------------------------------------------------------------------------
void CControlZone::Spawn( void )
{
// set the starting controlling team
m_ControllingTeam.Set( GetTeamNumber(), this, this );
// remember who the original controlling team was (for control locking)
m_iDefendingTeam = GetTeamNumber();
// Solid
SetSolid( SOLID_BSP );
AddSolidFlags( FSOLID_TRIGGER );
SetMoveType( MOVETYPE_NONE );
SetModel( STRING( GetModelName() ) ); // set size and link into world
// TF2 rules
m_flTimeTillContested = 10.0; // Go to contested 10 seconds after enemies enter the zone
m_flTimeTillCaptured = 5.0; // Go to captured state as soon as only one team holds the zone
if ( m_nZoneNumber == 0 )
{
Warning( "Warning, trigger_controlzone without Zone Number set\n" );
}
m_ZonePlayerList.Purge();
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has entered the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::StartTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.AddToTail( hHandle );
ReevaluateControllingTeam();
// Set this player's current zone to this zone
pl->SetCurrentZone( this );
}
//-----------------------------------------------------------------------------
// Purpose: Records that a player has left the zone, and updates it's state
// according, maybe starting to change team.
// Input : *pOther - the entity that left the zone
//-----------------------------------------------------------------------------
void CControlZone::EndTouch( CBaseEntity *pOther )
{
CBaseTFPlayer *pl = ToBaseTFPlayer( pOther );
if ( !pl )
return;
CHandle< CBaseTFPlayer > hHandle;
hHandle = pl;
m_ZonePlayerList.FindAndRemove( hHandle );
ReevaluateControllingTeam();
// Unset this player's current zone if it's this one
if ( pl->GetCurrentZone() == this )
pl->SetCurrentZone( NULL );
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if it's time to change controllers
//-----------------------------------------------------------------------------
void CControlZone::ReevaluateControllingTeam( void )
{
// Count the number of players in each team
int i;
memset( m_iPlayersInZone, 0, sizeof( m_iPlayersInZone ) );
for ( i = 0; i < m_ZonePlayerList.Size(); i++ )
{
if ( m_ZonePlayerList[i] != NULL && (m_ZonePlayerList[i]->GetTeamNumber() > 0) )
{
m_iPlayersInZone[ m_ZonePlayerList[i]->GetTeamNumber() ] += 1;
}
}
// Abort immediately if we're not using touches to changes teams
if ( HasSpawnFlags( CZF_DONT_USE_TOUCHES ) )
return;
// if we're locked in place, no changes can occur to controlling team except through an explicit map ResetTeam
if ( m_iLocked )
return;
bool foundAnyTeam = false;
int teamFound = 0;
// check to see if any teams have no players
for ( i = 0; i < GetNumberOfTeams(); i++ )
{
if ( m_iPlayersInZone[i] )
{
if ( foundAnyTeam )
{
// we've already found a team, so it's being contested;
teamFound = ZONE_CONTESTED;
break;
}
foundAnyTeam = true;
teamFound = i;
}
}
// no one in the area!
if ( teamFound == 0 )
{
// just leave it as it is, let it continue to change team
// exception: if the zone state is contested, and there aren't any players in the zone,
// just return to the team who used to own the zone.
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ChangeTeam(m_iDefendingTeam);
SetControllingTeam( this, m_iDefendingTeam );
}
return;
}
// if it's the same controlling team, don't worry about it
if ( teamFound == GetTeamNumber() )
{
// the right team is in control, don't even think of switching
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
return;
}
// Find out if the zone isn't owned by anyone at all (hasn't been touched since the map started, and it started un-owned)
bool bHasBeenOwned = true;
if ( m_iDefendingTeam == 0 && GetTeamNumber() == 0 )
bHasBeenOwned = false;
// if it's not contested, always go to contested mode
if ( GetTeamNumber() != ZONE_CONTESTED && teamFound != GetTeamNumber() )
{
// Unowned zones are captured immediately (no contesting stage)
if ( bHasBeenOwned )
teamFound = ZONE_CONTESTED;
}
// if it's the team we're trying to change to, don't worry about it
if ( teamFound == m_iTryingToChangeToTeam )
return;
// set up the time to change to the new team soon
m_iTryingToChangeToTeam = teamFound;
// changing from contested->uncontested and visa-versa have different delays
if ( m_iTryingToChangeToTeam != ZONE_CONTESTED )
{
if ( !bHasBeenOwned )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d NOW\n", GetDebugName(), m_iTryingToChangeToTeam );
SetNextThink( gpGlobals->curtime + 0.1f );
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to %d in %.2f seconds\n", GetDebugName(), m_iTryingToChangeToTeam, m_flTimeTillCaptured );
SetNextThink( gpGlobals->curtime + m_flTimeTillCaptured );
}
}
else
{
DevMsg( 1, "trigger_controlzone: (%s) changing to contested in %f seconds\n", GetDebugName(), m_flTimeTillContested );
SetNextThink( gpGlobals->curtime + m_flTimeTillContested );
}
}
//-----------------------------------------------------------------------------
// Purpose: Checks to see if an uncontested territory is ready to change state
// to the new controlling team.
//-----------------------------------------------------------------------------
void CControlZone::Think( void )
{
if ( m_iTryingToChangeToTeam != 0 )
{
// held zone long enough
SetControllingTeam( this, m_iTryingToChangeToTeam );
// lock against further change if set
if ( m_iLockAfterChange )
{
LockControllingTeam();
}
// Re-evaluate controlling team if we were changing to Contested (enemy may have withdrawn)
if ( GetTeamNumber() == ZONE_CONTESTED )
{
ReevaluateControllingTeam();
}
}
}
//-----------------------------------------------------------------------------
// Purpose: set it so the team can no longer change, until a set controlling team action occurs
//-----------------------------------------------------------------------------
void CControlZone::InputLockControllingTeam( inputdata_t &inputdata )
{
LockControllingTeam();
}
//-----------------------------------------------------------------------------
// Purpose: Input handler that sets the controlling team to the activator's team.
//-----------------------------------------------------------------------------
void CControlZone::InputSetTeam( inputdata_t &inputdata )
{
// Abort if it's already the defending team
if ( inputdata.pActivator->GetTeamNumber() == GetTeamNumber() )
return;
// set the new team
ChangeTeam(inputdata.pActivator->GetTeamNumber());
SetControllingTeam( inputdata.pActivator, GetTeamNumber() );
}
//-----------------------------------------------------------------------------
// Purpose: Changes the team controlling this zone
// Input : newTeam - the new team to change to
//-----------------------------------------------------------------------------
void CControlZone::SetControllingTeam( CBaseEntity *pActivator, int newTeam )
{
DevMsg( 1, "trigger_controlzone: (%s) changing team to: %d\n", GetDebugName(), newTeam );
// remember this team as the defenders of the zone
m_iDefendingTeam = GetTeamNumber();
// reset state, firing the output
ChangeTeam(newTeam);
m_ControllingTeam.Set( GetTeamNumber(), pActivator, this );
m_iLocked = FALSE;
m_iTryingToChangeToTeam = 0;
SetNextThink( TICK_NEVER_THINK );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CControlZone::LockControllingTeam( void )
{
// never lock a zone in contested mode
if ( GetTeamNumber() == ZONE_CONTESTED )
return;
// zones never lock to the defenders
if ( GetTeamNumber() == m_iDefendingTeam )
return;
m_iLocked = TRUE;
}