2020-04-22 16:56:21 +00:00
|
|
|
//========= 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,
|
2021-10-23 11:41:59 +00:00
|
|
|
1e24,
|
2020-04-22 16:56:21 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|