Finish lift movement

This commit is contained in:
RGreenlees 2023-12-05 20:52:32 +00:00 committed by pierow
parent f5ca613a4f
commit c63ad98c14
7 changed files with 307 additions and 117 deletions

View file

@ -1918,25 +1918,25 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
{
return DT_FAILURE | DT_INVALID_PARAM;
}
dtStatus stat = 0;
// TODO: Should this be callers responsibility?
float closestStartPos[3];
if (dtStatusFailed(closestPointOnPolyBoundary(path[0], startPos, closestStartPos)))
return DT_FAILURE | DT_INVALID_PARAM;
float closestEndPos[3];
if (dtStatusFailed(closestPointOnPolyBoundary(path[pathSize-1], endPos, closestEndPos)))
if (dtStatusFailed(closestPointOnPolyBoundary(path[pathSize - 1], endPos, closestEndPos)))
return DT_FAILURE | DT_INVALID_PARAM;
// Add start point.
stat = appendVertex(closestStartPos, DT_STRAIGHTPATH_START, path[0],
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
if (stat != DT_IN_PROGRESS)
return stat;
if (pathSize > 1)
{
float portalApex[3], portalLeft[3], portalRight[3];
@ -1946,28 +1946,28 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
int apexIndex = 0;
int leftIndex = 0;
int rightIndex = 0;
unsigned char leftPolyType = 0;
unsigned char rightPolyType = 0;
dtPolyRef leftPolyRef = path[0];
dtPolyRef rightPolyRef = path[0];
for (int i = 0; i < pathSize; ++i)
{
float left[3], right[3];
unsigned char toType;
if (i+1 < pathSize)
if (i + 1 < pathSize)
{
unsigned char fromType; // fromType is ignored.
// Next portal.
if (dtStatusFailed(getPortalPoints(path[i], path[i+1], left, right, fromType, toType)))
if (dtStatusFailed(getPortalPoints(path[i], path[i + 1], left, right, fromType, toType)))
{
// Failed to get portal points, in practice this means that path[i+1] is invalid polygon.
// Clamp the end point to path[i], and return the path so far.
if (dtStatusFailed(closestPointOnPolyBoundary(path[i], endPos, closestEndPos)))
{
// This should only happen when the first polygon is invalid.
@ -1979,18 +1979,18 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
{
// Ignore status return value as we're just about to return anyway.
appendPortals(apexIndex, i, closestEndPos, path,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
}
// Ignore status return value as we're just about to return anyway.
appendVertex(closestEndPos, 0, path[i],
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
return DT_SUCCESS | DT_PARTIAL_RESULT | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
}
// If starting really close the portal, advance.
if (i == 0)
{
@ -2004,17 +2004,17 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
// End of the path.
dtVcopy(left, closestEndPos);
dtVcopy(right, closestEndPos);
toType = DT_POLYTYPE_GROUND;
}
// Right vertex.
if (dtTriArea2D(portalApex, portalRight, right) <= 0.0f)
{
if (dtVequal(portalApex, portalRight) || dtTriArea2D(portalApex, portalLeft, right) > 0.0f)
{
dtVcopy(portalRight, right);
rightPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
rightPolyRef = (i + 1 < pathSize) ? path[i + 1] : 0;
rightPolyType = toType;
rightIndex = i;
}
@ -2024,48 +2024,48 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
{
stat = appendPortals(apexIndex, leftIndex, portalLeft, path,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
if (stat != DT_IN_PROGRESS)
return stat;
return stat;
}
dtVcopy(portalApex, portalLeft);
apexIndex = leftIndex;
unsigned char flags = 0;
if (!leftPolyRef)
flags = DT_STRAIGHTPATH_END;
else if (leftPolyType == DT_POLYTYPE_OFFMESH_CONNECTION)
flags = DT_STRAIGHTPATH_OFFMESH_CONNECTION;
dtPolyRef ref = leftPolyRef;
// Append or update vertex
stat = appendVertex(portalApex, flags, ref,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
if (stat != DT_IN_PROGRESS)
return stat;
dtVcopy(portalLeft, portalApex);
dtVcopy(portalRight, portalApex);
leftIndex = apexIndex;
rightIndex = apexIndex;
// Restart
i = apexIndex;
continue;
}
}
// Left vertex.
if (dtTriArea2D(portalApex, portalLeft, left) >= 0.0f)
{
if (dtVequal(portalApex, portalLeft) || dtTriArea2D(portalApex, portalRight, left) < 0.0f)
{
dtVcopy(portalLeft, left);
leftPolyRef = (i+1 < pathSize) ? path[i+1] : 0;
leftPolyRef = (i + 1 < pathSize) ? path[i + 1] : 0;
leftPolyType = toType;
leftIndex = i;
}
@ -2075,15 +2075,15 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
{
stat = appendPortals(apexIndex, rightIndex, portalRight, path,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
if (stat != DT_IN_PROGRESS)
return stat;
}
dtVcopy(portalApex, portalRight);
apexIndex = rightIndex;
unsigned char flags = 0;
if (!rightPolyRef)
flags = DT_STRAIGHTPATH_END;
@ -2093,19 +2093,19 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
// Append or update vertex
stat = appendVertex(portalApex, flags, ref,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
if (stat != DT_IN_PROGRESS)
return stat;
dtVcopy(portalLeft, portalApex);
dtVcopy(portalRight, portalApex);
leftIndex = apexIndex;
rightIndex = apexIndex;
// Restart
i = apexIndex;
continue;
}
}
@ -2114,9 +2114,9 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
// Append portals along the current straight path segment.
if (options & (DT_STRAIGHTPATH_AREA_CROSSINGS | DT_STRAIGHTPATH_ALL_CROSSINGS))
{
stat = appendPortals(apexIndex, pathSize-1, closestEndPos, path,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
stat = appendPortals(apexIndex, pathSize - 1, closestEndPos, path,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath, options);
if (stat != DT_IN_PROGRESS)
return stat;
}
@ -2124,9 +2124,9 @@ dtStatus dtNavMeshQuery::findStraightPath(const float* startPos, const float* en
// Ignore status return value as we're just about to return anyway.
appendVertex(closestEndPos, DT_STRAIGHTPATH_END, 0,
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
straightPath, straightPathFlags, straightPathRefs,
straightPathCount, maxStraightPath);
return DT_SUCCESS | ((*straightPathCount >= maxStraightPath) ? DT_BUFFER_TOO_SMALL : 0);
}

View file

@ -190,6 +190,7 @@ typedef struct _RESOURCE_NODE
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Is this reachable by the bots? Checks for marine reachability only
bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated
float NextReachabilityRefreshTime = 0.0f;
} AvHAIResourceNode;
// Data structure to hold information about each hive in the map

View file

@ -42,6 +42,8 @@ nav_profile BaseNavProfiles[MAX_NAV_PROFILES] = { }; // Array of nav profiles
extern bool bNavMeshModified;
bool bTileCacheUpToDate = false;
struct NavMeshSetHeader
{
int magic;
@ -309,13 +311,11 @@ void AIDEBUG_DrawOffMeshConnections(float DrawTime)
void UTIL_UpdateTileCache()
{
bool bUpToDate = true;
for (int i = 0; i < MAX_NAV_MESHES; i++)
{
if (NavMeshes[i].tileCache)
{
NavMeshes[i].tileCache->update(0.0f, NavMeshes[i].navMesh, &bUpToDate);
NavMeshes[i].tileCache->update(0.0f, NavMeshes[i].navMesh, &bTileCacheUpToDate);
}
}
}
@ -669,7 +669,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileHeaderReadReturnCode = fread(&tileHeader, sizeof(tileHeader), 1, savedFile);
if (tileHeaderReadReturnCode != 1)
{
ALERT(at_console, "Tile header read returned code\n");
fclose(savedFile);
UnloadNavigationData();
return false;
@ -683,8 +682,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileDataReadReturnCode = fread(data, tileHeader.dataSize, 1, savedFile);
if (tileDataReadReturnCode != 1)
{
ALERT(at_console, "Tile data read returned code\n");
// Error or early EOF
dtFree(data);
fclose(savedFile);
UnloadNavigationData();
@ -708,7 +705,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileHeaderReadReturnCode = fread(&tileHeader, sizeof(tileHeader), 1, savedFile);
if (tileHeaderReadReturnCode != 1)
{
ALERT(at_console, "Tile header read returned code\n");
fclose(savedFile);
UnloadNavigationData();
return false;
@ -722,8 +718,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileDataReadReturnCode = fread(data, tileHeader.dataSize, 1, savedFile);
if (tileDataReadReturnCode != 1)
{
ALERT(at_console, "Tile data read returned code\n");
// Error or early EOF
dtFree(data);
fclose(savedFile);
UnloadNavigationData();
@ -747,7 +741,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileHeaderReadReturnCode = fread(&tileHeader, sizeof(tileHeader), 1, savedFile);
if (tileHeaderReadReturnCode != 1)
{
ALERT(at_console, "Tile header read returned code\n");
fclose(savedFile);
UnloadNavigationData();
return false;
@ -761,8 +754,6 @@ bool LoadNavMesh(const char* mapname)
size_t tileDataReadReturnCode = fread(data, tileHeader.dataSize, 1, savedFile);
if (tileDataReadReturnCode != 1)
{
ALERT(at_console, "Tile data read returned code\n");
// Error or early EOF
dtFree(data);
fclose(savedFile);
UnloadNavigationData();
@ -804,7 +795,6 @@ bool LoadNavMesh(const char* mapname)
if (dtStatusFailed(initStatus))
{
ALERT(at_console, "Could not initialise nav query\n");
UnloadNavigationData();
return false;
}
@ -1622,7 +1612,7 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(pBot->BotNavInfo.NavProfile);
const dtQueryFilter* m_navFilter = &pBot->BotNavInfo.NavProfile.Filters;
bool bHasWelder = !(m_navFilter->getExcludeFlags() & SAMPLE_POLYFLAGS_WELD);
bool bHasWelder = (m_navFilter->getIncludeFlags() & SAMPLE_POLYFLAGS_WELD);
if (!m_navQuery || !m_navMesh || !m_navFilter || vIsZero(FromLocation) || vIsZero(ToLocation))
{
@ -1755,6 +1745,24 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle,
NextPathNode.requiredZ += 5.0f;
}
if (IsPlayerSkulk(pBot->Edict))
{
Vector CurrMoveDest = NextPathNode.Location;
NextPathNode.flag = SAMPLE_POLYFLAGS_WALLCLIMB;
Vector ThisMoveDest = Vector(NodeFromLocation.x, NodeFromLocation.y, NextPathNode.Location.z);
NextPathNode.Location = ThisMoveDest;
path.push_back(NextPathNode);
NextPathNode.Location = CurrMoveDest;
NextPathNode.FromLocation = ThisMoveDest;
CurrFlags = SAMPLE_POLYFLAGS_WALLCLIMB;
}
}
else
{
@ -2026,7 +2034,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot)
}
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(pBot->Edict->v.origin, Door, nullptr);
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(pBot->Edict->v.origin, Door, nullptr, true);
if (Trigger && Trigger->NextActivationTime < gpGlobals->time)
{
@ -2593,7 +2601,7 @@ DoorTrigger* UTIL_GetNearestDoorTriggerFromLift(edict_t* LiftEdict, nav_door* Do
return NearestTrigger;
}
DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger)
DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger, bool bCheckBlockedByDoor)
{
if (!Door) { return nullptr; }
@ -2610,7 +2618,7 @@ DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, C
{
Vector ButtonLocation = UTIL_GetButtonFloorLocation(Location, it->Edict);
if ((true || !UTIL_IsPathBlockedByDoor(Location, ButtonLocation, Door->DoorEdict)) && UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), Location, ButtonLocation, 64.0f))
if ((!bCheckBlockedByDoor || !UTIL_IsPathBlockedByDoor(Location, ButtonLocation, Door->DoorEdict)) && UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), Location, ButtonLocation, 64.0f))
{
float ThisDist = vDist3DSq(Location, ButtonLocation);
@ -3453,7 +3461,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
if ((bIsOnLift && !bIsLiftMoving && bIsLiftAtOrNearEnd) || UTIL_PointIsDirectlyReachable(BotNavPosition, EndPoint))
{
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(EndPoint - pBot->CollisionHullBottomLocation);
MoveToWithoutNav(pBot, EndPoint);
return;
}
@ -3529,7 +3537,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
if (!NearestLiftTrigger)
{
NearestLiftTrigger = UTIL_GetNearestDoorTrigger(pBot->Edict->v.origin, NearestLift, nullptr);
NearestLiftTrigger = UTIL_GetNearestDoorTrigger(pBot->Edict->v.origin, NearestLift, nullptr, false);
}
if (NearestLiftTrigger)
@ -3928,6 +3936,75 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
}
void MoveToWithoutNav(AvHAIPlayer* pBot, const Vector Destination)
{
Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition;
const Vector vForward = UTIL_GetVectorNormal2D(Destination - CurrentPos);
// Same goes for the right vector, might not be the same as the bot's right
const Vector vRight = UTIL_GetVectorNormal2D(UTIL_GetCrossProduct(vForward, UP_VECTOR));
const float PlayerRadius = GetPlayerRadius(pBot->Player);
Vector stTrcLft = pBot->Edict->v.origin - (vRight * PlayerRadius);
Vector stTrcRt = pBot->Edict->v.origin + (vRight * PlayerRadius);
stTrcLft.z += 2.0f;
stTrcRt.z += 2.0f;
Vector endTrcLft = stTrcLft + (vForward * (PlayerRadius * 2.0f));
Vector endTrcRt = stTrcRt + (vForward * (PlayerRadius * 2.0f));
bool bumpLeft = !UTIL_QuickHullTrace(pBot->Edict, stTrcLft, endTrcLft, head_hull);
bool bumpRight = !UTIL_QuickHullTrace(pBot->Edict, stTrcRt, endTrcRt, head_hull);
UTIL_DrawLine(INDEXENT(1), stTrcLft, endTrcLft, 255, 0, 0);
UTIL_DrawLine(INDEXENT(1), stTrcRt, endTrcRt, 0, 0, 255);
pBot->desiredMovementDir = vForward;
if (bumpRight && !bumpLeft)
{
pBot->desiredMovementDir = pBot->desiredMovementDir - vRight;
}
else if (bumpLeft && !bumpRight)
{
pBot->desiredMovementDir = pBot->desiredMovementDir + vRight;
}
else if (bumpLeft && bumpRight)
{
endTrcLft = endTrcLft - (vRight * PlayerRadius);
endTrcRt = endTrcRt + (vRight * PlayerRadius);
if (!UTIL_QuickTrace(pBot->Edict, stTrcLft, endTrcLft))
{
pBot->desiredMovementDir = pBot->desiredMovementDir + vRight;
}
else
{
pBot->desiredMovementDir = pBot->desiredMovementDir - vRight;
}
}
float DistFromDestination = vDist2DSq(pBot->Edict->v.origin, Destination);
if (vIsZero(pBot->LookTargetLocation))
{
Vector LookTarget = Destination;
if (DistFromDestination < sqrf(200.0f))
{
Vector LookNormal = UTIL_GetVectorNormal2D(LookTarget - pBot->CurrentEyePosition);
LookTarget = LookTarget + (LookNormal * 1000.0f);
}
BotLookAt(pBot, LookTarget);
}
HandlePlayerAvoidance(pBot, Destination);
BotMovementInputs(pBot);
}
void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination)
{
Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition;
@ -4915,7 +4992,7 @@ void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
}
// Did our nav profile previously indicate we could go through weldable doors?
bool bHadWelder = !(NavProfile->Filters.getExcludeFlags() & SAMPLE_POLYFLAGS_WELD);
bool bHadWelder = (NavProfile->Filters.getIncludeFlags() & SAMPLE_POLYFLAGS_WELD);
if (bHasWelder != bHadWelder)
{
@ -4923,12 +5000,12 @@ void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
if (bHasWelder)
{
NavProfile->Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
NavProfile->Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
NavProfile->ReachabilityFlag = AI_REACHABILITY_WELDER;
}
else
{
NavProfile->Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
NavProfile->Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WELD);
NavProfile->ReachabilityFlag = AI_REACHABILITY_MARINE;
}
}
@ -4936,12 +5013,12 @@ void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle)
SamplePolyFlags ExcludePhaseGateFlag = (pBot->Player->GetTeam() == GetGameRules()->GetTeamANumber()) ? SAMPLE_POLYFLAGS_TEAM2PHASEGATE : SAMPLE_POLYFLAGS_TEAM1PHASEGATE;
SamplePolyFlags IncludePhaseGateFlag = (ExcludePhaseGateFlag & SAMPLE_POLYFLAGS_TEAM1PHASEGATE) ? SAMPLE_POLYFLAGS_TEAM2PHASEGATE : SAMPLE_POLYFLAGS_TEAM1PHASEGATE;
if (!(NavProfile->Filters.getExcludeFlags() & ExcludePhaseGateFlag))
if (!(NavProfile->Filters.getIncludeFlags() & IncludePhaseGateFlag))
{
pBot->BotNavInfo.bNavProfileChanged = true;
NavProfile->Filters.removeExcludeFlags(IncludePhaseGateFlag);
NavProfile->Filters.addExcludeFlags(ExcludePhaseGateFlag);
NavProfile->Filters.addIncludeFlags(IncludePhaseGateFlag);
NavProfile->Filters.removeIncludeFlags(ExcludePhaseGateFlag);
}
if (MoveStyle == pBot->BotNavInfo.PreviousMoveStyle) { return; }
@ -6674,8 +6751,8 @@ Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdi
nav_profile ButtonNavProfile;
memcpy(&ButtonNavProfile, &BaseNavProfiles[ALL_NAV_PROFILE], sizeof(nav_profile));
ButtonNavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD);
ButtonNavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DOOR);
ButtonNavProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_WELD);
ButtonNavProfile.Filters.removeIncludeFlags(SAMPLE_POLYFLAGS_DOOR);
Vector ButtonAccessPoint = UTIL_ProjectPointToNavmesh(ClosestPoint, Vector(100.0f, 100.0f, 100.0f), ButtonNavProfile);
@ -7745,4 +7822,95 @@ const dtOffMeshConnection* DEBUG_FindNearestOffMeshConnectionToPoint(const Vecto
}
return Result;
}
dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance)
{
const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(NavProfile);
const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(NavProfile);
const dtQueryFilter* m_navFilter = &NavProfile.Filters;
if (!m_navQuery || !m_navMesh || !m_navFilter || vIsZero(FromLocation) || vIsZero(ToLocation))
{
return DT_FAILURE;
}
Vector FromFloorLocation = AdjustPointForPathfinding(FromLocation);
Vector ToFloorLocation = AdjustPointForPathfinding(ToLocation);
float pStartPos[3] = { FromFloorLocation.x, FromFloorLocation.z, -FromFloorLocation.y };
float pEndPos[3] = { ToFloorLocation.x, ToFloorLocation.z, -ToFloorLocation.y };
dtStatus status;
dtPolyRef StartPoly;
float StartNearest[3];
dtPolyRef EndPoly;
float EndNearest[3];
dtPolyRef PolyPath[MAX_PATH_POLY];
dtPolyRef StraightPolyPath[MAX_AI_PATH_SIZE];
int nPathCount = 0;
float StraightPath[MAX_AI_PATH_SIZE * 3];
unsigned char straightPathFlags[MAX_AI_PATH_SIZE];
memset(straightPathFlags, 0, sizeof(straightPathFlags));
int nVertCount = 0;
// find the start polygon
status = m_navQuery->findNearestPoly(pStartPos, pExtents, m_navFilter, &StartPoly, StartNearest);
if ((status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
{
//BotSay(pBot, "findNearestPoly start failed!");
return (status & DT_STATUS_DETAIL_MASK); // couldn't find a polygon
}
// find the end polygon
status = m_navQuery->findNearestPoly(pEndPos, pExtents, m_navFilter, &EndPoly, EndNearest);
if ((status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
{
//BotSay(pBot, "findNearestPoly end failed!");
return (status & DT_STATUS_DETAIL_MASK); // couldn't find a polygon
}
status = m_navQuery->findPath(StartPoly, EndPoly, StartNearest, EndNearest, m_navFilter, PolyPath, &nPathCount, MAX_PATH_POLY);
if (PolyPath[nPathCount - 1] != EndPoly)
{
return DT_FAILURE;
}
status = m_navQuery->findStraightPath(StartNearest, EndNearest, PolyPath, nPathCount, StraightPath, straightPathFlags, StraightPolyPath, &nVertCount, MAX_AI_PATH_SIZE, DT_STRAIGHTPATH_AREA_CROSSINGS);
if ((status & DT_FAILURE) || (status & DT_STATUS_DETAIL_MASK))
{
return (status & DT_STATUS_DETAIL_MASK); // couldn't create a path
}
if (nVertCount == 0)
{
return DT_FAILURE; // couldn't find a path
}
path.clear();
// At this point we have our path. Copy it to the path store
int nIndex = 0;
Vector NodeFromLocation = FromFloorLocation;
for (int nVert = 0; nVert < nVertCount; nVert++)
{
bot_path_node NextPathNode;
NextPathNode.FromLocation = NodeFromLocation;
NextPathNode.Location.x = StraightPath[nIndex++];
NextPathNode.Location.z = StraightPath[nIndex++];
NextPathNode.Location.y = -StraightPath[nIndex++];
NextPathNode.area = SAMPLE_POLYAREA_GROUND;
NextPathNode.flag = SAMPLE_POLYFLAGS_WALK;
path.push_back(NextPathNode);
NodeFromLocation = NextPathNode.Location;
}
return DT_SUCCESS;
}

View file

@ -261,7 +261,7 @@ void CheckAndHandleBreakableObstruction(AvHAIPlayer* pBot, const Vector MoveFrom
void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot);
DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger);
DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger, bool bCheckBlockedByDoor);
DoorTrigger* UTIL_GetNearestDoorTriggerFromLift(edict_t* LiftEdict, nav_door* Door, CBaseEntity* IgnoreTrigger);
bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_t* SearchDoor);
@ -341,6 +341,7 @@ bool IsBotPermaStuck(AvHAIPlayer* pBot);
// Walks directly towards the destination. No path finding, just raw movement input. Will detect obstacles and try to jump/duck under them.
void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination);
void MoveToWithoutNav(AvHAIPlayer* pBot, const Vector Destination);
// Check if there are any players in our way and try to move around them. If we can't, then back up to let them through
void HandlePlayerAvoidance(AvHAIPlayer* pBot, const Vector MoveDestination);
@ -356,6 +357,8 @@ Vector UTIL_FindHighestSuccessfulTracePoint(const Vector TraceFrom, const Vector
dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector<bot_path_node>& path, float MaxAcceptableDistance);
// If the bot is stuck and off the path or nav mesh, this will try to find a point it can directly move towards to get it back on track
Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot);

View file

@ -43,6 +43,10 @@ int AIMGR_AIPlayerExistsOnTeam(AvHTeamNumber Team);
void AIMGR_UpdateAIMapData();
void AIDEBUG_SetDebugVector1(const Vector NewVector);
void AIDEBUG_SetDebugVector2(const Vector NewVector);
void AIDEBUG_TestPathFind();
int AIMGR_GetNumAIPlayersOnTeam(AvHTeamNumber Team);
AvHAIPlayer* AIMGR_GetAICommander(AvHTeamNumber Team);

View file

@ -51,6 +51,7 @@ extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently onl
extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles
bool bNavMeshModified = false;
extern bool bTileCacheUpToDate;
std::vector<AvHAIBuildableStructure*> AITAC_FindAllDeployables(const Vector& Location, const DeployableSearchFilter* Filter)
{
@ -693,7 +694,7 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), Item->edict->v.origin, max_player_use_reach);
@ -718,7 +719,7 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), Item->edict->v.origin, max_player_use_reach);
@ -745,14 +746,17 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
AITAC_RefreshHiveData();
}
if (!bTileCacheUpToDate) { return; }
ResNode->bReachabilityMarkedDirty = false;
ResNode->NextReachabilityRefreshTime = 0.0f;
ResNode->TeamAReachabilityFlags = AI_REACHABILITY_NONE;
ResNode->TeamBReachabilityFlags = AI_REACHABILITY_NONE;
Vector ResNodeLocation = AdjustPointForPathfinding(ResNode->Location);
Vector ResNodeLocation = ResNode->Location;
bool bOnNavMesh = UTIL_PointIsOnNavmesh(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], ResNodeLocation, Vector(max_player_use_reach, max_player_use_reach, max_player_use_reach));
bool bOnNavMesh = UTIL_PointIsOnNavmesh(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ResNodeLocation, Vector(max_player_use_reach, max_player_use_reach, max_player_use_reach));
if (!bOnNavMesh)
{
@ -761,9 +765,12 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
return;
}
Vector TeamAStart = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber());
Vector TeamBStart = AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber());
if (GetGameRules()->GetTeamA()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
bool bIsReachableMarine = UTIL_PointIsReachable(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableMarine)
{
@ -775,9 +782,9 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamAStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableWelder)
{
@ -787,9 +794,9 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
}
else
{
bool bIsReachableSkulk = UTIL_PointIsReachable(BaseNavProfiles[SKULK_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableGorge = UTIL_PointIsReachable(BaseNavProfiles[GORGE_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach);
bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamAStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableSkulk)
{
@ -809,7 +816,7 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE)
{
bool bIsReachableMarine = UTIL_PointIsReachable(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableMarine = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableMarine)
{
@ -821,9 +828,9 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, TeamBStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableWelder)
{
@ -833,16 +840,16 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode)
}
else
{
bool bIsReachableSkulk = UTIL_PointIsReachable(BaseNavProfiles[SKULK_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableSkulk = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach);
if (!bIsReachableSkulk)
{
UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, ResNodeLocation, 20.0f);
UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), 20.0f, 255, 255, 0);
UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, TeamBStart, 20.0f, 255, 255, 0);
}
bool bIsReachableGorge = UTIL_PointIsReachable(BaseNavProfiles[GORGE_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(BaseNavProfiles[ONOS_BASE_NAV_PROFILE], AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamBNumber()), ResNodeLocation, max_player_use_reach);
bool bIsReachableGorge = UTIL_PointIsReachable(GetBaseNavProfile(GORGE_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach);
bool bIsReachableOnos = UTIL_PointIsReachable(GetBaseNavProfile(ONOS_BASE_NAV_PROFILE), TeamBStart, ResNodeLocation, max_player_use_reach);
if (bIsReachableSkulk)
{
@ -873,7 +880,8 @@ void AITAC_RefreshResourceNodes()
NewResNode.Location = theEntity->pev->origin;
NewResNode.TeamAReachabilityFlags = AI_REACHABILITY_NONE;
NewResNode.TeamBReachabilityFlags = AI_REACHABILITY_NONE;
NewResNode.bReachabilityMarkedDirty = true;
NewResNode.bReachabilityMarkedDirty = true;
NewResNode.NextReachabilityRefreshTime = 0.0f;
ResourceNodes.push_back(NewResNode);
@ -907,7 +915,18 @@ void AITAC_RefreshResourceNodes()
if (it->bReachabilityMarkedDirty)
{
AITAC_RefreshReachabilityForResNode(&(*it));
if (it->NextReachabilityRefreshTime == 0.0f)
{
it->NextReachabilityRefreshTime = gpGlobals->time + 1.0f;
}
else
{
if (gpGlobals->time > it->NextReachabilityRefreshTime)
{
AITAC_RefreshReachabilityForResNode(&(*it));
}
}
}
}
}
@ -1352,7 +1371,7 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), UTIL_GetEntityGroundLocation(Structure->edict), max_player_use_reach);
@ -1399,7 +1418,7 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure)
nav_profile WelderProfile;
memcpy(&WelderProfile, &BaseNavProfiles[MARINE_BASE_NAV_PROFILE], sizeof(nav_profile));
WelderProfile.Filters.removeExcludeFlags(SAMPLE_POLYFLAGS_WELD);
WelderProfile.Filters.addIncludeFlags(SAMPLE_POLYFLAGS_WELD);
bool bIsReachableWelder = UTIL_PointIsReachable(WelderProfile, AITAC_GetTeamStartingLocation(GetGameRules()->GetTeamANumber()), UTIL_GetEntityGroundLocation(Structure->edict), max_player_use_reach);

View file

@ -1474,7 +1474,7 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
bool bThing = true;
}
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(theAvHPlayer->pev->origin, Door, nullptr);
DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(theAvHPlayer->pev->origin, Door, nullptr, true);
if (Trigger)
{
@ -1485,26 +1485,21 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
else if (FStrEq(pcmd, "testreachable"))
else if (FStrEq(pcmd, "setdebugvector1"))
{
Vector TeamAStart = AITAC_GetTeamStartingLocation(TEAM_ONE);
Vector TeamBStart = AITAC_GetTeamStartingLocation(TEAM_TWO);
AIDEBUG_SetDebugVector1(UTIL_GetFloorUnderEntity(theAvHPlayer->edict()));
Vector CheckLoc = UTIL_GetFloorUnderEntity(theAvHPlayer->edict());
theSuccess = true;
}
else if (FStrEq(pcmd, "setdebugvector2"))
{
AIDEBUG_SetDebugVector2(UTIL_GetFloorUnderEntity(theAvHPlayer->edict()));
UTIL_DrawLine(INDEXENT(1), TeamAStart, CheckLoc, 10.0f, 0, 0, 255);
UTIL_DrawLine(INDEXENT(1), TeamBStart, CheckLoc, 10.0f, 255, 255, 0);
bool bTeamARechable = UTIL_PointIsReachable(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TeamAStart, CheckLoc, max_player_use_reach);
bool bTeamBRechable = UTIL_PointIsReachable(GetBaseNavProfile(SKULK_BASE_NAV_PROFILE), TeamBStart, CheckLoc, max_player_use_reach);
char buf[64];
sprintf(buf, "Team A: %s\n", (bTeamARechable) ? "True" : "False");
UTIL_SayText(buf, theAvHPlayer);
sprintf(buf, "Team B: %s\n", (bTeamBRechable) ? "True" : "False");
UTIL_SayText(buf, theAvHPlayer);
theSuccess = true;
}
else if (FStrEq(pcmd, "testpathfind"))
{
AIDEBUG_TestPathFind();
theSuccess = true;
}