You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
929 lines
29 KiB
929 lines
29 KiB
//========= Copyright Valve Corporation, All rights reserved. ============// |
|
// |
|
// Purpose: Medic's portable power generator |
|
// |
|
//=============================================================================// |
|
|
|
#include "cbase.h" |
|
#include "tf_obj_buff_station.h" |
|
#include "tf_player.h" |
|
#include "rope.h" |
|
#include "rope_shared.h" |
|
#include "entitylist.h" |
|
#include "VGuiScreen.h" |
|
#include "engine/IEngineSound.h" |
|
#include "tf_team.h" |
|
|
|
//============================================================================= |
|
// |
|
// Console Variables |
|
// |
|
static ConVar obj_buff_station_damage_modifier( "obj_buff_station_damage_modifier", "1.5", 0, "Scales the damage a player does while connected to the buff station." ); |
|
static ConVar obj_buff_station_heal_rate( "obj_buff_station_heal_rate", "10" ); |
|
static ConVar obj_buff_station_range( "obj_buff_station_range", "300" ); |
|
static ConVar obj_buff_station_obj_range( "obj_buff_station_obj_range", "800" ); |
|
static ConVar obj_buff_station_health( "obj_buff_station_health","100", FCVAR_NONE, "Buff Station health" ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Buff Station defines |
|
//----------------------------------------------------------------------------- |
|
#define BUFF_STATION_MINS Vector( -30, -30, 0 ) |
|
#define BUFF_STATION_MAXS Vector( 30, 30, 50 ) |
|
|
|
#define BUFF_STATION_HUMAN_MODEL "models/objects/human_obj_buffstation.mdl" |
|
#define BUFF_STATION_HUMAN_ASSEMBLING_MODEL "models/objects/human_obj_buffstation_build.mdl" |
|
#define BUFF_STATION_ALIEN_MODEL "models/objects/alien_obj_buffstation.mdl" |
|
#define BUFF_STATION_ALIEN_ASSEMBLING_MODEL "models/objects/alien_obj_buffstation_build.mdl" |
|
|
|
#define BUFF_STATION_VGUI_SCREEN "screen_obj_buffstation" |
|
|
|
#define BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT "BoostPlayerThink" |
|
#define BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT "BoostObjectThink" |
|
#define BUFF_STATION_BOOST_PLAYER_THINK_INTERVAL 0.1f |
|
#define BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL 2.0f |
|
|
|
#define BUFF_STATION_BUFF_RANGE ( 600 * 600 ) |
|
|
|
//============================================================================= |
|
// |
|
// Data Description |
|
// |
|
BEGIN_DATADESC( CObjectBuffStation ) |
|
DEFINE_INPUTFUNC( FIELD_VOID, "PlayerSpawned", InputPlayerSpawned ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "PlayerAttachedToGenerator", InputPlayerAttachedToGenerator ), |
|
DEFINE_INPUTFUNC( FIELD_VOID, "PlayerEnteredVehicle", InputPlayerSpawned ), // NJS: Detach player from buff pack. |
|
END_DATADESC() |
|
|
|
//============================================================================= |
|
// |
|
// Server Class |
|
// |
|
IMPLEMENT_SERVERCLASS_ST( CObjectBuffStation, DT_ObjectBuffStation ) |
|
SendPropInt( SENDINFO( m_nPlayerCount ), BUFF_STATION_MAX_PLAYER_BITS, SPROP_UNSIGNED ), |
|
SendPropArray( SendPropEHandle( SENDINFO_ARRAY( m_hPlayers ) ), m_hPlayers ), |
|
SendPropInt( SENDINFO( m_nObjectCount ), BUFF_STATION_MAX_OBJECT_BITS, SPROP_UNSIGNED ), |
|
SendPropArray( SendPropEHandle( SENDINFO_ARRAY( m_hObjects ) ), m_hObjects ), |
|
END_SEND_TABLE() |
|
|
|
//============================================================================= |
|
// |
|
// Linking and Precache |
|
// |
|
LINK_ENTITY_TO_CLASS( obj_buff_station, CObjectBuffStation ); |
|
PRECACHE_REGISTER( obj_buff_station ); |
|
|
|
// Backwards compatability... |
|
LINK_ENTITY_TO_CLASS( obj_portable_power_generator, CObjectBuffStation ); |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Constructor |
|
//----------------------------------------------------------------------------- |
|
CObjectBuffStation::CObjectBuffStation() |
|
{ |
|
// Verify networking data. |
|
COMPILE_TIME_ASSERT( BUFF_STATION_MAX_PLAYERS < ( 1 << BUFF_STATION_MAX_PLAYER_BITS ) ); |
|
COMPILE_TIME_ASSERT( BUFF_STATION_MAX_PLAYERS >= ( 1 << ( BUFF_STATION_MAX_PLAYER_BITS - 1 ) ) ); |
|
|
|
COMPILE_TIME_ASSERT( BUFF_STATION_MAX_OBJECTS < ( 1 << BUFF_STATION_MAX_OBJECT_BITS ) ); |
|
COMPILE_TIME_ASSERT( BUFF_STATION_MAX_OBJECTS >= ( 1 << ( BUFF_STATION_MAX_OBJECT_BITS - 1 ) ) ); |
|
|
|
// Uses the client-side animation system. |
|
UseClientSideAnimation(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Spawn |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::Spawn() |
|
{ |
|
// This must be set before calling the base class spawn. |
|
m_iHealth = obj_buff_station_health.GetInt(); |
|
|
|
BaseClass::Spawn(); |
|
|
|
SetModel( BUFF_STATION_HUMAN_MODEL ); |
|
SetSolid( SOLID_BBOX ); |
|
|
|
SetType( OBJ_BUFF_STATION ); |
|
UTIL_SetSize( this, BUFF_STATION_MINS, BUFF_STATION_MAXS ); |
|
|
|
m_takedamage = DAMAGE_YES; |
|
|
|
// Initialize buff station attachment data. |
|
InitAttachmentData(); |
|
|
|
// Thinking |
|
SetContextThink( BoostPlayerThink, 1.0f, BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT ); |
|
SetContextThink( BoostObjectThink, 2.0f, BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT ); |
|
|
|
m_bBuilding = false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Precache model, vgui elements, and sound. |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::Precache() |
|
{ |
|
// Models |
|
PrecacheModel( BUFF_STATION_HUMAN_MODEL ); |
|
PrecacheModel( BUFF_STATION_ALIEN_MODEL ); |
|
|
|
// Build models |
|
PrecacheModel( BUFF_STATION_HUMAN_ASSEMBLING_MODEL ); |
|
PrecacheModel( BUFF_STATION_ALIEN_ASSEMBLING_MODEL ); |
|
|
|
// VGUI Screen |
|
PrecacheVGuiScreen( BUFF_STATION_VGUI_SCREEN ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::SetupTeamModel( void ) |
|
{ |
|
if ( GetTeamNumber() == TEAM_HUMANS ) |
|
{ |
|
if ( m_bBuilding ) |
|
{ |
|
SetModel( BUFF_STATION_HUMAN_ASSEMBLING_MODEL ); |
|
} |
|
else |
|
{ |
|
SetModel( BUFF_STATION_HUMAN_MODEL ); |
|
} |
|
} |
|
else |
|
{ |
|
if ( m_bBuilding ) |
|
{ |
|
SetModel( BUFF_STATION_ALIEN_ASSEMBLING_MODEL ); |
|
} |
|
else |
|
{ |
|
SetModel( BUFF_STATION_ALIEN_MODEL ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Gets info about the control panels |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::GetControlPanelInfo( int nControlPanelIndex, const char *&pPanelName ) |
|
{ |
|
pPanelName = BUFF_STATION_VGUI_SCREEN; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Remove this object from it's team and mark it for deletion. |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DestroyObject( void ) |
|
{ |
|
// Detach all players. |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
DetachPlayerByIndex( iPlayer ); |
|
} |
|
|
|
// Detach all objects. |
|
for( int iObject = m_nObjectCount - 1; iObject >= 0; --iObject ) |
|
{ |
|
DetachObjectByIndex( iObject ); |
|
} |
|
|
|
// Inform all other buff stations on this team to attempt to power object (cover for this one). |
|
if ( GetTFTeam() ) |
|
{ |
|
GetTFTeam()->UpdateBuffStations( this, NULL, false ); |
|
} |
|
|
|
// We shouldn't get any more messages |
|
g_pNotify->ClearEntity( this ); |
|
BaseClass::DestroyObject(); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::OnGoInactive( void ) |
|
{ |
|
BaseClass::OnGoInactive(); |
|
|
|
// Detach all players. |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
CBaseTFPlayer *pPlayer = m_hPlayers[iPlayer].Get(); |
|
if ( pPlayer ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTCENTER, "Lost power to Buff Station!" ); |
|
} |
|
|
|
DetachPlayerByIndex( iPlayer ); |
|
} |
|
|
|
// Detach all objects. |
|
for ( int iObject = m_nObjectCount - 1; iObject >= 0; --iObject ) |
|
{ |
|
DetachObjectByIndex( iObject ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attach to players who touch me |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value ) |
|
{ |
|
if ( useType == USE_ON ) |
|
{ |
|
// See if the activator is a player |
|
if ( !pActivator->IsPlayer() || !InSameTeam( pActivator ) || !pActivator->CanBePoweredUp() ) |
|
return; |
|
|
|
CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>(pActivator); |
|
if ( pPlayer ) |
|
{ |
|
UpdatePlayerAttachment( pPlayer ); |
|
} |
|
} |
|
|
|
BaseClass::Use( pActivator, pCaller, useType, value ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Handle commands sent from vgui panels on the client |
|
//----------------------------------------------------------------------------- |
|
bool CObjectBuffStation::ClientCommand( CBaseTFPlayer *pPlayer, const char *pCmd, ICommandArguments *pArg ) |
|
{ |
|
if ( FStrEq( pCmd, "toggle_connect" ) ) |
|
{ |
|
UpdatePlayerAttachment( pPlayer ); |
|
return true; |
|
} |
|
|
|
return BaseClass::ClientCommand( pPlayer, pCmd, pArg ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::InitAttachmentData( void ) |
|
{ |
|
// Initialize the attachment data. |
|
char szAttachName[13]; |
|
|
|
m_nPlayerCount = 0; |
|
Q_strncpy( szAttachName, "playercable1", 13 ); |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; ++iPlayer ) |
|
{ |
|
m_hPlayers.Set( iPlayer, NULL ); |
|
|
|
szAttachName[11] = '1' + iPlayer; |
|
m_aPlayerAttachInfo[iPlayer].m_iAttachPoint = LookupAttachment( szAttachName ); |
|
} |
|
|
|
m_nObjectCount = 0; |
|
Q_strncpy( szAttachName, "objectcable1", 13 ); |
|
for ( int iObject = 0; iObject < BUFF_STATION_MAX_OBJECTS; ++iObject ) |
|
{ |
|
m_hObjects.Set( iObject, NULL ); |
|
|
|
szAttachName[11] = '1' + iObject; |
|
m_aObjectAttachInfo[iObject].m_iAttachPoint = LookupAttachment( szAttachName ); |
|
} |
|
} |
|
|
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create "Buff Station" cable (rope). |
|
//----------------------------------------------------------------------------- |
|
CRopeKeyframe *CObjectBuffStation::CreateRope( CBaseTFPlayer *pPlayer, int iAttachPoint ) |
|
{ |
|
CRopeKeyframe *pRope = CRopeKeyframe::Create( this, pPlayer, iAttachPoint, 0 ); |
|
if ( pRope ) |
|
{ |
|
pRope->m_RopeLength = obj_buff_station_range.GetFloat(); |
|
pRope->m_Slack = 0.0f; |
|
pRope->m_Width = 2; |
|
pRope->m_nSegments = ROPE_MAX_SEGMENTS; |
|
pRope->m_RopeFlags |= ROPE_COLLIDE; |
|
pRope->EnablePlayerWeaponAttach( true ); |
|
pRope->ActivateStartDirectionConstraints( true ); |
|
if ( GetTeamNumber() == TEAM_HUMANS ) |
|
{ |
|
pRope->SetMaterial( "cable/human_buffcable.vmt" ); |
|
} |
|
else |
|
{ |
|
pRope->SetMaterial( "cable/alien_buffcable.vmt" ); |
|
} |
|
} |
|
|
|
return pRope; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Create "Buff Station" cable (rope). |
|
//----------------------------------------------------------------------------- |
|
CRopeKeyframe *CObjectBuffStation::CreateRope( CBaseObject *pObject, int iAttachPoint, int iObjectAttachPoint ) |
|
{ |
|
CRopeKeyframe *pRope = CRopeKeyframe::Create( this, pObject, iAttachPoint, iObjectAttachPoint ); |
|
if ( pRope ) |
|
{ |
|
pRope->m_RopeLength = obj_buff_station_obj_range.GetFloat(); |
|
pRope->m_Slack = 0.0f; |
|
pRope->m_Width = 2; |
|
pRope->m_nSegments = ROPE_MAX_SEGMENTS; |
|
pRope->m_RopeFlags |= ROPE_COLLIDE; |
|
pRope->EnablePlayerWeaponAttach( true ); |
|
pRope->ActivateStartDirectionConstraints( true ); |
|
if ( GetTeamNumber() == TEAM_HUMANS ) |
|
{ |
|
pRope->SetMaterial( "cable/human_buffcable.vmt" ); |
|
} |
|
else |
|
{ |
|
pRope->SetMaterial( "cable/alien_buffcable.vmt" ); |
|
} |
|
} |
|
|
|
return pRope; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Is a particular player attached? |
|
//----------------------------------------------------------------------------- |
|
bool CObjectBuffStation::IsPlayerAttached( CBaseTFPlayer *pPlayer ) |
|
{ |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
if ( m_hPlayers[iPlayer].Get() == pPlayer ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Is a particular object attached? |
|
//----------------------------------------------------------------------------- |
|
bool CObjectBuffStation::IsObjectAttached( CBaseObject *pObject ) |
|
{ |
|
for ( int iObject = 0; iObject < m_nObjectCount; ++iObject ) |
|
{ |
|
if ( m_hObjects[iObject].Get() == pObject ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attach the player to the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::AttachPlayer( CBaseTFPlayer *pPlayer ) |
|
{ |
|
// Player shouldn't already be attached. |
|
Assert( !IsPlayerAttached( pPlayer ) ); |
|
|
|
// Check to see if the player is alive and on the correct team. |
|
if ( !pPlayer->IsAlive() || !pPlayer->InSameTeam( this ) ) |
|
return; |
|
|
|
// Check attachment availability. |
|
if ( m_nPlayerCount == BUFF_STATION_MAX_PLAYERS ) |
|
{ |
|
// Unless the player is the owner he cannot connect. |
|
if ( pPlayer != GetOwner() ) |
|
return; |
|
|
|
// Kick a non-owning player off. |
|
DetachPlayerByIndex( BUFF_STATION_MAX_PLAYERS - 1 ); |
|
} |
|
|
|
// This will disconnect the player from other Buff Stations, and keep track of important player events. |
|
g_pNotify->ReportNamedEvent( pPlayer, "PlayerAttachedToGenerator" ); |
|
g_pNotify->AddEntity( this, pPlayer ); |
|
|
|
// Connect player. |
|
// Find the nearest empty slot |
|
int iNearest = BUFF_STATION_MAX_PLAYERS; |
|
float flNearestDist = 9999*9999; |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
if ( !m_hPlayers[iPlayer] ) |
|
{ |
|
Vector vecPoint; |
|
QAngle angPoint; |
|
GetAttachment( m_aPlayerAttachInfo[iPlayer].m_iAttachPoint, vecPoint, angPoint ); |
|
float flDistance = ( vecPoint - pPlayer->GetAbsOrigin() ).LengthSqr(); |
|
if ( flDistance < flNearestDist ) |
|
{ |
|
flNearestDist = flDistance; |
|
iNearest = iPlayer; |
|
} |
|
} |
|
} |
|
Assert( iNearest != BUFF_STATION_MAX_PLAYERS ); |
|
|
|
m_hPlayers.Set( iNearest, pPlayer ); |
|
m_aPlayerAttachInfo[iNearest].m_DamageModifier.SetModifier( obj_buff_station_damage_modifier.GetFloat() ); |
|
m_aPlayerAttachInfo[iNearest].m_hRope = CreateRope( pPlayer, m_aPlayerAttachInfo[iNearest].m_iAttachPoint ); |
|
m_nPlayerCount++; |
|
|
|
// Tell the player to constrain his movement. |
|
pPlayer->ActivateMovementConstraint( this, GetAbsOrigin(), obj_buff_station_range.GetFloat(), 75.0f, 0.15f ); |
|
|
|
// Update think. |
|
if ( GetNextThink(BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT) > gpGlobals->curtime + BUFF_STATION_BOOST_PLAYER_THINK_INTERVAL ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + BUFF_STATION_BOOST_PLAYER_THINK_INTERVAL, BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Detach the player from the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DetachPlayer( CBaseTFPlayer *pPlayer ) |
|
{ |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
if ( m_hPlayers[iPlayer].Get() == pPlayer ) |
|
{ |
|
DetachPlayerByIndex( iPlayer ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Detach the player from the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DetachPlayerByIndex( int nIndex ) |
|
{ |
|
// Valid index? |
|
Assert( nIndex < BUFF_STATION_MAX_PLAYERS ); |
|
|
|
// Get the player. |
|
CBaseTFPlayer *pPlayer = m_hPlayers[nIndex].Get(); |
|
if ( !pPlayer ) |
|
{ |
|
m_hPlayers.Set( nIndex, NULL ); |
|
return; |
|
} |
|
|
|
// Remove the damage modifier. |
|
m_aPlayerAttachInfo[nIndex].m_DamageModifier.RemoveModifier(); |
|
|
|
// Remove the rope (cable). |
|
if ( m_aPlayerAttachInfo[nIndex].m_hRope.Get() ) |
|
{ |
|
m_aPlayerAttachInfo[nIndex].m_hRope->DetachPoint( 1 ); |
|
m_aPlayerAttachInfo[nIndex].m_hRope->DieAtNextRest(); |
|
} |
|
|
|
// Unconstrain the player movement. |
|
pPlayer->DeactivateMovementConstraint(); |
|
|
|
// Keep track of player events. |
|
g_pNotify->RemoveEntity( this, pPlayer ); |
|
|
|
// Reduce player count. |
|
m_nPlayerCount--; |
|
m_hPlayers.Set( nIndex, NULL ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Attach the object to the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::AttachObject( CBaseObject *pObject, bool bPlacing ) |
|
{ |
|
// Check to see if the object is already attached. |
|
if ( IsObjectAttached( pObject ) ) |
|
return; |
|
|
|
// Check to see if the object is on the correct team. |
|
if ( !pObject->InSameTeam( this ) ) |
|
return; |
|
|
|
// Check to see if the object is already being buffed by another station. |
|
if ( pObject->IsHookedAndBuffed() ) |
|
return; |
|
|
|
// Check attachment availability. |
|
if ( m_nObjectCount == BUFF_STATION_MAX_OBJECTS ) |
|
return; |
|
|
|
// Attach cable to object - get the attachment point. |
|
int nObjectAttachPoint = pObject->LookupAttachment( "boostpoint" ); |
|
if ( nObjectAttachPoint <= 0 ) |
|
nObjectAttachPoint = 1; |
|
|
|
// Connect object. |
|
m_hObjects.Set( m_nObjectCount, pObject ); |
|
m_aObjectAttachInfo[m_nObjectCount].m_DamageModifier.SetModifier( obj_buff_station_damage_modifier.GetFloat() ); |
|
m_aObjectAttachInfo[m_nObjectCount].m_hRope = CreateRope( pObject, m_aObjectAttachInfo[m_nObjectCount].m_iAttachPoint, nObjectAttachPoint ); |
|
m_nObjectCount += 1; |
|
|
|
// If we're placing, we're pretending to buff objects, but not really powering them |
|
pObject->SetBuffStation( this, bPlacing ); |
|
|
|
// Update think. |
|
if ( GetNextThink(BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT) > gpGlobals->curtime + BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL, BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Detach the object from the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DetachObject( CBaseObject *pObject ) |
|
{ |
|
for ( int iObject = 0; iObject < m_nObjectCount; ++iObject ) |
|
{ |
|
if ( m_hObjects[iObject].Get() == pObject ) |
|
{ |
|
DetachObjectByIndex( iObject ); |
|
return; |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Detach the object from the "Buff Station." |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DetachObjectByIndex( int nIndex ) |
|
{ |
|
// Valid index? |
|
Assert( nIndex >= 0 ); |
|
Assert( nIndex < m_nObjectCount ); |
|
|
|
// Get the object. |
|
CBaseObject *pObject = m_hObjects[nIndex].Get(); |
|
if ( !pObject ) |
|
return; |
|
|
|
// Remove the damage modifier. |
|
m_aObjectAttachInfo[nIndex].m_DamageModifier.RemoveModifier(); |
|
|
|
// Remove the rope (cable). |
|
if ( m_aObjectAttachInfo[nIndex].m_hRope.Get() ) |
|
{ |
|
m_aObjectAttachInfo[nIndex].m_hRope->DetachPoint( 1 ); |
|
m_aObjectAttachInfo[nIndex].m_hRope->DieAtNextRest(); |
|
} |
|
|
|
// Reduce object count. |
|
m_nObjectCount -= 1; |
|
|
|
// Set the object as unbuffed. |
|
pObject->SetBuffStation( NULL, false ); |
|
|
|
// If the detached object wasn't the last object in the list, swap placement. |
|
if ( nIndex != m_nObjectCount ) |
|
{ |
|
SwapObjectAttachment( nIndex ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::UpdatePlayerAttachment( CBaseTFPlayer *pPlayer ) |
|
{ |
|
// Valid player? |
|
if ( !pPlayer ) |
|
return; |
|
|
|
// Attach/Detach (toggle). |
|
if ( IsPlayerAttached( pPlayer ) ) |
|
{ |
|
DetachPlayer( pPlayer ); |
|
} |
|
else |
|
{ |
|
// Check for power, do not attach to unpowered generator. |
|
if ( !IsPowered() ) |
|
{ |
|
ClientPrint( pPlayer, HUD_PRINTCENTER, "No power source for the Buff Station!" ); |
|
} |
|
else |
|
{ |
|
AttachPlayer( pPlayer ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::SwapObjectAttachment( int nIndex ) |
|
{ |
|
bool bModifierActive = m_aObjectAttachInfo[m_nObjectCount].m_DamageModifier.GetCharacter() != NULL; |
|
m_aObjectAttachInfo[m_nObjectCount].m_DamageModifier.RemoveModifier(); |
|
|
|
m_hObjects.Set( nIndex, m_hObjects[m_nObjectCount] ); |
|
m_aObjectAttachInfo[nIndex] = m_aObjectAttachInfo[m_nObjectCount]; |
|
|
|
if ( bModifierActive ) |
|
{ |
|
m_aObjectAttachInfo[nIndex].m_DamageModifier.AddModifierToEntity( m_hObjects[nIndex].Get() ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::InputPlayerSpawned( inputdata_t &inputdata ) |
|
{ |
|
if ( inputdata.pActivator->IsPlayer() ) |
|
{ |
|
CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( inputdata.pActivator ); |
|
if ( IsPlayerAttached( pPlayer ) ) |
|
{ |
|
DetachPlayer( pPlayer ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Input handler |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::InputPlayerAttachedToGenerator( inputdata_t &inputdata ) |
|
{ |
|
if ( inputdata.pActivator->IsPlayer() ) |
|
{ |
|
CBaseTFPlayer *pPlayer = static_cast<CBaseTFPlayer*>( inputdata.pActivator ); |
|
if ( IsPlayerAttached( pPlayer ) ) |
|
{ |
|
DetachPlayer( pPlayer ); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Boost those attached to me as long as I'm not EMPed |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::BoostPlayerThink( void ) |
|
{ |
|
// Are we emped? |
|
bool bIsEmped = HasPowerup( POWERUP_EMP ); |
|
|
|
// Get range (squared = faster test). |
|
float flMaxRangeSq = obj_buff_station_range.GetFloat(); |
|
flMaxRangeSq *= flMaxRangeSq; |
|
|
|
// Boost all attached players and objects. |
|
for ( int iPlayer = 0; iPlayer < BUFF_STATION_MAX_PLAYERS; iPlayer++ ) |
|
{ |
|
// Clean up dangling pointers + dead players, subversion, disconnection |
|
CBaseTFPlayer *pPlayer = m_hPlayers[iPlayer].Get(); |
|
if ( !pPlayer || !pPlayer->IsAlive() || !InSameTeam( pPlayer ) || !pPlayer->PlayerClass() ) |
|
{ |
|
DetachPlayerByIndex( iPlayer ); |
|
continue; |
|
} |
|
|
|
// Check for out of range. |
|
float flDistSq = GetAbsOrigin().DistToSqr( pPlayer->GetAbsOrigin() ); |
|
if ( flDistSq > flMaxRangeSq ) |
|
{ |
|
DetachPlayerByIndex( iPlayer ); |
|
continue; |
|
} |
|
|
|
bool bBoosted = false; |
|
if ( !bIsEmped ) |
|
{ |
|
float flHealAmount = obj_buff_station_heal_rate.GetFloat() * BUFF_STATION_BOOST_PLAYER_THINK_INTERVAL; |
|
bBoosted = pPlayer->AttemptToPowerup( POWERUP_BOOST, 0, flHealAmount, this, &m_aPlayerAttachInfo[iPlayer].m_DamageModifier ); |
|
} |
|
|
|
if ( !bBoosted ) |
|
{ |
|
m_aPlayerAttachInfo[iPlayer].m_DamageModifier.RemoveModifier(); |
|
} |
|
} |
|
|
|
// Set next think time. |
|
if ( m_nPlayerCount > 0 ) |
|
{ |
|
SetNextThink( gpGlobals->curtime + BUFF_STATION_BOOST_PLAYER_THINK_INTERVAL, |
|
BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT ); |
|
} |
|
else |
|
{ |
|
SetNextThink( gpGlobals->curtime + 1.0f, BUFF_STATION_BOOST_PLAYER_THINK_CONTEXT ); |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::BoostObjectThink( void ) |
|
{ |
|
// Set next boost object think time. |
|
SetNextThink( gpGlobals->curtime + BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL, |
|
BUFF_STATION_BOOST_OBJECT_THINK_CONTEXT ); |
|
|
|
// If we're emped, placing, or building, we're not ready to powerup |
|
if ( IsPlacing() || IsBuilding() || HasPowerup( POWERUP_EMP ) ) |
|
return; |
|
|
|
float flMaxRangeSq = obj_buff_station_obj_range.GetFloat(); |
|
flMaxRangeSq *= flMaxRangeSq; |
|
|
|
// Boost objects. |
|
for ( int iObject = m_nObjectCount; --iObject >= 0; ) |
|
{ |
|
CBaseObject *pObject = m_hObjects[iObject].Get(); |
|
if ( !pObject || !InSameTeam( pObject ) ) |
|
{ |
|
DetachObjectByIndex( iObject ); |
|
continue; |
|
} |
|
|
|
// Check for out of range. |
|
float flDistSq = GetAbsOrigin().DistToSqr( pObject->GetAbsOrigin() ); |
|
if ( flDistSq > flMaxRangeSq ) |
|
{ |
|
DetachObjectByIndex( iObject ); |
|
continue; |
|
} |
|
|
|
// Don't powerup it until it's finished building |
|
if ( pObject->IsPlacing() || pObject->IsBuilding() ) |
|
continue; |
|
|
|
// Boost it |
|
if ( !pObject->AttemptToPowerup( POWERUP_BOOST, BUFF_STATION_BOOST_OBJECT_THINK_INTERVAL, 0, |
|
this, &m_aObjectAttachInfo[iObject].m_DamageModifier ) ) |
|
{ |
|
m_aObjectAttachInfo[iObject].m_DamageModifier.RemoveModifier(); |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::DeBuffObject( CBaseObject *pObject ) |
|
{ |
|
DetachObject( pObject ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Find nearby objects and buff them. |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::BuffNearbyObjects( CBaseObject *pObjectToTarget, bool bPlacing ) |
|
{ |
|
// ROBIN: Disabled object buffing for now |
|
return; |
|
|
|
// Check for a team. |
|
if ( !GetTFTeam() ) |
|
return; |
|
|
|
// Am I ready to power anything? |
|
if ( IsBuilding() || ( !bPlacing && IsPlacing() ) ) |
|
return; |
|
|
|
// Am I already full? |
|
if ( m_nObjectCount >= BUFF_STATION_MAX_OBJECTS ) |
|
return; |
|
|
|
// Do we have a specific target? |
|
if ( pObjectToTarget ) |
|
{ |
|
if( !pObjectToTarget->CanBeHookedToBuffStation() || pObjectToTarget->GetBuffStation() ) |
|
return; |
|
|
|
if ( IsWithinBuffRange( pObjectToTarget ) ) |
|
{ |
|
AttachObject( pObjectToTarget, bPlacing ); |
|
} |
|
} |
|
else |
|
{ |
|
// Find nearby objects |
|
for ( int iObject = 0; iObject < GetTFTeam()->GetNumObjects(); iObject++ ) |
|
{ |
|
CBaseObject *pObject = GetTFTeam()->GetObject( iObject ); |
|
assert(pObject); |
|
|
|
if ( pObject == this || !pObject->CanBeHookedToBuffStation() || pObject->GetBuffStation() ) |
|
continue; |
|
|
|
// Make sure it's within range |
|
if ( IsWithinBuffRange( pObject ) ) |
|
{ |
|
AttachObject( pObject, bPlacing ); |
|
|
|
// Am I now full? |
|
if ( m_nObjectCount >= BUFF_STATION_MAX_OBJECTS ) |
|
break; |
|
} |
|
} |
|
} |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Update buff connections on the fly while placing |
|
//----------------------------------------------------------------------------- |
|
bool CObjectBuffStation::CalculatePlacement( CBaseTFPlayer *pPlayer ) |
|
{ |
|
bool bReturn = BaseClass::CalculatePlacement( pPlayer ); |
|
|
|
// First, disconnect any connections that should break (too far away). |
|
for ( int iObject = m_nObjectCount - 1; iObject >= 0; --iObject ) |
|
{ |
|
if ( GetBuffedObject( iObject ) ) |
|
{ |
|
CheckBuffConnection( GetBuffedObject( iObject ) ); |
|
} |
|
} |
|
|
|
// If we have any spare connections, look for nearby objects to buff |
|
if ( m_nObjectCount < BUFF_STATION_MAX_OBJECTS ) |
|
{ |
|
BuffNearbyObjects( NULL, true ); |
|
} |
|
|
|
return bReturn; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::FinishedBuilding( void ) |
|
{ |
|
BaseClass::FinishedBuilding(); |
|
|
|
for( int iObject = 0; iObject < m_nObjectCount; ++iObject ) |
|
{ |
|
if ( GetBuffedObject( iObject ) ) |
|
{ |
|
GetBuffedObject( iObject )->SetBuffStation( this, false ); |
|
} |
|
} |
|
|
|
BuffNearbyObjects( NULL, false ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::CheckBuffConnection( CBaseObject *pObject ) |
|
{ |
|
if ( !pObject->CanBeHookedToBuffStation() ) |
|
return; |
|
|
|
// Check to see if the object is within the buff range. |
|
if ( IsWithinBuffRange( pObject ) ) |
|
return; |
|
|
|
// It's obscured, or out of range. Remove it. |
|
DetachObject( pObject ); |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: Return true if this object is powerable |
|
//----------------------------------------------------------------------------- |
|
bool CObjectBuffStation::IsWithinBuffRange( CBaseObject *pObject ) |
|
{ |
|
if ( ( pObject->GetAbsOrigin() - GetAbsOrigin() ).LengthSqr() < BUFF_STATION_BUFF_RANGE ) |
|
{ |
|
// Can I see it? |
|
// Ignore things we're attached to |
|
trace_t tr; |
|
CTraceFilterWorldAndPropsOnly buffFilter; |
|
UTIL_TraceLine( WorldSpaceCenter(), pObject->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, &buffFilter, &tr ); |
|
CBaseEntity *pEntity = tr.m_pEnt; |
|
if ( ( tr.fraction == 1.0 ) || ( pEntity == pObject ) ) |
|
return true; |
|
} |
|
|
|
return false; |
|
} |
|
|
|
//----------------------------------------------------------------------------- |
|
// Purpose: |
|
// Input : act - |
|
//----------------------------------------------------------------------------- |
|
void CObjectBuffStation::OnActivityChanged( Activity act ) |
|
{ |
|
BaseClass::OnActivityChanged( act ); |
|
|
|
switch ( act ) |
|
{ |
|
case ACT_OBJ_ASSEMBLING: |
|
m_bBuilding = true; |
|
break; |
|
default: |
|
m_bBuilding = false; |
|
break; |
|
} |
|
|
|
SetupTeamModel(); |
|
} |
|
|
|
|
|
|