summaryrefslogtreecommitdiff
path: root/game/server/tf2/tf_obj_powerpack.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf2/tf_obj_powerpack.cpp')
-rw-r--r--game/server/tf2/tf_obj_powerpack.cpp366
1 files changed, 366 insertions, 0 deletions
diff --git a/game/server/tf2/tf_obj_powerpack.cpp b/game/server/tf2/tf_obj_powerpack.cpp
new file mode 100644
index 0000000..5ab64d6
--- /dev/null
+++ b/game/server/tf2/tf_obj_powerpack.cpp
@@ -0,0 +1,366 @@
+//========= 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;
+ }
+}