diff --git a/main/navmeshes/ns_askr_b6.nav b/main/navmeshes/ns_askr_b6.nav index e6326885..e90b148a 100644 Binary files a/main/navmeshes/ns_askr_b6.nav and b/main/navmeshes/ns_askr_b6.nav differ diff --git a/main/navmeshes/ns_hera.nav b/main/navmeshes/ns_hera.nav index 8780aeaa..8c41a2b5 100644 Binary files a/main/navmeshes/ns_hera.nav and b/main/navmeshes/ns_hera.nav differ diff --git a/main/navmeshes/ns_machina.nav b/main/navmeshes/ns_machina.nav index d6bce9a4..b08b78ac 100644 Binary files a/main/navmeshes/ns_machina.nav and b/main/navmeshes/ns_machina.nav differ diff --git a/main/source/mod/AvHAICommander.cpp b/main/source/mod/AvHAICommander.cpp index aa9f63de..65629fa7 100644 --- a/main/source/mod/AvHAICommander.cpp +++ b/main/source/mod/AvHAICommander.cpp @@ -1065,7 +1065,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (HiveUnderSiege) { - bool bAlreadyScanning = AITAC_ItemExistsInLocation(HiveUnderSiege->Location, DEPLOYABLE_ITEM_SCAN, pBot->Player->GetTeam(), AI_REACHABILITY_NONE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false); + bool bAlreadyScanning = AITAC_ItemExistsInLocation(HiveUnderSiege->Location, DEPLOYABLE_ITEM_SCAN, TEAM_IND, AI_REACHABILITY_NONE, 0.0f, UTIL_MetresToGoldSrcUnits(10.0f), false); if (!bAlreadyScanning) { diff --git a/main/source/mod/AvHAIConstants.h b/main/source/mod/AvHAIConstants.h index 3d2a0e25..e1786e50 100644 --- a/main/source/mod/AvHAIConstants.h +++ b/main/source/mod/AvHAIConstants.h @@ -354,7 +354,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE vector OffMeshConnections; // References to any off-mesh connections this structure is associated with Vector LastSuccessfulCommanderLocation = g_vecZero; // Tracks the last commander view location where it successfully placed or selected the building Vector LastSuccessfulCommanderAngle = g_vecZero; // Tracks the last commander input angle ("click" location) used to successfully place or select building - StructurePurpose Purpose = STRUCTURE_PURPOSE_GENERAL; + StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE; bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; } diff --git a/main/source/mod/AvHAINavigation.cpp b/main/source/mod/AvHAINavigation.cpp index 0b31ba4e..314bf3df 100644 --- a/main/source/mod/AvHAINavigation.cpp +++ b/main/source/mod/AvHAINavigation.cpp @@ -2429,7 +2429,7 @@ bool HasBotCompletedWalkMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector M if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED) { - bNextPointReachable = UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination); + bNextPointReachable = UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination, GetPlayerRadius(pBot->Edict)); } return vPointOverlaps3D(MoveEnd, pBot->Edict->v.absmin, pBot->Edict->v.absmax) || (bNextPointReachable && vDist2DSq(pBot->Edict->v.origin, MoveEnd) < sqrf(GetPlayerRadius(pBot->Edict) * 2.0f)); @@ -2448,11 +2448,11 @@ bool HasBotCompletedLadderMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector { if (pBot->BotNavInfo.IsOnGround) { - if (UTIL_PointIsDirectlyReachable(pBot->CollisionHullBottomLocation, NextMoveDestination)) { return true; } + if (UTIL_PointIsDirectlyReachable(pBot->CollisionHullBottomLocation, NextMoveDestination, GetPlayerRadius(pBot->Edict))) { return true; } } else { - if (vDist2DSq(pBot->Edict->v.origin, MoveEnd) < sqrf(GetPlayerRadius(pBot->Edict)) && UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination)) { return true; } + if (vDist2DSq(pBot->Edict->v.origin, MoveEnd) < sqrf(GetPlayerRadius(pBot->Edict)) && UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination, GetPlayerRadius(pBot->Edict))) { return true; } } } @@ -2628,7 +2628,7 @@ void CheckAndHandleDoorObstruction(AvHAIPlayer* pBot) } // If we're blocked by a door that's open, and its wait time isn't infinite (i.e. it will close shortly) then just wait it out - if (BlockingDoor->m_toggle_state == TS_AT_TOP && BlockingDoor->m_flWait >= 0.0f) + if (BlockingDoor->m_toggle_state == TS_AT_TOP && BlockingDoor->m_flWait > 0.0f) { // Wait for the door to start closing if (vDist2DSq(pBot->Edict->v.origin, NearestPoint) < sqrf(UTIL_MetresToGoldSrcUnits(1.5f))) @@ -5049,7 +5049,8 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination) if (vIsZero(Destination)) { return; } - Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition; + Vector CurrentPos = (pBot->BotNavInfo.IsOnGround) ? pBot->Edict->v.origin : pBot->CurrentFloorPosition + GetPlayerOriginOffsetFromFloor(pBot->Edict, false); + CurrentPos.z += 18.0f; const Vector vForward = UTIL_GetVectorNormal2D(Destination - CurrentPos); // Same goes for the right vector, might not be the same as the bot's right @@ -5059,11 +5060,18 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination) Vector stTrcLft = CurrentPos - (vRight * PlayerRadius); Vector stTrcRt = CurrentPos + (vRight * PlayerRadius); - Vector endTrcLft = stTrcLft + (vForward * 24.0f); - Vector endTrcRt = stTrcRt + (vForward * 24.0f); + Vector endTrcLft = stTrcLft + (vForward * (PlayerRadius * 1.5f)); + Vector endTrcRt = stTrcRt + (vForward * (PlayerRadius * 1.5f)); - const bool bumpLeft = !UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, stTrcLft, endTrcLft); - const bool bumpRight = !UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, stTrcRt, endTrcRt); + TraceResult hit; + + UTIL_TraceHull(stTrcLft, endTrcLft, ignore_monsters, head_hull, pBot->Edict->v.pContainingEntity, &hit); + + const bool bumpLeft = (hit.flFraction < 1.0f || hit.fAllSolid > 0 || hit.fStartSolid > 0); + + UTIL_TraceHull(stTrcRt, endTrcRt, ignore_monsters, head_hull, pBot->Edict->v.pContainingEntity, &hit); + + const bool bumpRight = (hit.flFraction < 1.0f || hit.fAllSolid > 0 || hit.fStartSolid > 0); pBot->desiredMovementDir = vForward; @@ -5077,19 +5085,89 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination) } else if (bumpLeft && bumpRight) { - stTrcLft.z = pBot->Edict->v.origin.z; - stTrcRt.z = pBot->Edict->v.origin.z; - endTrcLft.z = pBot->Edict->v.origin.z; - endTrcRt.z = pBot->Edict->v.origin.z; + float MaxScaleHeight = CanPlayerClimb(pBot->Edict) ? 200.0f : GetPlayerMaxJumpHeight(pBot->Edict); + if (pBot->Edict->v.iuser3 == AVH_USER3_ALIEN_PLAYER2) { MaxScaleHeight = 44.0f; } - if (!UTIL_QuickTrace(pBot->Edict, stTrcLft, endTrcLft)) + float JumpHeight = 0.0f; + + bool bFoundJumpHeight = false; + + Vector StartTrace = pBot->CurrentFloorPosition; + Vector EndTrace = StartTrace + (vForward * 50.0f); + EndTrace.z = StartTrace.z; + + TraceResult JumpTestHit; + + while (JumpHeight < MaxScaleHeight && !bFoundJumpHeight) { - pBot->desiredMovementDir = pBot->desiredMovementDir + vRight; + UTIL_TraceHull(StartTrace, EndTrace, ignore_monsters, head_hull, pBot->Edict->v.pContainingEntity, &JumpTestHit); + + if (JumpTestHit.flFraction >= 1.0f && !JumpTestHit.fAllSolid) + { + bFoundJumpHeight = true; + break; + } + + JumpHeight += 5.0f; + + StartTrace.z += 5.0f; + EndTrace.z += 5.0f; + } + + if (JumpHeight <= MaxScaleHeight) + { + if (JumpHeight <= GetPlayerMaxJumpHeight(pBot->Edict)) + { + BotJump(pBot); + } + else + { + switch (pBot->Edict->v.iuser3) + { + case AVH_USER3_ALIEN_PLAYER3: + { + LerkFlightBehaviour FlightBehaviour = BotFlightClimbMove(pBot, pBot->CurrentFloorPosition, EndTrace, pBot->Edict->v.origin.z + JumpHeight); + + if (FlightBehaviour == FLIGHT_GLIDE) + { + pBot->Button |= IN_JUMP; + } + else if (FlightBehaviour == FLIGHT_FLAP) + { + if (gpGlobals->time - pBot->BotNavInfo.LastFlapTime > 0.2f) + { + pBot->Button |= IN_JUMP; + pBot->BotNavInfo.LastFlapTime = gpGlobals->time; + } + } + } + break; + case AVH_USER3_ALIEN_PLAYER4: + BlinkClimbMove(pBot, pBot->CurrentFloorPosition, EndTrace, pBot->Edict->v.origin.z + JumpHeight); + break; + default: + WallClimbMove(pBot, pBot->CurrentFloorPosition, EndTrace, pBot->Edict->v.origin.z + JumpHeight); + break; + } + } } else { - pBot->desiredMovementDir = pBot->desiredMovementDir - vRight; + stTrcLft.z = pBot->Edict->v.origin.z; + stTrcRt.z = pBot->Edict->v.origin.z; + endTrcLft.z = pBot->Edict->v.origin.z; + endTrcRt.z = pBot->Edict->v.origin.z; + + 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); @@ -5114,7 +5192,7 @@ void MoveDirectlyTo(AvHAIPlayer* pBot, const Vector Destination) } -bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetPoint) +bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetPoint, const float MaxDist) { const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(pBot->BotNavInfo.NavProfile); const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(pBot->BotNavInfo.NavProfile); @@ -5160,7 +5238,24 @@ bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetP m_navQuery->raycast(StartPoly, StartNearest, EndNearest, m_navFilter, &hitDist, HitNormal, PolyPath, &pathCount, MAX_AI_PATH_SIZE); - if (hitDist < 1.0f) { return false; } + if (hitDist < 1.0f) + { + if (pathCount == 0) { return false; } + + float epos[3]; + dtVcopy(epos, EndNearest); + + m_navQuery->closestPointOnPoly(PolyPath[pathCount - 1], EndNearest, epos, 0); + + if (dtVdistSqr(EndNearest, epos) > sqrf(MaxDist)) + { + return false; + } + else + { + return true; + } + } if (EndPoly == PolyPath[pathCount - 1]) { return true; } @@ -5174,7 +5269,7 @@ bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetP } -bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, const Vector target) +bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, const Vector target, const float MaxDist) { const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(pBot->BotNavInfo.NavProfile); const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(pBot->BotNavInfo.NavProfile); @@ -5217,7 +5312,24 @@ bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, m_navQuery->raycast(StartPoly, StartNearest, EndNearest, m_navFilter, &hitDist, HitNormal, PolyPath, &pathCount, MAX_AI_PATH_SIZE); - if (hitDist < 1.0f) { return false; } + if (hitDist < 1.0f) + { + if (pathCount == 0) { return false; } + + float epos[3]; + dtVcopy(epos, EndNearest); + + m_navQuery->closestPointOnPoly(PolyPath[pathCount - 1], EndNearest, epos, 0); + + if (dtVdistSqr(EndNearest, epos) > sqrf(MaxDist)) + { + return false; + } + else + { + return true; + } + } if (EndPoly == PolyPath[pathCount - 1]) { return true; } @@ -5252,7 +5364,7 @@ const dtTileCache* UTIL_GetTileCacheForProfile(const nav_profile& NavProfile) return NavMeshes[NavProfile.NavMeshIndex].tileCache; } -bool UTIL_PointIsDirectlyReachable(const nav_profile &NavProfile, const Vector start, const Vector target) +bool UTIL_PointIsDirectlyReachable(const nav_profile &NavProfile, const Vector start, const Vector target, const float MaxDist) { const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(NavProfile); const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(NavProfile); @@ -5274,7 +5386,6 @@ bool UTIL_PointIsDirectlyReachable(const nav_profile &NavProfile, const Vector s dtPolyRef PolyPath[MAX_PATH_POLY]; int pathCount = 0; - dtStatus FoundStartPoly = m_navQuery->findNearestPoly(pStartPos, pReachableExtents, m_navFilter, &StartPoly, StartNearest); if (!dtStatusSucceed(FoundStartPoly)) @@ -5303,7 +5414,7 @@ bool UTIL_PointIsDirectlyReachable(const nav_profile &NavProfile, const Vector s m_navQuery->closestPointOnPoly(PolyPath[pathCount - 1], EndNearest, epos, 0); - if (dtVdistSqr(EndNearest, epos) > sqrf(max_ai_use_reach)) + if (dtVdistSqr(EndNearest, epos) > sqrf(MaxDist)) { return false; } @@ -5481,7 +5592,7 @@ void UTIL_TraceNavLine(const nav_profile &NavProfile, const Vector Start, const HitResult->TraceEndPoint = HitLocation; } -bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target) +bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target, const float MaxDist) { const dtNavMeshQuery* m_navQuery = UTIL_GetNavMeshQueryForProfile(BaseNavProfiles[ALL_NAV_PROFILE]); const dtNavMesh* m_navMesh = UTIL_GetNavMeshForProfile(BaseNavProfiles[ALL_NAV_PROFILE]); @@ -5532,7 +5643,7 @@ bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target) m_navQuery->closestPointOnPoly(PolyPath[pathCount - 1], EndNearest, epos, 0); - if (dtVdistSqr(EndNearest, epos) > sqrf(max_ai_use_reach)) + if (dtVdistSqr(EndNearest, epos) > sqrf(MaxDist)) { return false; } @@ -7778,7 +7889,7 @@ float UTIL_FindZHeightForWallClimb(const Vector ClimbStart, const Vector ClimbEn testCount++; } - if (hit.flFraction >= 1.0f && !hit.fStartSolid) + if (hit.flFraction >= 1.0f && !hit.fStartSolid && !hit.fAllSolid) { if (UTIL_QuickHullTrace(nullptr, EndTrace, EndTrace + Vector(0.0f, 0.0f, 8.0f), false)) { @@ -8216,7 +8327,10 @@ bool UTIL_IsTriggerLinkedToDoor(CBaseEntity* TriggerEntity, vector if (ToggleRef && ToggleRef->pev->target) { - CBaseEntity* TargetEntity = UTIL_FindEntityByTargetname(NULL, STRING(ToggleRef->pev->target)); + const char* TargetEntityName = STRING(ToggleRef->pev->target); + CBaseEntity* TargetEntity = UTIL_FindEntityByTargetname(NULL, TargetEntityName); + + if (!TargetEntity) { return false; } const char* TestTriggerTargetname = STRING(TriggerEntity->pev->targetname); const char* ThisTriggerTarget = STRING(TargetEntity->pev->target); diff --git a/main/source/mod/AvHAINavigation.h b/main/source/mod/AvHAINavigation.h index 067fead4..aa9d3eea 100644 --- a/main/source/mod/AvHAINavigation.h +++ b/main/source/mod/AvHAINavigation.h @@ -253,6 +253,7 @@ void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint) void LadderMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight, unsigned char NextArea); // Called by NewMove, determines the movement direction and inputs required to climb a wall to reach endpoint +LerkFlightBehaviour BotFlightClimbMove(AvHAIPlayer* pBot, Vector FromLocation, Vector ToLocation, float RequiredZ); void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight); void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint, float RequiredClimbHeight); // Called by NewMove, determines the movement direction and inputs required to use a phase gate to reach end point @@ -395,10 +396,10 @@ float UTIL_PointIsDirectlyReachable_DEBUG(const Vector start, const Vector targe Determines if the bot can walk directly between the two points. Ignores map geometry, so will return true even if stairs are in the way, provided the bot can walk up/down them unobstructed */ -bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetPoint); -bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, const Vector target); -bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target); -bool UTIL_PointIsDirectlyReachable(const nav_profile& NavProfile, const Vector start, const Vector target); +bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector targetPoint, const float MaxDist = max_ai_use_reach); +bool UTIL_PointIsDirectlyReachable(const AvHAIPlayer* pBot, const Vector start, const Vector target, const float MaxDist = max_ai_use_reach); +bool UTIL_PointIsDirectlyReachable(const Vector start, const Vector target, const float MaxDist = max_ai_use_reach); +bool UTIL_PointIsDirectlyReachable(const nav_profile& NavProfile, const Vector start, const Vector target, const float MaxDist = max_ai_use_reach); // Will trace along the nav mesh from start to target and return true if the trace reaches within MaxAcceptableDistance bool UTIL_TraceNav(const nav_profile& NavProfile, const Vector start, const Vector target, const float MaxAcceptableDistance); diff --git a/main/source/mod/AvHAIPlayer.cpp b/main/source/mod/AvHAIPlayer.cpp index 01bbf170..aa961c1e 100644 --- a/main/source/mod/AvHAIPlayer.cpp +++ b/main/source/mod/AvHAIPlayer.cpp @@ -1865,82 +1865,12 @@ void EndBotFrame(AvHAIPlayer* pBot) void CustomThink(AvHAIPlayer* pBot) { - // Test Combat Stuff - //DEBUG_PrintCombatInfo(INDEXENT(1), pBot); + AITASK_BotUpdateAndClearTasks(pBot); - pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot); - - if (pBot->CurrentEnemy > -1) + if (pBot->PrimaryBotTask.TaskType != TASK_NONE && !vIsZero(pBot->PrimaryBotTask.TaskLocation)) { - if (IsPlayerMarine(pBot->Edict)) - { - MarineCombatThink(pBot); - } - else - { - AlienCombatThink(pBot); - } + MoveDirectlyTo(pBot, pBot->PrimaryBotTask.TaskLocation); } - else - { - if (AITAC_ShouldBotBeCautious(pBot)) - { - MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_AMBUSH); - } - else - { - MoveTo(pBot, AITAC_GetTeamStartingLocation(AIMGR_GetEnemyTeam(pBot->Player->GetTeam())), MOVESTYLE_NORMAL); - } - - } - - /*AITASK_BotUpdateAndClearTasks(pBot); - - if (pBot->PrimaryBotTask.TaskType != TASK_SECURE_HIVE) - { - AvHTeamNumber BotTeam = pBot->Player->GetTeam(); - AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); - - AvHAIHiveDefinition* HiveToClear = nullptr; - float MinDist = 0.0f; - - vector Hives = AITAC_GetAllHives(); - - for (auto it = Hives.begin(); it != Hives.end(); it++) - { - AvHAIHiveDefinition* ThisHive = (*it); - - if (ThisHive->Status == HIVE_STATUS_UNBUILT) - { - DeployableSearchFilter EnemyStuffFilter; - EnemyStuffFilter.DeployableTeam = EnemyTeam; - EnemyStuffFilter.DeployableTypes = SEARCH_ALL_STRUCTURES; - EnemyStuffFilter.ReachabilityTeam = BotTeam; - EnemyStuffFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; - EnemyStuffFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); - - if (AITAC_DeployableExistsAtLocation(ThisHive->FloorLocation, &EnemyStuffFilter)) - { - float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisHive->FloorLocation); - - if (!HiveToClear || ThisDist < MinDist) - { - HiveToClear = ThisHive; - MinDist = ThisDist; - } - } - } - } - - if (HiveToClear) - { - AITASK_SetSecureHiveTask(pBot, &pBot->PrimaryBotTask, HiveToClear->HiveEdict, HiveToClear->FloorLocation, true); - } - } - else - { - BotProgressTask(pBot, &pBot->PrimaryBotTask); - }*/ } void DroneThink(AvHAIPlayer* pBot) @@ -4463,6 +4393,21 @@ bool AIPlayerMustFinishCurrentTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) return (pBot->Player->GetResources() >= (BALANCE_VAR(kResourceTowerCost) * 0.65f)); } + + if (Task->TaskType == TASK_REINFORCE_STRUCTURE && vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) + { + float SearchRadius = (IsEdictHive(Task->TaskTarget)) ? UTIL_MetresToGoldSrcUnits(10.0f) : UTIL_MetresToGoldSrcUnits(5.0f); + + DeployableSearchFilter UnfinishedStructures; + UnfinishedStructures.DeployableTeam = BotTeam; + UnfinishedStructures.DeployableTypes = SEARCH_ALL_STRUCTURES; + UnfinishedStructures.ReachabilityTeam = BotTeam; + UnfinishedStructures.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + UnfinishedStructures.MaxSearchRadius = SearchRadius; + UnfinishedStructures.ExcludeStatusFlags = STRUCTURE_STATUS_COMPLETED; + + return (AITAC_DeployableExistsAtLocation(Task->TaskTarget->v.origin, &UnfinishedStructures)); + } } return false; @@ -7639,7 +7584,7 @@ bool SkulkCombatThink(AvHAIPlayer* pBot) bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; // EVASIVE MANOEUVRES! Only do this if we're running along the floor and aren't approaching a path point (so we don't stray off the path) - if (CurrentPathNode.flag == SAMPLE_POLYFLAGS_WALK && vDist2DSq(pBot->Edict->v.origin, CurrentPathNode.Location) > sqrf(50.0f)) + if (TrackedEnemyRef->bHasLOS && CurrentPathNode.flag == SAMPLE_POLYFLAGS_WALK && vDist2DSq(pBot->Edict->v.origin, CurrentPathNode.Location) > sqrf(50.0f)) { Vector RightDir = UTIL_GetCrossProduct(pBot->desiredMovementDir, UP_VECTOR); @@ -7790,16 +7735,20 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) Vector EnemyOrientation = UTIL_GetVectorNormal2D(pBot->desiredMovementDir); Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); - pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); - - // Let's get ziggy with it - if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + if (TrackedEnemyRef->bHasLOS) { - pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; - pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); - } - BotMovementInputs(pBot); + pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); + + // Let's get ziggy with it + if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + { + pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; + pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); + } + + BotMovementInputs(pBot); + } return true; } @@ -7859,19 +7808,22 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) } - Vector EnemyOrientation = UTIL_GetVectorNormal2D(pBot->desiredMovementDir); - Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); - - pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); - - // Let's get ziggy with it - if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + if (TrackedEnemyRef->bHasLOS) { - pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; - pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); - } + Vector EnemyOrientation = UTIL_GetVectorNormal2D(pBot->desiredMovementDir); + Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); - BotMovementInputs(pBot); + pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); + + // Let's get ziggy with it + if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + { + pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; + pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); + } + + BotMovementInputs(pBot); + } return true; } @@ -7882,19 +7834,23 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) BotShootTarget(pBot, WEAPON_GORGE_SPIT, CurrentEnemy); } - Vector EnemyOrientation = UTIL_GetVectorNormal2D(pBot->desiredMovementDir); - Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); - - pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); - - // Let's get ziggy with it - if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + if (TrackedEnemyRef->bHasLOS) { - pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; - pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); - } - BotMovementInputs(pBot); + Vector EnemyOrientation = UTIL_GetVectorNormal2D(pBot->desiredMovementDir); + Vector RightDir = UTIL_GetCrossProduct(EnemyOrientation, UP_VECTOR); + + pBot->desiredMovementDir = (pBot->BotNavInfo.bZig) ? UTIL_GetVectorNormal2D(pBot->desiredMovementDir + RightDir) : UTIL_GetVectorNormal2D(pBot->desiredMovementDir - RightDir); + + // Let's get ziggy with it + if (gpGlobals->time > pBot->BotNavInfo.NextZigTime) + { + pBot->BotNavInfo.bZig = !pBot->BotNavInfo.bZig; + pBot->BotNavInfo.NextZigTime = gpGlobals->time + frandrange(0.5f, 1.0f); + } + + BotMovementInputs(pBot); + } } } diff --git a/main/source/mod/AvHAIPlayerUtil.cpp b/main/source/mod/AvHAIPlayerUtil.cpp index be7d2c1f..180f4f0f 100644 --- a/main/source/mod/AvHAIPlayerUtil.cpp +++ b/main/source/mod/AvHAIPlayerUtil.cpp @@ -246,6 +246,16 @@ float GetPlayerRadius(const edict_t* Player) } } +bool CanPlayerClimb(const edict_t* Player) +{ + return IsPlayerSkulk(Player) || IsPlayerFade(Player) || IsPlayerLerk(Player) || PlayerHasJetpack(Player); +} + +float GetPlayerMaxJumpHeight(const edict_t* Player) +{ + return (CanPlayerCrouch(Player) ? max_player_crouchjump_height : max_player_normaljump_height); +} + bool CanPlayerCrouch(const edict_t* Player) { if (FNullEnt(Player) || Player->free || !IsEdictPlayer(Player)) { return false; } @@ -610,7 +620,7 @@ bool PlayerHasHeavyArmour(const edict_t* Player) return (Player->v.iuser4 & MASK_UPGRADE_13); } -bool PlayerHasJetpack(edict_t* Player) +bool PlayerHasJetpack(const edict_t* Player) { if (!IsPlayerMarine(Player)) { return false; } return (Player->v.iuser4 & MASK_UPGRADE_7); diff --git a/main/source/mod/AvHAIPlayerUtil.h b/main/source/mod/AvHAIPlayerUtil.h index 5678e843..e0c9a0a1 100644 --- a/main/source/mod/AvHAIPlayerUtil.h +++ b/main/source/mod/AvHAIPlayerUtil.h @@ -12,8 +12,11 @@ static const float max_player_use_reach = 55.0f; // Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down) static const float min_player_use_interval = 0.5f; -// Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down) -static const float max_player_jump_height = 62.0f; +// Max height reachable by crouch-jumping +static const float max_player_normaljump_height = 44.0f; + +// Max height reachable by crouch-jumping +static const float max_player_crouchjump_height = 62.0f; /**************** @@ -95,6 +98,9 @@ int GetPlayerCombatLevel(const AvHPlayer* Player); float GetPlayerRadius(const AvHPlayer* Player); float GetPlayerRadius(const edict_t* Player); +bool CanPlayerClimb(const edict_t* Player); +float GetPlayerMaxJumpHeight(const edict_t* Player); + // Returns the hull index that should be used for this player when performing hull traces. Depends on if player is crouching right now or not int GetPlayerHullIndex(const edict_t* Player); @@ -137,7 +143,7 @@ bool IsPlayerInUseRange(const edict_t* Player, const edict_t* Target); bool PlayerHasHeavyArmour(const edict_t* Player); -bool PlayerHasJetpack(edict_t* Player); +bool PlayerHasJetpack(const edict_t* Player); bool PlayerHasWeapon(const AvHPlayer* Player, const AvHAIWeapon DesiredCombatWeapon); bool PlayerHasEquipment(edict_t* Player); diff --git a/main/source/mod/AvHAITactical.cpp b/main/source/mod/AvHAITactical.cpp index 026d716a..44883129 100644 --- a/main/source/mod/AvHAITactical.cpp +++ b/main/source/mod/AvHAITactical.cpp @@ -2413,24 +2413,26 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) std::unordered_map& BuildingMap = ((AvHTeamNumber)BuildingEdict->v.team == TeamANumber) ? TeamAStructureMap : TeamBStructureMap; + AvHAIBuildableStructure* StructureRef = &BuildingMap[EntIndex]; + if (StructureType == STRUCTURE_MARINE_DEPLOYEDMINE) { - BuildingMap[EntIndex].StructureType = StructureType; - if (BuildingMap[EntIndex].LastSeen == 0) + StructureRef->StructureType = StructureType; + if (StructureRef->LastSeen == 0) { - BuildingMap[EntIndex].Location = BuildingEdict->v.origin; - BuildingMap[EntIndex].edict = BuildingEdict; - BuildingMap[EntIndex].healthPercent = 1.0f; - BuildingMap[EntIndex].EntityRef = nullptr; - BuildingMap[EntIndex].StructureStatusFlags = STRUCTURE_STATUS_COMPLETED; - BuildingMap[EntIndex].TeamAReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE)); - BuildingMap[EntIndex].TeamBReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE)); + StructureRef->Location = BuildingEdict->v.origin; + StructureRef->edict = BuildingEdict; + StructureRef->healthPercent = 1.0f; + StructureRef->EntityRef = nullptr; + StructureRef->StructureStatusFlags = STRUCTURE_STATUS_COMPLETED; + StructureRef->TeamAReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE)); + StructureRef->TeamBReachabilityFlags = (AI_REACHABILITY_ALL & ~(AI_REACHABILITY_UNREACHABLE)); AITAC_OnStructureCreated(&BuildingMap[EntIndex]); } - BuildingMap[EntIndex].LastSeen = StructureRefreshFrame; + StructureRef->LastSeen = StructureRefreshFrame; - return &BuildingMap[EntIndex]; + return StructureRef; } AvHBaseBuildable* BaseBuildable = dynamic_cast(Structure); @@ -2440,27 +2442,27 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) return nullptr; } - BuildingMap[EntIndex].StructureType = StructureType; + StructureRef->StructureType = StructureType; // This is the first time we've seen this structure, so it must be new - if (BuildingMap[EntIndex].LastSeen == 0) + if (StructureRef->LastSeen == 0) { - BuildingMap[EntIndex].EntityRef = BaseBuildable; - BuildingMap[EntIndex].edict = BuildingEdict; + StructureRef->EntityRef = BaseBuildable; + StructureRef->edict = BuildingEdict; - BuildingMap[EntIndex].OffMeshConnections.clear(); - BuildingMap[EntIndex].Obstacles.clear(); + StructureRef->OffMeshConnections.clear(); + StructureRef->Obstacles.clear(); - BuildingMap[EntIndex].Location = g_vecZero; // We set this just below after calculating reachability + StructureRef->Location = g_vecZero; // We set this just below after calculating reachability AITAC_OnStructureCreated(&BuildingMap[EntIndex]); } - if (vIsZero(BuildingMap[EntIndex].Location) || !vEquals(BaseBuildable->pev->origin, BuildingMap[EntIndex].Location, 5.0f)) + if (vIsZero(StructureRef->Location) || !vEquals(BaseBuildable->pev->origin, StructureRef->Location, 5.0f)) { AITAC_RefreshReachabilityForStructure(&BuildingMap[EntIndex]); - BuildingMap[EntIndex].Location = BaseBuildable->pev->origin; + StructureRef->Location = BaseBuildable->pev->origin; } unsigned int NewFlags = STRUCTURE_STATUS_NONE; @@ -2471,7 +2473,7 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) if (BaseBuildable->GetIsBuilt()) { - if (!(BuildingMap[EntIndex].StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) + if (!(StructureRef->StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) { bJustCompleted = true; } @@ -2490,7 +2492,7 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) if (BaseBuildable->GetIsRecycling()) { - if (!(BuildingMap[EntIndex].StructureStatusFlags & STRUCTURE_STATUS_RECYCLING)) + if (!(StructureRef->StructureStatusFlags & STRUCTURE_STATUS_RECYCLING)) { bJustRecycled = true; } @@ -2514,25 +2516,25 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) float NewHealthPercent = (BuildingEdict->v.health / BuildingEdict->v.max_health); - if (NewHealthPercent < BuildingMap[EntIndex].healthPercent) + if (NewHealthPercent < StructureRef->healthPercent) { - BuildingMap[EntIndex].lastDamagedTime = gpGlobals->time; + StructureRef->lastDamagedTime = gpGlobals->time; } - BuildingMap[EntIndex].healthPercent = NewHealthPercent; + StructureRef->healthPercent = NewHealthPercent; - if (BuildingMap[EntIndex].healthPercent < 0.99f && BaseBuildable->GetIsBuilt()) + if (StructureRef->healthPercent < 0.99f && BaseBuildable->GetIsBuilt()) { NewFlags |= STRUCTURE_STATUS_DAMAGED; } - if (gpGlobals->time - BuildingMap[EntIndex].lastDamagedTime < 10.0f) + if (gpGlobals->time - StructureRef->lastDamagedTime < 10.0f) { NewFlags |= STRUCTURE_STATUS_UNDERATTACK; } - BuildingMap[EntIndex].StructureStatusFlags = NewFlags; - BuildingMap[EntIndex].LastSeen = StructureRefreshFrame; + StructureRef->StructureStatusFlags = NewFlags; + StructureRef->LastSeen = StructureRefreshFrame; if (bJustCompleted) { @@ -2544,7 +2546,76 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) AITAC_OnStructureBeginRecycling(&BuildingMap[EntIndex]); } - return &BuildingMap[EntIndex]; + if (StructureRef->Purpose == STRUCTURE_PURPOSE_NONE) + { + AvHTeamNumber StructureTeam = (AvHTeamNumber)StructureRef->edict->v.team; + + if (AIMGR_GetTeamType(StructureTeam) == AVH_CLASS_TYPE_MARINE) + { + switch (StructureRef->StructureType) + { + case STRUCTURE_MARINE_COMMCHAIR: + case STRUCTURE_MARINE_INFANTRYPORTAL: + case STRUCTURE_MARINE_ARMSLAB: + case STRUCTURE_MARINE_PROTOTYPELAB: + StructureRef->Purpose = STRUCTURE_PURPOSE_BASE; + break; + case STRUCTURE_MARINE_RESTOWER: + StructureRef->Purpose = STRUCTURE_PURPOSE_GENERAL; + break; + case STRUCTURE_MARINE_TURRET: + StructureRef->Purpose = STRUCTURE_PURPOSE_FORTIFY; + break; + case STRUCTURE_MARINE_SIEGETURRET: + StructureRef->Purpose = STRUCTURE_PURPOSE_SIEGE; + break; + default: + { + Vector TeamStart = AITAC_GetTeamStartingLocation(StructureTeam); + + if (vDist2DSq(StructureRef->Location, TeamStart) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) + { + StructureRef->Purpose = STRUCTURE_PURPOSE_BASE; + } + else + { + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(StructureTeam); + + const AvHAIHiveDefinition* NearestHive = AITAC_GetHiveNearestLocation(StructureRef->Location); + + if (NearestHive) + { + if (NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) + { + StructureRef->Purpose = STRUCTURE_PURPOSE_FORTIFY; + } + else if (NearestHive->Status != HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(25.0f))) + { + StructureRef->Purpose = STRUCTURE_PURPOSE_SIEGE; + } + else + { + StructureRef->Purpose = STRUCTURE_PURPOSE_GENERAL; + } + } + else + { + StructureRef->Purpose = STRUCTURE_PURPOSE_GENERAL; + } + } + + } + break; + + } + } + else + { + StructureRef->Purpose = STRUCTURE_PURPOSE_GENERAL; + } + } + + return StructureRef; } diff --git a/main/source/mod/AvHAITask.cpp b/main/source/mod/AvHAITask.cpp index 5ed000d0..2a200767 100644 --- a/main/source/mod/AvHAITask.cpp +++ b/main/source/mod/AvHAITask.cpp @@ -877,7 +877,7 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas DeployableSearchFilter StructureFilter; StructureFilter.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER | ALIEN_BUILD_DEFENSE_CHAMBER | ALIEN_BUILD_MOVEMENT_CHAMBER | ALIEN_BUILD_SENSORY_CHAMBER; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); + StructureFilter.MaxSearchRadius = (IsEdictHive(Task->TaskTarget)) ? UTIL_MetresToGoldSrcUnits(10.0f) : UTIL_MetresToGoldSrcUnits(5.0f); StructureFilter.DeployableTeam = pBot->Player->GetTeam(); vector AllNearbyStructures = AITAC_FindAllDeployables(Task->TaskTarget->v.origin, &StructureFilter); @@ -1339,7 +1339,7 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) Vector ReinforceLocation = UTIL_ProjectPointToNavmesh(UTIL_GetEntityGroundLocation(Task->TaskTarget), pBot->BotNavInfo.NavProfile); float SearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); - if (Task->StructureType == STRUCTURE_ALIEN_HIVE) + if (IsEdictHive(Task->TaskTarget)) { AvHAIHiveDefinition* HiveToReinforce = AITAC_GetHiveFromEdict(Task->TaskTarget); @@ -2834,6 +2834,12 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) return; } + if (Hive->Status != HIVE_STATUS_UNBUILT && Hive->OwningTeam != BotTeam) + { + BotAttackNonPlayerTarget(pBot, Hive->HiveEdict); + return; + } + const AvHAIResourceNode* ResNode = Hive->HiveResNodeRef; if (ResNode && ResNode->bIsOccupied) @@ -2877,6 +2883,7 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) } } + BotGuardLocation(pBot, Task->TaskLocation); }