diff --git a/main/source/detour/DetourNavMesh.cpp b/main/source/detour/DetourNavMesh.cpp index 4d4c2fcc..4c7cfd3b 100644 --- a/main/source/detour/DetourNavMesh.cpp +++ b/main/source/detour/DetourNavMesh.cpp @@ -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; diff --git a/main/source/detour/DetourNavMeshQuery.cpp b/main/source/detour/DetourNavMeshQuery.cpp index 30a77b6b..f3ec71ce 100644 --- a/main/source/detour/DetourNavMeshQuery.cpp +++ b/main/source/detour/DetourNavMeshQuery.cpp @@ -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) diff --git a/main/source/detour/DetourTileCache.cpp b/main/source/detour/DetourTileCache.cpp index ba74b935..233bb53c 100644 --- a/main/source/detour/DetourTileCache.cpp +++ b/main/source/detour/DetourTileCache.cpp @@ -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; diff --git a/main/source/detour/DetourTileCacheBuilder.cpp b/main/source/detour/DetourTileCacheBuilder.cpp index 05e34acd..7f4a39b4 100644 --- a/main/source/detour/DetourTileCacheBuilder.cpp +++ b/main/source/detour/DetourTileCacheBuilder.cpp @@ -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; diff --git a/main/source/detour/Include/DetourNavMesh.h b/main/source/detour/Include/DetourNavMesh.h index 46dfc24a..312ef4e3 100644 --- a/main/source/detour/Include/DetourNavMesh.h +++ b/main/source/detour/Include/DetourNavMesh.h @@ -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. diff --git a/main/source/detour/Include/DetourNavMeshBuilder.h b/main/source/detour/Include/DetourNavMeshBuilder.h index 9cee9a6a..befeed49 100644 --- a/main/source/detour/Include/DetourNavMeshBuilder.h +++ b/main/source/detour/Include/DetourNavMeshBuilder.h @@ -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] diff --git a/main/source/detour/Include/DetourNavMeshQuery.h b/main/source/detour/Include/DetourNavMeshQuery.h index 83d478ab..e43dd3f4 100644 --- a/main/source/detour/Include/DetourNavMeshQuery.h +++ b/main/source/detour/Include/DetourNavMeshQuery.h @@ -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; } ///@} diff --git a/main/source/detour/Include/DetourTileCache.h b/main/source/detour/Include/DetourTileCache.h index 83745e1d..f7b42e98 100644 --- a/main/source/detour/Include/DetourTileCache.h +++ b/main/source/detour/Include/DetourTileCache.h @@ -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); diff --git a/main/source/detour/Include/DetourTileCacheBuilder.h b/main/source/detour/Include/DetourTileCacheBuilder.h index 547cc1b2..172b0ff0 100644 --- a/main/source/detour/Include/DetourTileCacheBuilder.h +++ b/main/source/detour/Include/DetourTileCacheBuilder.h @@ -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. }; diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index d3e3657d..cad37bf9 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -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; } diff --git a/main/source/mod/AIPlayers/AvHAIHelper.h b/main/source/mod/AIPlayers/AvHAIHelper.h index a8b1c601..fc1df6e3 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.h +++ b/main/source/mod/AIPlayers/AvHAIHelper.h @@ -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); diff --git a/main/source/mod/AIPlayers/AvHAIMath.cpp b/main/source/mod/AIPlayers/AvHAIMath.cpp index d69c0fb5..619e5990 100644 --- a/main/source/mod/AIPlayers/AvHAIMath.cpp +++ b/main/source/mod/AIPlayers/AvHAIMath.cpp @@ -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) diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index 86d358e1..19c0ea30 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -24,6 +24,8 @@ #include #include +#include "../../dlls/plats.h" + #include "DetourNavMesh.h" #include "DetourCommon.h" #include "DetourTileCache.h" @@ -35,8 +37,8 @@ vector NavDoors; vector 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(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(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(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(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(TriggerEntity); + + if (TCTRef) + { + return FStrEq(STRING(TCTRef->GetNewTargetName()), STRING(Door->pev->targetname)); + } + + CBaseDelay* ToggleRef = dynamic_cast(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& 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(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(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(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(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(TriggerRef); + NewTrigger.TriggerType = NewTriggerType; + NewTrigger.bIsActivated = (!NewTrigger.ToggleEnt || !NewTrigger.ToggleEnt->IsLockedByMaster()); + + TriggerList.push_back(NewTrigger); + } + } +} + void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector& TriggerList) { CBaseEntity* EntityRef = CBaseEntity::Instance(Entity); + const char* EntName = STRING(Entity->v.targetname); + if (!EntityRef) { return; } CBaseButton* ButtonRef = dynamic_cast(EntityRef); @@ -6475,6 +6690,13 @@ void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector& Trigge CBaseTrigger* TriggerRef = dynamic_cast(EntityRef); CBreakable* BreakableRef = dynamic_cast(EntityRef); + CBasePlatTrain* TrainRef = dynamic_cast(EntityRef); + + if (TrainRef) + { + bool bBreak = true; + } + if (ButtonRef || WeldableRef || TriggerRef || BreakableRef) { CBaseToggle* ToggleRef = dynamic_cast(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(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 DoorsToPopulate; + DoorsToPopulate.clear(); + CBaseEntity* currDoor = NULL; while ((currDoor = UTIL_FindEntityByClassname(currDoor, "func_door")) != NULL) { - CBaseToggle* ToggleRef = dynamic_cast(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(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(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(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(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; } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index fa278c39..75f0c090 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -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 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& TriggerList); +bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door); +void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector& 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 diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp index 745d280a..3f797347 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.cpp @@ -102,7 +102,7 @@ void AIMGR_UpdateAIPlayerCounts() if (AIMGR_GetNumAIPlayers() > 0) { AIMGR_RemoveAIPlayerFromTeam(0); - + } return; } @@ -790,8 +790,9 @@ vector AIMGR_GetAIPlayersOnTeam(AvHTeamNumber Team) void AIMGR_UpdateAIMapData() { - UTIL_UpdateTileCache(); AITAC_UpdateMapAIData(); + UTIL_UpdateTileCache(); + AITAC_CheckNavMeshModified(); } void AIMGR_BotPrecache() diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index b3380384..93809a21 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -940,7 +940,10 @@ void AITAC_UpdateMapAIData() UTIL_UpdateWeldableObstacles(); AITAC_RefreshHiveData(); +} +void AITAC_CheckNavMeshModified() +{ if (bNavMeshModified) { AITAC_OnNavMeshModified(); diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index 6217318b..8a462438 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -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); diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index 092ec2ec..8ae6ac66 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -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 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 AIPlayers = AIMGR_GetAllAIPlayers();