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.
345 lines
8.0 KiB
345 lines
8.0 KiB
//========= 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; |
|
} |
|
}
|
|
|