summaryrefslogtreecommitdiff
path: root/game/server/tf2/order_helpers.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'game/server/tf2/order_helpers.cpp')
-rw-r--r--game/server/tf2/order_helpers.cpp345
1 files changed, 345 insertions, 0 deletions
diff --git a/game/server/tf2/order_helpers.cpp b/game/server/tf2/order_helpers.cpp
new file mode 100644
index 0000000..56274ce
--- /dev/null
+++ b/game/server/tf2/order_helpers.cpp
@@ -0,0 +1,345 @@
+//========= Copyright Valve Corporation, All rights reserved. ============//
+//
+// Purpose:
+//
+// $NoKeywords: $
+//=============================================================================//
+
+#include "cbase.h"
+
+#include "order_helpers.h"
+#include "tf_team.h"
+#include "tf_func_resource.h"
+#include "tf_obj.h"
+
+
+// ------------------------------------------------------------------------ //
+// CSortBase implementation.
+// ------------------------------------------------------------------------ //
+
+CSortBase::CSortBase()
+{
+ m_pPlayer = 0;
+ m_pTeam = 0;
+}
+
+
+CTFTeam* CSortBase::GetTeam()
+{
+ if ( m_pTeam )
+ return m_pTeam;
+ else
+ return m_pPlayer->GetTFTeam();
+}
+
+
+
+// ------------------------------------------------------------------------ //
+// Global functions.
+// ------------------------------------------------------------------------ //
+
+int SortFn_TeamPlayersByDistance( void *pUserData, int a, int b )
+{
+ CSortBase *p = (CSortBase*)pUserData;
+
+ const Vector &vPlayer = p->m_pPlayer->GetAbsOrigin();
+ const Vector &va = p->m_pPlayer->GetTeam()->GetPlayer( a )->GetAbsOrigin();
+ const Vector &vb = p->m_pPlayer->GetTeam()->GetPlayer( b )->GetAbsOrigin();
+
+ return vPlayer.DistTo( va ) < vPlayer.DistTo( vb );
+}
+
+
+// This is a generic function that takes a number of items and builds a sorted
+// list of the valid items.
+int BuildSortedActiveList(
+ int *pList, // This is the list where the final data is placed.
+ int nMaxItems,
+ sortFn pSortFn, // Callbacks.
+ isValidFn pIsValidFn, // This can be null, in which case all items are valid.
+ void *pUserData, // Passed into the function pointers.
+ int nItems // Number of items in the list to sort.
+ )
+{
+ // First build the list of active items.
+ if( nItems > nMaxItems )
+ nItems = nMaxItems;
+
+ int nActive = 0;
+ for( int i=0; i < nItems; i++ )
+ {
+ if( pIsValidFn )
+ {
+ if( !pIsValidFn( pUserData, i ) )
+ continue;
+ }
+
+ int j;
+ for( j=0; j < nActive; j++ )
+ {
+ Assert( pList[j] < nItems );
+ if( pSortFn( pUserData, i, pList[j] ) > 0 )
+ {
+ break;
+ }
+ }
+
+ // Slide everything up.
+ if( nActive )
+ {
+ Q_memmove( &pList[j+1], &pList[j], (nActive-j) * sizeof(int) );
+ }
+
+ // Add the new item to the list.
+ pList[j] = i;
+ ++nActive;
+
+ for (int l = 0; l < nActive ; ++l )
+ {
+ Assert( pList[l] < nItems );
+ }
+
+ }
+
+ return nActive;
+}
+
+
+// Finds the closest resource zone without the specified object on it and
+// gives an order to the player to build the object.
+bool OrderCreator_ResourceZoneObject(
+ CBaseTFPlayer *pPlayer,
+ int objType,
+ COrder *pOrder
+ )
+{
+ // Can we even build a resource box?
+ if ( pPlayer->CanBuild( objType ) != CB_CAN_BUILD )
+ return false;
+
+ CTFTeam *pTeam = pPlayer->GetTFTeam();
+ if( !pTeam )
+ return false;
+
+ // Let's have one near each resource zone that we own.
+ CResourceZone *pClosest = 0;
+ float flClosestDist = 100000000;
+
+ CBaseEntity *pEntity = NULL;
+ while( (pEntity = gEntList.FindEntityByClassname( pEntity, "trigger_resourcezone" )) != NULL )
+ {
+ CResourceZone *pZone = (CResourceZone*)pEntity;
+
+ // Ignore empty zones and zones not captured by this team.
+ if ( pZone->IsEmpty() || !pZone->GetActive() )
+ continue;
+
+ Vector vZoneCenter = pZone->WorldSpaceCenter();
+
+ // Look for a resource pump on this zone.
+ bool bPump = objType == OBJ_RESOURCEPUMP && pPlayer->NumPumpsOnResourceZone( pZone ) == 0;
+ if ( bPump )
+ {
+ // Make sure it's their preferred tech.
+ float flTestDist = pPlayer->GetAbsOrigin().DistTo( vZoneCenter );
+ if ( flTestDist < flClosestDist )
+ {
+ pClosest = pZone;
+ flClosestDist = flTestDist;
+ }
+ }
+ }
+
+ if ( pClosest )
+ {
+ // No pump here. Build one!
+ pPlayer->GetTFTeam()->AddOrder(
+ ORDER_BUILD,
+ pClosest,
+ pPlayer,
+ 1e24,
+ 60,
+ pOrder
+ );
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}
+
+
+int SortFn_PlayerObjectsByDistance( void *pUserData, int a, int b )
+{
+ CSortBase *pSortBase = (CSortBase*)pUserData;
+
+ CBaseObject* pObjA = pSortBase->m_pPlayer->GetObject(a);
+ CBaseObject* pObjB = pSortBase->m_pPlayer->GetObject(b);
+ if (!pObjA)
+ return false;
+ if (!pObjB)
+ return true;
+
+ const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
+
+ return v.DistTo( pObjA->GetAbsOrigin() ) < v.DistTo( pObjB->GetAbsOrigin() );
+}
+
+
+int SortFn_TeamObjectsByDistance( void *pUserData, int a, int b )
+{
+ CSortBase *pSortBase = (CSortBase*)pUserData;
+
+ CBaseObject *pObj1 = pSortBase->m_pPlayer->GetTFTeam()->GetObject( a );
+ CBaseObject *pObj2 = pSortBase->m_pPlayer->GetTFTeam()->GetObject( b );
+ const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
+
+ return v.DistTo( pObj1->GetAbsOrigin() ) < v.DistTo( pObj2->GetAbsOrigin() );
+}
+
+
+int SortFn_PlayerEntitiesByDistance( void *pUserData, int a, int b )
+{
+ CSortBase *pSortBase = (CSortBase*)pUserData;
+
+ CBaseEntity *pObj1 = CBaseEntity::Instance( engine->PEntityOfEntIndex( a+1 ) );
+ CBaseEntity *pObj2 = CBaseEntity::Instance( engine->PEntityOfEntIndex( b+1 ) );
+ const Vector &v = pSortBase->m_pPlayer->GetAbsOrigin();
+
+ return v.DistTo( pObj1->GetAbsOrigin() ) < v.DistTo( pObj2->GetAbsOrigin() );
+}
+
+
+int SortFn_DistanceAndConcentration( void *pUserData, int a, int b )
+{
+ CSortBase *p = (CSortBase*)pUserData;
+
+ // Compare distances. Each rope attachment to another ent
+ // subtracts 200 inches, so the order is biased towards covering
+ // groups of objects together.
+ CBaseObject *pObjectA = p->GetTeam()->GetObject( a );
+ CBaseObject *pObjectB = p->GetTeam()->GetObject( b );
+
+ const Vector &vOrigin1 = pObjectA->GetAbsOrigin();
+ const Vector &vOrigin2 = p->GetTeam()->GetObject( b )->GetAbsOrigin();
+
+ float flScore1 = -p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin1 );
+ float flScore2 = -p->m_pPlayer->GetAbsOrigin().DistTo( vOrigin2 );
+
+ flScore1 += pObjectA->RopeCount() * 200;
+ flScore2 += pObjectB->RopeCount() * 200;
+
+ return flScore1 > flScore2;
+}
+
+
+bool IsValidFn_NearAndNotCovered( void *pUserData, int a )
+{
+ CSortBase *p = (CSortBase*)pUserData;
+ CBaseObject *pObj = p->m_pPlayer->GetTFTeam()->GetObject( a );
+
+ // Is the object too far away to be covered?
+ if ( p->m_pPlayer->GetAbsOrigin().DistTo( pObj->GetAbsOrigin() ) > p->m_flMaxDist )
+ return false;
+
+ // Don't cover certain entities (like sentry guns, sand bags, etc).
+ switch( p->m_ObjectType )
+ {
+ case OBJ_SENTRYGUN_PLASMA:
+ {
+ if ( !pObj->WantsCoverFromSentryGun() )
+ return false;
+
+ if ( p->m_pPlayer->GetTFTeam()->IsCoveredBySentryGun( pObj->GetAbsOrigin() ) )
+ return false;
+ }
+ break;
+
+ case OBJ_SHIELDWALL:
+ {
+ if ( !pObj->WantsCover() )
+ return false;
+
+ if ( p->m_pPlayer->GetTFTeam()->GetNumShieldWallsCoveringPosition( pObj->GetAbsOrigin() ) )
+ return false;
+ }
+ break;
+
+ case OBJ_RESUPPLY:
+ {
+ if ( p->m_pPlayer->GetTFTeam()->GetNumResuppliesCoveringPosition( pObj->GetAbsOrigin() ) )
+ return false;
+ }
+ break;
+
+ case OBJ_RESPAWN_STATION:
+ {
+ if ( p->m_pPlayer->GetTFTeam()->GetNumRespawnStationsCoveringPosition( pObj->GetAbsOrigin() ) )
+ return false;
+ }
+ break;
+
+ default:
+ {
+ Assert( !"Unsupported object type" );
+ }
+ break;
+ }
+
+ return true;
+}
+
+
+bool OrderCreator_GenericObject(
+ CPlayerClass *pClass,
+ int objectType,
+ float flMaxDist,
+ COrder *pOrder
+ )
+{
+ // Can we build one?
+ if ( pClass->CanBuild( objectType ) != CB_CAN_BUILD )
+ return false;
+
+ CBaseTFPlayer *pPlayer = pClass->GetPlayer();
+ CTFTeam *pTeam = pClass->GetTeam();
+
+ // Sort nearby objects.
+ CSortBase info;
+ info.m_pPlayer = pPlayer;
+ info.m_flMaxDist = flMaxDist;
+ info.m_ObjectType = objectType;
+
+ int sorted[MAX_TEAM_OBJECTS];
+ int nSorted = BuildSortedActiveList(
+ sorted, // the sorted list of objects
+ MAX_TEAM_OBJECTS,
+ SortFn_DistanceAndConcentration, // sort on distance and entity concentration
+ IsValidFn_NearAndNotCovered, // filter function
+ &info, // user data
+ pTeam->GetNumObjects() // number of objects to check
+ );
+
+ if( nSorted )
+ {
+ // Ok, make an order to cover the closest object with a sentry gun.
+ CBaseEntity *pEnt = pTeam->GetObject( sorted[0] );
+
+ pTeam->AddOrder(
+ ORDER_BUILD,
+ pEnt,
+ pPlayer,
+ flMaxDist,
+ 60,
+ pOrder
+ );
+
+ return true;
+ }
+ else
+ {
+ return false;
+ }
+}