diff options
Diffstat (limited to 'game/server/tf2/order_helpers.cpp')
| -rw-r--r-- | game/server/tf2/order_helpers.cpp | 345 |
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; + } +} |