source-engine/game/server/tf2/order_helpers.cpp
2021-10-23 14:41:59 +03:00

346 lines
8.0 KiB
C++

//========= 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;
}
}