Redid population of door triggers

This commit is contained in:
RGreenlees 2023-11-19 19:39:24 +00:00 committed by pierow
parent 12fb10f998
commit 086a87c308
18 changed files with 491 additions and 99 deletions

View file

@ -1680,7 +1680,7 @@ struct dtTileState
struct dtPolyState
{
unsigned short flags; // Flags (see dtPolyFlags).
unsigned int flags; // Flags (see dtPolyFlags).
unsigned char area; // Area ID of the polygon.
};
@ -1835,7 +1835,7 @@ const dtOffMeshConnection* dtNavMesh::getOffMeshConnectionByRef(dtPolyRef ref) c
}
dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned int flags)
{
if (!ref) return DT_FAILURE;
unsigned int salt, it, ip;
@ -1852,7 +1852,7 @@ dtStatus dtNavMesh::setPolyFlags(dtPolyRef ref, unsigned short flags)
return DT_SUCCESS;
}
dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const
dtStatus dtNavMesh::getPolyFlags(dtPolyRef ref, unsigned int* resultFlags) const
{
if (!ref) return DT_FAILURE;
unsigned int salt, it, ip;

View file

@ -61,7 +61,7 @@
/// @see dtNavMeshQuery
dtQueryFilter::dtQueryFilter() :
m_includeFlags(0xffff),
m_includeFlags(0xffffffff),
m_excludeFlags(0)
{
for (int i = 0; i < DT_MAX_AREAS; ++i)

View file

@ -94,12 +94,16 @@ dtTileCache::~dtTileCache()
}
dtFree(m_obstacles);
m_obstacles = 0;
dtFree(m_offMeshConnections);
m_offMeshConnections = 0;
dtFree(m_posLookup);
m_posLookup = 0;
dtFree(m_tiles);
m_tiles = 0;
m_nreqs = 0;
m_nOffMeshReqs = 0;
m_nupdate = 0;
@ -389,7 +393,7 @@ dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data,
return DT_SUCCESS;
}
dtStatus dtTileCache::addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned short flags, const bool bBiDirectional, dtOffMeshConnectionRef* result)
dtStatus dtTileCache::addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned int flags, const bool bBiDirectional, dtOffMeshConnectionRef* result)
{
if (m_nOffMeshReqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;

View file

@ -1775,12 +1775,12 @@ dtStatus dtBuildTileCachePolyMesh(dtTileCacheAlloc* alloc,
if (!mesh.areas)
return DT_FAILURE | DT_OUT_OF_MEMORY;
mesh.flags = (unsigned short*)alloc->alloc(sizeof(unsigned short)*maxTris);
mesh.flags = (unsigned int*)alloc->alloc(sizeof(unsigned int)*maxTris);
if (!mesh.flags)
return DT_FAILURE | DT_OUT_OF_MEMORY;
// Just allocate and clean the mesh flags array. The user is resposible for filling it.
memset(mesh.flags, 0, sizeof(unsigned short) * maxTris);
memset(mesh.flags, 0, sizeof(unsigned int) * maxTris);
mesh.nverts = 0;
mesh.npolys = 0;

View file

@ -173,7 +173,7 @@ struct dtPoly
unsigned short neis[DT_VERTS_PER_POLYGON];
/// The user defined polygon flags.
unsigned short flags;
unsigned int flags;
/// The number of vertices in the polygon.
unsigned char vertCount;
@ -191,7 +191,7 @@ struct dtPoly
/// Gets the user defined area id.
inline unsigned char getArea() const { return areaAndtype & 0x3f; }
inline unsigned short getFlags() const { return flags; }
inline unsigned int getFlags() const { return flags; }
/// Gets the polygon type. (See: #dtPolyTypes)
inline unsigned char getType() const { return areaAndtype >> 6; }
@ -246,7 +246,7 @@ struct dtOffMeshConnection
/// Link flags.
/// @note These are not the connection's user defined flags. Those are assigned via the
/// connection's dtPoly definition. These are link flags used for internal purposes.
unsigned short flags = 0;
unsigned int flags = 0;
unsigned char area = 0;
/// End point side.
@ -518,13 +518,13 @@ public:
/// @param[in] ref The polygon reference.
/// @param[in] flags The new flags for the polygon.
/// @return The status flags for the operation.
dtStatus setPolyFlags(dtPolyRef ref, unsigned short flags);
dtStatus setPolyFlags(dtPolyRef ref, unsigned int flags);
/// Gets the user defined flags for the specified polygon.
/// @param[in] ref The polygon reference.
/// @param[out] resultFlags The polygon flags.
/// @return The status flags for the operation.
dtStatus getPolyFlags(dtPolyRef ref, unsigned short* resultFlags) const;
dtStatus getPolyFlags(dtPolyRef ref, unsigned int* resultFlags) const;
/// Sets the user defined area for the specified polygon.
/// @param[in] ref The polygon reference.

View file

@ -37,7 +37,7 @@ struct dtNavMeshCreateParams
const unsigned short* verts; ///< The polygon mesh vertices. [(x, y, z) * #vertCount] [Unit: vx]
int vertCount; ///< The number vertices in the polygon mesh. [Limit: >= 3]
const unsigned short* polys; ///< The polygon data. [Size: #polyCount * 2 * #nvp]
const unsigned short* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount]
const unsigned int* polyFlags; ///< The user defined flags assigned to each polygon. [Size: #polyCount]
const unsigned char* polyAreas; ///< The user defined area ids assigned to each polygon. [Size: #polyCount]
int polyCount; ///< Number of polygons in the mesh. [Limit: >= 1]
int nvp; ///< Number maximum number of vertices per polygon. [Limit: >= 3]
@ -65,7 +65,7 @@ struct dtNavMeshCreateParams
/// Off-mesh connection radii. [Size: #offMeshConCount] [Unit: wu]
const float* offMeshConRad;
/// User defined flags assigned to the off-mesh connections. [Size: #offMeshConCount]
const unsigned short* offMeshConFlags;
const unsigned int* offMeshConFlags;
/// User defined area ids assigned to the off-mesh connections. [Size: #offMeshConCount]
const unsigned char* offMeshConAreas;
/// The permitted travel direction of the off-mesh connections. [Size: #offMeshConCount]

View file

@ -35,8 +35,8 @@
class dtQueryFilter
{
float m_areaCost[DT_MAX_AREAS]; ///< Cost per area type. (Used by default implementation.)
unsigned short m_includeFlags; ///< Flags for polygons that can be visited. (Used by default implementation.)
unsigned short m_excludeFlags; ///< Flags for polygons that should not be visted. (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.)
public:
dtQueryFilter();
@ -100,25 +100,25 @@ public:
/// Returns the include flags for the filter.
/// Any polygons that include one or more of these flags will be
/// included in the operation.
inline unsigned short getIncludeFlags() const { return m_includeFlags; }
inline unsigned int getIncludeFlags() const { return m_includeFlags; }
/// Sets the include flags for the filter.
/// @param[in] flags The new flags.
inline void setIncludeFlags(const unsigned short flags) { m_includeFlags = flags; }
inline void addIncludeFlags(const unsigned short flags) { m_includeFlags |= flags; }
inline void removeIncludeFlags(const unsigned short flags) { m_includeFlags &= ~flags; }
inline void setIncludeFlags(const unsigned int flags) { m_includeFlags = flags; }
inline void addIncludeFlags(const unsigned int flags) { m_includeFlags |= flags; }
inline void removeIncludeFlags(const unsigned int flags) { m_includeFlags &= ~flags; }
/// Returns the exclude flags for the filter.
/// Any polygons that include one ore more of these flags will be
/// excluded from the operation.
inline unsigned short getExcludeFlags() const { return m_excludeFlags; }
inline unsigned int getExcludeFlags() const { return m_excludeFlags; }
/// Sets the exclude flags for the filter.
/// @param[in] flags The new flags.
inline void setExcludeFlags(const unsigned short flags) { m_excludeFlags = flags; }
inline void addExcludeFlags(const unsigned short flags) { m_excludeFlags |= flags; }
inline void removeExcludeFlags(const unsigned short flags) { m_excludeFlags &= ~flags; }
inline void setExcludeFlags(const unsigned int flags) { m_excludeFlags = flags; }
inline void addExcludeFlags(const unsigned int flags) { m_excludeFlags |= flags; }
inline void removeExcludeFlags(const unsigned int flags) { m_excludeFlags &= ~flags; }
///@}

View file

@ -106,7 +106,7 @@ struct dtTileCacheMeshProcess
virtual ~dtTileCacheMeshProcess() { }
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags) = 0;
unsigned char* polyAreas, unsigned int* polyFlags) = 0;
};
@ -156,7 +156,7 @@ public:
// Cylinder obstacle.
dtStatus addObstacle(const float* pos, const float radius, const float height, const int area, dtObstacleRef* result);
dtStatus addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned short flags, const bool bBiDirectional, dtOffMeshConnectionRef* result);
dtStatus addOffMeshConnection(const float* spos, const float* epos, const float radius, const unsigned char area, const unsigned int flags, const bool bBiDirectional, dtOffMeshConnectionRef* result);
// Aabb obstacle.
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);

View file

@ -80,7 +80,7 @@ struct dtTileCachePolyMesh
int npolys; ///< Number of polygons.
unsigned short* verts; ///< Vertices of the mesh, 3 elements per vertex.
unsigned short* polys; ///< Polygons of the mesh, nvp*2 elements per polygon.
unsigned short* flags; ///< Per polygon flags.
unsigned int* flags; ///< Per polygon flags.
unsigned char* areas; ///< Area ID of polygons.
};

View file

@ -47,7 +47,7 @@ edict_t* UTIL_TraceEntity(const edict_t* pEdict, const Vector& start, const Vect
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceLine(start, end, dont_ignore_monsters, ignore_glass, IgnoreEdict, &hit);
UTIL_TraceLine(start, end, dont_ignore_monsters, dont_ignore_glass, IgnoreEdict, &hit);
return hit.pHit;
}
@ -175,6 +175,14 @@ Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, edict_t* En
return Vector(clampf(Location.x, Entity->v.absmin.x, Entity->v.absmax.x), clampf(Location.y, Entity->v.absmin.y, Entity->v.absmax.y), clampf(Location.z, Entity->v.absmin.z, Entity->v.absmax.z));
}
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, edict_t* Entity, const Vector EntityLocation)
{
Vector MinVec = EntityLocation - (Entity->v.size * 0.5f);
Vector MaxVec = EntityLocation + (Entity->v.size * 0.5f);
return Vector(clampf(Location.x, MinVec.x, MaxVec.x), clampf(Location.y, MinVec.y, MaxVec.y), clampf(Location.z, MinVec.z, MaxVec.z));
}
AvHAIDeployableStructureType IUSER3ToStructureType(const int inIUSER3)
{
if (inIUSER3 == AVH_USER3_COMMANDER_STATION) { return STRUCTURE_MARINE_COMMCHAIR; }

View file

@ -20,6 +20,7 @@ Vector UTIL_GetCentreOfEntity(const edict_t* Entity);
Vector UTIL_GetFloorUnderEntity(const edict_t* Edict);
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector UserLocation, edict_t* Entity);
Vector UTIL_GetClosestPointOnEntityToLocation(const Vector Location, edict_t* Entity, const Vector EntityLocation);
AvHAIDeployableStructureType IUSER3ToStructureType(const int inIUSER3);

View file

@ -256,10 +256,10 @@ float vSize2D(const Vector V)
return sqrtf((V.x * V.x) + (V.y * V.y));
}
// Returns true if the two vectors are the same (all components are within 0.01f of each other)
// Returns true if the two vectors are the same (all components are within 0.0001f of each other)
bool vEquals(const Vector v1, const Vector v2)
{
return fabsf(v1.x - v2.x) <= 0.01f && fabsf(v1.y - v2.y) <= 0.01f && fabsf(v1.z - v2.z) <= 0.01f;
return fabsf(v1.x - v2.x) <= 0.0001f && fabsf(v1.y - v2.y) <= 0.0001f && fabsf(v1.z - v2.z) <= 0.0001f;
}
bool vEquals2D(const Vector v1, const Vector v2)

View file

@ -24,6 +24,8 @@
#include <stdlib.h>
#include <math.h>
#include "../../dlls/plats.h"
#include "DetourNavMesh.h"
#include "DetourCommon.h"
#include "DetourTileCache.h"
@ -35,8 +37,8 @@
vector<nav_door> NavDoors;
vector<nav_weldable> NavWeldableObstacles;
nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular)
nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
nav_mesh NavMeshes[MAX_NAV_MESHES] = { }; // Array of nav meshes. Currently only 3 are used (building, onos, and regular)
nav_profile BaseNavProfiles[MAX_NAV_PROFILES] = { }; // Array of nav profiles
extern bool bNavMeshModified;
@ -181,7 +183,7 @@ struct MeshProcess : public dtTileCacheMeshProcess
}
virtual void process(struct dtNavMeshCreateParams* params,
unsigned char* polyAreas, unsigned short* polyFlags)
unsigned char* polyAreas, unsigned int* polyFlags)
{
// Update poly flags from areas.
for (int i = 0; i < params->polyCount; ++i)
@ -1401,8 +1403,8 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
unsigned char CurrArea;
unsigned char ThisArea;
unsigned short CurrFlags;
unsigned short ThisFlags;
unsigned int CurrFlags;
unsigned int ThisFlags;
m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea);
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
@ -1636,10 +1638,10 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From
path.clear();
unsigned short CurrFlags;
unsigned int CurrFlags;
unsigned char CurrArea;
unsigned char ThisArea;
unsigned short ThisFlags;
unsigned int ThisFlags;
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea);
@ -1807,7 +1809,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
path.clear();
unsigned short CurrFlags;
unsigned int CurrFlags;
unsigned char CurrArea;
m_navMesh->getPolyFlags(StraightPolyPath[0], &CurrFlags);
@ -3478,6 +3480,7 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
PGFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE;
PGFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(2.0f);
PGFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
PGFilter.DeployableTeam = (AvHTeamNumber)pBot->Edict->v.team;
if (!AITAC_DeployableExistsAtLocation(pBot->Edict->v.origin, &PGFilter))
@ -6464,10 +6467,222 @@ Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdi
return NewButtonAccessPoint;
}
bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door)
{
if (!TriggerEntity || !Door) { return false; }
if (TriggerEntity == Door) { return true; }
const char* DoorName = STRING(Door->pev->targetname);
const char* TriggerName = STRING(TriggerEntity->pev->targetname);
const char* TriggerTarget = STRING(TriggerEntity->pev->target);
if (FStrEq(STRING(TriggerEntity->pev->target), DoorName)) { return true; }
AvHWeldable* WeldableRef = dynamic_cast<AvHWeldable*>(TriggerEntity);
if (WeldableRef)
{
string targetString = WeldableRef->GetTargetOnFinish();
const char* targetOnFinish = targetString.c_str();
CBaseEntity* TargetEntity = UTIL_FindEntityByTargetname(NULL, targetOnFinish);
if (TargetEntity && UTIL_IsTriggerLinkedToDoor(TargetEntity, Door)) { return true; }
return false;
}
CMultiManager* MMRef = dynamic_cast<CMultiManager*>(TriggerEntity);
if (MMRef)
{
for (int i = 0; i < MMRef->m_cTargets; i++)
{
CBaseEntity* MMTargetEntity = UTIL_FindEntityByTargetname(NULL, STRING(MMRef->m_iTargetName[i]));
if (MMTargetEntity == Door) { return true; }
if (MMTargetEntity && UTIL_IsTriggerLinkedToDoor(MMTargetEntity, Door)) { return true; }
}
return false;
}
CEnvGlobal* EnvGlobalRef = dynamic_cast<CEnvGlobal*>(TriggerEntity);
if (EnvGlobalRef && EnvGlobalRef->m_globalstate)
{
const char* EnvGlobalState = STRING(EnvGlobalRef->m_globalstate);
FOR_ALL_ENTITIES("multisource", CMultiSource*)
const char* SourceGlobalState = STRING(theEntity->m_globalstate);
if (FStrEq(EnvGlobalState, SourceGlobalState))
{
if (UTIL_IsTriggerLinkedToDoor(theEntity, Door)) { return true; }
}
END_FOR_ALL_ENTITIES("multisource")
return false;
}
CMultiSource* MSRef = dynamic_cast<CMultiSource*>(TriggerEntity);
if (MSRef && MSRef->m_globalstate)
{
const char* targetName = STRING(MSRef->pev->targetname);
FOR_ALL_ENTITIES("func_button", CBaseButton*)
if (theEntity->m_sMaster && FStrEq(STRING(theEntity->m_sMaster), targetName))
{
if (UTIL_IsTriggerLinkedToDoor(theEntity, Door)) { return true; }
}
END_FOR_ALL_ENTITIES("func_button")
FOR_ALL_ENTITIES("trigger_once", CBaseTrigger*)
if (theEntity->m_sMaster && FStrEq(STRING(theEntity->m_sMaster), targetName))
{
if (UTIL_IsTriggerLinkedToDoor(theEntity, Door)) { return true; }
}
END_FOR_ALL_ENTITIES("trigger_once")
FOR_ALL_ENTITIES("trigger_multiple", CBaseTrigger*)
if (theEntity->m_sMaster && FStrEq(STRING(theEntity->m_sMaster), targetName))
{
if (UTIL_IsTriggerLinkedToDoor(theEntity, Door)) { return true; }
}
END_FOR_ALL_ENTITIES("trigger_multiple")
return false;
}
CTriggerChangeTarget* TCTRef = dynamic_cast<CTriggerChangeTarget*>(TriggerEntity);
if (TCTRef)
{
return FStrEq(STRING(TCTRef->GetNewTargetName()), STRING(Door->pev->targetname));
}
CBaseDelay* ToggleRef = dynamic_cast<CBaseDelay*>(TriggerEntity);
if (ToggleRef && ToggleRef->pev->target)
{
CBaseEntity* TargetEntity = UTIL_FindEntityByTargetname(NULL, STRING(ToggleRef->pev->target));
if (TargetEntity && UTIL_IsTriggerLinkedToDoor(TargetEntity, Door)) { return true; }
}
return false;
}
void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector<DoorTrigger>& TriggerList)
{
CBaseEntity* TriggerRef = NULL;
CBaseEntity* DoorRef = CBaseEntity::Instance(Entity);
if (!DoorRef) { return; }
while ((TriggerRef = UTIL_FindEntityByClassname(TriggerRef, "func_button")) != NULL)
{
if (UTIL_IsTriggerLinkedToDoor(TriggerRef, DoorRef))
{
DoorActivationType NewTriggerType = DOOR_BUTTON;
DoorTrigger NewTrigger;
NewTrigger.Entity = TriggerRef;
NewTrigger.Edict = TriggerRef->edict();
NewTrigger.ToggleEnt = dynamic_cast<CBaseToggle*>(TriggerRef);
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
}
}
TriggerRef = NULL;
while ((TriggerRef = UTIL_FindEntityByClassname(TriggerRef, "avhweldable")) != NULL)
{
if (UTIL_IsTriggerLinkedToDoor(TriggerRef, DoorRef))
{
DoorActivationType NewTriggerType = DOOR_BUTTON;
DoorTrigger NewTrigger;
NewTrigger.Entity = TriggerRef;
NewTrigger.Edict = TriggerRef->edict();
NewTrigger.ToggleEnt = dynamic_cast<CBaseToggle*>(TriggerRef);
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
}
}
TriggerRef = NULL;
while ((TriggerRef = UTIL_FindEntityByClassname(TriggerRef, "func_breakable")) != NULL)
{
if (UTIL_IsTriggerLinkedToDoor(TriggerRef, DoorRef))
{
DoorActivationType NewTriggerType = DOOR_BUTTON;
DoorTrigger NewTrigger;
NewTrigger.Entity = TriggerRef;
NewTrigger.Edict = TriggerRef->edict();
NewTrigger.ToggleEnt = dynamic_cast<CBaseToggle*>(TriggerRef);
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
}
}
TriggerRef = NULL;
while ((TriggerRef = UTIL_FindEntityByClassname(TriggerRef, "trigger_once")) != NULL)
{
if (UTIL_IsTriggerLinkedToDoor(TriggerRef, DoorRef))
{
DoorActivationType NewTriggerType = DOOR_BUTTON;
DoorTrigger NewTrigger;
NewTrigger.Entity = TriggerRef;
NewTrigger.Edict = TriggerRef->edict();
NewTrigger.ToggleEnt = dynamic_cast<CBaseToggle*>(TriggerRef);
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
}
}
TriggerRef = NULL;
while ((TriggerRef = UTIL_FindEntityByClassname(TriggerRef, "trigger_multiple")) != NULL)
{
if (UTIL_IsTriggerLinkedToDoor(TriggerRef, DoorRef))
{
DoorActivationType NewTriggerType = DOOR_BUTTON;
DoorTrigger NewTrigger;
NewTrigger.Entity = TriggerRef;
NewTrigger.Edict = TriggerRef->edict();
NewTrigger.ToggleEnt = dynamic_cast<CBaseToggle*>(TriggerRef);
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
}
}
}
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList)
{
CBaseEntity* EntityRef = CBaseEntity::Instance(Entity);
const char* EntName = STRING(Entity->v.targetname);
if (!EntityRef) { return; }
CBaseButton* ButtonRef = dynamic_cast<CBaseButton*>(EntityRef);
@ -6475,6 +6690,13 @@ void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& Trigge
CBaseTrigger* TriggerRef = dynamic_cast<CBaseTrigger*>(EntityRef);
CBreakable* BreakableRef = dynamic_cast<CBreakable*>(EntityRef);
CBasePlatTrain* TrainRef = dynamic_cast<CBasePlatTrain*>(EntityRef);
if (TrainRef)
{
bool bBreak = true;
}
if (ButtonRef || WeldableRef || TriggerRef || BreakableRef)
{
CBaseToggle* ToggleRef = dynamic_cast<CBaseToggle*>(EntityRef);
@ -6963,6 +7185,8 @@ void UTIL_ClearDoorData()
it->NumObstacles = 0;
}
it->StopPoints.clear();
}
NavDoors.clear();
@ -6987,89 +7211,120 @@ void UTIL_ClearWeldablesData()
NavWeldableObstacles.clear();
}
// TODO: Need to add orientated box obstacle for door
// TODO: This
void UTIL_PopulateTrainStopPoints(nav_door* TrainDoor)
{
CBasePlatTrain* TrainRef = dynamic_cast<CBasePlatTrain*>(TrainDoor->DoorEntity);
if (!TrainRef) { return; }
CBaseEntity* StartCorner = TrainRef->GetNextTarget();
if (!StartCorner)
{
// We aren't using path corners, so we're probably a func_plat
TrainDoor->StopPoints.push_back(TrainRef->m_vecPosition1);
TrainDoor->StopPoints.push_back(TrainRef->m_vecPosition2);
return;
}
if (StartCorner->pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER)
{
TrainDoor->StopPoints.push_back(StartCorner->pev->origin);
}
// Populate all path corners at which this func_train stops. Bot will use this to determine when to board the train
CBaseEntity* CurrentCorner = StartCorner->GetNextTarget();
while (CurrentCorner != NULL && CurrentCorner != StartCorner)
{
// Check if the train stops at this path corner, and if so, add it to the stop points array
if (CurrentCorner->pev->spawnflags & SF_TRAIN_WAIT_RETRIGGER)
{
TrainDoor->StopPoints.push_back(CurrentCorner->pev->origin);
}
CurrentCorner = CurrentCorner->GetNextTarget();
}
}
void UTIL_PopulateDoors()
{
UTIL_ClearDoorData();
vector<CBaseEntity*> DoorsToPopulate;
DoorsToPopulate.clear();
CBaseEntity* currDoor = NULL;
while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_door")) != NULL)
{
CBaseToggle* ToggleRef = dynamic_cast<CBaseToggle*>(currDoor);
if (!ToggleRef) { continue; }
nav_door NewDoor;
NewDoor.NumObstacles = 0;
NewDoor.DoorEntity = ToggleRef;
NewDoor.DoorEdict = currDoor->edict();
NewDoor.CurrentState = ToggleRef->m_toggle_state;
if (currDoor->pev->spawnflags & DOOR_USE_ONLY)
{
NewDoor.ActivationType = DOOR_USE;
}
else
{
NewDoor.TriggerEnts.clear();
UTIL_PopulateTriggersForEntity(currDoor->edict(), NewDoor.TriggerEnts);
}
NavDoors.push_back(NewDoor);
DoorsToPopulate.push_back(currDoor);
}
currDoor = NULL;
while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_seethroughdoor")) != NULL)
{
CBaseToggle* ToggleRef = dynamic_cast<CBaseToggle*>(currDoor);
if (!ToggleRef) { continue; }
nav_door NewDoor;
NewDoor.NumObstacles = 0;
NewDoor.DoorEntity = ToggleRef;
NewDoor.DoorEdict = currDoor->edict();
NewDoor.CurrentState = ToggleRef->m_toggle_state;
if (currDoor->pev->spawnflags & DOOR_USE_ONLY)
{
NewDoor.ActivationType = DOOR_USE;
}
else
{
NewDoor.TriggerEnts.clear();
UTIL_PopulateTriggersForEntity(currDoor->edict(), NewDoor.TriggerEnts);
}
NavDoors.push_back(NewDoor);
DoorsToPopulate.push_back(currDoor);
}
currDoor = NULL;
while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_door_rotating")) != NULL)
{
CBaseToggle* ToggleRef = dynamic_cast<CBaseToggle*>(currDoor);
DoorsToPopulate.push_back(currDoor);
}
currDoor = NULL;
while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_plat")) != NULL)
{
DoorsToPopulate.push_back(currDoor);
}
currDoor = NULL;
while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_train")) != NULL)
{
DoorsToPopulate.push_back(currDoor);
}
for (auto it = DoorsToPopulate.begin(); it != DoorsToPopulate.end(); it++)
{
CBaseEntity* DoorEnt = *it;
CBaseToggle* ToggleRef = dynamic_cast<CBaseToggle*>(DoorEnt);
if (!ToggleRef) { continue; }
nav_door NewDoor;
NewDoor.NumObstacles = 0;
NewDoor.DoorEntity = ToggleRef;
NewDoor.DoorEdict = currDoor->edict();
NewDoor.DoorEdict = DoorEnt->edict();
NewDoor.CurrentState = ToggleRef->m_toggle_state;
if (currDoor->pev->spawnflags & DOOR_USE_ONLY)
if (DoorEnt->pev->spawnflags & DOOR_USE_ONLY)
{
NewDoor.ActivationType = DOOR_USE;
}
else
{
NewDoor.TriggerEnts.clear();
UTIL_PopulateTriggersForEntity(currDoor->edict(), NewDoor.TriggerEnts);
UTIL_PopulateTriggersForEntity2(DoorEnt->edict(), NewDoor.TriggerEnts);
}
CBasePlatTrain* TrainRef = dynamic_cast<CBasePlatTrain*>(DoorEnt);
if (TrainRef)
{
UTIL_PopulateTrainStopPoints(&NewDoor);
}
else
{
NewDoor.StopPoints.push_back(ToggleRef->m_vecPosition1);
NewDoor.StopPoints.push_back(ToggleRef->m_vecPosition2);
}
NavDoors.push_back(NewDoor);
}
@ -7089,6 +7344,36 @@ nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict)
return nullptr;
}
// TODO: Find the topmost point when open, and topmost point when closed, and see how closely they align to the top and bottom point parameters
nav_door* UTIL_GetClosestLiftToPoints(const Vector TopPoint, const Vector BottomPoint)
{
nav_door* Result = nullptr;
float minDist = 0.0f;
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
{
float distTopPoint = FLT_MAX;
float distBottomPoint = FLT_MAX;
for (auto stop = it->StopPoints.begin(); stop != it->StopPoints.end(); stop++)
{
distTopPoint = fminf(distTopPoint, vDist3DSq(UTIL_GetClosestPointOnEntityToLocation(TopPoint, it->DoorEdict, *stop), TopPoint));
distBottomPoint = fminf(distBottomPoint, vDist3DSq(UTIL_GetClosestPointOnEntityToLocation(BottomPoint, it->DoorEdict, *stop), BottomPoint));
}
float thisDist = fminf(distTopPoint, distBottomPoint);
if (!Result || thisDist < minDist)
{
Result = &(*it);
minDist = thisDist;
}
}
return Result;
}
void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char area, unsigned short flags, bool bBiDirectional, AvHAIOffMeshConnection* RemoveConnectionDef)
{
Vector ConnStart, ConnEnd;
@ -7113,21 +7398,56 @@ void UTIL_AddOffMeshConnection(Vector StartLoc, Vector EndLoc, unsigned char are
RemoveConnectionDef->ConnectionRefs[i] = (unsigned int)ref;
}
bNavMeshModified = true;
}
void UTIL_RemoveOffMeshConnections(AvHAIOffMeshConnection* RemoveConnectionDef)
{
for (int i = 0; i < BUILDING_NAV_MESH; i++)
{
dtOffMeshConnectionRef ref = 0;
NavMeshes[i].tileCache->removeOffMeshConnection(RemoveConnectionDef->ConnectionRefs[i]);
RemoveConnectionDef->ConnectionRefs[i] = 0;
}
bNavMeshModified = true;
}
const nav_profile GetBaseNavProfile(const int index)
{
return BaseNavProfiles[index];
}
const dtOffMeshConnection* DEBUG_FindNearestOffMeshConnectionToPoint(const Vector Point, unsigned int FilterFlags)
{
const dtOffMeshConnection* Result = nullptr;
if (NavMeshes[REGULAR_NAV_MESH].tileCache)
{
float PointConverted[3] = { Point.x, Point.z, -Point.y };
float minDist = 0.0f;
for (int i = 0; i < NavMeshes[REGULAR_NAV_MESH].tileCache->getOffMeshCount(); i++)
{
const dtOffMeshConnection* con = NavMeshes[REGULAR_NAV_MESH].tileCache->getOffMeshConnection(i);
if (!con || con->state == DT_OFFMESH_EMPTY || con->state == DT_OFFMESH_REMOVING || !(con->flags & FilterFlags)) { continue; }
float distSpos = dtVdistSqr(PointConverted, &con->pos[0]);
float distEpos = dtVdistSqr(PointConverted, &con->pos[3]);
float thisDist = dtMin(distSpos, distEpos);
if (!Result || thisDist < minDist)
{
Result = con;
minDist = thisDist;
}
}
}
return Result;
}

View file

@ -63,9 +63,10 @@ enum SamplePolyFlags
SAMPLE_POLYFLAGS_TEAM2STRUCTURE = 1 << 12, // A team 2 structure is in the way that cannot be jumped over. Impassable to team 2 players (assume cannot teamkill own structures)
SAMPLE_POLYFLAGS_WELD = 1 << 13, // Requires a welder to get through here
SAMPLE_POLYFLAGS_DOOR = 1 << 14, // Requires a welder to get through here
SAMPLE_POLYFLAGS_LIFT = 1 << 15, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_DISABLED = 1 << 15, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
SAMPLE_POLYFLAGS_DISABLED = 1 << 16, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_ALL = -1 // All abilities.
};
// Door type. Not currently used, future feature so bots know how to open a door
@ -101,6 +102,7 @@ typedef struct _NAV_DOOR
DoorActivationType ActivationType = DOOR_NONE; // How the door should be opened
TOGGLE_STATE CurrentState = TS_AT_BOTTOM;
float OpenDelay = 0.0f; // How long the door takes to start opening after activation
vector<Vector> StopPoints; // Where does this door/platform stop when triggered?
} nav_door;
typedef struct _NAV_WELDABLE
@ -121,9 +123,9 @@ typedef struct _NAV_HITRESULT
// Links together a tile cache, nav query and the nav mesh into one handy structure for all your querying needs
typedef struct _NAV_MESH
{
class dtTileCache* tileCache;
class dtNavMeshQuery* navQuery;
class dtNavMesh* navMesh;
class dtTileCache* tileCache = nullptr;
class dtNavMeshQuery* navQuery = nullptr;
class dtNavMesh* navMesh = nullptr;
} nav_mesh;
@ -448,17 +450,22 @@ void OnBotEndLadder(AvHAIPlayer* pBot);
// Tracks all doors and their current status
void UTIL_PopulateDoors();
void UTIL_PopulateTrainStopPoints(nav_door* TrainDoor);
void UTIL_UpdateWeldableObstacles();
void UTIL_UpdateDoors(bool bInitial = false);
void UTIL_UpdateDoorTriggers(nav_door* Door);
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList);
bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door);
void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector<DoorTrigger>& TriggerList);
void UTIL_PopulateWeldableObstacles();
void UTIL_ApplyTempObstaclesToDoor(nav_door* DoorRef, const int Area);
nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict);
nav_door* UTIL_GetClosestLiftToPoints(const Vector TopPoint, const Vector BottomPoint);
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall);
@ -466,5 +473,7 @@ void UTIL_PopulateBaseNavProfiles();
void OnOffMeshConnectionAdded(dtOffMeshConnection* NewConnection);
const dtOffMeshConnection* DEBUG_FindNearestOffMeshConnectionToPoint(const Vector Point, unsigned int FilterFlags);
#endif // BOT_NAVIGATION_H

View file

@ -102,7 +102,7 @@ void AIMGR_UpdateAIPlayerCounts()
if (AIMGR_GetNumAIPlayers() > 0)
{
AIMGR_RemoveAIPlayerFromTeam(0);
}
return;
}
@ -790,8 +790,9 @@ vector<AvHAIPlayer*> AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team)
void AIMGR_UpdateAIMapData()
{
UTIL_UpdateTileCache();
AITAC_UpdateMapAIData();
UTIL_UpdateTileCache();
AITAC_CheckNavMeshModified();
}
void AIMGR_BotPrecache()

View file

@ -940,7 +940,10 @@ void AITAC_UpdateMapAIData()
UTIL_UpdateWeldableObstacles();
AITAC_RefreshHiveData();
}
void AITAC_CheckNavMeshModified()
{
if (bNavMeshModified)
{
AITAC_OnNavMeshModified();

View file

@ -29,6 +29,7 @@ int AITAC_GetNumDeployablesNearLocation(const Vector& Location, const Depl
void AITAC_RefreshHiveData();
void AITAC_RefreshResourceNodes();
void AITAC_UpdateMapAIData();
void AITAC_CheckNavMeshModified();
void AITAC_RefreshBuildableStructures();
void AITAC_UpdateBuildableStructure(CBaseEntity* Structure);
void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure);

View file

@ -1371,7 +1371,7 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
#ifdef WIN32
else if(FStrEq(pcmd, "createfake"))
{
if(!theAvHPlayer || theIsServerOp || theIsPlaytest || theIsDedicatedServer || this->GetCheatsEnabled())
/*if (!theAvHPlayer || theIsServerOp || theIsPlaytest || theIsDedicatedServer || this->GetCheatsEnabled())
{
char theFakeClientName[256];
sprintf(theFakeClientName, "Bot%d", RANDOM_LONG(0, 2000));
@ -1388,9 +1388,9 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
ClientPutInServer(BotEnt);
BotEnt->v.flags |= FL_FAKECLIENT;
}
}*/
return true;
theSuccess = true;
}
#endif
#endif
@ -1450,6 +1450,31 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "tracedoor2"))
{
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
Vector LookDir = UTIL_GetForwardVector(theAvHPlayer->edict()->v.v_angle); // Converts view angles to normalized unit vector
Vector TraceEnd = TraceStart + (LookDir * 1000.0f);
edict_t* TracedEntity = UTIL_TraceEntity(theAvHPlayer->edict(), TraceStart, TraceEnd);
if (!FNullEnt(TracedEntity))
{
nav_door* Door = UTIL_GetNavDoorByEdict(TracedEntity);
if (Door)
{
vector<DoorTrigger> TriggerEnts;
UTIL_PopulateTriggersForEntity2(Door->DoorEdict, TriggerEnts);
bool bThing = true;
}
}
theSuccess = true;
}
else if (FStrEq(pcmd, "tracedoor"))
{
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
@ -1465,6 +1490,7 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
if (Door)
{
for (auto it = Door->TriggerEnts.begin(); it != Door->TriggerEnts.end(); it++)
{
const char* ButtonTarget = STRING(it->Edict->v.target);
@ -1484,6 +1510,25 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "getlift"))
{
const dtOffMeshConnection* NearestCon = DEBUG_FindNearestOffMeshConnectionToPoint(theAvHPlayer->pev->origin, SAMPLE_POLYFLAGS_LIFT);
Vector ConnectionStart = Vector(NearestCon->pos[0], -NearestCon->pos[2], NearestCon->pos[1]);
Vector ConnectionEnd = Vector(NearestCon->pos[3], -NearestCon->pos[5], NearestCon->pos[4]);
if (NearestCon)
{
nav_door* NearestDoor = UTIL_GetClosestLiftToPoints(ConnectionStart, ConnectionEnd);
if (NearestDoor)
{
UTIL_DrawLine(INDEXENT(1), theAvHPlayer->pev->origin, UTIL_GetCentreOfEntity(NearestDoor->DoorEdict), 10.0f, 255, 255, 0);
}
}
theSuccess = true;
}
else if (FStrEq(pcmd, "cometome"))
{
vector<AvHAIPlayer*> AIPlayers = AIMGR_GetAllAIPlayers();