Doors block off-mesh connections

This commit is contained in:
RGreenlees 2024-02-15 14:29:38 +00:00 committed by pierow
parent 754c48327e
commit dfff725c44
11 changed files with 300 additions and 227 deletions

View file

@ -390,6 +390,26 @@ dtStatus dtTileCache::removeTile(dtCompressedTileRef ref, unsigned char** data,
return DT_SUCCESS;
}
dtStatus dtTileCache::modifyOffMeshConnection(dtOffMeshConnectionRef ConRef, const unsigned int newFlag)
{
if (m_nOffMeshReqs >= MAX_REQUESTS)
return DT_FAILURE | DT_BUFFER_TOO_SMALL;
dtOffMeshConnection* con = getOffMeshConnectionByRef(ConRef);
if (!con) { return DT_FAILURE; }
con->state = DT_OFFMESH_DIRTY;
con->flags = newFlag;
OffMeshRequest* req = &m_OffMeshReqs[m_nOffMeshReqs++];
memset(req, 0, sizeof(OffMeshRequest));
req->action = REQUEST_OFFMESH_REFRESH;
req->ref = ConRef;
return DT_SUCCESS;
}
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)

View file

@ -158,6 +158,7 @@ public:
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 int flags, const bool bBiDirectional, dtOffMeshConnectionRef* result);
dtStatus modifyOffMeshConnection(dtOffMeshConnectionRef ConRef, const unsigned int newFlag);
// Aabb obstacle.
dtStatus addBoxObstacle(const float* bmin, const float* bmax, dtObstacleRef* result);

View file

@ -215,6 +215,7 @@ typedef struct _OFF_MESH_CONN
{
unsigned int ConnectionRefs[2];
unsigned int ConnectionFlags = 0;
unsigned int DefaultConnectionFlags = 0;
Vector FromLocation = g_vecZero;
Vector ToLocation = g_vecZero;
edict_t* TargetObject = nullptr;
@ -340,7 +341,7 @@ typedef struct _DROPPED_MARINE_ITEM
int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the item has been removed from play
} AvHAIDroppedItem;
// How far a bot can be from a useable object when trying to interact with it. Used also for melee attacks
// How far a bot can be from a useable object when trying to interact with it. Used also for melee attacks. We make it slightly less than actual to avoid edge cases
static const float max_ai_use_reach = 55.0f;
// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down)

View file

@ -24,23 +24,23 @@ bool UTIL_QuickTrace(const edict_t* pEdict, const Vector& start, const Vector& e
return (hit.flFraction >= 1.0f && !hit.fAllSolid);
}
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end)
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid)
{
int hullNum = (!FNullEnt(pEdict)) ? GetPlayerHullIndex(pEdict) : point_hull;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
TraceResult hit;
UTIL_TraceHull(start, end, ignore_monsters, hullNum, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f && !hit.fAllSolid);
return (hit.flFraction >= 1.0f && !hit.fAllSolid && (bAllowStartSolid || !hit.fStartSolid));
}
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum)
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum, bool bAllowStartSolid)
{
TraceResult hit;
edict_t* IgnoreEdict = (!FNullEnt(pEdict)) ? pEdict->v.pContainingEntity : NULL;
UTIL_TraceHull(start, end, ignore_monsters, hullNum, IgnoreEdict, &hit);
return (hit.flFraction >= 1.0f && !hit.fAllSolid);
return (hit.flFraction >= 1.0f && !hit.fAllSolid && (bAllowStartSolid || !hit.fStartSolid));
}
edict_t* UTIL_TraceEntity(const edict_t* pEdict, const Vector& start, const Vector& end)

View file

@ -8,8 +8,8 @@
bool UTIL_CommanderTrace(const edict_t* pEdict, const Vector& start, const Vector& end);
bool UTIL_QuickTrace(const edict_t* pEdict, const Vector& start, const Vector& end);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, bool bAllowStartSolid = false);
bool UTIL_QuickHullTrace(const edict_t* pEdict, const Vector& start, const Vector& end, int hullNum, bool bAllowStartSolid = false);
edict_t* UTIL_TraceEntity(const edict_t* pEdict, const Vector& start, const Vector& end);
Vector UTIL_GetTraceHitLocation(const Vector Start, const Vector End);
Vector UTIL_GetHullTraceHitLocation(const Vector Start, const Vector End, int HullNum);

View file

@ -46,6 +46,13 @@ Vector UTIL_GetSurfaceNormal(const Vector v1, const Vector v2, const Vector v3)
return normal;
}
bool vPointOverlaps3D(const Vector Point, const Vector MinBB, const Vector MaxBB)
{
return (Point.x >= MinBB.x && Point.x <= MaxBB.x
&& Point.y >= MinBB.y && Point.y <= MaxBB.y
&& Point.z >= MinBB.z && Point.z <= MaxBB.z);
}
bool vPointOverlaps2D(const Vector Point, const Vector MinBB, const Vector MaxBB)
{
return (Point.x >= MinBB.x && Point.x <= MaxBB.x

View file

@ -115,6 +115,7 @@ Vector UTIL_GetCrossProduct(const Vector v1, const Vector v2);
// Returns the surface normal of a poly defined at points v1, v2 and v3 (clockwise)
Vector UTIL_GetSurfaceNormal(const Vector v1, const Vector v2, const Vector v3);
bool vPointOverlaps3D(const Vector Point, const Vector MinBB, const Vector MaxBB);
bool vPointOverlaps2D(const Vector Point, const Vector MinBB, const Vector MaxBB);
bool vBBOverlaps2D(const Vector MinBBA, const Vector MaxBBA, const Vector MinBBB, const Vector MaxBBB);

View file

@ -36,6 +36,7 @@
vector<nav_door> NavDoors;
vector<nav_weldable> NavWeldableObstacles;
vector<AvHAIOffMeshConnection> BaseMapConnections;
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
@ -300,6 +301,9 @@ void AIDEBUG_DrawOffMeshConnections(float DrawTime)
case SAMPLE_POLYFLAGS_TEAM2PHASEGATE:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 255, 128, 128);
break;
case SAMPLE_POLYFLAGS_DISABLED:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 128, 128, 128);
break;
default:
UTIL_DrawLine(INDEXENT(1), StartLine, EndLine, DrawTime, 0, 255, 255);
break;
@ -640,6 +644,8 @@ void UnloadNavMeshes()
NavMeshes[i].tileCache = nullptr;
}
}
BaseMapConnections.clear();
}
void UnloadNavigationData()
@ -658,6 +664,7 @@ void UnloadNavigationData()
bool LoadNavMesh(const char* mapname)
{
memset(NavMeshes, 0, sizeof(NavMeshes));
BaseMapConnections.clear();
char filename[256]; // Full path to BSP file
@ -846,10 +853,23 @@ bool LoadNavMesh(const char* mapname)
fread(&def, sizeof(dtOffMeshConnection), 1, savedFile);
AvHAIOffMeshConnection NewMapConnection;
NewMapConnection.ConnectionFlags = def.flags;
NewMapConnection.DefaultConnectionFlags = def.flags;
NewMapConnection.TargetObject = nullptr;
NewMapConnection.FromLocation = Vector(def.pos[0], -def.pos[2], def.pos[1]);
NewMapConnection.ToLocation = Vector(def.pos[3], -def.pos[5], def.pos[4]);
for (int ii = 0; ii < BUILDING_NAV_MESH; ii++)
{
NavMeshes[ii].tileCache->addOffMeshConnection(&def.pos[0], &def.pos[3], 10.0f, def.area, def.flags, def.bBiDir, 0);
dtOffMeshConnectionRef ref = 0;
NavMeshes[ii].tileCache->addOffMeshConnection(&def.pos[0], &def.pos[3], 10.0f, def.area, def.flags, def.bBiDir, &ref);
NewMapConnection.ConnectionRefs[ii] = (unsigned int)ref;
}
BaseMapConnections.push_back(NewMapConnection);
}
fclose(savedFile);
@ -899,7 +919,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_FLY | SAMPLE_POLYFLAGS_WALLCLIMB | SAMPLE_POLYFLAGS_WELD);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH;
@ -912,7 +932,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE | SAMPLE_POLYFLAGS_TEAM2PHASEGATE | SAMPLE_POLYFLAGS_DUCKJUMP | SAMPLE_POLYFLAGS_WELD | SAMPLE_POLYFLAGS_FLY);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH;
@ -925,7 +945,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
@ -942,7 +962,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WELD);
@ -957,7 +977,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 20.0f);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WELD);
@ -973,7 +993,7 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_STRUCTUREBLOCK, 5.0f); // Onos is a wrecking machine, structures shouldn't be such an obstacle for them!
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WALLCLIMB);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM1PHASEGATE);
BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_TEAM2PHASEGATE);
@ -983,13 +1003,13 @@ void UTIL_PopulateBaseNavProfiles()
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].NavMeshIndex = BUILDING_NAV_MESH;
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].bFlyingProfile = false;
BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_MARINE;
BaseNavProfiles[ALL_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH;
BaseNavProfiles[ALL_NAV_PROFILE].Filters.setIncludeFlags(SAMPLE_POLYFLAGS_ALL);
BaseNavProfiles[ALL_NAV_PROFILE].Filters.setExcludeFlags(0);
BaseNavProfiles[ALL_NAV_PROFILE].Filters.setExcludeFlags(SAMPLE_POLYFLAGS_DISABLED);
BaseNavProfiles[ALL_NAV_PROFILE].bFlyingProfile = false;
BaseNavProfiles[ALL_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_SKULK;
}
@ -1247,7 +1267,7 @@ dtStatus FindFlightPathToPoint(const nav_profile &NavProfile, Vector FromLocatio
{
TraceResult directHit;
if (UTIL_QuickHullTrace(nullptr, FromLocation, ToLocation, head_hull))
if (UTIL_QuickHullTrace(nullptr, FromLocation, ToLocation, head_hull, false))
{
path.clear();
@ -3885,7 +3905,7 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
if (vDist2DSq(pBot->Edict->v.origin, PointOnPath) > sqrf(100.0f)) { return true; }
bool bAtMoveStart = vEquals(PointOnPath, MoveFrom, 2.0f);
bool bAtMoveStart = vEquals(PointOnPath, MoveFrom, GetPlayerRadius(pBot->Player));
// If we're on the from or to move points, but the height is significantly different, we must be under or over the path somehow
@ -3894,7 +3914,7 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
return true;
}
bool bAtMoveEnd = vEquals(PointOnPath, MoveTo, 2.0f);
bool bAtMoveEnd = vEquals(PointOnPath, MoveTo, GetPlayerRadius(pBot->Player));
if (bAtMoveEnd && fabs(pBot->CurrentFloorPosition.z - MoveTo.z) > PlayerHeight)
{
@ -5133,6 +5153,17 @@ void UpdateBotStuck(AvHAIPlayer* pBot)
return;
}
if (pBot->BotNavInfo.StuckInfo.TotalStuckTime > 5.0f)
{
if (pBot->BotNavInfo.MovementTask.TaskType != MOVE_TASK_NONE)
{
NAV_ClearMovementTask(pBot);
}
ClearBotPath(pBot);
}
if (!vIsZero(pBot->desiredMovementDir))
{
BotJump(pBot);
@ -5642,7 +5673,7 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot)
// Early exit if we don't have a path, or we're already on the last path point
if (BotNavInfo->CurrentPath.size() == 0 || BotNavInfo->CurrentPathPoint == prev(BotNavInfo->CurrentPath.end())) { return; }
if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, prev(BotNavInfo->CurrentPath.end())->Location, head_hull))
if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, prev(BotNavInfo->CurrentPath.end())->Location, head_hull, false))
{
pBot->BotNavInfo.CurrentPathPoint = prev(BotNavInfo->CurrentPath.end());
return;
@ -6637,7 +6668,7 @@ float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEn
UTIL_TraceLine(ClimbEnd, ClimbEnd - Vector(0.0f, 0.0f, 50.0f), ignore_monsters, nullptr, &hit);
if (hit.flFraction < 1.0f)
if (hit.fAllSolid || hit.fStartSolid || hit.flFraction < 1.0f)
{
StartTrace.z = hit.vecEndPos.z + 18.0f;
}
@ -6650,7 +6681,7 @@ float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEn
UTIL_TraceHull(StartTrace, EndTrace, ignore_monsters, HullNum, nullptr, &hit);
if (hit.flFraction >= 1.0f && !hit.fStartSolid)
if (hit.flFraction >= 1.0f && !hit.fAllSolid && !hit.fStartSolid)
{
return StartTrace.z;
}
@ -6659,7 +6690,7 @@ float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEn
int maxTests = 100;
int testCount = 0;
while ((hit.flFraction < 1.0f || hit.fStartSolid) && testCount < maxTests)
while ((hit.flFraction < 1.0f || hit.fStartSolid || hit.fAllSolid) && testCount < maxTests)
{
CurrTraceStart.z += 1.0f;
EndTrace.z = CurrTraceStart.z;
@ -7115,7 +7146,44 @@ bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door)
return false;
}
void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector<DoorTrigger>& TriggerList)
void UTIL_PopulateAffectedConnectionsForDoor(nav_door* Door)
{
Door->AffectedConnections.clear();
Vector HalfExtents = (Door->DoorEdict->v.size * 0.5f);
for (auto it = BaseMapConnections.begin(); it != BaseMapConnections.end(); it++)
{
if (it->ConnectionFlags == SAMPLE_POLYFLAGS_TEAM1PHASEGATE || it->ConnectionFlags == SAMPLE_POLYFLAGS_TEAM2PHASEGATE) { continue; }
Vector ConnStart = it->FromLocation + Vector(0.0f, 0.0f, 10.0f);
Vector ConnEnd = it->ToLocation + Vector(0.0f, 0.0f, 10.0f);
Vector MidPoint = ConnStart + ((ConnEnd - ConnStart) * 0.5f);
MidPoint.z = fmaxf(ConnStart.z, ConnEnd.z);
for (auto stopIt = Door->StopPoints.begin(); stopIt != Door->StopPoints.end(); stopIt++)
{
Vector DoorCentre = (*stopIt);
Vector NearestPointOnLine = vClosestPointOnLine(ConnStart, MidPoint, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
Door->AffectedConnections.push_back(&(*it));
break;
}
NearestPointOnLine = vClosestPointOnLine(MidPoint, ConnEnd, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
Door->AffectedConnections.push_back(&(*it));
break;
}
}
}
}
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList)
{
CBaseEntity* TriggerRef = NULL;
CBaseEntity* DoorRef = CBaseEntity::Instance(Entity);
@ -7217,200 +7285,7 @@ void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector<DoorTrigger>& Trigg
}
}
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);
AvHWeldable* WeldableRef = dynamic_cast<AvHWeldable*>(EntityRef);
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);
DoorActivationType NewTriggerType = DOOR_NONE;
if (ButtonRef)
{
NewTriggerType = DOOR_BUTTON;
}
else if (TriggerRef)
{
NewTriggerType = DOOR_TRIGGER;
}
else if (WeldableRef)
{
NewTriggerType = DOOR_WELD;
}
else if (BreakableRef)
{
NewTriggerType = DOOR_BREAK;
}
DoorTrigger NewTrigger;
NewTrigger.Entity = EntityRef;
NewTrigger.Edict = EntityRef->edict();
NewTrigger.ToggleEnt = ToggleRef;
NewTrigger.TriggerType = NewTriggerType;
NewTrigger.bIsActivated = (!ToggleRef || !ToggleRef->IsLockedByMaster());
TriggerList.push_back(NewTrigger);
if (!NewTrigger.bIsActivated)
{
CBaseEntity* MasterEntity = UTIL_FindEntityByString(NULL, "targetname", STRING(ToggleRef->m_sMaster));
if (MasterEntity)
{
UTIL_PopulateTriggersForEntity(MasterEntity->edict(), TriggerList);
}
}
return;
}
CMultiSource* MultiSourceRef = dynamic_cast<CMultiSource*>(EntityRef);
if (MultiSourceRef)
{
for (int i = 0; i < MultiSourceRef->m_iTotal; i++)
{
UTIL_PopulateTriggersForEntity(MultiSourceRef->m_rgEntities[i]->edict(), TriggerList);
}
if (MultiSourceRef->m_globalstate)
{
const char* GlobalName = STRING(MultiSourceRef->m_globalstate);
CBaseEntity* EnvBaseRef = NULL;
while ((EnvBaseRef = UTIL_FindEntityByClassname(EnvBaseRef, "env_global")) != NULL)
{
CEnvGlobal* EnvGlobalRef = dynamic_cast<CEnvGlobal*>(EnvBaseRef);
if (FStrEq(STRING(EnvGlobalRef->m_globalstate), GlobalName))
{
UTIL_PopulateTriggersForEntity(EnvGlobalRef->edict(), TriggerList);
}
}
}
return;
}
const char* EntityName = STRING(Entity->v.targetname);
CBaseEntity* currTrigger = NULL;
while ((currTrigger = UTIL_FindEntityByString(currTrigger, "target", EntityName)) != NULL)
{
UTIL_PopulateTriggersForEntity(currTrigger->edict(), TriggerList);
}
FOR_ALL_ENTITIES(kwsWeldableClassName, AvHWeldable*)
if (theEntity->GetTargetOnFinish() == EntityName)
{
UTIL_PopulateTriggersForEntity(theEntity->edict(), TriggerList);
}
END_FOR_ALL_ENTITIES(kwsWeldableClassName)
FOR_ALL_ENTITIES("trigger_changetarget", CTriggerChangeTarget*)
const char* TargetName = STRING(theEntity->pev->targetname);
const char* NewTargetName = STRING(theEntity->GetNewTargetName());
if (FStrEq(STRING(theEntity->GetNewTargetName()), EntityName))
{
currTrigger = NULL;
while ((currTrigger = UTIL_FindEntityByString(currTrigger, "target", STRING(theEntity->pev->targetname))) != NULL)
{
UTIL_PopulateTriggersForEntity(currTrigger->edict(), TriggerList);
}
currTrigger = NULL;
while ((currTrigger = UTIL_FindEntityByString(currTrigger, "targetname", STRING(theEntity->pev->target))) != NULL)
{
UTIL_PopulateTriggersForEntity(currTrigger->edict(), TriggerList);
}
string NewString = TargetName;
CBaseEntity* CurrWeldable = NULL;
while ((CurrWeldable = UTIL_FindEntityByClassname(CurrWeldable, kwsWeldableClassName)) != NULL)
{
AvHWeldable* ThisWeldableRef = dynamic_cast<AvHWeldable*>(CurrWeldable);
if (ThisWeldableRef)
{
if (ThisWeldableRef->GetTargetOnFinish() == NewString)
{
UTIL_PopulateTriggersForEntity(ThisWeldableRef->edict(), TriggerList);
}
}
}
}
END_FOR_ALL_ENTITIES("trigger_changetarget")
while (((currTrigger = UTIL_FindEntityByClassname(currTrigger, "multi_manager")) != NULL))
{
CMultiManager* MMRef = dynamic_cast<CMultiManager*>(currTrigger);
bool bTargetsDoor = false;
if (MMRef)
{
for (int i = 0; i < MMRef->m_cTargets; i++)
{
if (FStrEq(EntityName, STRING(MMRef->m_iTargetName[i])))
{
bTargetsDoor = true;
break;
}
}
}
if (bTargetsDoor)
{
CBaseEntity* MMTrigger = NULL;
const char* MMNameChar = STRING(MMRef->pev->targetname);
while ((MMTrigger = UTIL_FindEntityByString(MMTrigger, "target", MMNameChar)) != NULL)
{
UTIL_PopulateTriggersForEntity(MMTrigger->edict(), TriggerList);
}
const string MMName = MMNameChar;
FOR_ALL_ENTITIES(kwsWeldableClassName, AvHWeldable*)
if (theEntity->GetTargetOnFinish() == MMName)
{
UTIL_PopulateTriggersForEntity(theEntity->edict(), TriggerList);
}
END_FOR_ALL_ENTITIES(kwsWeldableClassName)
}
}
}
void UTIL_PopulateWeldableObstacles()
{
@ -7478,18 +7353,39 @@ void UTIL_PopulateWeldableObstacles()
}
}
void UTIL_ModifyOffMeshConnectionFlag(AvHAIOffMeshConnection* Connection, const unsigned int NewFlag)
{
if (!Connection) { return; }
Connection->ConnectionFlags = NewFlag;
for (int i = 0; i < BUILDING_NAV_MESH; i++)
{
if (NavMeshes[i].tileCache && Connection->ConnectionRefs[i])
{
NavMeshes[i].tileCache->modifyOffMeshConnection(Connection->ConnectionRefs[i], NewFlag);
}
}
}
void UTIL_UpdateDoors(bool bInitial)
{
for (auto it = NavDoors.begin(); it != NavDoors.end(); it++)
{
nav_door* NavDoor = &(*it);
DoorActivationType PrevType = it->ActivationType;
UTIL_UpdateDoorTriggers(&(*it));
UTIL_UpdateDoorTriggers(NavDoor);
CBaseToggle* DoorRef = it->DoorEntity;
if (!DoorRef) { continue; }
if (bInitial)
{
UTIL_PopulateAffectedConnectionsForDoor(NavDoor);
}
if (DoorRef->m_toggle_state == TS_GOING_UP || DoorRef->m_toggle_state == TS_GOING_DOWN)
{
if (it->NumObstacles > 0)
@ -7520,15 +7416,97 @@ void UTIL_UpdateDoors(bool bInitial)
if (it->ActivationType == DOOR_NONE)
{
UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_NULL_AREA);
Vector HalfExtents = (NavDoor->DoorEdict->v.size) * 0.5f;
for (auto conIt = NavDoor->AffectedConnections.begin(); conIt != NavDoor->AffectedConnections.end(); conIt++)
{
AvHAIOffMeshConnection* ThisConnection = (*conIt);
Vector ConnStart = ThisConnection->FromLocation + Vector(0.0f, 0.0f, 10.0f);
Vector ConnEnd = ThisConnection->ToLocation + Vector(0.0f, 0.0f, 10.0f);
Vector MidPoint = ConnStart + ((ConnEnd - ConnStart) * 0.5f);
MidPoint.z = fmaxf(ConnStart.z, ConnEnd.z);
Vector DoorCentre = UTIL_GetCentreOfEntity(NavDoor->DoorEdict);
bool bThisConnectionAffected = false;
Vector NearestPointOnLine = vClosestPointOnLine(ConnStart, MidPoint, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, SAMPLE_POLYFLAGS_DISABLED);
bThisConnectionAffected = true;
}
else
{
NearestPointOnLine = vClosestPointOnLine(MidPoint, ConnEnd, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, SAMPLE_POLYFLAGS_DISABLED);
bThisConnectionAffected = true;
}
}
if (!bThisConnectionAffected)
{
if (ThisConnection->ConnectionFlags != ThisConnection->DefaultConnectionFlags)
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, ThisConnection->DefaultConnectionFlags);
}
}
}
UTIL_ApplyTempObstaclesToDoor(NavDoor, DT_TILECACHE_NULL_AREA);
}
else if (it->ActivationType == DOOR_WELD)
{
UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_WELD_AREA);
Vector HalfExtents = (NavDoor->DoorEdict->v.size) * 0.5f;
for (auto conIt = NavDoor->AffectedConnections.begin(); conIt != NavDoor->AffectedConnections.end(); conIt++)
{
AvHAIOffMeshConnection* ThisConnection = (*conIt);
Vector ConnStart = ThisConnection->FromLocation + Vector(0.0f, 0.0f, 10.0f);
Vector ConnEnd = ThisConnection->ToLocation + Vector(0.0f, 0.0f, 10.0f);
Vector MidPoint = ConnStart + ((ConnEnd - ConnStart) * 0.5f);
MidPoint.z = fmaxf(ConnStart.z, ConnEnd.z);
Vector DoorCentre = UTIL_GetCentreOfEntity(NavDoor->DoorEdict);
bool bThisConnectionAffected = false;
Vector NearestPointOnLine = vClosestPointOnLine(ConnStart, MidPoint, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, SAMPLE_POLYFLAGS_WELD);
bThisConnectionAffected = true;
}
else
{
NearestPointOnLine = vClosestPointOnLine(MidPoint, ConnEnd, DoorCentre);
if (vPointOverlaps3D(NearestPointOnLine, DoorCentre - HalfExtents, DoorCentre + HalfExtents))
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, SAMPLE_POLYFLAGS_WELD);
bThisConnectionAffected = true;
}
}
if (!bThisConnectionAffected)
{
if (ThisConnection->ConnectionFlags != ThisConnection->DefaultConnectionFlags)
{
UTIL_ModifyOffMeshConnectionFlag(ThisConnection, ThisConnection->DefaultConnectionFlags);
}
}
}
UTIL_ApplyTempObstaclesToDoor(NavDoor, DT_TILECACHE_WELD_AREA);
}
else
{
UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_DOOR_AREA);
UTIL_ApplyTempObstaclesToDoor(NavDoor, DT_TILECACHE_DOOR_AREA);
}
it->CurrentState = DoorRef->m_toggle_state;
@ -7865,7 +7843,7 @@ void UTIL_PopulateDoors()
else
{
NewDoor.TriggerEnts.clear();
UTIL_PopulateTriggersForEntity2(DoorEnt->edict(), NewDoor.TriggerEnts);
UTIL_PopulateTriggersForEntity(DoorEnt->edict(), NewDoor.TriggerEnts);
}
CBasePlatTrain* TrainRef = dynamic_cast<CBasePlatTrain*>(DoorEnt);
@ -7888,7 +7866,6 @@ void UTIL_PopulateDoors()
NewDoor.StopPoints.push_back(UTIL_GetCentreOfEntity(NewDoor.DoorEdict) + ToggleRef->m_vecPosition1);
NewDoor.StopPoints.push_back(UTIL_GetCentreOfEntity(NewDoor.DoorEdict) + ToggleRef->m_vecPosition2);
}
}

View file

@ -85,6 +85,7 @@ typedef struct _NAV_DOOR
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?
NavDoorType DoorType = DOORTYPE_DOOR;
vector<AvHAIOffMeshConnection*> AffectedConnections;
} nav_door;
typedef struct _NAV_WELDABLE
@ -451,10 +452,12 @@ 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);
void UTIL_ModifyOffMeshConnectionFlag(AvHAIOffMeshConnection* Connection, const unsigned int NewFlag);
bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, CBaseEntity* Door);
void UTIL_PopulateTriggersForEntity2(edict_t* Entity, vector<DoorTrigger>& TriggerList);
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList);
void UTIL_PopulateAffectedConnectionsForDoor(nav_door* Door);
void UTIL_PopulateWeldableObstacles();

View file

@ -3600,6 +3600,7 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, (*it)->Location, ThisDist - 5.0f, false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
// Two builders if we're not in the marine base, one to guard and keep lookout while the other builds
int NumDesiredBuilders = (vDist2DSq((*it)->Location, AITAC_GetCommChairLocation(BotTeam)) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) ? 1 : 2;
if (NumBuilders < NumDesiredBuilders)
@ -3918,13 +3919,76 @@ void AIPlayerThink(AvHAIPlayer* pBot)
void TestNavThink(AvHAIPlayer* pBot)
{
if (pBot == AIMGR_GetDebugAIPlayer())
{
bool bBreak = true; // Add a break point here if you want to debug a specific bot
AIDEBUG_DrawBotPath(pBot);
if (pBot->BotNavInfo.CurrentPathPoint != pBot->BotNavInfo.CurrentPath.end())
{
UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPathPoint->Location, 0, 255, 255);
}
}
AITASK_BotUpdateAndClearTasks(pBot);
pBot->CurrentTask = &pBot->PrimaryBotTask;
if (IsPlayerAlien(pBot->Edict) && IsPlayerSkulk(pBot->Edict))
{
if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER2, pBot->Edict) == 0)
{
if (pBot->Player->GetResources() >= BALANCE_VAR(kGorgeCost))
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_TWO);
return;
}
}
if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER3, pBot->Edict) == 0)
{
if (pBot->Player->GetResources() >= BALANCE_VAR(kLerkCost))
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_THREE);
return;
}
else
{
pBot->Player->GiveResources(BALANCE_VAR(kLerkCost));
}
}
if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER4, pBot->Edict) == 0)
{
if (pBot->Player->GetResources() >= BALANCE_VAR(kFadeCost))
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_FOUR);
return;
}
else
{
pBot->Player->GiveResources(BALANCE_VAR(kFadeCost));
}
}
if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER5, pBot->Edict) == 0)
{
if (pBot->Player->GetResources() >= BALANCE_VAR(kOnosCost))
{
BotEvolveLifeform(pBot, pBot->Edict->v.origin, ALIEN_LIFEFORM_FIVE);
return;
}
else
{
pBot->Player->GiveResources(BALANCE_VAR(kOnosCost));
}
}
}
if (pBot->PrimaryBotTask.TaskType == TASK_MOVE)
{
if (vDist2DSq(pBot->Edict->v.origin, pBot->PrimaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
if (vDist2DSq(pBot->Edict->v.origin, pBot->PrimaryBotTask.TaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(1.0f)))
{
AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask);
return;

View file

@ -1466,7 +1466,6 @@ 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);