diff options
Diffstat (limited to 'game/server/tf2/tf_obj_buff_station.cpp')
| -rw-r--r-- | game/server/tf2/tf_obj_buff_station.cpp | 929 |
1 files changed, 929 insertions, 0 deletions
diff --git a/game/server/tf2/tf_obj_buff_station.cpp b/game/server/tf2/tf_obj_buff_station.cpp new file mode 100644 index 0000000..0dc6791 --- /dev/null +++ b/game/server/tf2/tf_obj_buff_station.cpp @@ -0,0 +1,929 @@ +//========= 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(); +} + + |