mirror of
https://github.com/ENSL/NS.git
synced 2025-01-21 08:50:55 +00:00
Add cloak behaviour
Bots will no longer see cloaked players, and have a chance based on cloak level, movement and size of the players. Bots will also sneak when approaching enemies while cloaked.
This commit is contained in:
parent
0cb450605d
commit
7d659fb8c2
13 changed files with 609 additions and 589 deletions
|
@ -1130,6 +1130,9 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
{
|
||||
// Remove node from open list and put it in closed list.
|
||||
dtNode* bestNode = m_openList->pop();
|
||||
|
||||
if (!bestNode) { continue; }
|
||||
|
||||
bestNode->flags &= ~DT_NODE_OPEN;
|
||||
bestNode->flags |= DT_NODE_CLOSED;
|
||||
|
||||
|
@ -1143,14 +1146,16 @@ dtStatus dtNavMeshQuery::findPath(dtPolyRef startRef, dtPolyRef endRef,
|
|||
// Get current poly and tile.
|
||||
// The API input has been cheked already, skip checking internal data.
|
||||
const dtPolyRef bestRef = bestNode->id;
|
||||
const dtMeshTile* bestTile = 0;
|
||||
const dtPoly* bestPoly = 0;
|
||||
const dtMeshTile* bestTile = nullptr;
|
||||
const dtPoly* bestPoly = nullptr;
|
||||
m_nav->getTileAndPolyByRefUnsafe(bestRef, &bestTile, &bestPoly);
|
||||
|
||||
if (!bestTile || !bestPoly || !bestTile->links) { continue; }
|
||||
|
||||
// Get parent poly and tile.
|
||||
dtPolyRef parentRef = 0;
|
||||
const dtMeshTile* parentTile = 0;
|
||||
const dtPoly* parentPoly = 0;
|
||||
const dtMeshTile* parentTile = nullptr;
|
||||
const dtPoly* parentPoly = nullptr;
|
||||
if (bestNode->pidx)
|
||||
parentRef = m_nodePool->getNodeAtIdx(bestNode->pidx)->id;
|
||||
if (parentRef)
|
||||
|
|
|
@ -163,7 +163,7 @@ enum OffMeshState
|
|||
struct dtPoly
|
||||
{
|
||||
/// Index to first link in linked list. (Or #DT_NULL_LINK if there is no link.)
|
||||
unsigned int firstLink;
|
||||
unsigned int firstLink = DT_NULL_LINK;
|
||||
|
||||
/// The indices of the polygon's vertices.
|
||||
/// The actual vertices are located in dtMeshTile::verts.
|
||||
|
@ -173,14 +173,14 @@ struct dtPoly
|
|||
unsigned short neis[DT_VERTS_PER_POLYGON];
|
||||
|
||||
/// The user defined polygon flags.
|
||||
unsigned int flags;
|
||||
unsigned int flags = 0;
|
||||
|
||||
/// The number of vertices in the polygon.
|
||||
unsigned char vertCount;
|
||||
unsigned char vertCount = 0;
|
||||
|
||||
/// The bit packed area id and polygon type.
|
||||
/// @note Use the structure's set and get methods to acess this value.
|
||||
unsigned char areaAndtype;
|
||||
unsigned char areaAndtype = 0;
|
||||
|
||||
/// Sets the user defined area id. [Limit: < #DT_MAX_AREAS]
|
||||
inline void setArea(unsigned char a) { areaAndtype = (areaAndtype & 0xc0) | (a & 0x3f); }
|
||||
|
@ -200,10 +200,10 @@ struct dtPoly
|
|||
/// Defines the location of detail sub-mesh data within a dtMeshTile.
|
||||
struct dtPolyDetail
|
||||
{
|
||||
unsigned int vertBase; ///< The offset of the vertices in the dtMeshTile::detailVerts array.
|
||||
unsigned int triBase; ///< The offset of the triangles in the dtMeshTile::detailTris array.
|
||||
unsigned char vertCount; ///< The number of vertices in the sub-mesh.
|
||||
unsigned char triCount; ///< The number of triangles in the sub-mesh.
|
||||
unsigned int vertBase = 0; ///< The offset of the vertices in the dtMeshTile::detailVerts array.
|
||||
unsigned int triBase = 0; ///< The offset of the triangles in the dtMeshTile::detailTris array.
|
||||
unsigned char vertCount = 0; ///< The number of vertices in the sub-mesh.
|
||||
unsigned char triCount = 0; ///< The number of triangles in the sub-mesh.
|
||||
};
|
||||
|
||||
/// Defines a link between polygons.
|
||||
|
@ -211,12 +211,12 @@ struct dtPolyDetail
|
|||
/// @see dtMeshTile
|
||||
struct dtLink
|
||||
{
|
||||
dtPolyRef ref; ///< Neighbour reference. (The neighbor that is linked to.)
|
||||
unsigned int next; ///< Index of the next link.
|
||||
unsigned char edge; ///< Index of the polygon edge that owns this link.
|
||||
unsigned char side; ///< If a boundary link, defines on which side the link is.
|
||||
unsigned char bmin; ///< If a boundary link, defines the minimum sub-edge area.
|
||||
unsigned char bmax; ///< If a boundary link, defines the maximum sub-edge area.
|
||||
dtPolyRef ref = 0; ///< Neighbour reference. (The neighbor that is linked to.)
|
||||
unsigned int next = 0; ///< Index of the next link.
|
||||
unsigned char edge = 0; ///< Index of the polygon edge that owns this link.
|
||||
unsigned char side = 0; ///< If a boundary link, defines on which side the link is.
|
||||
unsigned char bmin = 0; ///< If a boundary link, defines the minimum sub-edge area.
|
||||
unsigned char bmax = 0; ///< If a boundary link, defines the maximum sub-edge area.
|
||||
int OffMeshID = -1; ///< If an off-mesh connection, this will be the UserID of the connection that made this link
|
||||
};
|
||||
|
||||
|
@ -225,8 +225,8 @@ struct dtLink
|
|||
/// @see dtMeshTile
|
||||
struct dtBVNode
|
||||
{
|
||||
unsigned short bmin[3]; ///< Minimum bounds of the node's AABB. [(x, y, z)]
|
||||
unsigned short bmax[3]; ///< Maximum bounds of the node's AABB. [(x, y, z)]
|
||||
unsigned short bmin[3] = { 0 }; ///< Minimum bounds of the node's AABB. [(x, y, z)]
|
||||
unsigned short bmax[3] = { 0 }; ///< Maximum bounds of the node's AABB. [(x, y, z)]
|
||||
int i; ///< The node's index. (Negative for escape sequence.)
|
||||
};
|
||||
|
||||
|
@ -235,7 +235,7 @@ struct dtBVNode
|
|||
struct dtOffMeshConnection
|
||||
{
|
||||
/// The endpoints of the connection. [(ax, ay, az, bx, by, bz)]
|
||||
float pos[6] = { 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f };
|
||||
float pos[6] = { 0.0f };
|
||||
|
||||
/// The radius of the endpoints. [Limit: >= 0]
|
||||
float rad = 0.0f;
|
||||
|
@ -280,65 +280,65 @@ struct dtOffMeshConnection
|
|||
/// @ingroup detour
|
||||
struct dtMeshHeader
|
||||
{
|
||||
int magic; ///< Tile magic number. (Used to identify the data format.)
|
||||
int version; ///< Tile data format version number.
|
||||
int x; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int y; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int layer; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
unsigned int userId; ///< The user defined id of the tile.
|
||||
int polyCount; ///< The number of polygons in the tile.
|
||||
int vertCount; ///< The number of vertices in the tile.
|
||||
int maxLinkCount; ///< The number of allocated links.
|
||||
int detailMeshCount; ///< The number of sub-meshes in the detail mesh.
|
||||
int magic = 0; ///< Tile magic number. (Used to identify the data format.)
|
||||
int version = 0; ///< Tile data format version number.
|
||||
int x = 0; ///< The x-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int y = 0; ///< The y-position of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
int layer = 0; ///< The layer of the tile within the dtNavMesh tile grid. (x, y, layer)
|
||||
unsigned int userId = 0; ///< The user defined id of the tile.
|
||||
int polyCount = 0; ///< The number of polygons in the tile.
|
||||
int vertCount = 0; ///< The number of vertices in the tile.
|
||||
int maxLinkCount = 0; ///< The number of allocated links.
|
||||
int detailMeshCount = 0; ///< The number of sub-meshes in the detail mesh.
|
||||
|
||||
/// The number of unique vertices in the detail mesh. (In addition to the polygon vertices.)
|
||||
int detailVertCount;
|
||||
int detailVertCount = 0;
|
||||
|
||||
int detailTriCount; ///< The number of triangles in the detail mesh.
|
||||
int bvNodeCount; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
|
||||
int offMeshConCount; ///< The number of off-mesh connections.
|
||||
int detailTriCount = 0; ///< The number of triangles in the detail mesh.
|
||||
int bvNodeCount = 0; ///< The number of bounding volume nodes. (Zero if bounding volumes are disabled.)
|
||||
int offMeshConCount = 0; ///< The number of off-mesh connections.
|
||||
|
||||
int offMeshBase; ///< The index of the first polygon which is an off-mesh connection.
|
||||
float walkableHeight; ///< The height of the agents using the tile.
|
||||
float walkableRadius; ///< The radius of the agents using the tile.
|
||||
float walkableClimb; ///< The maximum climb height of the agents using the tile.
|
||||
float bmin[3]; ///< The minimum bounds of the tile's AABB. [(x, y, z)]
|
||||
float bmax[3]; ///< The maximum bounds of the tile's AABB. [(x, y, z)]
|
||||
int offMeshBase = 0; ///< The index of the first polygon which is an off-mesh connection.
|
||||
float walkableHeight = 0.0f; ///< The height of the agents using the tile.
|
||||
float walkableRadius = 0.0f; ///< The radius of the agents using the tile.
|
||||
float walkableClimb = 0.0f; ///< The maximum climb height of the agents using the tile.
|
||||
float bmin[3] = { 0.0f }; ///< The minimum bounds of the tile's AABB. [(x, y, z)]
|
||||
float bmax[3] = { 0.0f }; ///< The maximum bounds of the tile's AABB. [(x, y, z)]
|
||||
|
||||
/// The bounding volume quantization factor.
|
||||
float bvQuantFactor;
|
||||
float bvQuantFactor = 0.0f;
|
||||
};
|
||||
|
||||
/// Defines a navigation mesh tile.
|
||||
/// @ingroup detour
|
||||
struct dtMeshTile
|
||||
{
|
||||
unsigned int salt; ///< Counter describing modifications to the tile.
|
||||
unsigned int salt = 0; ///< Counter describing modifications to the tile.
|
||||
|
||||
unsigned int linksFreeList; ///< Index to the next free link.
|
||||
dtMeshHeader* header; ///< The tile header.
|
||||
dtPoly* polys; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
||||
float* verts; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
|
||||
dtLink* links; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
||||
dtPolyDetail* detailMeshes; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
||||
unsigned int linksFreeList = 0; ///< Index to the next free link.
|
||||
dtMeshHeader* header = nullptr; ///< The tile header.
|
||||
dtPoly* polys = nullptr; ///< The tile polygons. [Size: dtMeshHeader::polyCount]
|
||||
float* verts = nullptr; ///< The tile vertices. [Size: dtMeshHeader::vertCount]
|
||||
dtLink* links = nullptr; ///< The tile links. [Size: dtMeshHeader::maxLinkCount]
|
||||
dtPolyDetail* detailMeshes = nullptr; ///< The tile's detail sub-meshes. [Size: dtMeshHeader::detailMeshCount]
|
||||
|
||||
/// The detail mesh's unique vertices. [(x, y, z) * dtMeshHeader::detailVertCount]
|
||||
float* detailVerts;
|
||||
float* detailVerts = nullptr;
|
||||
|
||||
/// The detail mesh's triangles. [(vertA, vertB, vertC, triFlags) * dtMeshHeader::detailTriCount].
|
||||
/// See dtDetailTriEdgeFlags and dtGetDetailTriEdgeFlags.
|
||||
unsigned char* detailTris;
|
||||
unsigned char* detailTris = nullptr;
|
||||
|
||||
/// The tile bounding volume nodes. [Size: dtMeshHeader::bvNodeCount]
|
||||
/// (Will be null if bounding volumes are disabled.)
|
||||
dtBVNode* bvTree;
|
||||
dtBVNode* bvTree = nullptr;
|
||||
|
||||
dtOffMeshConnection** offMeshCons; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
|
||||
dtOffMeshConnection** offMeshCons = nullptr; ///< The tile off-mesh connections. [Size: dtMeshHeader::offMeshConCount]
|
||||
|
||||
unsigned char* data; ///< The tile data. (Not directly accessed under normal situations.)
|
||||
int dataSize; ///< Size of the tile data.
|
||||
int flags; ///< Tile flags. (See: #dtTileFlags)
|
||||
dtMeshTile* next; ///< The next free tile, or the next tile in the spatial grid.
|
||||
unsigned char* data = nullptr; ///< The tile data. (Not directly accessed under normal situations.)
|
||||
int dataSize = 0; ///< Size of the tile data.
|
||||
int flags = 0; ///< Tile flags. (See: #dtTileFlags)
|
||||
dtMeshTile* next = nullptr; ///< The next free tile, or the next tile in the spatial grid.
|
||||
private:
|
||||
dtMeshTile(const dtMeshTile&);
|
||||
dtMeshTile& operator=(const dtMeshTile&);
|
||||
|
@ -359,11 +359,11 @@ inline int dtGetDetailTriEdgeFlags(unsigned char triFlags, int edgeIndex)
|
|||
/// @ingroup detour
|
||||
struct dtNavMeshParams
|
||||
{
|
||||
float orig[3]; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
|
||||
float tileWidth; ///< The width of each tile. (Along the x-axis.)
|
||||
float tileHeight; ///< The height of each tile. (Along the z-axis.)
|
||||
int maxTiles; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
int maxPolys; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
float orig[3] = { 0.0f }; ///< The world space origin of the navigation mesh's tile space. [(x, y, z)]
|
||||
float tileWidth = 0.0f; ///< The width of each tile. (Along the x-axis.)
|
||||
float tileHeight = 0.0f; ///< The height of each tile. (Along the z-axis.)
|
||||
int maxTiles = 0; ///< The maximum number of tiles the navigation mesh can contain. This and maxPolys are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
int maxPolys = 0; ///< The maximum number of polygons each tile can contain. This and maxTiles are used to calculate how many bits are needed to identify tiles and polygons uniquely.
|
||||
};
|
||||
|
||||
/// A navigation mesh based on tiles of convex polygons.
|
||||
|
@ -700,20 +700,20 @@ private:
|
|||
void closestPointOnPoly(dtPolyRef ref, const float* pos, float* closest, bool* posOverPoly) const;
|
||||
|
||||
dtNavMeshParams m_params; ///< Current initialization params. TODO: do not store this info twice.
|
||||
float m_orig[3]; ///< Origin of the tile (0,0)
|
||||
float m_tileWidth, m_tileHeight; ///< Dimensions of each tile.
|
||||
int m_maxTiles; ///< Max number of tiles.
|
||||
int m_tileLutSize; ///< Tile hash lookup size (must be pot).
|
||||
int m_tileLutMask; ///< Tile hash lookup mask.
|
||||
float m_orig[3] = { 0.0f }; ///< Origin of the tile (0,0)
|
||||
float m_tileWidth = 0.0f, m_tileHeight = 0.0f; ///< Dimensions of each tile.
|
||||
int m_maxTiles = 0; ///< Max number of tiles.
|
||||
int m_tileLutSize = 0; ///< Tile hash lookup size (must be pot).
|
||||
int m_tileLutMask = 0; ///< Tile hash lookup mask.
|
||||
|
||||
dtMeshTile** m_posLookup; ///< Tile hash lookup.
|
||||
dtMeshTile* m_nextFree; ///< Freelist of tiles.
|
||||
dtMeshTile* m_tiles; ///< List of tiles.
|
||||
dtMeshTile** m_posLookup = nullptr; ///< Tile hash lookup.
|
||||
dtMeshTile* m_nextFree = nullptr; ///< Freelist of tiles.
|
||||
dtMeshTile* m_tiles = nullptr; ///< List of tiles.
|
||||
|
||||
#ifndef DT_POLYREF64
|
||||
unsigned int m_saltBits; ///< Number of salt bits in the tile ID.
|
||||
unsigned int m_tileBits; ///< Number of tile bits in the tile ID.
|
||||
unsigned int m_polyBits; ///< Number of poly bits in the tile ID.
|
||||
unsigned int m_saltBits = 0; ///< Number of salt bits in the tile ID.
|
||||
unsigned int m_tileBits = 0; ///< Number of tile bits in the tile ID.
|
||||
unsigned int m_polyBits = 0; ///< Number of poly bits in the tile ID.
|
||||
#endif
|
||||
|
||||
friend class dtNavMeshQuery;
|
||||
|
|
|
@ -34,9 +34,9 @@
|
|||
/// @ingroup detour
|
||||
class dtQueryFilter
|
||||
{
|
||||
float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
|
||||
unsigned int m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
|
||||
unsigned int m_excludeFlags; ///< Flags for polygons that should not be visted. (Used by default implementation.)
|
||||
float m_areaCost[DT_MAX_AREAS] = { 1.0f }; ///< Cost per area type. (Used by default implementation.)
|
||||
unsigned int m_includeFlags = 0; ///< Flags for polygons that can be visited. (Used by default implementation.)
|
||||
unsigned int m_excludeFlags = 0; ///< Flags for polygons that should not be visted. (Used by default implementation.)
|
||||
|
||||
public:
|
||||
dtQueryFilter();
|
||||
|
@ -130,25 +130,25 @@ public:
|
|||
struct dtRaycastHit
|
||||
{
|
||||
/// The hit parameter. (FLT_MAX if no wall hit.)
|
||||
float t;
|
||||
float t = 0.0f;
|
||||
|
||||
/// hitNormal The normal of the nearest wall hit. [(x, y, z)]
|
||||
float hitNormal[3];
|
||||
float hitNormal[3] = { 0.0f };
|
||||
|
||||
/// The index of the edge on the final polygon where the wall was hit.
|
||||
int hitEdgeIndex;
|
||||
int hitEdgeIndex = 0;
|
||||
|
||||
/// Pointer to an array of reference ids of the visited polygons. [opt]
|
||||
dtPolyRef* path;
|
||||
dtPolyRef* path = nullptr;
|
||||
|
||||
/// The number of visited polygons. [opt]
|
||||
int pathCount;
|
||||
int pathCount = 0;
|
||||
|
||||
/// The maximum number of polygons the @p path array can hold.
|
||||
int maxPath;
|
||||
int maxPath = 0;
|
||||
|
||||
/// The cost of the path until hit.
|
||||
float pathCost;
|
||||
float pathCost = 0;
|
||||
};
|
||||
|
||||
/// Provides custom polygon query behavior.
|
||||
|
|
|
@ -35,13 +35,13 @@ static const int DT_NODE_PARENT_BITS = 24;
|
|||
static const int DT_NODE_STATE_BITS = 2;
|
||||
struct dtNode
|
||||
{
|
||||
float pos[3]; ///< Position of the node.
|
||||
float cost; ///< Cost from previous node to current node.
|
||||
float total; ///< Cost up to the node.
|
||||
float pos[3] = { 0.0f }; ///< Position of the node.
|
||||
float cost = 0.0f; ///< Cost from previous node to current node.
|
||||
float total = 0.0f; ///< Cost up to the node.
|
||||
unsigned int pidx : DT_NODE_PARENT_BITS; ///< Index to parent node.
|
||||
unsigned int state : DT_NODE_STATE_BITS; ///< extra state information. A polyRef can have multiple nodes with different extra info. see DT_MAX_STATES_PER_NODE
|
||||
unsigned int flags : 3; ///< Node flags. A combination of dtNodeFlags.
|
||||
dtPolyRef id; ///< Polygon ref the node corresponds to.
|
||||
dtPolyRef id = 0; ///< Polygon ref the node corresponds to.
|
||||
};
|
||||
|
||||
static const int DT_MAX_STATES_PER_NODE = 1 << DT_NODE_STATE_BITS; // number of extra states per node. See dtNode::state
|
||||
|
@ -97,12 +97,12 @@ private:
|
|||
dtNodePool(const dtNodePool&);
|
||||
dtNodePool& operator=(const dtNodePool&);
|
||||
|
||||
dtNode* m_nodes;
|
||||
dtNodeIndex* m_first;
|
||||
dtNodeIndex* m_next;
|
||||
const int m_maxNodes;
|
||||
const int m_hashSize;
|
||||
int m_nodeCount;
|
||||
dtNode* m_nodes = nullptr;
|
||||
dtNodeIndex* m_first = nullptr;
|
||||
dtNodeIndex* m_next = nullptr;
|
||||
const int m_maxNodes = 0;
|
||||
const int m_hashSize = 0;
|
||||
int m_nodeCount = 0;
|
||||
};
|
||||
|
||||
class dtNodeQueue
|
||||
|
@ -159,9 +159,9 @@ private:
|
|||
void bubbleUp(int i, dtNode* node);
|
||||
void trickleDown(int i, dtNode* node);
|
||||
|
||||
dtNode** m_heap;
|
||||
const int m_capacity;
|
||||
int m_size;
|
||||
dtNode** m_heap = nullptr;
|
||||
const int m_capacity = 0;
|
||||
int m_size = 0;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -145,7 +145,7 @@ bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* Structu
|
|||
|
||||
bool AICOMM_RecycleStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToRecycle)
|
||||
{
|
||||
if (!StructureToRecycle || StructureToRecycle->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE) { return false; }
|
||||
if (!StructureToRecycle || StructureToRecycle->StructureType == STRUCTURE_MARINE_DEPLOYEDMINE || UTIL_StructureIsRecycling(StructureToRecycle->edict)) { return false; }
|
||||
|
||||
return AICOMM_ResearchTech(pBot, StructureToRecycle, BUILD_RECYCLE);
|
||||
}
|
||||
|
@ -956,11 +956,11 @@ bool AICOMM_IsRequestValid(ai_commander_request* Request)
|
|||
return !IsPlayerBuffed(Requestor)
|
||||
&& !AITAC_ItemExistsInLocation(Requestor->v.origin, DEPLOYABLE_ITEM_CATALYSTS, RequestorTeam, AI_REACHABILITY_MARINE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
|
||||
case BUILD_PHASEGATE:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, STRUCTURE_MARINE_PHASEGATE, Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, STRUCTURE_MARINE_PHASEGATE, Requestor->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
|
||||
case BUILD_TURRET_FACTORY:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(15.0f));
|
||||
case BUILD_ARMORY:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(15.0f));
|
||||
case BUILD_COMMANDSTATION:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, STRUCTURE_MARINE_COMMCHAIR, Requestor->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
|
||||
case BUILD_SCAN:
|
||||
|
@ -2927,305 +2927,124 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_PHASEGATE)
|
||||
|
||||
if (NextRequest->RequestType == BUILD_PHASEGATE && !AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_PHASETECH))
|
||||
{
|
||||
if (!AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_PHASETECH))
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "We haven't got phase tech yet, %s. Ask again later.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() < BALANCE_VAR(kPhaseGateCost))
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "Just waiting on resources, %s. Will drop asap.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bAcknowledged = true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
char msg[128];
|
||||
sprintf(msg, "We haven't got phase tech yet, %s. Ask again later.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_ARMORY || NextRequest->RequestType == BUILD_COMMANDSTATION)
|
||||
if (NextRequest->RequestType == BUILD_OBSERVATORY && !AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_PHASETECH))
|
||||
{
|
||||
float RequiredRes = (NextRequest->RequestType == BUILD_ARMORY) ? BALANCE_VAR(kArmoryCost) : BALANCE_VAR(kCommandStationCost);
|
||||
DeployableSearchFilter ArmouryFilter;
|
||||
ArmouryFilter.DeployableTeam = CommanderTeam;
|
||||
ArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
ArmouryFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ArmouryFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
if (pBot->Player->GetResources() < RequiredRes)
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "Just waiting on resources, %s. Will drop asap.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bAcknowledged = true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool bHasArmoury = AITAC_DeployableExistsAtLocation(ZERO_VECTOR, &ArmouryFilter);
|
||||
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
AvHAIDeployableStructureType StructureToDeploy = (NextRequest->RequestType == BUILD_ARMORY) ? STRUCTURE_MARINE_ARMOURY : STRUCTURE_MARINE_COMMCHAIR;
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
if (!bHasArmoury)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
sprintf(msg, "We haven't got an armory yet, %s. Ask again later.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_TURRET_FACTORY)
|
||||
|
||||
float RequiredRes = 0.0f;
|
||||
AvHAIDeployableStructureType StructureToDeploy = STRUCTURE_NONE;
|
||||
|
||||
switch (NextRequest->RequestType)
|
||||
{
|
||||
if (pBot->Player->GetResources() < BALANCE_VAR(kTurretFactoryCost))
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "Just waiting on resources, %s. Will drop asap.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bAcknowledged = true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
case BUILD_ARMORY:
|
||||
RequiredRes = BALANCE_VAR(kArmoryCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_ARMOURY;
|
||||
break;
|
||||
case BUILD_COMMANDSTATION:
|
||||
RequiredRes = BALANCE_VAR(kCommandStationCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_COMMCHAIR;
|
||||
break;
|
||||
case BUILD_OBSERVATORY:
|
||||
RequiredRes = BALANCE_VAR(kObservatoryCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_OBSERVATORY;
|
||||
break;
|
||||
case BUILD_TURRET_FACTORY:
|
||||
RequiredRes = BALANCE_VAR(kTurretFactoryCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_TURRETFACTORY;
|
||||
case BUILD_TURRET:
|
||||
RequiredRes = BALANCE_VAR(kSentryCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_TURRET;
|
||||
case BUILD_PHASEGATE:
|
||||
RequiredRes = BALANCE_VAR(kPhaseGateCost);
|
||||
StructureToDeploy = STRUCTURE_MARINE_PHASEGATE;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_TURRET)
|
||||
// Invalid commander request
|
||||
if (StructureToDeploy == STRUCTURE_NONE)
|
||||
{
|
||||
if (pBot->Player->GetResources() < BALANCE_VAR(kSentryCost))
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (pBot->Player->GetResources() < RequiredRes)
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "Just waiting on resources, %s. Will drop asap.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bAcknowledged = true;
|
||||
return false;
|
||||
}
|
||||
char msg[128];
|
||||
sprintf(msg, "Just waiting on resources, %s. Will drop asap.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bAcknowledged = true;
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3235,9 +3054,14 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good deploy spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -3679,6 +3503,10 @@ void AICOMM_ReceiveChatRequest(AvHAIPlayer* Commander, edict_t* Requestor, const
|
|||
{
|
||||
NewRequestType = BUILD_COMMANDSTATION;
|
||||
}
|
||||
else if (!stricmp(Request, "obs") || !stricmp(Request, "observatory"))
|
||||
{
|
||||
NewRequestType = BUILD_OBSERVATORY;
|
||||
}
|
||||
|
||||
if (NewRequestType == MESSAGE_NULL) { return; }
|
||||
|
||||
|
|
|
@ -815,6 +815,8 @@ float UTIL_CalculateSlopeAngleBetweenPoints(const Vector StartPoint, const Vecto
|
|||
// Function to check if a finite line intersects with an AABB
|
||||
bool vlineIntersectsAABB(Vector lineStart, Vector lineEnd, Vector BoxMinPosition, Vector BoxMaxPosition)
|
||||
{
|
||||
if (vPointOverlaps3D(lineStart, BoxMinPosition, BoxMaxPosition) || vPointOverlaps3D(lineEnd, BoxMinPosition, BoxMaxPosition)) { return true; }
|
||||
|
||||
Vector RayDir = UTIL_GetVectorNormal(lineEnd - lineStart);
|
||||
float LineLength = vDist3D(lineStart, lineEnd);
|
||||
Vector dirfrac;
|
||||
|
|
|
@ -2522,6 +2522,27 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
|
|||
|
||||
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(pBot->CurrentFloorPosition, Door, nullptr, true);
|
||||
|
||||
// Fail-safe: If the bot cannot reach any trigger for whatever reason, then telepathically trigger one otherwise it will be stuck forever
|
||||
if (!Trigger)
|
||||
{
|
||||
for (auto it = Door->TriggerEnts.begin(); it != Door->TriggerEnts.end(); it++)
|
||||
{
|
||||
if (it->NextActivationTime > gpGlobals->time)
|
||||
{
|
||||
Trigger = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
Trigger = &(*it);
|
||||
}
|
||||
|
||||
if (Trigger)
|
||||
{
|
||||
Trigger->Entity->Use(pBot->Player, pBot->Player, USE_TOGGLE, 0.0f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Trigger && Trigger->NextActivationTime < gpGlobals->time)
|
||||
{
|
||||
if (Trigger->TriggerType == DOOR_BUTTON)
|
||||
|
@ -2562,45 +2583,56 @@ edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNod
|
|||
{
|
||||
Vector TargetLoc = Vector(FromLoc.x, FromLoc.y, PathNode->requiredZ);
|
||||
|
||||
UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, (pBot!= nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Vector TargetLoc2 = Vector(ToLoc.x, ToLoc.y, PathNode->requiredZ);
|
||||
|
||||
UTIL_TraceLine(TargetLoc, TargetLoc2, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(TargetLoc, TargetLoc2, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(TargetLoc, TargetLoc2, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(TargetLoc, TargetLoc2, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2610,70 +2642,90 @@ edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNod
|
|||
{
|
||||
Vector TargetLoc = Vector(ToLoc.x, ToLoc.y, FromLoc.z);
|
||||
|
||||
UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit);
|
||||
Vector NextTargetLoc = ToLoc + Vector(0.0f, 0.0f, 10.0f);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(TargetLoc, NextTargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(TargetLoc, NextTargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(TargetLoc, NextTargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector StartTrace = FromLoc + Vector(0.0f, 0.0f, 16.0f);
|
||||
Vector EndTrace = ToLoc + Vector(0.0f, 0.0f, 16.0f);
|
||||
|
||||
UTIL_TraceLine(StartTrace, EndTrace, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(StartTrace, EndTrace, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(StartTrace, EndTrace, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(StartTrace, EndTrace, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
|
@ -2908,45 +2960,56 @@ edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector T
|
|||
{
|
||||
Vector TargetLoc = Vector(FromLoc.x, FromLoc.y, ToLocation.z);
|
||||
|
||||
UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0 )
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Vector TargetLoc2 = Vector(ToLoc.x, ToLoc.y, ToLocation.z);
|
||||
|
||||
UTIL_TraceLine(TargetLoc, TargetLoc2, ignore_monsters, dont_ignore_glass, nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc2, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc2, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc2, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2956,64 +3019,84 @@ edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector T
|
|||
{
|
||||
Vector TargetLoc = Vector(ToLoc.x, ToLoc.y, FromLoc.z);
|
||||
|
||||
UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, nullptr, &doorHit);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), ignore_monsters, dont_ignore_glass, nullptr, &doorHit);
|
||||
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(TargetLoc, ToLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(TargetLoc, ToLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(TargetLoc, ToLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
UTIL_TraceLine(FromLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), ignore_monsters, dont_ignore_glass, nullptr, &doorHit);
|
||||
Vector TargetLoc = ToLoc + Vector(0.0f, 0.0f, 10.0f);
|
||||
|
||||
if (!FNullEnt(SearchDoor))
|
||||
{
|
||||
if (doorHit.pHit == SearchDoor) { return doorHit.pHit; }
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, SearchDoor->v.absmin, SearchDoor->v.absmax))
|
||||
{
|
||||
return SearchDoor;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!FNullEnt(doorHit.pHit))
|
||||
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
|
||||
{
|
||||
if (strcmp(STRING(doorHit.pHit->v.classname), "func_door") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_seethroughdoor") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "func_door_rotating") == 0
|
||||
|| strcmp(STRING(doorHit.pHit->v.classname), "avhweldable") == 0)
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->DoorEdict->v.absmin, it->DoorEdict->v.absmax))
|
||||
{
|
||||
return doorHit.pHit;
|
||||
return it->DoorEdict;
|
||||
}
|
||||
}
|
||||
|
||||
for (auto it = NavWeldableObstacles.begin(); it != NavWeldableObstacles.end(); it++)
|
||||
{
|
||||
if (vlineIntersectsAABB(FromLoc, TargetLoc, it->WeldableEdict->v.absmin, it->WeldableEdict->v.absmax))
|
||||
{
|
||||
return it->WeldableEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -3406,7 +3489,9 @@ void GroundMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoin
|
|||
LeapDist = UTIL_MetresToGoldSrcUnits(1.0f);
|
||||
}
|
||||
|
||||
if (CanBotLeap(pBot) && vDist2DSq(pBot->Edict->v.origin, EndPoint) > sqrf(LeapDist) && UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, EndPoint))
|
||||
bool bIsAmbush = (pBot->BotNavInfo.MoveStyle == MOVESTYLE_AMBUSH);
|
||||
|
||||
if (!bIsAmbush && CanBotLeap(pBot) && vDist2DSq(pBot->Edict->v.origin, EndPoint) > sqrf(LeapDist) && UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, pBot->Edict->v.origin, EndPoint))
|
||||
{
|
||||
float CombatWeaponEnergyCost = GetEnergyCostForWeapon(pBot->DesiredCombatWeapon);
|
||||
float RequiredEnergy = (CombatWeaponEnergyCost + GetLeapCost(pBot)) - (GetPlayerEnergyRegenPerSecond(pEdict) * 0.5f); // We allow for around .5s of regen time as well
|
||||
|
@ -7770,7 +7855,6 @@ void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& Trigge
|
|||
|
||||
void UTIL_PopulateWeldableObstacles()
|
||||
{
|
||||
UTIL_ClearWeldablesData();
|
||||
|
||||
CBaseEntity* currWeldable = NULL;
|
||||
while (((currWeldable = UTIL_FindEntityByClassname(currWeldable, "avhweldable")) != NULL))
|
||||
|
@ -8394,6 +8478,7 @@ void UTIL_PopulateDoors()
|
|||
NewDoor.DoorEntity = ToggleRef;
|
||||
NewDoor.DoorEdict = DoorEnt->edict();
|
||||
NewDoor.CurrentState = ToggleRef->m_toggle_state;
|
||||
NewDoor.DoorName = STRING(NewDoor.DoorEdict->v.targetname);
|
||||
|
||||
const char* DoorName = STRING(NewDoor.DoorEdict->v.targetname);
|
||||
|
||||
|
|
|
@ -88,6 +88,7 @@ typedef struct _NAV_DOOR
|
|||
vector<Vector> StopPoints; // Where does this door/platform stop when triggered?
|
||||
NavDoorType DoorType = DOORTYPE_DOOR;
|
||||
vector<AvHAIOffMeshConnection*> AffectedConnections;
|
||||
const char* DoorName;
|
||||
} nav_door;
|
||||
|
||||
typedef struct _NAV_WELDABLE
|
||||
|
|
|
@ -465,11 +465,18 @@ void BotAlienAttackNonPlayerTarget(AvHAIPlayer* pBot, edict_t* Target)
|
|||
}
|
||||
}
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
bool bPlayerCloaked = pBot->Player->GetOpacity() < 0.5f && !GetHasUpgrade(pBot->Edict->v.iuser4, MASK_SENSORY_NEARBY);
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict) || bPlayerCloaked)
|
||||
{
|
||||
if (AITAC_ShouldBotBeCautious(pBot))
|
||||
{
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_HIDE, 100.0f);
|
||||
if (bPlayerCloaked)
|
||||
{
|
||||
pBot->BotNavInfo.bShouldWalk = true;
|
||||
}
|
||||
|
||||
MoveTo(pBot, AttackPoint, MOVESTYLE_AMBUSH, 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1398,7 +1405,9 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
|
||||
float bot_reaction_time = (IsPlayerMarine(pBot->Edict)) ? pBot->BotSkillSettings.marine_bot_reaction_time : pBot->BotSkillSettings.alien_bot_reaction_time;
|
||||
|
||||
bool bIsVisible = (bInFOV && (bHasLOS || bIsTracked));
|
||||
bool bIsPlayerInvisible = UTIL_IsCloakedPlayerInvisible(pBot->Edict, PlayerRef);
|
||||
|
||||
bool bIsVisible = !bIsPlayerInvisible && (bInFOV && (bHasLOS || bIsTracked));
|
||||
|
||||
if (bIsVisible != TrackingInfo->bIsVisible)
|
||||
{
|
||||
|
@ -1416,7 +1425,9 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
|
||||
TrackingInfo->bHasLOS = bHasLOS;
|
||||
|
||||
if (bInFOV && (bHasLOS || bIsTracked))
|
||||
bool bCanSeeEnemy = (!bIsPlayerInvisible && bHasLOS);
|
||||
|
||||
if (bInFOV && (bCanSeeEnemy || bIsTracked))
|
||||
{
|
||||
Vector FloorLocation = UTIL_GetEntityGroundLocation(Enemy);
|
||||
Vector BotVelocity = Enemy->v.velocity;
|
||||
|
@ -1462,7 +1473,7 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!bInFOV || !bHasLOS)
|
||||
if (!bInFOV || !bCanSeeEnemy)
|
||||
{
|
||||
if (gpGlobals->time < TrackingInfo->EndTrackingTime)
|
||||
{
|
||||
|
@ -1540,6 +1551,35 @@ void BotUpdateView(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
bool UTIL_IsCloakedPlayerInvisible(edict_t* Observer, AvHPlayer* Player)
|
||||
{
|
||||
if (Player->GetOpacity() > 0.6f) { return false; }
|
||||
|
||||
if (Player->GetIsCloaked()) { return true; }
|
||||
|
||||
switch (Player->GetUser3())
|
||||
{
|
||||
case AVH_USER3_ALIEN_PLAYER1:
|
||||
case AVH_USER3_ALIEN_PLAYER2:
|
||||
case AVH_USER3_ALIEN_PLAYER3:
|
||||
{
|
||||
if (Player->GetOpacity() < 0.3f) { return true; }
|
||||
|
||||
return (vDist3DSq(Observer->v.origin, Player->pev->origin) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)) || Player->pev->velocity.Length2D() < 50.0f);
|
||||
}
|
||||
case AVH_USER3_ALIEN_PLAYER4:
|
||||
case AVH_USER3_ALIEN_PLAYER5:
|
||||
{
|
||||
if (Player->GetOpacity() > 0.4f) { return false; }
|
||||
if (Player->GetOpacity() < 0.2f) { return true; }
|
||||
|
||||
return vDist3DSq(Observer->v.origin, Player->pev->origin) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f));
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void BotClearEnemyTrackingInfo(enemy_status* TrackingInfo)
|
||||
{
|
||||
TrackingInfo->bIsVisible = false;
|
||||
|
@ -1848,64 +1888,11 @@ void EndBotFrame(AvHAIPlayer* pBot)
|
|||
|
||||
void CustomThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
if (IsPlayerMarine(pBot->Player))
|
||||
if (IsPlayerMarine(pBot->Edict)) { return; }
|
||||
|
||||
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_SENSORY))
|
||||
{
|
||||
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_MINES))
|
||||
{
|
||||
AvHAIDroppedItem* NearestMines = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_MINES, pBot->Player->GetTeam(), AI_REACHABILITY_MARINE, 0.0f, 5000.0f, false);
|
||||
|
||||
if (NearestMines)
|
||||
{
|
||||
AITASK_SetPickupTask(pBot, &pBot->PrimaryBotTask, NearestMines->edict, true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
DeployableSearchFilter MineStructureFilter;
|
||||
MineStructureFilter.DeployableTeam = pBot->Player->GetTeam();
|
||||
MineStructureFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL;
|
||||
MineStructureFilter.ReachabilityTeam = pBot->Player->GetTeam();
|
||||
MineStructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE;
|
||||
|
||||
AvHAIBuildableStructure NearestIP = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &MineStructureFilter);
|
||||
|
||||
if (NearestIP.IsValid())
|
||||
{
|
||||
AITASK_SetMineStructureTask(pBot, &pBot->PrimaryBotTask, NearestIP.edict, true);
|
||||
}
|
||||
}
|
||||
|
||||
BotProgressTask(pBot, &pBot->PrimaryBotTask);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
if (IsPlayerMarine(pBot->Player))
|
||||
{
|
||||
pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot);
|
||||
|
||||
if (pBot->CurrentEnemy < 0)
|
||||
{
|
||||
MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_NORMAL);
|
||||
}
|
||||
else
|
||||
{
|
||||
MarineCombatThink(pBot);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (!IsPlayerOnos(pBot->Edict))
|
||||
{
|
||||
if (pBot->Player->GetResources() < BALANCE_VAR(kOnosCost))
|
||||
{
|
||||
pBot->Player->GiveResources(70.0f);
|
||||
}
|
||||
|
||||
BotEvolveLifeform(pBot, pBot->CurrentFloorPosition, ALIEN_LIFEFORM_FIVE);
|
||||
|
||||
BotEvolveUpgrade(pBot, pBot->CurrentFloorPosition, ALIEN_EVOLUTION_TEN);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -1918,8 +1905,7 @@ void CustomThink(AvHAIPlayer* pBot)
|
|||
else
|
||||
{
|
||||
AlienCombatThink(pBot);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
void DroneThink(AvHAIPlayer* pBot)
|
||||
|
@ -2344,6 +2330,9 @@ AvHAICombatStrategy GetSkulkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_sta
|
|||
return COMBAT_STRATEGY_ATTACK;
|
||||
}
|
||||
|
||||
// We're invisible, so go get them
|
||||
if (pBot->Player->GetOpacity() < 0.1f) { return COMBAT_STRATEGY_ATTACK; }
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
|
||||
|
@ -2955,7 +2944,7 @@ bool RegularMarineCombatThink(AvHAIPlayer* pBot)
|
|||
|
||||
AvHAIBuildableStructure NearestArmouryRef = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &NearestArmoury);
|
||||
|
||||
if (NearestArmouryRef.IsValid() && !IsAreaAffectedBySpores(NearestArmouryRef.Location))
|
||||
if (NearestArmouryRef.IsValid() && (!IsAreaAffectedBySpores(NearestArmouryRef.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
if (!TrackedEnemyRef->bHasLOS || (IsPlayerAlien(pBot->Edict) && vDist2DSq(NearestArmouryRef.Location, CurrentEnemy->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f))))
|
||||
{
|
||||
|
@ -3299,7 +3288,7 @@ void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
|
|||
|
||||
AvHAIBuildableStructure UnbuiltIP = AITAC_FindClosestDeployableToLocation(CommChairLocation, &StructureFilter);
|
||||
|
||||
if (UnbuiltIP.IsValid())
|
||||
if (UnbuiltIP.IsValid() && (!IsAreaAffectedBySpores(UnbuiltIP.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
AITASK_SetBuildTask(pBot, Task, UnbuiltIP.edict, true);
|
||||
return;
|
||||
|
@ -3310,7 +3299,7 @@ void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
|
|||
|
||||
AvHAIBuildableStructure UnbuiltStructure = AITAC_FindClosestDeployableToLocation(CommChairLocation, &StructureFilter);
|
||||
|
||||
if (UnbuiltStructure.IsValid())
|
||||
if (UnbuiltStructure.IsValid() && (!IsAreaAffectedBySpores(UnbuiltStructure.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
AITASK_SetBuildTask(pBot, Task, UnbuiltStructure.edict, true);
|
||||
return;
|
||||
|
@ -3340,7 +3329,7 @@ void AIPlayerSetMarineSweeperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
|
|||
|
||||
AvHAIBuildableStructure AttackedStructure = AITAC_FindClosestDeployableToLocation(CommChairLocation, &AttackedStructureFilter);
|
||||
|
||||
if (AttackedStructure.IsValid())
|
||||
if (AttackedStructure.IsValid() && (!IsAreaAffectedBySpores(AttackedStructure.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
AITASK_SetWeldTask(pBot, Task, AttackedStructure.edict, true);
|
||||
return;
|
||||
|
@ -3632,11 +3621,14 @@ void AIPlayerSetWantsAndNeedsCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Ta
|
|||
// We really need some health or ammo, hit the armoury
|
||||
if (NearestArmoury.IsValid())
|
||||
{
|
||||
Task->TaskType = TASK_RESUPPLY;
|
||||
Task->bTaskIsUrgent = true;
|
||||
Task->TaskLocation = NearestArmoury.Location;
|
||||
Task->TaskTarget = NearestArmoury.edict;
|
||||
return;
|
||||
if (!IsAreaAffectedBySpores(NearestArmoury.Location) || PlayerHasHeavyArmour(pBot->Edict))
|
||||
{
|
||||
Task->TaskType = TASK_RESUPPLY;
|
||||
Task->bTaskIsUrgent = true;
|
||||
Task->TaskLocation = NearestArmoury.Location;
|
||||
Task->TaskTarget = NearestArmoury.edict;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3691,11 +3683,14 @@ void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
|
|||
// We really need some health or ammo, either hit the armoury, or ask for a resupply
|
||||
if (NearestArmoury.IsValid())
|
||||
{
|
||||
Task->TaskType = TASK_RESUPPLY;
|
||||
Task->bTaskIsUrgent = true;
|
||||
Task->TaskLocation = NearestArmoury.Location;
|
||||
Task->TaskTarget = NearestArmoury.edict;
|
||||
return;
|
||||
if (!IsAreaAffectedBySpores(NearestArmoury.Location) || PlayerHasHeavyArmour(pBot->Edict))
|
||||
{
|
||||
Task->TaskType = TASK_RESUPPLY;
|
||||
Task->bTaskIsUrgent = true;
|
||||
Task->TaskLocation = NearestArmoury.Location;
|
||||
Task->TaskTarget = NearestArmoury.edict;
|
||||
return;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -3971,7 +3966,7 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
AvHAIBuildableStructure UnbuiltIP = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &UnbuiltFilter);
|
||||
|
||||
if (UnbuiltIP.IsValid())
|
||||
if (UnbuiltIP.IsValid() && (!IsAreaAffectedBySpores(UnbuiltIP.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
float ThisDist = vDist2D(UnbuiltIP.Location, pBot->Edict->v.origin);
|
||||
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, UnbuiltIP.Location, ThisDist - 5.0f, false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
|
@ -4009,7 +4004,7 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
}
|
||||
|
||||
if (NearestStructure.IsValid())
|
||||
if (NearestStructure.IsValid() && (!IsAreaAffectedBySpores(NearestStructure.Location) || PlayerHasHeavyArmour(pBot->Edict)))
|
||||
{
|
||||
AITASK_SetBuildTask(pBot, Task, NearestStructure.edict, true);
|
||||
return;
|
||||
|
@ -4121,6 +4116,8 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
AvHAIBuildableStructure ThisStructure = (*it);
|
||||
|
||||
if (ThisStructure.edict->v.waterlevel > 0) { continue; }
|
||||
|
||||
int NumMines = AITAC_GetNumDeployablesNearLocation(ThisStructure.Location, &MineFilter);
|
||||
|
||||
if (NumMines < 4)
|
||||
|
@ -4168,8 +4165,6 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
}
|
||||
|
||||
bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -5351,6 +5346,10 @@ void AIPlayerThink(AvHAIPlayer* pBot)
|
|||
{
|
||||
TestNavThink(pBot);
|
||||
}
|
||||
else if (avh_botdebugmode.value == 3)
|
||||
{
|
||||
CustomThink(pBot);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (GetGameRules()->GetMapMode())
|
||||
|
@ -5479,7 +5478,7 @@ void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot)
|
|||
|
||||
bool ShouldBotThink(AvHAIPlayer* pBot)
|
||||
{
|
||||
return NavmeshLoaded() && GetGameRules()->GetGameStarted() && (IsPlayerActiveInGame(pBot->Edict) || IsPlayerCommander(pBot->Edict)) && !IsPlayerGestating(pBot->Edict);
|
||||
return NavmeshLoaded() && GetGameRules()->GetGameStarted() && !AIMGR_HasMatchEnded() && (IsPlayerActiveInGame(pBot->Edict) || IsPlayerCommander(pBot->Edict)) && !IsPlayerGestating(pBot->Edict);
|
||||
}
|
||||
|
||||
void BotResumePlay(AvHAIPlayer* pBot)
|
||||
|
@ -6904,6 +6903,15 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
return;
|
||||
}
|
||||
|
||||
// If we're engaging an enemy turret then finish the job
|
||||
if (Task->TaskType == TASK_ATTACK)
|
||||
{
|
||||
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(20.0f)))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
DeployableSearchFilter AttackedStructuresFilter;
|
||||
AttackedStructuresFilter.DeployableTypes = (IsPlayerLerk(pBot->Edict)) ? SEARCH_ALL_STRUCTURES : STRUCTURE_ALIEN_RESTOWER;
|
||||
AttackedStructuresFilter.DeployableTeam = BotTeam;
|
||||
|
@ -6945,6 +6953,24 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
return;
|
||||
}
|
||||
|
||||
DeployableSearchFilter EnemyStructures;
|
||||
EnemyStructures.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
EnemyStructures.DeployableTeam = BotTeam;
|
||||
EnemyStructures.ReachabilityTeam = BotTeam;
|
||||
EnemyStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
|
||||
EnemyStructures.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
|
||||
|
||||
vector<AvHAIBuildableStructure> NearbyEnemyStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &EnemyStructures);
|
||||
|
||||
for (auto it = NearbyEnemyStructures.begin(); it != NearbyEnemyStructures.end(); it++)
|
||||
{
|
||||
if (UTIL_PlayerHasLOSToEntity(pBot->Edict, it->edict, UTIL_MetresToGoldSrcUnits(20.0f), false))
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, it->edict, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -7200,6 +7226,8 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
|
|||
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_ATTACK || (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_AMBUSH && bShouldBreakAmbush))
|
||||
{
|
||||
|
||||
bool bIsCloaked = (UTIL_IsCloakedPlayerInvisible(CurrentEnemy, pBot->Player) || pBot->Player->GetOpacity() < 0.5f);
|
||||
|
||||
AvHAIWeapon DesiredWeapon = WEAPON_SKULK_BITE;
|
||||
|
||||
// If we have xenocide, then choose it if we have lots of good targets in blast radius
|
||||
|
@ -7235,7 +7263,7 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
if (DesiredWeapon != WEAPON_SKULK_XENOCIDE)
|
||||
if (!bIsCloaked && DesiredWeapon != WEAPON_SKULK_XENOCIDE)
|
||||
{
|
||||
if (!IsPlayerParasited(CurrentEnemy) && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
|
@ -7267,9 +7295,12 @@ bool SkulkCombatThink(AvHAIPlayer* pBot)
|
|||
}
|
||||
}
|
||||
|
||||
MoveTo(pBot, MoveTarget, MOVESTYLE_NORMAL);
|
||||
BotMoveStyle DesiredMoveStyle = (bIsCloaked) ? MOVESTYLE_AMBUSH : MOVESTYLE_NORMAL;
|
||||
pBot->BotNavInfo.bShouldWalk = bIsCloaked && !GetHasUpgrade(pBot->Edict->v.iuser4, MASK_SENSORY_NEARBY);
|
||||
|
||||
if (DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
MoveTo(pBot, MoveTarget, DesiredMoveStyle);
|
||||
|
||||
if (!bIsCloaked && DistToEnemy > sqrf(UTIL_MetresToGoldSrcUnits(5.0f)))
|
||||
{
|
||||
if (CanBotLeap(pBot))
|
||||
{
|
||||
|
|
|
@ -66,6 +66,8 @@ void BotClearEnemyTrackingInfo(enemy_status* TrackingInfo);
|
|||
bool IsPlayerInBotFOV(AvHAIPlayer* Observer, edict_t* TargetPlayer);
|
||||
void UpdateAIPlayerViewFrustum(AvHAIPlayer* pBot);
|
||||
|
||||
bool UTIL_IsCloakedPlayerInvisible(edict_t* Observer, AvHPlayer* Player);
|
||||
|
||||
|
||||
Vector GetVisiblePointOnPlayerFromObserver(edict_t* Observer, edict_t* TargetPlayer);
|
||||
|
||||
|
|
|
@ -101,7 +101,7 @@ void AIMGR_UpdateAIPlayerCounts()
|
|||
// If bots are disabled or we've exceeded max AI time and no humans are playing, ensure we've removed all bots from the game
|
||||
// Max AI time is configurable in nsbots.ini, and helps prevent infinite stalemates
|
||||
// Default time is 90 minutes before bots start leaving to let the map cycle
|
||||
if (!AIMGR_IsBotEnabled() || (bMatchExceededMaxLength && AIMGR_GetNumActiveHumanPlayers() == 0))
|
||||
if (!AIMGR_IsBotEnabled() || (bMatchExceededMaxLength && AIMGR_GetNumActiveHumanPlayers() == 0) || (AIMGR_HasMatchEnded() && gpGlobals->time - GetGameRules()->GetVictoryTime() > 5.0f))
|
||||
{
|
||||
if (AIMGR_GetNumAIPlayers() > 0)
|
||||
{
|
||||
|
@ -113,38 +113,6 @@ void AIMGR_UpdateAIPlayerCounts()
|
|||
|
||||
if (!AIMGR_ShouldStartPlayerBalancing()) { return; }
|
||||
|
||||
// If game has ended, kick bots that have dropped back to the ready room
|
||||
if (GetGameRules()->GetVictoryTeam() != TEAM_IND)
|
||||
{
|
||||
AIMGR_RemoveBotsInReadyRoom();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
|
||||
BotFillTiming CurrentFillTiming = CONFIG_GetBotFillTiming();
|
||||
|
||||
if (!GetGameRules()->GetGameStarted())
|
||||
{
|
||||
if (CurrentFillTiming == FILLTIMING_ROUNDSTART) { return; } // Do nothing if we're only meant to add bots after round start, and the round hasn't started
|
||||
|
||||
if (CurrentFillTiming == FILLTIMING_ALLHUMANS)
|
||||
{
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
edict_t* PlayerEdict = INDEXENT(i);
|
||||
if (FNullEnt(PlayerEdict) || PlayerEdict->free || (PlayerEdict->v.flags & FL_FAKECLIENT)) { continue; } // Ignore fake clients
|
||||
|
||||
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
|
||||
|
||||
if (!PlayerRef) { continue; }
|
||||
|
||||
if (PlayerRef->GetInReadyRoom()) { return; } // If there is a human in the ready room, don't add any more bots
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (avh_botautomode.value == 1) // Fill teams: bots will be added and removed to maintain a minimum player count
|
||||
{
|
||||
AIMGR_UpdateFillTeams();
|
||||
|
@ -298,6 +266,28 @@ void AIMGR_RemoveAIPlayerFromTeam(int Team)
|
|||
AvHTeamNumber teamA = GetGameRules()->GetTeamANumber();
|
||||
AvHTeamNumber teamB = GetGameRules()->GetTeamBNumber();
|
||||
|
||||
if (AIMGR_HasMatchEnded() && Team == 0)
|
||||
{
|
||||
vector<AvHAIPlayer>::iterator ItemToRemove = ActiveAIPlayers.end(); // Current bot to be kicked
|
||||
|
||||
for (auto it = ActiveAIPlayers.begin(); it != ActiveAIPlayers.end(); it++)
|
||||
{
|
||||
if (IsPlayerInReadyRoom(it->Edict))
|
||||
{
|
||||
ItemToRemove = it;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ItemToRemove != ActiveAIPlayers.end())
|
||||
{
|
||||
ItemToRemove->Player->Kick();
|
||||
|
||||
ActiveAIPlayers.erase(ItemToRemove);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (Team > 0)
|
||||
{
|
||||
DesiredTeam = (Team == 1) ? teamA : teamB;
|
||||
|
@ -1190,7 +1180,43 @@ vector<AvHPlayer*> AIMGR_GetNonAIPlayersOnTeam(AvHTeamNumber Team)
|
|||
|
||||
bool AIMGR_ShouldStartPlayerBalancing()
|
||||
{
|
||||
return (bPlayerSpawned && gpGlobals->time - AIStartedTime > AI_GRACE_PERIOD) || (gpGlobals->time - AIStartedTime > AI_MAX_START_TIMEOUT);
|
||||
if (gpGlobals->time - AIStartedTime < AI_GRACE_PERIOD) { return false; }
|
||||
|
||||
if (AIMGR_HasMatchEnded()) { return false; }
|
||||
|
||||
BotFillTiming FillTiming = CONFIG_GetBotFillTiming();
|
||||
|
||||
switch (FillTiming)
|
||||
{
|
||||
case FILLTIMING_MAPLOAD:
|
||||
return true;
|
||||
case FILLTIMING_ROUNDSTART:
|
||||
return GetGameRules()->GetGameStarted();
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (!bPlayerSpawned)
|
||||
{
|
||||
return (gpGlobals->time - AIStartedTime > AI_MAX_START_TIMEOUT);
|
||||
}
|
||||
|
||||
// We've started adding bots, keep going
|
||||
if (AIMGR_GetNumAIPlayers() > 0) { return true; }
|
||||
|
||||
for (int i = 1; i <= gpGlobals->maxClients; i++)
|
||||
{
|
||||
edict_t* PlayerEdict = INDEXENT(i);
|
||||
if (FNullEnt(PlayerEdict) || PlayerEdict->free || (PlayerEdict->v.flags & FL_FAKECLIENT)) { continue; } // Ignore fake clients
|
||||
|
||||
AvHPlayer* PlayerRef = dynamic_cast<AvHPlayer*>(CBaseEntity::Instance(PlayerEdict));
|
||||
|
||||
if (!PlayerRef) { continue; }
|
||||
|
||||
if (PlayerRef->GetInReadyRoom()) { return false; } // If there is a human in the ready room, don't add any more bots
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void AIMGR_UpdateAIMapData()
|
||||
|
@ -1280,6 +1306,9 @@ void AIMGR_OnBotEnabled()
|
|||
UnloadNavigationData();
|
||||
}
|
||||
|
||||
CONFIG_ParseConfigFile();
|
||||
CONFIG_PopulateBotNames();
|
||||
|
||||
AITAC_ClearMapAIData(true);
|
||||
|
||||
bBotsEnabled = true;
|
||||
|
@ -1353,13 +1382,31 @@ void AIMGR_UpdateAISystem()
|
|||
|
||||
if (AIMGR_IsBotEnabled())
|
||||
{
|
||||
if (AIMGR_GetNavMeshStatus() == NAVMESH_STATUS_PENDING)
|
||||
if (!AIMGR_HasMatchEnded())
|
||||
{
|
||||
AIMGR_LoadNavigationData();
|
||||
}
|
||||
if (AIMGR_GetNavMeshStatus() == NAVMESH_STATUS_PENDING)
|
||||
{
|
||||
AIMGR_LoadNavigationData();
|
||||
}
|
||||
|
||||
AIMGR_UpdateAIMapData();
|
||||
AIMGR_UpdateAIMapData();
|
||||
}
|
||||
|
||||
AIMGR_UpdateAIPlayers();
|
||||
}
|
||||
}
|
||||
|
||||
bool AIMGR_HasMatchEnded()
|
||||
{
|
||||
// Game has finished
|
||||
if (GetGameRules()->GetVictoryTeam() != TEAM_IND) { return true; }
|
||||
|
||||
// Game is still going, but if it's exceeded the max AI match time and there are no humans playing, consider the match over
|
||||
// Helps prevent stalemates if bots get stuck and keeps map rotations going
|
||||
float MaxMinutes = CONFIG_GetMaxAIMatchTimeMinutes();
|
||||
float MaxSeconds = MaxMinutes * 60.0f;
|
||||
|
||||
bool bMatchExceededMaxLength = (GetGameRules()->GetGameTime() > MaxSeconds);
|
||||
|
||||
return (bMatchExceededMaxLength && AIMGR_GetNumActiveHumanPlayers() == 0);
|
||||
}
|
|
@ -122,4 +122,6 @@ void AIMGR_OnBotDisabled();
|
|||
|
||||
void AIMGR_UpdateAISystem();
|
||||
|
||||
bool AIMGR_HasMatchEnded();
|
||||
|
||||
#endif
|
|
@ -563,6 +563,8 @@ bool AITASK_IsResupplyTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!UTIL_StructureIsFullyBuilt(Task->TaskTarget) || UTIL_StructureIsRecycling(Task->TaskTarget)) { return false; }
|
||||
|
||||
if (IsAreaAffectedBySpores(Task->TaskTarget->v.origin) && !PlayerHasHeavyArmour(pBot->Edict)) { return false; }
|
||||
|
||||
return (
|
||||
(pBot->Edict->v.health < pBot->Edict->v.max_health)
|
||||
|| (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player))
|
||||
|
@ -1095,11 +1097,18 @@ void BotProgressMoveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
{
|
||||
Task->TaskStartedTime = gpGlobals->time;
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
bool bPlayerCloaked = pBot->Player->GetOpacity() < 0.5f && !GetHasUpgrade(pBot->Edict->v.iuser4, MASK_SENSORY_NEARBY);
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict) || bPlayerCloaked)
|
||||
{
|
||||
if (AITAC_ShouldBotBeCautious(pBot))
|
||||
{
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_HIDE, 100.0f);
|
||||
if (bPlayerCloaked)
|
||||
{
|
||||
pBot->BotNavInfo.bShouldWalk = true;
|
||||
}
|
||||
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_AMBUSH, 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1649,11 +1658,19 @@ void BotProgressGuardTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation) > sqrf(UTIL_MetresToGoldSrcUnits(10.0f)))
|
||||
{
|
||||
Task->TaskStartedTime = 0.0f;
|
||||
if (IsPlayerLerk(pBot->Edict))
|
||||
|
||||
bool bPlayerCloaked = pBot->Player->GetOpacity() < 0.5f && !GetHasUpgrade(pBot->Edict->v.iuser4, MASK_SENSORY_NEARBY);
|
||||
|
||||
if (IsPlayerLerk(pBot->Edict) || bPlayerCloaked)
|
||||
{
|
||||
if (AITAC_ShouldBotBeCautious(pBot))
|
||||
{
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_HIDE, 100.0f);
|
||||
if (bPlayerCloaked)
|
||||
{
|
||||
pBot->BotNavInfo.bShouldWalk = true;
|
||||
}
|
||||
|
||||
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_AMBUSH, 100.0f);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue