source-engine/game/server/tf2/tf_obj_powerpack.cpp

367 lines
10 KiB
C++
Raw Normal View History

2020-04-22 16:56:21 +00:00
//========= Copyright Valve Corporation, All rights reserved. ============//
//
// Purpose: Human's power pack
//
// $NoKeywords: $
//=============================================================================//
#include "cbase.h"
#include "tf_player.h"
#include "tf_team.h"
#include "tf_obj.h"
#include "tf_obj_powerpack.h"
#include "tf_func_resource.h"
#include "resource_chunk.h"
#include "techtree.h"
#include "sendproxy.h"
#include "vstdlib/random.h"
#include "tf_stats.h"
#include "rope.h"
#include "tf_shareddefs.h"
#include "VGuiScreen.h"
#include "hierarchy.h"
#define POWERPACK_MODEL "models/objects/human_obj_powerpack.mdl"
#define POWERPACK_ASSEMBLING_MODEL "models/objects/human_obj_powerpack_build.mdl"
IMPLEMENT_SERVERCLASS_ST( CObjectPowerPack, DT_ObjectPowerPack )
SendPropInt( SENDINFO(m_iObjectsAttached), 3, SPROP_UNSIGNED ),
END_SEND_TABLE();
LINK_ENTITY_TO_CLASS(obj_powerpack, CObjectPowerPack);
PRECACHE_REGISTER(obj_powerpack);
ConVar obj_powerpack_health( "obj_powerpack_health","100", FCVAR_NONE, "Human powerpack health" );
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
CObjectPowerPack::CObjectPowerPack()
{
UseClientSideAnimation();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectPowerPack::Spawn( void )
{
SetModel( POWERPACK_MODEL );
SetSolid( SOLID_BBOX );
UTIL_SetSize(this, POWERPACK_MINS, POWERPACK_MAXS);
m_iHealth = obj_powerpack_health.GetInt();
m_fObjectFlags |= OF_DOESNT_NEED_POWER;
SetType( OBJ_POWERPACK );
m_hPoweredObjects.Purge();
m_iFreeAttachments = 0;
m_iObjectsAttached = 0;
BaseClass::Spawn();
}
//-----------------------------------------------------------------------------
// Gets info about the control panels
//-----------------------------------------------------------------------------
void CObjectPowerPack::GetControlPanelInfo( int nPanelIndex, const char *&pPanelName )
{
pPanelName = "screen_obj_power_pack";
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectPowerPack::FinishedBuilding( void )
{
BaseClass::FinishedBuilding();
// Now tell all our objects we connected to, during placement, that they're really getting power
// Walk backwards, because we might remove objects from our list that have somehow gained power
// inbetween the time we placed and the time we finished building.
int iSize = m_hPoweredObjects.Count();
for (int i = iSize-1; i >= 0; i--)
{
if ( m_hPoweredObjects[i] )
{
if ( m_hPoweredObjects[i]->IsPowered() )
{
UnPowerObject( m_hPoweredObjects[i] );
}
else
{
m_hPoweredObjects[i]->SetPowerPack( this );
}
}
}
PowerNearbyObjects();
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectPowerPack::Precache()
{
BaseClass::Precache();
PrecacheModel( POWERPACK_MODEL );
PrecacheModel( POWERPACK_ASSEMBLING_MODEL );
PrecacheVGuiScreen( "screen_obj_power_pack" );
}
//-----------------------------------------------------------------------------
// Purpose:
//-----------------------------------------------------------------------------
void CObjectPowerPack::DestroyObject( void )
{
// Remove power from all my objects (backwards because list will change)
int iSize = m_hPoweredObjects.Count();
for (int i = iSize-1; i >= 0; i--)
{
if ( m_hPoweredObjects[i] )
{
UnPowerObject( m_hPoweredObjects[i] );
}
}
// Now tell all other powerpacks on this team to power nearby objects, in case they can cover for this one.
if ( GetTFTeam() )
{
GetTFTeam()->UpdatePowerpacks( this, NULL );
}
BaseClass::DestroyObject();
}
//-----------------------------------------------------------------------------
// Purpose: Update power connections on the fly while placing
//-----------------------------------------------------------------------------
bool CObjectPowerPack::CalculatePlacement( CBaseTFPlayer *pPlayer )
{
bool bReturn = BaseClass::CalculatePlacement( pPlayer );
// First, disconnect any connections that should break
int iSize = m_hPoweredObjects.Count();
for (int i = iSize-1; i >= 0; i--)
{
if ( m_hPoweredObjects[i] )
{
EnsureObjectPower( m_hPoweredObjects[i] );
}
}
// If we have any spare connections, look for nearby objects to power
if ( m_hPoweredObjects.Count() < MAX_OBJECTS_PER_PACK )
{
PowerNearbyObjects( NULL, true );
}
return bReturn;
}
//-----------------------------------------------------------------------------
// Purpose: Find nearby objects and provide them with power
//-----------------------------------------------------------------------------
void CObjectPowerPack::PowerNearbyObjects( CBaseObject *pObjectToTarget, bool bPlacing )
{
if ( !GetTFTeam() )
return;
// Am I ready to power anything?
if ( IsBuilding() || (!bPlacing && IsPlacing()) )
return;
// Am I already full?
if ( m_hPoweredObjects.Count() >= MAX_OBJECTS_PER_PACK )
return;
// Do we have a specific target?
if ( pObjectToTarget )
{
if ( !pObjectToTarget->CanPowerupNow(POWERUP_POWER) )
return;
if ( IsWithinPowerRange( pObjectToTarget ) )
{
PowerObject( pObjectToTarget );
}
}
else
{
// Find nearby objects
for ( int i = 0; i < GetTFTeam()->GetNumObjects(); i++ )
{
CBaseObject *pObject = GetTFTeam()->GetObject(i);
assert(pObject);
if ( pObject == this || !pObject->CanPowerupNow(POWERUP_POWER) )
continue;
// We might be rechecking our power because one of our own objects is dying.
// Make sure we don't re-attach the sucker.
if ( pObject->IsDying() )
continue;
// Make sure it's within range
if ( IsWithinPowerRange( pObject ) )
{
PowerObject( pObject, bPlacing );
}
// Am I now full?
if ( m_hPoweredObjects.Count() >= MAX_OBJECTS_PER_PACK )
break;
}
}
}
//-----------------------------------------------------------------------------
// Purpose: Provide power to the specified object
//-----------------------------------------------------------------------------
void CObjectPowerPack::PowerObject( CBaseObject *pObject, bool bPlacing )
{
// Make sure we're not already powering it
ObjectHandle hObject;
hObject = pObject;
if ( m_hPoweredObjects.Find( hObject ) != m_hPoweredObjects.InvalidIndex() )
return;
// Add it to our list
m_hPoweredObjects.AddToTail( hObject );
m_iObjectsAttached = m_hPoweredObjects.Count();
// Find a free attachment point
int iPoint = 1;
for ( int i = 0; i < MAX_OBJECTS_PER_PACK; i++ )
{
if ( !(m_iFreeAttachments & (1<<i)) )
{
m_iFreeAttachments |= (1<<i);
iPoint = i+1;
break;
}
}
// Lookup the attachment point...
int nAttachmentIndex = pObject->LookupAttachment("powerpoint");
if (nAttachmentIndex < 0)
nAttachmentIndex = 1;
// FIXME: Cache these off
char sAttachment[32];
Q_snprintf( sAttachment,sizeof(sAttachment), "cablepoint%d", iPoint );
int nLocalAttachment = LookupAttachment( sAttachment );
if ( nLocalAttachment > 0 )
{
// Throw a cable to it
CRopeKeyframe *pRope = ConnectCableTo( pObject, nLocalAttachment, nAttachmentIndex );
if ( pRope )
{
pRope->SetMaterial( "cable/human_powercable.vmt" );
}
}
// If we're placing only, don't tell it we're supplying power yet
if ( IsPlacing() )
return;
pObject->SetPowerPack( this );
}
//-----------------------------------------------------------------------------
// Purpose: Remove power to the specified object
//-----------------------------------------------------------------------------
void CObjectPowerPack::UnPowerObject( CBaseObject *pObject )
{
// Make sure it's in our list
ObjectHandle hObject;
hObject = pObject;
if ( m_hPoweredObjects.Find( hObject ) == m_hPoweredObjects.InvalidIndex() )
return;
// Remove it from our list
m_hPoweredObjects.FindAndRemove( hObject );
m_iObjectsAttached = m_hPoweredObjects.Count();
// Remove our cable to it
for ( int i = 0; i < m_aRopes.Count(); i++ )
{
if ( (m_aRopes[i] != NULL) && (m_aRopes[i]->GetEndPoint() == pObject) )
{
// Free up the attachment point
m_iFreeAttachments &= ~(1 << (m_aRopes[i]->GetEndAttachment()-1));
UTIL_Remove( m_aRopes[i] );
m_aRopes.Remove(i);
break;
}
}
// Tell the object that it has lost power
if ( pObject->GetPowerPack() == this )
{
pObject->SetPowerPack( NULL );
}
// If I'm not dying, immediately look for other things to power
if ( !IsDying() )
{
PowerNearbyObjects();
}
}
//-----------------------------------------------------------------------------
// Purpose: Make sure the specified object is still within powering range
//-----------------------------------------------------------------------------
void CObjectPowerPack::EnsureObjectPower( CBaseObject *pObject )
{
if ( IsWithinPowerRange( pObject ) )
return;
// It's obscured, or out of range. Remove it.
UnPowerObject( pObject );
}
//-----------------------------------------------------------------------------
// Purpose: Return true if this object is powerable
//-----------------------------------------------------------------------------
bool CObjectPowerPack::IsWithinPowerRange( CBaseObject *pObject )
{
// If this powerpack is built on an attachment, it'll only power objects in the same hierarchy
if ( GetParentObject() )
{
if ( GetRootMoveParent() != pObject->GetRootMoveParent() )
return false;
}
if ( (pObject->GetAbsOrigin() - GetAbsOrigin()).LengthSqr() < POWERPACK_RANGE )
{
// Can I see it?
// Ignore things we're attached to
trace_t tr;
CTraceFilterWorldAndPropsOnly powerFilter;
UTIL_TraceLine( WorldSpaceCenter(), pObject->WorldSpaceCenter(), MASK_SOLID_BRUSHONLY, &powerFilter, &tr );
CBaseEntity *pEntity = tr.m_pEnt;
if ( (tr.fraction == 1.0) || ( pEntity == pObject ) )
return true;
}
return false;
}
//-----------------------------------------------------------------------------
// Purpose:
// Input : act -
//-----------------------------------------------------------------------------
void CObjectPowerPack::OnActivityChanged( Activity act )
{
BaseClass::OnActivityChanged( act );
switch ( act )
{
case ACT_OBJ_ASSEMBLING:
SetModel( POWERPACK_ASSEMBLING_MODEL );
break;
default:
SetModel( POWERPACK_MODEL );
break;
}
}