summaryrefslogtreecommitdiff
path: root/game/server/tf2/tf_team.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf2/tf_team.cpp')
-rw-r--r--game/server/tf2/tf_team.cpp1434
1 files changed, 1434 insertions, 0 deletions
diff --git a/game/server/tf2/tf_team.cpp b/game/server/tf2/tf_team.cpp
new file mode 100644
index 0000000..0bec380
--- /dev/null
+++ b/game/server/tf2/tf_team.cpp
@@ -0,0 +1,1434 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose: Team management class. Contains all the details for a specific team
+//
+// $NoKeywords: $
+//=============================================================================//
+#include "cbase.h"
+#include "team.h"
+#include "tf_team.h"
+#include "tf_func_resource.h"
+#include "tf_player.h"
+#include "techtree.h"
+#include "tf_obj.h"
+#include "tf_obj_resupply.h"
+#include "orders.h"
+#include "entitylist.h"
+#include "team_spawnpoint.h"
+#include "team_messages.h"
+#include "tf_obj_powerpack.h"
+#include "tf_gamerules.h"
+#include "engine/IEngineSound.h"
+#include "tier1/strtools.h"
+#include "tf_stats.h"
+#include "tf_obj_buff_station.h"
+
+// memdbgon must be the last include file in a .cpp file!!!
+#include "tier0/memdbgon.h"
+
+#define OBJECT_COVERED_DIST 1000
+#define RESOURCE_GIVE_TIME 30
+#define RESOURCE_GIVE_AMOUNT 150
+#define RESOURCE_DONATION_AMT_PER_PLAYER 10
+
+bool IsEntityVisibleToTactical( int iLocalTeamNumber, int iLocalTeamPlayers,
+ int iLocalTeamObjects, int iEntIndex, const char *pEntName, int pEntTeamNumber, const Vector &pEntOrigin );
+extern ConVar tf_destroyobjects;
+
+//-----------------------------------------------------------------------------
+// Purpose: SendProxy that converts the UtlVector list of radar scanners to entindexes, where it's reassembled on the client
+//-----------------------------------------------------------------------------
+void SendProxy_ObjectList( const SendProp *pProp, const void *pStruct, const void *pData, DVariant *pOut, int iElement, int objectID )
+{
+ CTFTeam *pTeam = (CTFTeam*)pData;
+
+ // If this fails, then SendProxyArrayLength_TeamObjects didn't work.
+ Assert( iElement < pTeam->GetNumObjects() );
+
+ CBaseObject *pObject = pTeam->GetObject(iElement);
+ EHANDLE hObject;
+ hObject = pObject;
+
+ SendProxy_EHandleToInt( pProp, pStruct, &hObject, pOut, iElement, objectID );
+}
+
+
+int SendProxyArrayLength_TeamObjects( const void *pStruct, int objectID )
+{
+ CTFTeam *pTeam = (CTFTeam*)pStruct;
+ int iObjects = pTeam->GetNumObjects();
+ Assert( iObjects < MAX_OBJECTS_PER_TEAM );
+ return iObjects;
+}
+
+
+// Datatable
+IMPLEMENT_SERVERCLASS_ST(CTFTeam, DT_TFTeam)
+ SendPropFloat( SENDINFO(m_fResources), 16, SPROP_NOSCALE ),
+ SendPropFloat( SENDINFO(m_fPotentialResources), 16, SPROP_NOSCALE ),
+ SendPropInt( SENDINFO(m_bHaveZone), 1, SPROP_UNSIGNED ),
+
+ SendPropArray2(
+ SendProxyArrayLength_TeamObjects,
+ SendPropInt("object_array_element", 0, SIZEOF_IGNORE, NUM_NETWORKED_EHANDLE_BITS, SPROP_UNSIGNED, SendProxy_ObjectList),
+ MAX_OBJECTS_PER_TEAM,
+ 0,
+ "object_array"
+ )
+END_SEND_TABLE()
+
+LINK_ENTITY_TO_CLASS( tf_team_manager, CTFTeam );
+
+//-----------------------------------------------------------------------------
+// Purpose: Get a pointer to the specified TF team manager
+//-----------------------------------------------------------------------------
+CTFTeam *GetGlobalTFTeam( int iIndex )
+{
+ return (CTFTeam*)GetGlobalTeam( iIndex );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Needed because this is an entity, but should never be used
+//-----------------------------------------------------------------------------
+void CTFTeam::Init( const char *pName, int iNumber )
+{
+ BaseClass::Init( pName, iNumber );
+
+ InitializeTeamResources();
+ InitializeTechTree();
+ InitializeOrders();
+ ClearMessages();
+
+ m_flNextResourceTime = 0;
+
+ // Only detect changes every half-second.
+ NetworkProp()->SetUpdateInterval( 0.75f );
+
+ m_flTotalResourcesSoFar = m_iLastUpdateSentAt = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFTeam::~CTFTeam( void )
+{
+ m_aResourcesBeingCollected.Purge();
+ m_aResupplyBeacons.Purge();
+ m_aObjects.Purge();
+ m_aOrders.Purge();
+
+ delete m_pTechnologyTree;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::Precache( void )
+{
+ // Precache all the technologies in the techtree
+ for ( int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ PrecacheTechnology( m_pTechnologyTree->GetTechnology(i) );
+ }
+
+ PrecacheScriptSound( "TFTeam.CapturedZone" );
+ PrecacheScriptSound( "TFTeam.LostZone" );
+ PrecacheScriptSound( "TFTeam.ObtainStolenTechnology" );
+ PrecacheScriptSound( "TFTeam.BoughtPreferredTechnology" );
+ PrecacheScriptSound( "TFTeam.AddOrder" );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Precache a technology's files
+//-----------------------------------------------------------------------------
+void CTFTeam::PrecacheTechnology( CBaseTechnology *pTech )
+{
+ // Precache sounds for every class result
+ for (int i = 0; i < TFCLASS_CLASS_COUNT; i++ )
+ {
+ if ( pTech->GetSoundFile(i) && (pTech->GetSoundFile(i)[0] != 0) )
+ {
+ PrecacheScriptSound( pTech->GetSoundFile(i) );
+ pTech->SetClassResultSound( i, 0 );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called every frame
+//-----------------------------------------------------------------------------
+void CTFTeam::Think( void )
+{
+ UpdateOrders();
+ UpdateMessages();
+
+ // FIXME: Try this out?
+ /*
+ // Give resources to the team at regular intervals
+ if (gpGlobals->curtime >= m_flNextResourceTime)
+ {
+ AddTeamResources( RESOURCE_GIVE_AMOUNT );
+ m_flNextResourceTime = gpGlobals->curtime + RESOURCE_GIVE_TIME;
+ }
+ */
+
+ UpdateTechnologies();
+
+ /* FIXME: Re-enable once we figure out what the correct orders should be
+ // Create new personal orders
+ if ( m_flPersonalOrderUpdateTime < gpGlobals->curtime )
+ {
+ CreatePersonalOrders();
+ m_flPersonalOrderUpdateTime = gpGlobals->curtime + PERSONAL_ORDER_UPDATE_TIME;
+ }
+ */
+}
+
+//-----------------------------------------------------------------------------
+// DATA HANDLING
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if we should resend the entire tech tree to a player on Hud reinitialisation, which happens every player respawn
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateClientData( CBasePlayer *pPlayer )
+{
+ CBaseTFPlayer *pTFPlayer = (CBaseTFPlayer *)pPlayer;
+ // If we're initialising the hud, update all technologies
+ if ( pTFPlayer->HUDNeedsRestart() )
+ {
+ // Check all the technologies and resend any that differ from this client's representation
+ for ( int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ // Update all technologies
+ CBaseTechnology *technology = m_pTechnologyTree->GetTechnology(i);
+ if ( technology )
+ {
+ // Check to see if any resource levels have changed
+ if ( pTFPlayer->AvailableTech(i).m_nResourceLevel != technology->GetResourceLevel() )
+ {
+ UpdateClientTechnology( i, pTFPlayer );
+ continue;
+ }
+
+ if ( technology->GetAvailable() != pTFPlayer->AvailableTech(i).m_nAvailable )
+ {
+ UpdateClientTechnology( i, pTFPlayer );
+ continue;
+ }
+
+ byte pcount = technology->GetPreferenceCount();
+ if ( pTFPlayer->GetPreferredTechnology() == i )
+ {
+ pcount |= 0x80;
+ }
+
+ if ( pcount != pTFPlayer->AvailableTech(i).m_nUserCount )
+ {
+ UpdateClientTechnology( i, pTFPlayer );
+ }
+ }
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Update a technology for a player
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateClientTechnology( int iTechID, CBaseTFPlayer *pPlayer )
+{
+ CBaseTechnology *pTechnology = m_pTechnologyTree->GetTechnology( iTechID );
+ if ( !pTechnology )
+ return;
+
+ byte pcount = pTechnology->GetPreferenceCount();
+
+ if ( pPlayer->GetPreferredTechnology() == iTechID )
+ {
+ pcount |= 0x80;
+ }
+
+ CSingleUserRecipientFilter user( pPlayer );
+ user.MakeReliable();
+
+ // Update this technology
+ UserMessageBegin( user, "Technology" );
+ WRITE_BYTE( iTechID );
+ WRITE_BYTE( pTechnology->GetAvailable() );
+ WRITE_BYTE( pcount );
+ WRITE_SHORT( (short)pTechnology->GetResourceLevel() );
+ MessageEnd();
+
+ // Update the player's client tech representation
+ pPlayer->AvailableTech(iTechID).m_nAvailable = pTechnology->GetAvailable();
+ pPlayer->AvailableTech(iTechID).m_nUserCount = pcount;
+ pPlayer->AvailableTech(iTechID).m_nResourceLevel = pTechnology->GetResourceLevel();
+
+ /*
+ Msg( "Sent %s(%d) to %s:\n", pTechnology->GetName(), iTechID, pPlayer->GetPlayerName() );
+ Msg( " Available: %d\n", pTechnology->GetAvailable() );
+ Msg( " PrefCount: %d\n", pTechnology->GetPreferenceCount() );
+ Msg( " Level : %0.2f\n", pTechnology->GetResourceLevel() );
+ */
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Check to see if any technology has changed, and resend it to players if it has
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateTechnologyData( void )
+{
+ for ( int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ // Update all technologies
+ CBaseTechnology *pTechnology = m_pTechnologyTree->GetTechnology(i);
+ if ( pTechnology && pTechnology->IsDirty() )
+ {
+ // Send it to all our clients
+ for ( int iPlayer = 0; iPlayer < m_aPlayers.Count(); iPlayer++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[iPlayer];
+ UpdateClientTechnology( i, pPlayer );
+ }
+
+ pTechnology->SetDirty( false );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+bool CTFTeam::ShouldTransmitToPlayer( CBasePlayer* pRecipient, CBaseEntity* pEntity )
+{
+ return IsEntityVisibleToTactical( pEntity );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Is the specified entity visible on this team's tactical view?
+//-----------------------------------------------------------------------------
+bool CTFTeam::IsEntityVisibleToTactical( CBaseEntity *pEntity )
+{
+ return ::IsEntityVisibleToTactical( GetTeamNumber(), GetNumPlayers(),
+ GetNumObjects(), pEntity->entindex(), (char*)STRING(pEntity->m_iClassname),
+ pEntity->GetTeamNumber(), pEntity->GetAbsOrigin() );
+}
+
+//-----------------------------------------------------------------------------
+// RESOURCES
+//-----------------------------------------------------------------------------
+//-----------------------------------------------------------------------------
+// Purpose: Add a resource zone to the list of zones to collect from
+//-----------------------------------------------------------------------------
+void CTFTeam::AddResourceZone( CResourceZone *pResource )
+{
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::AddResourceZone adding res zone %p to team %s\n", gpGlobals->curtime,
+ pResource, GetName() ) );
+
+ // If this resource is already owned by another team, remove it from them
+ CTFTeam *pOwners = pResource->GetOwningTeam();
+ if ( pOwners )
+ {
+ pOwners->RemoveResourceZone( pResource );
+ }
+
+ pResource->SetOwningTeam( GetTeamNumber() );
+
+ m_aResourcesBeingCollected.AddToTail( pResource );
+ m_bHaveZone = true;
+
+ // Tell all the team's members
+ for ( int i = 0; i < m_aPlayers.Count(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[i];
+ CSingleUserRecipientFilter filter( pPlayer );
+ filter.MakeReliable();
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(), "TFTeam.CapturedZone" );
+ }
+
+ // Recalculate team's orders
+ RecalcOrders();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove a resource zone from the list of zones being collected from
+//-----------------------------------------------------------------------------
+void CTFTeam::RemoveResourceZone( CResourceZone *pResource )
+{
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::RemoveResourceZone removing res zone %p from team %s\n", gpGlobals->curtime,
+ pResource, GetName() ) );
+
+ // Now remove the zone from our list
+ m_aResourcesBeingCollected.FindAndRemove( pResource );
+
+ // Still have a zone if there are other zones in the list
+ m_bHaveZone = ( m_aResourcesBeingCollected.Count() > 0 );
+
+ // Tell all the team's members
+ for ( int i = 0; i < m_aPlayers.Count(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[i];
+ CSingleUserRecipientFilter filter( pPlayer );
+ filter.MakeReliable();
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(),"TFTeam.LostZone" );
+ }
+
+ // Recalculate team's orders
+ RecalcOrders();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate the potential resources
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdatePotentialResources( void )
+{
+ // Set potential to current amount
+ m_fPotentialResources = GetTeamResources();
+
+ // This used to be used for collectors.
+ // It could be updated to count all incoming resources in en-route resource boxes.
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the amount of resources a player should get when joining this team
+//-----------------------------------------------------------------------------
+float CTFTeam::GetJoiningPlayerResources( void )
+{
+ // If we had our banks set recently, use that amount
+ if ( gpGlobals->curtime < (m_flLastBankSetTime + 30.0) )
+ return m_flLastBankSetAmount;
+
+ if ( !GetNumPlayers() )
+ return 0;
+
+ // Otherwise, take the average of all the players on the team
+ RecomputeTeamResources();
+ return ( GetTeamResources() / GetNumPlayers() );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::SetRecentBankSet( float flResources )
+{
+ m_flLastBankSetAmount = flResources;
+ m_flLastBankSetTime = gpGlobals->curtime;
+}
+
+//------------------------------------------------------------------------------------------------------------------
+// TECHNOLOGY TREE
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::InitializeTechTree( void )
+{
+ m_pTechnologyTree = new CTechnologyTree( filesystem, GetTeamNumber() );
+
+ // Now iterate through added techs and automatically make level 0 techs available
+ for (int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ CBaseTechnology *tech = m_pTechnologyTree->GetTechnology(i);
+ if ( !tech )
+ continue;
+
+ if ( tech->GetLevel() == 0 )
+ {
+ EnableTechnology( tech );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTechnologyTree *CTFTeam::GetTechnologyTree( void )
+{
+ return m_pTechnologyTree;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: A new technology has been attained by this team. Give it to every player.
+//-----------------------------------------------------------------------------
+void CTFTeam::EnableTechnology( CBaseTechnology *technology, bool bStolen )
+{
+ CTeamFortress *rules = TFGameRules();
+ if ( rules )
+ {
+ // Disable autoswitching if we are getting a weapon
+ rules->SetAllowWeaponSwitch( false );
+ }
+
+ // Set the technology's resources to the costs
+ // Needed because technologies can be enabled through other means than resource spending
+ technology->ForceComplete();
+
+ // Apply technology to team first.
+ technology->AddTechnologyToTeam( this );
+
+ // Iterate though players
+ for (int i = 0; i < m_aPlayers.Count(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[i];
+ technology->AddTechnologyToPlayer( pPlayer );
+
+ CSingleUserRecipientFilter filter( pPlayer );
+ filter.MakeReliable();
+
+ // Play the sound
+ if (bStolen)
+ {
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(), "TFTeam.ObtainStolenTechnology" );
+ }
+ else
+ {
+ if ( technology->GetSoundFile(0) && technology->GetSoundFile(0)[0] )
+ {
+ EmitSound_t ep;
+ ep.m_nChannel = CHAN_STATIC;
+ ep.m_pSoundName = technology->GetSoundFile(0);
+
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(), ep );
+ }
+ }
+
+ // Remove all the player's votes on this technology
+ CBaseTechnology *pPreferredTech = m_pTechnologyTree->GetTechnology( pPlayer->GetPreferredTechnology() );
+ if ( pPreferredTech && pPreferredTech == technology )
+ {
+ // Tell the player his preferred tech has been bought
+ if (!bStolen)
+ {
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(), "TFTeam.BoughtPreferredTechnology" );
+ }
+
+ pPlayer->SetPreferredTechnology( m_pTechnologyTree, -1 );
+ }
+ }
+
+ // Let the team see if it wants to do anything with this specific technology
+ GainedNewTechnology( technology );
+
+ // Reenable autoswitching
+ if ( rules )
+ {
+ rules->SetAllowWeaponSwitch( true );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: For debugging..
+//-----------------------------------------------------------------------------
+void CTFTeam::EnableAllTechnologies()
+{
+ for ( int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ CBaseTechnology *technology = m_pTechnologyTree->GetTechnology(i);
+ if ( !technology || technology->IsHidden() )
+ continue;
+
+ EnableTechnology( technology );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Called any time a player votes/changes preferences on the client
+//-----------------------------------------------------------------------------
+void CTFTeam::RecomputePreferences( void )
+{
+ // Zero total counters
+ m_pTechnologyTree->ClearPreferenceCount();
+
+ // Zero out all preferences and iterate through active players
+ int i;
+ for ( i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ CBaseTechnology *technology = m_pTechnologyTree->GetTechnology(i);
+ if ( technology )
+ {
+ // Zero internal counters
+ technology->ZeroPreferences();
+ }
+ }
+
+ // Now loop through players and see what's preferred
+ for ( i = 0; i < m_aPlayers.Count(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[i];
+ if ( !pPlayer )
+ continue;
+
+ int preferred = pPlayer->GetPreferredTechnology();
+ // No preference set, don't worry about this player
+ if ( preferred == -1 )
+ continue;
+
+ if ( preferred < 0 || preferred >= MAX_TECHNOLOGIES )
+ {
+ Msg( "Player %s tried to set preference to out of range tech %i\n", preferred );
+ continue;
+ }
+
+ // Reference technology
+ CBaseTechnology *technology = m_pTechnologyTree->GetTechnology(preferred);
+ Assert( technology );
+ if ( !technology )
+ continue;
+
+ // Msg( "player %s prefers %s\n", pPlayer->GetPlayerName(), technology->GetPrintName() );
+
+ // Add one vote
+ technology->IncrementPreferences();
+ // Add one vote to totals
+ m_pTechnologyTree->IncrementPreferences();
+ }
+
+ // Any time preferences are changed/set, see if we should make any purchases immediately.
+ RecomputePurchases();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Figure our how many resources we've got in the team
+//-----------------------------------------------------------------------------
+void CTFTeam::RecomputeTeamResources( void )
+{
+ // Recalculate the total amount of resources the team has
+ m_fResources = 0.0f;
+ for ( int i = 0; i < GetNumPlayers(); i++ )
+ {
+ m_fResources += ((CBaseTFPlayer*)GetPlayer(i))->GetBankResources();
+ }
+
+ UpdatePotentialResources();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Attempt to spend resources according to player's preferences
+//-----------------------------------------------------------------------------
+void CTFTeam::RecomputePurchases( void )
+{
+ RecomputeTeamResources();
+
+ // Cycle through all players, and spend their resources on the technologies they're voting for
+ for ( int iPlayer = 0; iPlayer < m_aPlayers.Count(); iPlayer++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)GetPlayer( iPlayer );
+
+ // See if he has any resources to spend on a tech
+ if ( pPlayer->GetBankResources() <= 0 )
+ continue;
+
+ // Has he got a preffered tech?
+ if ( pPlayer->GetPreferredTechnology() != -1 )
+ {
+ // Get the player's voted-for technology
+ CBaseTechnology *pPreferredTech = m_pTechnologyTree->GetTechnology( pPlayer->GetPreferredTechnology() );
+ if ( pPreferredTech && pPreferredTech->GetAvailable() == false )
+ {
+ if ( !pPreferredTech->GetResourceCost() )
+ continue;
+
+ // Try to spend resources on the tech
+ int iResourcesSpent = MIN( pPlayer->GetBankResources(), pPreferredTech->GetResourceCost() - pPreferredTech->GetResourceLevel() );
+ if ( pPreferredTech->IncreaseResourceLevel( iResourcesSpent ) )
+ {
+ // The technology's had enough resources spent to buy it, so enable it
+ EnableTechnology( pPreferredTech );
+ Msg( "%s bought %s\n", GetName(), pPreferredTech->GetPrintName() );
+ }
+
+ // Reduce the player's bank
+ if ( iResourcesSpent )
+ {
+ pPlayer->RemoveBankResources( iResourcesSpent );
+ }
+ }
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Return true if the team owns the specified technology
+//-----------------------------------------------------------------------------
+bool CTFTeam::HasNamedTechnology( const char *name )
+{
+ // Look it up
+ // FIXME: This could be too slow, consider using #define'd/indexed names?
+ CBaseTechnology *tech = m_pTechnologyTree->GetTechnology( name );
+ if ( !tech )
+ return false;
+ if ( !tech->GetAvailable() )
+ return false;
+
+ return true;
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: A new technology has been received by the team. Do anything specific to this technology here.
+//-----------------------------------------------------------------------------
+void CTFTeam::GainedNewTechnology( CBaseTechnology *pTechnology )
+{
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Called by the team's Think function
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateTechnologies( void )
+{
+ // Update clients
+ UpdateTechnologyData();
+}
+
+
+//------------------------------------------------------------------------------------------------------------------
+// PLAYERS
+//-----------------------------------------------------------------------------
+// Purpose: Add the specified player to this team. Remove them from their current team, if any.
+//-----------------------------------------------------------------------------
+void CTFTeam::AddPlayer( CBasePlayer *pPlayer )
+{
+ BaseClass::AddPlayer( pPlayer );
+
+ // Give the player this team's technology
+ for ( int i = 0; i < m_pTechnologyTree->GetNumberTechnologies(); i++ )
+ {
+ CBaseTechnology *technology = m_pTechnologyTree->GetTechnology(i);
+ if ( !technology )
+ continue;
+
+ if ( technology->IsHidden() )
+ continue;
+
+ // Not yet available to team, skip
+ if ( !technology->GetAvailable() )
+ continue;
+
+ // Add it.
+ technology->AddTechnologyToPlayer( (CBaseTFPlayer*)pPlayer );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Clean up the player's objects when they leave
+//-----------------------------------------------------------------------------
+void CTFTeam::RemovePlayer( CBasePlayer *pPlayer )
+{
+ BaseClass::RemovePlayer( pPlayer );
+
+ // Destroy all objects belonging to this player
+ if ( tf_destroyobjects.GetFloat() )
+ {
+ // Work backwards through the list because objects remove themselves
+ int iSize = m_aObjects.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ if ( (m_aObjects[i]->GetBuilder() == pPlayer) && (m_aObjects[i]->ShouldAutoRemove()) )
+ {
+ UTIL_Remove( m_aObjects[i] );
+ }
+ }
+ }
+
+ RecomputePreferences();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Return the number of team members of the specified class
+//-----------------------------------------------------------------------------
+int CTFTeam::GetNumOfClass( TFClass iClass )
+{
+ int iNumber = 0;
+ for ( int i = 0; i < GetNumPlayers(); i++ )
+ {
+ if ( ((CBaseTFPlayer*)GetPlayer(i))->IsClass(iClass) )
+ {
+ iNumber++;
+ }
+ }
+ return iNumber;
+}
+
+//------------------------------------------------------------------------------------------------------------------
+// RESOURCE BANK
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::InitializeTeamResources( void )
+{
+ m_fResources = 0.0f;
+ m_fPotentialResources = 0.0f;
+ m_bHaveZone = false;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+float CTFTeam::GetTeamResources( void )
+{
+ return m_fResources;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add resources to this team
+//-----------------------------------------------------------------------------
+int CTFTeam::AddTeamResources( float fAmount, int nStat )
+{
+ fAmount = clamp(fAmount, 0, 9999.f);
+
+ m_flTotalResourcesSoFar += fAmount;
+
+ TFStats()->IncrementTeamStat( GetTeamNumber(), TF_TEAM_STAT_RESOURCES_COLLECTED, fAmount );
+
+ // Divvy the resources out to the players
+ int iAmountPerPlayer = Ceil2Int( fAmount / GetNumPlayers() ); // Yes, this does create some resources in the roundoff.
+ for ( int i = 0; i < GetNumPlayers(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer*)GetPlayer(i);
+ pPlayer->AddBankResources( iAmountPerPlayer );
+ TFStats()->IncrementPlayerStat( pPlayer, TF_PLAYER_STAT_RESOURCES_ACQUIRED, fAmount );
+
+ if (nStat >= 0)
+ {
+ TFStats()->IncrementPlayerStat( pPlayer, (TFPlayerStatId_t)nStat, fAmount );
+ }
+ }
+
+ ResourceLoadDeposited();
+
+ return iAmountPerPlayer;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Give resources to the player
+//-----------------------------------------------------------------------------
+void CTFTeam::DonateResources( CBaseTFPlayer *pPlayer )
+{
+ int nPlayerCount = GetNumPlayers();
+ if (nPlayerCount <= 1)
+ return;
+
+ int pResourceCount;
+ int pResourcePerPlayer;
+ int pDonationCount;
+
+ bool bDonating = false;
+ pResourceCount = pPlayer->GetBankResources();
+
+ // Figure out how many resources per player to donate
+ pResourcePerPlayer = pResourceCount / (nPlayerCount - 1);
+ if (pResourceCount % (nPlayerCount - 1) != 0)
+ ++pResourcePerPlayer;
+
+ // Clamp to max amt per teammate for each hit...
+ if (pResourcePerPlayer > RESOURCE_DONATION_AMT_PER_PLAYER)
+ pResourcePerPlayer = RESOURCE_DONATION_AMT_PER_PLAYER;
+
+ // Figure out if we are donating anything at all
+ if (pResourceCount > 0)
+ bDonating = true;
+ if (!bDonating)
+ return;
+
+ // Now that we've figured how much to donate, do it!
+ for ( int i = 0; i < nPlayerCount; i++ )
+ {
+ CBaseTFPlayer *pDest = (CBaseTFPlayer*)GetPlayer(i);
+ if (pDest == pPlayer)
+ continue;
+
+ // The last guy(s) gets the scraps... too bad.
+ int nCountToDonate = pResourceCount;
+ if (nCountToDonate > pResourcePerPlayer)
+ nCountToDonate = pResourcePerPlayer;
+ pResourceCount -= nCountToDonate;
+ pDonationCount = nCountToDonate;
+
+ pPlayer->DonateResources( pDest, pDonationCount );
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: New resources have just been dumped in the bank
+//-----------------------------------------------------------------------------
+void CTFTeam::ResourceLoadDeposited( void )
+{
+ // HACK TEST CODE
+ // Remove after Resource Experiment!
+ static int iIncrements = 250;
+ if ( m_flTotalResourcesSoFar >= (m_iLastUpdateSentAt + iIncrements) )
+ {
+ while ( m_flTotalResourcesSoFar >= (m_iLastUpdateSentAt + iIncrements) )
+ {
+ m_iLastUpdateSentAt += iIncrements;
+ }
+
+ EntityMessageBegin( (CBaseEntity*)this );
+ WRITE_LONG( m_iLastUpdateSentAt );
+ MessageEnd();
+ }
+
+ // Now see if we should buy anything
+ RecomputePurchases();
+}
+
+//------------------------------------------------------------------------------------------------------------------
+// RESUPPLY BEACONS
+//-----------------------------------------------------------------------------
+// Purpose: Add the specified resupply beacon to this team.
+//-----------------------------------------------------------------------------
+void CTFTeam::AddResupply( CObjectResupply *pResupply )
+{
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::AddResupply adding resupply %p to team %s\n", gpGlobals->curtime,
+ pResupply, GetName() ) );
+
+ m_aResupplyBeacons.AddToTail( pResupply );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove this resupply beacon from the team
+//-----------------------------------------------------------------------------
+void CTFTeam::RemoveResupply( CObjectResupply *pResupply )
+{
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::RemoveResupply remove resupply %p from team %s\n", gpGlobals->curtime,
+ pResupply, GetName() ) );
+
+ // Now remove the beacon from our list
+ m_aResupplyBeacons.FindAndRemove( pResupply );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFTeam::GetNumObjects( int iObjectType )
+{
+ // Asking for a count of a specific object type?
+ if ( iObjectType > 0 )
+ {
+ int iCount = 0;
+ for ( int i = 0; i < GetNumObjects(); i++ )
+ {
+ CBaseObject *pObject = GetObject(i);
+ if ( pObject && pObject->GetType() == iObjectType )
+ {
+ iCount++;
+ }
+ }
+ return iCount;
+ }
+
+ return m_aObjects.Count();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CBaseObject *CTFTeam::GetObject( int num )
+{
+ Assert( num >= 0 && num < m_aObjects.Count() );
+ return m_aObjects[ num ];
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+int CTFTeam::GetNumResupplies( void )
+{
+ return m_aResupplyBeacons.Count();
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CObjectResupply *CTFTeam::GetResupply( int num )
+{
+ Assert( num >= 0 && num < m_aResupplyBeacons.Count() );
+ return m_aResupplyBeacons[ num ];
+}
+
+
+bool CTFTeam::IsCoveredBySentryGun( const Vector &vPos )
+{
+ for( int i=0; i < m_aObjects.Count(); i++ )
+ {
+ CBaseObject *pObj = m_aObjects[i];
+
+ if ( pObj->IsSentrygun() && vPos.DistTo( pObj->GetAbsOrigin() ) < OBJECT_COVERED_DIST )
+ return true;
+
+ }
+
+ return false;
+}
+
+
+int CTFTeam::GetNumShieldWallsCoveringPosition( const Vector &vPos )
+{
+ int count = 0;
+
+ for ( int i=0; i < m_aObjects.Count(); i++ )
+ {
+ CBaseObject *pObj = m_aObjects[i];
+
+ if ( pObj->GetType() == OBJ_SHIELDWALL )
+ {
+ if ( vPos.DistTo( pObj->GetAbsOrigin() ) < OBJECT_COVERED_DIST )
+ ++count;
+ }
+ }
+
+ return count;
+}
+
+
+int CTFTeam::GetNumResuppliesCoveringPosition( const Vector &vPos )
+{
+ int count = 0;
+
+ for ( int i=0; i < m_aResupplyBeacons.Count(); i++ )
+ {
+ CBaseObject *pObj = m_aResupplyBeacons[i];
+ if ( vPos.DistTo( pObj->GetAbsOrigin() ) < RESUPPLY_COVER_DIST )
+ ++count;
+ }
+
+ return count;
+}
+
+
+int CTFTeam::GetNumRespawnStationsCoveringPosition( const Vector &vPos )
+{
+ int count = 0;
+
+ for ( int i=0; i < m_aObjects.Count(); i++ )
+ {
+ CBaseObject *pObj = m_aObjects[i];
+
+ if ( pObj->GetType() == OBJ_RESPAWN_STATION )
+ {
+ if ( vPos.DistTo( pObj->GetAbsOrigin() ) < OBJECT_COVERED_DIST )
+ {
+ ++count;
+ }
+ }
+ }
+
+ return count;
+}
+
+
+//------------------------------------------------------------------------------------------------------------------
+// OBJECTS
+//-----------------------------------------------------------------------------
+// Purpose: Add the specified object to this team.
+//-----------------------------------------------------------------------------
+void CTFTeam::AddObject( CBaseObject *pObject )
+{
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::AddObject adding object %p:%s to team %s\n", gpGlobals->curtime,
+ pObject, pObject->GetClassname(), GetName() ) );
+
+ bool alreadyInList = IsObjectOnTeam( pObject );
+ Assert( !alreadyInList );
+ if ( !alreadyInList )
+ {
+ m_aObjects.AddToTail( pObject );
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Returns true if the object is in the team's list of objects
+//-----------------------------------------------------------------------------
+bool CTFTeam::IsObjectOnTeam( CBaseObject *pObject ) const
+{
+ return ( m_aObjects.Find( pObject ) != -1 );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Remove this object from the team
+// Removes all references from all sublists as well
+//-----------------------------------------------------------------------------
+void CTFTeam::RemoveObject( CBaseObject *pObject )
+{
+ if ( m_aObjects.Count() <= 0 )
+ return;
+
+ if ( m_aObjects.Find( pObject ) != -1 )
+ {
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::RemoveObject removing %p:%s from %s\n", gpGlobals->curtime,
+ pObject, pObject->GetClassname(), GetName() ) );
+
+ m_aObjects.FindAndRemove( pObject );
+ }
+ else
+ {
+ TRACE_OBJECT( UTIL_VarArgs( "%0.2f CTFTeam::RemoveObject couldn't remove %p:%s from %s\n", gpGlobals->curtime,
+ pObject, pObject->GetClassname(), GetName() ) );
+ }
+}
+
+
+//------------------------------------------------------------------------------------------------------------------
+// ORDERS
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::InitializeOrders( void )
+{
+ m_flPersonalOrderUpdateTime = 0;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Add a new order to our list. If it already exists, bump it's priority to the new priority.
+//-----------------------------------------------------------------------------
+COrder* CTFTeam::AddOrder(
+ int iOrderType,
+ CBaseEntity *pTarget,
+ CBaseTFPlayer *pPlayer,
+ float flDistanceToRemove,
+ float flLifetime,
+ COrder *pNewOrder
+ )
+{
+ // Remove any orders to the player.
+ RemoveOrdersToPlayer( pPlayer );
+
+ // The new system requires order class to be passed in.
+ Assert( pNewOrder );
+
+ // All the order create functions should just use new to create the order class,
+ // then we'll attach the edict in here. There's no reason to use LINK_ENTITY_TO_CLASS
+ // and CreateEntityByName.
+ Assert( !pNewOrder->edict() );
+ pNewOrder->NetworkProp()->AttachEdict();
+
+ pNewOrder->ChangeTeam( GetTeamNumber() );
+ OrderHandle hOrder;
+ hOrder = pNewOrder;
+ m_aOrders.AddToTail( hOrder );
+
+ // Update target
+ pNewOrder->SetTarget( pTarget );
+ pNewOrder->SetDistance( flDistanceToRemove );
+
+ // Update lifetime.
+ pNewOrder->SetLifetime( flLifetime );
+
+ Assert( pPlayer->GetOrder() == NULL );
+
+ pNewOrder->SetOwner( pPlayer );
+ pPlayer->SetOrder( pNewOrder );
+
+ // "New Order Received!"
+ CSingleUserRecipientFilter filter( pPlayer );
+ filter.MakeReliable();
+ CBaseEntity::EmitSound( filter, pPlayer->entindex(),"TFTeam.AddOrder" );
+
+ // Debug check.. it should never create an order with its termination conditions
+ // already met.
+ Assert( !pNewOrder->Update() );
+
+ return pNewOrder;
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::RemoveOrder( COrder *pOrder )
+{
+ OrderHandle hOrder;
+ hOrder = pOrder;
+
+ m_aOrders.FindAndRemove( hOrder );
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Recalculate the team's orders & their priorities
+//-----------------------------------------------------------------------------
+void CTFTeam::RecalcOrders( void )
+{
+ // Update all existing orders
+ UpdateOrders();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateOrders( void )
+{
+ // Tell all our current orders to update themselves. Walk backwards because we may remove them.
+ int iSize = m_aOrders.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ // Orders without owners should be removed
+ bool bShouldRemove = (
+ !m_aOrders[i] ||
+ !m_aOrders[i]->GetOwner() ||
+ m_aOrders[i]->Update() );
+ if ( bShouldRemove )
+ {
+ COrder *pOrder = m_aOrders[i];
+ m_aOrders.Remove( i );
+ UTIL_Remove( pOrder );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: An event has just occurred that affects orders. Tell all our orders that
+// have the specified entity as a target.
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateOrdersOnEvent( COrderEvent_Base *pOrder )
+{
+ // Tell all our current orders to update themselves. Walk backwards because we may remove them.
+ int iSize = m_aOrders.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ bool bShouldRemove = m_aOrders[i]->UpdateOnEvent( pOrder );
+ if ( bShouldRemove )
+ {
+ COrder *pOrder = m_aOrders[i];
+ m_aOrders.Remove( i );
+ UTIL_Remove( pOrder );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create personal orders for all the team's members
+//-----------------------------------------------------------------------------
+void CTFTeam::CreatePersonalOrders( void )
+{
+ // Create personal orders for each player
+ for ( int i = 0; i < m_aPlayers.Count(); i++ )
+ {
+ CBaseTFPlayer *pPlayer = (CBaseTFPlayer *)m_aPlayers[i];
+
+ // Don't create orders for bots, undefined or dead people
+ if ( !(pPlayer->GetFlags() & FL_FAKECLIENT) && pPlayer->IsAlive() && !pPlayer->IsClass( TFCLASS_UNDECIDED ) )
+ {
+ CreatePersonalOrder( pPlayer );
+ }
+ }
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Create personal orders for specified player
+//-----------------------------------------------------------------------------
+void CTFTeam::CreatePersonalOrder( CBaseTFPlayer *pPlayer )
+{
+ // We still haven't made a personal order, so ask the class if it wants to
+ if ( pPlayer->GetPlayerClass() )
+ {
+ pPlayer->GetPlayerClass()->CreatePersonalOrder();
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::RemoveOrdersToPlayer( CBaseTFPlayer *pPlayer )
+{
+ // Walk backwards because we're removing them.
+ int iSize = m_aOrders.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ // Orders without owners should be removed
+ if ( m_aOrders[i].Get() )
+ {
+ if( m_aOrders[i]->GetOwner() == pPlayer )
+ {
+ COrder *pOrder = m_aOrders[i];
+ m_aOrders.Remove( i );
+
+ pOrder->DetachFromPlayer();
+ UTIL_Remove( pOrder );
+ }
+ }
+ else
+ {
+ m_aOrders.Remove( i );
+ }
+ }
+
+ pPlayer->SetOrder( NULL );
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Count and return the number of orders of the type with the specified target
+//-----------------------------------------------------------------------------
+int CTFTeam::CountOrders( int flags, int iOrderType, CBaseEntity *pTarget, CBaseTFPlayer *pOwner )
+{
+ int iOrderCount = 0;
+
+ // Count the number of global orders
+ for ( int i = 0; i < m_aOrders.Count(); i++ )
+ {
+ COrder *pOrder = m_aOrders[i];
+
+ if( flags & COUNTORDERS_TYPE )
+ if( pOrder->GetType() != iOrderType )
+ continue;
+
+ if( flags & COUNTORDERS_TARGET )
+ if( pOrder->GetTargetEntity() != pTarget )
+ continue;
+
+ if( flags & COUNTORDERS_OWNER )
+ if( pOrder->GetOwner() != pOwner )
+ continue;
+
+ // Ok, this order matches the criteria.
+ iOrderCount++;
+ }
+
+ return iOrderCount;
+}
+
+
+int CTFTeam::CountOrdersOwnedByPlayer( CBaseTFPlayer *pPlayer )
+{
+ return CountOrders( COUNTORDERS_OWNER, 0, 0, pPlayer );
+}
+
+
+//------------------------------------------------------------------------------------------------------------------
+// MESSAGES
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::ClearMessages( void )
+{
+ int iSize = m_aMessages.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ CTeamMessage *pMessage = m_aMessages[i];
+ m_aMessages.Remove( i );
+ delete pMessage;
+ }
+
+ m_aMessages.Purge();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose: Post a message of the specified type
+//-----------------------------------------------------------------------------
+void CTFTeam::PostMessage( int iMessageID, CBaseEntity *pEntity, char *sData )
+{
+ // First see if we've got this message in the queue already
+ for ( int i = 0; i < m_aMessages.Count(); i++ )
+ {
+ CTeamMessage *pMessage = m_aMessages[i];
+ if ( (pMessage->GetID() == iMessageID) && (pMessage->GetEntity() == pEntity) )
+ {
+ // Already in the queue, abort.
+ return;
+ }
+ }
+
+ // Create a new message and add it to my tail
+ CTeamMessage *pMessage = CTeamMessage::Create( this, iMessageID, pEntity );
+ if ( sData && sData[0] )
+ {
+ pMessage->SetData( sData );
+ }
+ m_aMessages.AddToTail( pMessage );
+
+ // Tell the message to fire
+ pMessage->FireMessage();
+}
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateMessages( void )
+{
+ // Go through my messages and kill any that have reached their TTL
+ int iSize = m_aMessages.Count();
+ for (int i = iSize-1; i >= 0; i--)
+ {
+ CTeamMessage *pMessage = m_aMessages[i];
+ if ( gpGlobals->curtime > pMessage->GetTTL() )
+ {
+ m_aMessages.Remove( i );
+ delete pMessage;
+ }
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tell all our powerpacks to update their powered objects.
+// pPackToIgnore: Pack to ignore, because it's dying.
+// pObjectToTarget: An object looking for power, because it's being placed
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdatePowerpacks( CObjectPowerPack *pPackToIgnore, CBaseObject *pObjectToTarget )
+{
+ for ( int i = 0; i < GetNumObjects(); i++ )
+ {
+ CBaseObject *pObject = GetObject(i);
+ assert(pObject);
+ if ( pObject == pPackToIgnore || pObject->GetType() != OBJ_POWERPACK )
+ continue;
+
+ ((CObjectPowerPack*)pObject)->PowerNearbyObjects( pObjectToTarget );
+
+ // Quit as soon as we've powered the specified one, if there is one
+ if ( pObjectToTarget && pObjectToTarget->IsPowered() )
+ break;
+ }
+}
+
+
+//-----------------------------------------------------------------------------
+// Purpose: Tell all our buff stations to update and look for objects.
+// Input: pBuffStationToIgnore: Buff station to ignore, because it is dying
+// pObjectToTarget: an object looking for a buff station, because it is being placed
+//-----------------------------------------------------------------------------
+void CTFTeam::UpdateBuffStations( CObjectBuffStation *pBuffStationToIgnore, CBaseObject *pObjectToTarget, bool bPlacing )
+{
+ for ( int iObject = 0; iObject < GetNumObjects(); ++iObject )
+ {
+ CBaseObject *pObject = GetObject( iObject );
+ assert( pObject );
+
+ if ( pObject->GetType() != OBJ_BUFF_STATION )
+ continue;
+
+ CObjectBuffStation *pBuffStation = static_cast<CObjectBuffStation*>( pObject );
+ if ( pBuffStation == pBuffStationToIgnore )
+ continue;
+
+ pBuffStation->BuffNearbyObjects( pObjectToTarget, bPlacing );
+
+ // Quit as soon as we've powered the specified one, if there is one.
+ if ( pObjectToTarget && pObjectToTarget->IsHookedAndBuffed() )
+ break;
+ }
+}
+
+//------------------------------------------------------------------------------------------------------------------
+// UTILITY FUNCS
+//-----------------------------------------------------------------------------
+
+
+//-----------------------------------------------------------------------------
+// Purpose:
+//-----------------------------------------------------------------------------
+CTFTeam* CTFTeam::GetEnemyTeam()
+{
+ // Look for nearby enemy objects we can capture.
+ int iMyTeam = GetTeamNumber();
+ if( iMyTeam == 0 )
+ return NULL;
+
+ int iEnemyTeam = !(iMyTeam - 1) + 1;
+ return (CTFTeam*)GetGlobalTeam( iEnemyTeam );
+}
+
+