diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 55517147..a6d62e26 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -416,6 +416,7 @@ typedef struct _NAV_STATUS Vector TargetDestination = g_vecZero; // Desired destination Vector ActualMoveDestination = g_vecZero; // Actual destination on nav mesh + Vector PathDestination = g_vecZero; // Where the path is currently headed to Vector LastNavMeshPosition = g_vecZero; // Tracks the last place the bot was on the nav mesh. Useful if accidentally straying off it @@ -458,6 +459,9 @@ typedef struct _NAV_STATUS nav_profile NavProfile; bool bNavProfileChanged = false; + unsigned short SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.) + + } nav_status; // Type of goal the commander wants to achieve diff --git a/main/source/mod/AIPlayers/AvHAIHelper.cpp b/main/source/mod/AIPlayers/AvHAIHelper.cpp index d49714ff..f1a96659 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIHelper.cpp @@ -2,6 +2,7 @@ #include "AvHAIMath.h" #include "AvHAIPlayerUtil.h" #include "AvHAITactical.h" +#include "AvHAINavigation.h" #include "../AvHGamerules.h" @@ -243,6 +244,41 @@ bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation) return theSuccess; } +void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot) +{ + if (pBot->BotNavInfo.PathSize == 0) { return; } + + for (int i = 0; i < pBot->BotNavInfo.PathSize; i++) + { + Vector FromLoc = pBot->BotNavInfo.CurrentPath[i].FromLocation; + Vector ToLoc = pBot->BotNavInfo.CurrentPath[i].Location; + + switch (pBot->BotNavInfo.CurrentPath[i].flag) + { + case SAMPLE_POLYFLAGS_WELD: + case SAMPLE_POLYFLAGS_DOOR: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 255, 0, 0); + break; + case SAMPLE_POLYFLAGS_JUMP: + case SAMPLE_POLYFLAGS_DUCKJUMP: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 255, 255, 0); + break; + case SAMPLE_POLYFLAGS_LADDER: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 0, 0, 255); + break; + case SAMPLE_POLYFLAGS_WALLCLIMB: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 0, 128, 0); + break; + case SAMPLE_POLYFLAGS_BLOCKED: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc, 128, 128, 128); + break; + default: + UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc); + break; + } + } +} + void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end) { if (FNullEnt(pEntity) || pEntity->free) { return; } diff --git a/main/source/mod/AIPlayers/AvHAIHelper.h b/main/source/mod/AIPlayers/AvHAIHelper.h index 56fe1972..ce7acffb 100644 --- a/main/source/mod/AIPlayers/AvHAIHelper.h +++ b/main/source/mod/AIPlayers/AvHAIHelper.h @@ -30,6 +30,8 @@ bool GetNearestMapLocationAtPoint(vec3_t SearchLocation, string& outLocation); AvHAIDeployableStructureType GetDeployableObjectTypeFromEdict(const edict_t* StructureEdict); +void AIDEBUG_DrawBotPath(AvHAIPlayer* pBot); + // Draws a white line between start and end for the given player (pEntity) for 0.1s void UTIL_DrawLine(edict_t* pEntity, Vector start, Vector end); // Draws a white line between start and end for the given player (pEntity) for given number of seconds diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index a09e0ffd..1782e5b6 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -36,6 +36,9 @@ vector NavDoors; nav_weldable NavWeldableObstacles[32]; int NumWeldableObstacles; +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 + struct NavMeshSetHeader { int magic; @@ -276,9 +279,14 @@ struct MeshProcess : public dtTileCacheMeshProcess } else if (polyAreas[i] == DT_TILECACHE_WELD_AREA) { - polyAreas[i] = SAMPLE_POLYAREA_GROUND; + polyAreas[i] = SAMPLE_POLYAREA_OBSTRUCTION; polyFlags[i] = SAMPLE_POLYFLAGS_WELD; } + else if (polyAreas[i] == DT_TILECACHE_DOOR_AREA) + { + polyAreas[i] = SAMPLE_POLYAREA_OBSTRUCTION; + polyFlags[i] = SAMPLE_POLYFLAGS_DOOR; + } } params->offMeshConAreas = OffMeshAreas; @@ -545,6 +553,8 @@ void UnloadNavigationData() bool LoadNavMesh(const char* mapname) { + memset(NavMeshes, 0, sizeof(NavMeshes)); + char filename[256]; // Full path to BSP file GetFullFilePath(filename, mapname); @@ -882,10 +892,13 @@ bool LoadNavMesh(const char* mapname) void UTIL_PopulateBaseNavProfiles() { + memset(BaseNavProfiles, 0, sizeof(BaseNavProfiles)); + BaseNavProfiles[MARINE_BASE_NAV_PROFILE].NavMeshIndex = REGULAR_NAV_MESH; BaseNavProfiles[MARINE_BASE_NAV_PROFILE].bFlyingProfile = false; BaseNavProfiles[MARINE_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_MARINE; BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 2.0f); BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f); BaseNavProfiles[MARINE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f); @@ -899,6 +912,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setExcludeFlags(0); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); BaseNavProfiles[SKULK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); @@ -912,6 +926,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[GORGE_BASE_NAV_PROFILE].bFlyingProfile = false; BaseNavProfiles[GORGE_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_MARINE; BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f); BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 2.0f); BaseNavProfiles[GORGE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f); @@ -926,6 +941,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[LERK_BASE_NAV_PROFILE].bFlyingProfile = true; BaseNavProfiles[LERK_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_SKULK; BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); BaseNavProfiles[LERK_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); @@ -939,6 +955,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[FADE_BASE_NAV_PROFILE].bFlyingProfile = false; BaseNavProfiles[FADE_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_SKULK; BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.5f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); BaseNavProfiles[FADE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); @@ -952,6 +969,7 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[ONOS_BASE_NAV_PROFILE].bFlyingProfile = false; BaseNavProfiles[ONOS_BASE_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_ONOS; BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 2.0f); BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 2.0f); BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); BaseNavProfiles[ONOS_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 10.0f); @@ -965,12 +983,24 @@ void UTIL_PopulateBaseNavProfiles() BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].NavMeshIndex = BUILDING_NAV_MESH; BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setIncludeFlags(0xFFFF); BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setExcludeFlags(0); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 1.0f); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); + BaseNavProfiles[STRUCTURE_BASE_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f); 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(0xFFFF); BaseNavProfiles[ALL_NAV_PROFILE].Filters.setExcludeFlags(0); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_GROUND, 1.0f); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_OBSTRUCTION, 1.0f); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_CROUCH, 1.0f); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_BLOCKED, 1.0f); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_FALLDAMAGE, 1.0f); + BaseNavProfiles[ALL_NAV_PROFILE].Filters.setAreaCost(SAMPLE_POLYAREA_WALLCLIMB, 1.0f); BaseNavProfiles[ALL_NAV_PROFILE].bFlyingProfile = false; BaseNavProfiles[ALL_NAV_PROFILE].ReachabilityFlag = AI_REACHABILITY_SKULK; } @@ -1702,6 +1732,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); if (!m_navQuery || !m_navMesh || !m_navFilter || vIsZero(FromLocation) || vIsZero(ToLocation)) { @@ -1802,6 +1833,8 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, TraceResult hit; Vector TraceStart; + pBot->BotNavInfo.SpecialMovementFlags = 0; + Vector NodeFromLocation = Vector(StartNearest[0], -StartNearest[2], StartNearest[1]); for (int nVert = 0; nVert < nVertCount; nVert++) @@ -1841,6 +1874,8 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, } } + pBot->BotNavInfo.SpecialMovementFlags |= CurrFlags; + // End alignment to floor // For ladders and wall climbing, calculate the climb height needed to complete the move. @@ -1864,12 +1899,12 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, path[(nVert)].requiredZ = path[(nVert)].Location.z; } - path[(nVert)].flag = straightPathFlags[nVert]; + path[(nVert)].flag = CurrFlags; path[(nVert)].area = CurrArea; path[(nVert)].poly = StraightPolyPath[nVert]; m_navMesh->getPolyFlags(StraightPolyPath[nVert], &CurrFlags); - m_navMesh->getPolyArea(StraightPolyPath[0], &CurrArea); + m_navMesh->getPolyArea(StraightPolyPath[nVert], &CurrArea); CurrFlags &= ~(SAMPLE_POLYFLAGS_TEAM1STRUCTURE | SAMPLE_POLYFLAGS_TEAM2STRUCTURE); @@ -2020,31 +2055,18 @@ bool HasBotReachedPathPoint(const AvHAIPlayer* pBot) return false; } -void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, const Vector MoveTo) +void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot) { - + int PathNodeIndex = pBot->BotNavInfo.CurrentPathPoint + 1; edict_t* BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot->Edict->v.origin, pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint].Location, SAMPLE_POLYAREA_GROUND, nullptr); - - if (FNullEnt(BlockingDoorEdict)) + + while (FNullEnt(BlockingDoorEdict) && PathNodeIndex < pBot->BotNavInfo.PathSize - 1 && PathNodeIndex <= pBot->BotNavInfo.CurrentPathPoint + 2) { - if (pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.PathSize - 1) - { - BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(&pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 1], nullptr); - } + BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(pBot, &pBot->BotNavInfo.CurrentPath[PathNodeIndex], nullptr); + PathNodeIndex++; } - if (FNullEnt(BlockingDoorEdict)) - { - if (pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.PathSize - 2) - { - BlockingDoorEdict = UTIL_GetDoorBlockingPathPoint(&pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint + 2], nullptr); - } - } - - if (FNullEnt(BlockingDoorEdict)) - { - return; - } + if (FNullEnt(BlockingDoorEdict)) { return; } CBaseToggle* BlockingDoor = GetClassPtr((CBaseToggle*)VARS(BlockingDoorEdict)); @@ -2138,7 +2160,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, con } -edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchDoor) +edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchDoor) { if (!PathNode) { return nullptr; } @@ -2151,7 +2173,7 @@ edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchD { Vector TargetLoc = Vector(FromLoc.x, FromLoc.y, PathNode->requiredZ); - UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, nullptr, &doorHit); + UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, (pBot!= nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit); if (!FNullEnt(SearchDoor)) { @@ -2173,7 +2195,7 @@ edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchD Vector TargetLoc2 = Vector(ToLoc.x, ToLoc.y, PathNode->requiredZ); - UTIL_TraceLine(TargetLoc, TargetLoc2, ignore_monsters, dont_ignore_glass, nullptr, &doorHit); + UTIL_TraceLine(TargetLoc, TargetLoc2, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit); if (!FNullEnt(SearchDoor)) { @@ -2197,7 +2219,7 @@ edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchD { Vector TargetLoc = Vector(ToLoc.x, ToLoc.y, FromLoc.z); - UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, nullptr, &doorHit); + UTIL_TraceLine(FromLoc, TargetLoc, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit); if (!FNullEnt(SearchDoor)) { @@ -2216,7 +2238,7 @@ edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchD } } - UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), ignore_monsters, dont_ignore_glass, nullptr, &doorHit); + UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit); if (!FNullEnt(SearchDoor)) { @@ -2235,8 +2257,10 @@ edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchD } } } + Vector StartTrace = FromLoc + Vector(0.0f, 0.0f, 16.0f); + Vector EndTrace = ToLoc + Vector(0.0f, 0.0f, 16.0f); - UTIL_TraceLine(FromLoc + Vector(0.0f, 0.0f, 16.0f), ToLoc + Vector(0.0f, 0.0f, 16.0f), ignore_monsters, dont_ignore_glass, nullptr, &doorHit); + UTIL_TraceLine(StartTrace, EndTrace, ignore_monsters, dont_ignore_glass, (pBot != nullptr) ? pBot->Edict->v.pContainingEntity : nullptr, &doorHit); if (!FNullEnt(SearchDoor)) { @@ -2271,7 +2295,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa Vector TargetLoc = Vector(FromLoc.x, FromLoc.y, PathNode->requiredZ); UTIL_TraceLine(FromLoc, TargetLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, TargetLoc); if (!FNullEnt(SearchBreakable)) { @@ -2292,7 +2315,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa Vector TargetLoc2 = Vector(ToLoc.x, ToLoc.y, PathNode->requiredZ); UTIL_TraceLine(TargetLoc, TargetLoc2, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), TargetLoc, TargetLoc2); if (!FNullEnt(SearchBreakable)) { @@ -2315,7 +2337,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa Vector TargetLoc = Vector(ToLoc.x, ToLoc.y, FromLoc.z); UTIL_TraceLine(FromLoc, TargetLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, TargetLoc); if (!FNullEnt(SearchBreakable)) { @@ -2333,7 +2354,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa } UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f)); if (!FNullEnt(SearchBreakable)) { @@ -2352,7 +2372,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* Pa } UTIL_TraceLine(FromLoc, ToLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc); if (!FNullEnt(SearchBreakable)) @@ -2386,7 +2405,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From Vector TargetLoc = Vector(FromLoc.x, FromLoc.y, ToLocation.z); UTIL_TraceLine(FromLoc, TargetLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, TargetLoc); if (!FNullEnt(SearchBreakable)) { @@ -2407,7 +2425,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From Vector TargetLoc2 = Vector(ToLoc.x, ToLoc.y, ToLocation.z); UTIL_TraceLine(TargetLoc, TargetLoc2, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), TargetLoc, TargetLoc2); if (!FNullEnt(SearchBreakable)) { @@ -2430,7 +2447,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From Vector TargetLoc = Vector(ToLoc.x, ToLoc.y, FromLoc.z); UTIL_TraceLine(FromLoc, TargetLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, TargetLoc); if (!FNullEnt(SearchBreakable)) { @@ -2448,7 +2464,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From } UTIL_TraceLine(TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f), dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), TargetLoc, ToLoc + Vector(0.0f, 0.0f, 10.0f)); if (!FNullEnt(SearchBreakable)) { @@ -2467,7 +2482,6 @@ edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector From } UTIL_TraceLine(FromLoc, ToLoc, dont_ignore_monsters, dont_ignore_glass, pBot->Edict->v.pContainingEntity, &breakableHit); - UTIL_DrawLine(INDEXENT(1), FromLoc, ToLoc); if (!FNullEnt(SearchBreakable)) { @@ -2615,7 +2629,7 @@ bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_ if (!ValidNavmeshPoint) { - return g_vecZero; + return false; } bot_path_node Path[MAX_AI_PATH_SIZE]; @@ -2630,7 +2644,7 @@ bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_ { for (int i = 1; i < PathSize; i++) { - if (UTIL_GetDoorBlockingPathPoint(&Path[i], SearchDoor) != nullptr) + if (UTIL_GetDoorBlockingPathPoint(nullptr, &Path[i], SearchDoor) != nullptr) { return true; } @@ -2846,11 +2860,7 @@ void NewMove(AvHAIPlayer* pBot) // While moving, check to make sure we're not obstructed by a func_breakable, e.g. vent or window. CheckAndHandleBreakableObstruction(pBot, MoveFrom, MoveTo); - if (gpGlobals->time - pBot->LastUseTime >= 1.0f) - { - CheckAndHandleDoorObstruction(pBot, MoveFrom, MoveTo); - } - + CheckAndHandleDoorObstruction(pBot); } @@ -4506,7 +4516,6 @@ void SetBaseNavProfile(AvHAIPlayer* pBot) void UpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle) { - pBot->BotNavInfo.bNavProfileChanged = false; if (IsPlayerMarine(pBot->Player)) { @@ -4549,6 +4558,7 @@ void MarineUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle) bHasWelder = (NearbyWelder != nullptr); } + // Did our nav profile previously indicate we could go through weldable doors? bool bHadWelder = !(NavProfile->Filters.getExcludeFlags() & SAMPLE_POLYFLAGS_WELD); if (bHasWelder != bHadWelder) @@ -4695,38 +4705,7 @@ void OnosUpdateBotMoveProfile(AvHAIPlayer* pBot, BotMoveStyle MoveStyle) bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle MoveStyle, const float MaxAcceptableDist) { nav_status* BotNavInfo = &pBot->BotNavInfo; - - bool bHasMovementTask = (BotNavInfo->MovementTask.TaskType != TASK_NONE); - bool bIsPerformingMovementTask = false; - - if (bHasMovementTask) - { - bIsPerformingMovementTask = (vEquals(Destination, BotNavInfo->MovementTask.TaskLocation) || (!FNullEnt(BotNavInfo->MovementTask.TaskTarget) && vEquals(Destination, BotNavInfo->MovementTask.TaskTarget->v.origin)) || (!FNullEnt(BotNavInfo->MovementTask.TaskSecondaryTarget) && vEquals(Destination, BotNavInfo->MovementTask.TaskSecondaryTarget->v.origin))); - } - // Invalid destination, or we're already there - if (!bIsPerformingMovementTask && (vIsZero(Destination) || BotIsAtLocation(pBot, Destination))) - { - ClearBotMovement(pBot); - - return true; - } - - if (bHasMovementTask && !bIsPerformingMovementTask) - { - if (AITASK_IsTaskStillValid(pBot, &BotNavInfo->MovementTask)) - { - BotProgressTask(pBot, &BotNavInfo->MovementTask); - return true; - } - else - { - AITASK_ClearBotTask(pBot, &BotNavInfo->MovementTask); - } - } - - UTIL_UpdateBotMovementStatus(pBot); - // If we are currently in the process of getting back on the navmesh, don't interrupt if (BotNavInfo->UnstuckMoveLocation != g_vecZero) { @@ -4769,45 +4748,82 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move pBot->Button |= IN_DUCK; } - + return true; } } - + UpdateBotMoveProfile(pBot, MoveStyle); + UTIL_UpdateBotMovementStatus(pBot); bool bIsFlyingProfile = pBot->BotNavInfo.NavProfile.bFlyingProfile; - bool bNavProfileChanged = pBot->BotNavInfo.bNavProfileChanged; + + if (BotNavInfo->PathSize > 0) + { + bool bNavProfileChanged = pBot->BotNavInfo.bNavProfileChanged; + + bool bHasMovementTask = (BotNavInfo->MovementTask.TaskType != TASK_NONE); + + Vector MoveTaskDestination = g_vecZero; + + if (bHasMovementTask) + { + MoveTaskDestination = (!vIsZero(BotNavInfo->MovementTask.TaskLocation)) ? BotNavInfo->MovementTask.TaskLocation : BotNavInfo->MovementTask.TaskTarget->v.origin; + } + + bool bUltimateDestinationChanged = !vEquals(Destination, BotNavInfo->TargetDestination, GetPlayerRadius(pBot->Player)) && !vEquals(Destination, MoveTaskDestination); + + bool bHasReachedDestination = BotIsAtLocation(pBot, BotNavInfo->TargetDestination); + + if (bUltimateDestinationChanged || bNavProfileChanged || bHasReachedDestination) + { + // First abort our current move so we don't try to recalculate half-way up a wall or ladder + if (bIsFlyingProfile || AbortCurrentMove(pBot, Destination)) + { + ClearBotPath(pBot); + return true; + } + } + else + { + if (bHasMovementTask && !vEquals(Destination, MoveTaskDestination)) + { + if (AITASK_IsTaskStillValid(pBot, &BotNavInfo->MovementTask)) + { + BotProgressTask(pBot, &BotNavInfo->MovementTask); + return true; + } + else + { + AITASK_ClearBotTask(pBot, &BotNavInfo->MovementTask); + } + } + } + } + bool bCanRecalculatePath = (gpGlobals->time - pBot->BotNavInfo.LastPathCalcTime > MIN_PATH_RECALC_TIME); - bool bDestinationChanged = (!vEquals(Destination, BotNavInfo->TargetDestination, GetPlayerRadius(pBot->Player))); // Only recalculate the path if there isn't a path, or something has changed and enough time has elapsed since the last path calculation - bool bShouldCalculatePath = bCanRecalculatePath && (BotNavInfo->PathSize == 0 || (bNavProfileChanged || bDestinationChanged || BotNavInfo->bPendingRecalculation)); + bool bShouldCalculatePath = bCanRecalculatePath && (BotNavInfo->PathSize == 0 || !vEquals(Destination, BotNavInfo->PathDestination)); if (bShouldCalculatePath) { - // First abort our current move so we don't try to recalculate half-way up a wall or ladder - if (!bIsFlyingProfile && !AbortCurrentMove(pBot, Destination)) - { - return true; - } - if (pBot->Player->IsOnLadder()) { BotJump(pBot); return true; } - if (bDestinationChanged && !bIsPerformingMovementTask) - { - AITASK_ClearBotTask(pBot, &pBot->BotNavInfo.MovementTask); - } - pBot->BotNavInfo.LastPathCalcTime = gpGlobals->time; BotNavInfo->bPendingRecalculation = false; - BotNavInfo->TargetDestination = Destination; + if (vIsZero(BotNavInfo->TargetDestination)) + { + BotNavInfo->TargetDestination = Destination; + } + + BotNavInfo->PathDestination = Destination; Vector ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(Destination, Vector(max_ai_use_reach, max_ai_use_reach, max_ai_use_reach), pBot->BotNavInfo.NavProfile); @@ -4828,8 +4844,6 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move { PathFindingStatus = FindPathClosestToPoint(pBot, pBot->BotNavInfo.MoveStyle, pBot->CurrentFloorPosition, ValidNavmeshPoint, BotNavInfo->CurrentPath, &BotNavInfo->PathSize, MaxAcceptableDist); } - - if (dtStatusSucceed(PathFindingStatus)) { @@ -5162,10 +5176,7 @@ void BotFollowFlightPath(AvHAIPlayer* pBot) CheckAndHandleBreakableObstruction(pBot, MoveFrom, CurrentMoveDest); - if (gpGlobals->time - pBot->LastUseTime >= 3.0f) - { - CheckAndHandleDoorObstruction(pBot, MoveFrom, CurrentMoveDest); - } + CheckAndHandleDoorObstruction(pBot); } void BotFollowPath(AvHAIPlayer* pBot) @@ -5204,6 +5215,25 @@ void BotFollowPath(AvHAIPlayer* pBot) return; } + // If this path requires use of a welder and we don't have one, then find one + if ((pBot->BotNavInfo.SpecialMovementFlags & SAMPLE_POLYFLAGS_WELD) && !PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER)) + { + if (pBot->BotNavInfo.MovementTask.TaskType != TASK_GET_WEAPON) + { + AITASK_ClearBotTask(pBot, &pBot->BotNavInfo.MovementTask); + + AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_WELDER, 0.0f, 0.0f, true); + + if (NearestWelder) + { + AITASK_SetPickupTask(pBot, &pBot->BotNavInfo.MovementTask, NearestWelder->edict, true); + return; + } + + + } + } + Vector TargetMoveLocation = BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].Location; bool bIsUsingPhaseGate = (BotNavInfo->CurrentPath[BotNavInfo->CurrentPathPoint].flag == SAMPLE_POLYFLAGS_PHASEGATE); @@ -5888,8 +5918,15 @@ void ClearBotPath(AvHAIPlayer* pBot) memset(pBot->BotNavInfo.CurrentPath, 0, sizeof(pBot->BotNavInfo.CurrentPath)); } + pBot->BotNavInfo.SpecialMovementFlags = 0; + AITASK_ClearBotTask(pBot, &pBot->BotNavInfo.MovementTask); + pBot->BotNavInfo.bNavProfileChanged = false; + + pBot->BotNavInfo.TargetDestination = g_vecZero; + pBot->BotNavInfo.PathDestination = g_vecZero; + //pBot->BotNavInfo.LastNavMeshPosition = g_vecZero; } @@ -6104,9 +6141,24 @@ Vector UTIL_GetFurthestVisiblePointOnPath(const Vector ViewerLocation, const bot Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdict) { - Vector ClosestPoint = UTIL_GetClosestPointOnEntityToLocation(UserLocation, ButtonEdict); + Vector ClosestPoint = g_vecZero; + + if (ButtonEdict->v.size.x > 64.0f || ButtonEdict->v.size.y > 64.0f) + { + ClosestPoint = UTIL_GetClosestPointOnEntityToLocation(UserLocation, ButtonEdict); + } + else + { + ClosestPoint = UTIL_GetCentreOfEntity(ButtonEdict); + } - Vector ButtonAccessPoint = UTIL_ProjectPointToNavmesh(ClosestPoint, Vector(100.0f, 100.0f, 100.0f), BaseNavProfiles[ALL_NAV_PROFILE]); + nav_profile ButtonNavProfile; + memcpy(&ButtonNavProfile, &BaseNavProfiles[ALL_NAV_PROFILE], sizeof(nav_profile)); + + ButtonNavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_WELD); + ButtonNavProfile.Filters.addExcludeFlags(SAMPLE_POLYFLAGS_DOOR); + + Vector ButtonAccessPoint = UTIL_ProjectPointToNavmesh(ClosestPoint, Vector(100.0f, 100.0f, 100.0f), ButtonNavProfile); if (vIsZero(ButtonAccessPoint)) { @@ -6124,7 +6176,7 @@ Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdi PlayerAccessLoc.z += 36.0f; } - if (fabsf(PlayerAccessLoc.z - ClosestPoint.z) <= 60.0f) + if (fabsf(PlayerAccessLoc.z - ClosestPoint.z) <= max_player_use_reach) { return ButtonAccessPoint; } @@ -6140,7 +6192,7 @@ Vector UTIL_GetButtonFloorLocation(const Vector UserLocation, edict_t* ButtonEdi NewProjection = ClosestPoint + Vector(0.0f, 0.0f, 100.0f); } - Vector NewButtonAccessPoint = UTIL_ProjectPointToNavmesh(NewProjection, BaseNavProfiles[ALL_NAV_PROFILE]); + Vector NewButtonAccessPoint = UTIL_ProjectPointToNavmesh(NewProjection, ButtonNavProfile); if (vIsZero(NewButtonAccessPoint)) { @@ -6524,11 +6576,14 @@ void UTIL_UpdateDoors(bool bInitial) { UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_NULL_AREA); } - - if (it->ActivationType == DOOR_WELD) + else if (it->ActivationType == DOOR_WELD) { UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_WELD_AREA); } + else + { + UTIL_ApplyTempObstaclesToDoor(&(*it), DT_TILECACHE_DOOR_AREA); + } it->CurrentState = DoorRef->m_toggle_state; } @@ -6611,7 +6666,7 @@ void UTIL_ApplyTempObstaclesToDoor(nav_door* DoorRef, const int Area) for (int ii = 0; ii < NumObstacles; ii++) { - UTIL_AddTemporaryObstacles(CurrentPoint, CylinderRadius, SizeZ, Area, DoorRef->ObstacleRefs[ii]); + UTIL_AddTemporaryObstacles(CurrentPoint, CylinderRadius, SizeZ * 2.0f, Area, DoorRef->ObstacleRefs[ii]); if (bUseXAxis) { @@ -6623,7 +6678,6 @@ void UTIL_ApplyTempObstaclesToDoor(nav_door* DoorRef, const int Area) } } - } void UTIL_UpdateDoorTriggers(nav_door* Door) @@ -6742,6 +6796,7 @@ void UTIL_PopulateDoors() UTIL_PopulateTriggersForEntity(currDoor->edict(), NewDoor.TriggerEnts); } + NavDoors.push_back(NewDoor); } currDoor = NULL; @@ -6767,6 +6822,8 @@ void UTIL_PopulateDoors() UTIL_PopulateTriggersForEntity(currDoor->edict(), NewDoor.TriggerEnts); } + + NavDoors.push_back(NewDoor); } UTIL_UpdateDoors(true); diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index a6b18f1c..1f099848 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -20,7 +20,7 @@ */ constexpr auto MIN_PATH_RECALC_TIME = 0.33f; // How frequently can a bot recalculate its path? Default to max 3 times per second -constexpr auto MAX_BOT_STUCK_TIME = 30.0f; // How long a bot can be stuck, unable to move, before giving up and suiciding +constexpr auto MAX_BOT_STUCK_TIME = 0.0f; // How long a bot can be stuck, unable to move, before giving up and suiciding constexpr auto MARINE_BASE_NAV_PROFILE = 0; constexpr auto SKULK_BASE_NAV_PROFILE = 1; @@ -40,7 +40,8 @@ enum SamplePolyAreas SAMPLE_POLYAREA_CROUCH = 1, // Requires crouched movement SAMPLE_POLYAREA_BLOCKED = 2, // Requires a jump to get over SAMPLE_POLYAREA_FALLDAMAGE = 3, // Requires taking fall damage (if not immune to it) - SAMPLE_POLYAREA_WALLCLIMB = 4 // Requires the ability to wall-stick, fly or blink + SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall-stick, fly or blink + SAMPLE_POLYAREA_OBSTRUCTION = 5 // There is a door or weldable object in the way }; // Possible movement types. Swim and door are not used @@ -58,6 +59,7 @@ enum SamplePolyFlags SAMPLE_POLYFLAGS_TEAM1STRUCTURE = 1 << 9, // A team 1 structure is in the way that cannot be jumped over. Impassable to team 1 players SAMPLE_POLYFLAGS_TEAM2STRUCTURE = 1 << 10, // A team 2 structure is in the way that cannot be jumped over. Impassable to team 2 players SAMPLE_POLYFLAGS_WELD = 1 << 11, // Requires a welder to get through here + SAMPLE_POLYFLAGS_DOOR = 1 << 12, // Requires a welder to get through here SAMPLE_POLYFLAGS_DISABLED = 1 << 15, // Disabled, not usable by anyone SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities. @@ -146,9 +148,6 @@ static const int DOOR_START_OPEN = 1; static const float CHECK_STUCK_INTERVAL = 0.1f; // How frequently should the bot check if it's stuck? -static nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) -static nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles - // Returns true if a valid nav mesh has been loaded into memory bool NavmeshLoaded(); // Unloads all data, including loaded nav meshes, nav profiles, all the map data such as buildable structure maps and hive locations. @@ -244,12 +243,12 @@ void PhaseGateMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP // Will check for any func_breakable which might be in the way (e.g. window, vent) and make the bot aim and attack it to break it. Marines will switch to knife to break it. void CheckAndHandleBreakableObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, const Vector MoveTo); -void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot, const Vector MoveFrom, const Vector MoveTo); +void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot); DoorTrigger* UTIL_GetNearestDoorTrigger(const Vector Location, nav_door* Door, CBaseEntity* IgnoreTrigger); bool UTIL_IsPathBlockedByDoor(const Vector StartLoc, const Vector EndLoc, edict_t* SearchDoor); -edict_t* UTIL_GetDoorBlockingPathPoint(bot_path_node* PathNode, edict_t* SearchDoor); +edict_t* UTIL_GetDoorBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchDoor); edict_t* UTIL_GetDoorBlockingPathPoint(const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchDoor); edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, bot_path_node* PathNode, edict_t* SearchBreakable); edict_t* UTIL_GetBreakableBlockingPathPoint(AvHAIPlayer* pBot, const Vector FromLocation, const Vector ToLocation, const unsigned short MovementFlag, edict_t* SearchBreakable); diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index bcba3578..7be8131c 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -11,6 +11,9 @@ #include "../AvHMessage.h" +extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) +extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles + void BotJump(AvHAIPlayer* pBot) { if (pBot->BotNavInfo.IsOnGround) @@ -1450,6 +1453,11 @@ void StartNewBotFrame(AvHAIPlayer* pBot) pBot->BotNavInfo.bShouldWalk = false; + if (pBot->BotNavInfo.NavProfile.ReachabilityFlag == AI_REACHABILITY_NONE) + { + SetBaseNavProfile(pBot); + } + } void DroneThink(AvHAIPlayer* pBot) @@ -1462,6 +1470,8 @@ void DroneThink(AvHAIPlayer* pBot) { BotProgressTask(pBot, &pBot->PrimaryBotTask); } + + AIDEBUG_DrawBotPath(pBot); } void TestNavThink(AvHAIPlayer* pBot) diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 2a261c5a..f6850176 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -50,6 +50,9 @@ unsigned int ItemRefreshFrame = 0; Vector TeamAStartingLocation = ZERO_VECTOR; Vector TeamBStartingLocation = ZERO_VECTOR; +extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) +extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles + bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSearchFilter* Filter) { @@ -164,6 +167,17 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocation(const Vector& Loc return Result; } +AvHAIDroppedItem* AITAC_GetDroppedItemRefFromEdict(edict_t* ItemEdict) +{ + if (FNullEnt(ItemEdict)) { return nullptr; } + + int EntIndex = ENTINDEX(ItemEdict); + + if (EntIndex < 0) { return nullptr; } + + return &MarineDroppedItemMap[EntIndex]; +} + AvHAIDroppedItem* AITAC_FindClosestItemToLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance) { AvHAIDroppedItem* Result = NULL; diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index 739ea1cc..209cf609 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -52,6 +52,8 @@ AvHAIResourceNode* AITAC_GetRandomResourceNode(); AvHAIDroppedItem* AITAC_FindClosestItemToLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance); +AvHAIDroppedItem* AITAC_GetDroppedItemRefFromEdict(edict_t* ItemEdict); + Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive); int AITAC_GetNumHives(); diff --git a/main/source/mod/AIPlayers/AvHAITask.cpp b/main/source/mod/AIPlayers/AvHAITask.cpp index 589cdada..82e31677 100644 --- a/main/source/mod/AIPlayers/AvHAITask.cpp +++ b/main/source/mod/AIPlayers/AvHAITask.cpp @@ -14,6 +14,9 @@ #include "../AvHGamerules.h" #include "../AvHWeldable.h" +extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) +extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles + void AITASK_ClearAllBotTasks(AvHAIPlayer* pBot) { AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask); @@ -2626,6 +2629,59 @@ char* AITASK_TaskTypeToChar(const BotTaskType TaskType) } } +void AITASK_SetPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent) +{ + if (FNullEnt(Target) || (Target->v.effects & EF_NODRAW)) + { + AITASK_ClearBotTask(pBot, Task); + return; + } + + if (Task->TaskTarget == Target) + { + Task->bTaskIsUrgent = bIsUrgent; + return; + } + + AvHAIDroppedItem* ItemToPickup = AITAC_GetDroppedItemRefFromEdict(Target); + + if (!ItemToPickup || FNullEnt(ItemToPickup->edict) || !ItemToPickup->bIsReachableMarine) + { + AITASK_ClearBotTask(pBot, Task); + return; + } + + Vector PickupLocation = FindClosestNavigablePointToDestination(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, Target->v.origin, max_player_use_reach); + + if (vIsZero(PickupLocation)) + { + AITASK_ClearBotTask(pBot, Task); + return; + } + + switch (ItemToPickup->ItemType) + { + case DEPLOYABLE_ITEM_AMMO: + Task->TaskType = TASK_GET_AMMO; + break; + case DEPLOYABLE_ITEM_HEALTHPACK: + Task->TaskType = TASK_GET_HEALTH; + break; + case DEPLOYABLE_ITEM_JETPACK: + case DEPLOYABLE_ITEM_HEAVYARMOUR: + Task->TaskType = TASK_GET_EQUIPMENT; + break; + default: + Task->TaskType = TASK_GET_WEAPON; + break; + } + + Task->TaskTarget = Target; + Task->TaskLocation = PickupLocation; + Task->bTaskIsUrgent = bIsUrgent; + +} + void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent) { if (FNullEnt(Target) || (Target->v.deadflag != DEAD_NO)) @@ -2940,7 +2996,7 @@ void AITASK_SetUseTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target Task->TaskType = TASK_USE; Task->TaskTarget = Target; - Task->TaskLocation = FindClosestNavigablePointToDestination(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, UseLocation, UTIL_MetresToGoldSrcUnits(10.0f)); + Task->TaskLocation = UseLocation; Task->bTaskIsUrgent = bIsUrgent; Task->TaskLength = 10.0f; Task->TaskStartedTime = gpGlobals->time; diff --git a/main/source/mod/AIPlayers/AvHAITask.h b/main/source/mod/AIPlayers/AvHAITask.h index 3f505129..7fca57e4 100644 --- a/main/source/mod/AIPlayers/AvHAITask.h +++ b/main/source/mod/AIPlayers/AvHAITask.h @@ -66,6 +66,7 @@ void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, void AITASK_SetSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const Vector WaitLocation, bool bIsUrgent); void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent); void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent); +void AITASK_SetPickupTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent); void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task); diff --git a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp index 202151e2..efd48ee3 100644 --- a/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp +++ b/main/source/mod/AIPlayers/AvHAIWeaponHelper.cpp @@ -13,6 +13,9 @@ #include "../AvHMarineEquipmentConstants.h" #include "../AvHServerUtil.h" +extern nav_mesh NavMeshes[MAX_NAV_MESHES]; // Array of nav meshes. Currently only 3 are used (building, onos, and regular) +extern nav_profile BaseNavProfiles[MAX_NAV_PROFILES]; // Array of nav profiles + int BotGetCurrentWeaponClipAmmo(const AvHAIPlayer* pBot) { AvHBasePlayerWeapon* theBasePlayerWeapon = dynamic_cast(pBot->Player->m_pActiveItem); diff --git a/main/source/mod/AvHConsoleCommands.cpp b/main/source/mod/AvHConsoleCommands.cpp index 70409ce1..83685fd9 100644 --- a/main/source/mod/AvHConsoleCommands.cpp +++ b/main/source/mod/AvHConsoleCommands.cpp @@ -1424,11 +1424,16 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd ) if (!FNullEnt(TracedEntity)) { - const nav_door* Door = UTIL_GetNavDoorByEdict(TracedEntity); + nav_door* Door = UTIL_GetNavDoorByEdict(TracedEntity); if (Door) { - bool bThing = true; + DoorTrigger* Trigger = UTIL_GetNearestDoorTrigger(theAvHPlayer->pev->origin, Door, nullptr); + + if (Trigger) + { + UTIL_DrawLine(INDEXENT(1), theAvHPlayer->pev->origin, UTIL_GetButtonFloorLocation(theAvHPlayer->pev->origin, Trigger->Edict), 10.0f); + } } }