mirror of
https://github.com/nillerusr/source-engine.git
synced 2024-12-24 23:26:50 +00:00
930 lines
29 KiB
C++
930 lines
29 KiB
C++
//========= 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();
|
|
}
|
|
|
|
|