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.
366 lines
10 KiB
366 lines
10 KiB
//========= 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; |
|
} |
|
}
|
|
|