diff --git a/main/source/detour/DetourNavMeshQuery.cpp b/main/source/detour/DetourNavMeshQuery.cpp index 666981df..5fc9293c 100644 --- a/main/source/detour/DetourNavMeshQuery.cpp +++ b/main/source/detour/DetourNavMeshQuery.cpp @@ -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); } diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 33f679df..60b0d129 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -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 diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index f516b0cd..8df3e301 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -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& 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; } \ No newline at end of file diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index a948e9ad..a879673e 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -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& path, float MaxAcceptableDistance); dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector& path, float MaxAcceptableDistance); +dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector& 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); diff --git a/main/source/mod/AIPlayers/AvHAIPlayerManager.h b/main/source/mod/AIPlayers/AvHAIPlayerManager.h index 5afd4733..41455776 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayerManager.h +++ b/main/source/mod/AIPlayers/AvHAIPlayerManager.h @@ -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); diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 04e74586..48d96d30 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -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 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); diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index 90f686ad..670bedcd 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -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; }