mirror of
https://github.com/nillerusr/source-engine.git
synced 2026-08-09 02:09:35 +00:00
1
This commit is contained in:
@@ -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;
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user