From c2e41c2011907937f6c463ede3c2ee91f46de643 Mon Sep 17 00:00:00 2001 From: RGreenlees Date: Fri, 17 May 2024 21:46:35 +0100 Subject: [PATCH] Bot movement improvements * Commander no longer sends reminders once an order is issued * Fixed an issue where marines would loiter and refuse to move on once securing a hive * Improved wall climbing: bots handle varied wall angles better --- main/source/mod/AvHAICommander.cpp | 12 ++--- main/source/mod/AvHAIConstants.h | 1 + main/source/mod/AvHAINavigation.cpp | 75 ++++++++++++++++++++++++----- main/source/mod/AvHAIPlayer.cpp | 23 ++++++--- main/source/mod/AvHAIPlayer.h | 6 +-- main/source/mod/AvHAITactical.cpp | 7 +-- main/source/mod/AvHAITask.cpp | 7 ++- 7 files changed, 98 insertions(+), 33 deletions(-) diff --git a/main/source/mod/AvHAICommander.cpp b/main/source/mod/AvHAICommander.cpp index bfa87b0a..fa3ac2c2 100644 --- a/main/source/mod/AvHAICommander.cpp +++ b/main/source/mod/AvHAICommander.cpp @@ -380,7 +380,7 @@ bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order) bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order) { // For now, disable reminders as it is annoying for humans and pointless for bots who should obey it always anyway - return false; + return (Order->LastReminderTime < 0.1f); float NewDist = vDist2DSq(Order->Assignee->v.origin, Order->OrderLocation); float OldDist = Order->LastPlayerDistance; @@ -2154,7 +2154,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini { DeployableSearchFilter StructureFilter; StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); StructureFilter.DeployableTeam = pBot->Player->GetTeam(); StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; StructureFilter.ReachabilityTeam = pBot->Player->GetTeam(); @@ -2164,7 +2164,7 @@ bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefini AvHAIBuildableStructure ExistingPG; AvHAIBuildableStructure ExistingTF; - Vector OutpostLocation = (ExistingStructure.IsValid()) ? ExistingStructure.Location : HiveToSecure->FloorLocation; + Vector OutpostLocation = (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY) ? ExistingStructure.Location : HiveToSecure->FloorLocation; if (HiveToSecure->HiveResNodeRef && HiveToSecure->HiveResNodeRef->OwningTeam == TEAM_IND) { @@ -3360,11 +3360,11 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); AvHAIBuildableStructure ExistingStructure = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter); - if (ExistingStructure.IsValid() && UTIL_QuickTrace(nullptr, UTIL_GetCentreOfEntity(ExistingStructure.edict), Hive->Location)) + if (ExistingStructure.IsValid() && ExistingStructure.Purpose == STRUCTURE_PURPOSE_FORTIFY) { SecureLocation = ExistingStructure.Location; } @@ -3444,7 +3444,7 @@ bool AICOMM_IsHiveFullySecured(AvHAIPlayer* CommanderBot, const AvHAIHiveDefinit bTurretFactoryElectrified = (Structure.StructureStatusFlags & STRUCTURE_STATUS_ELECTRIFIED); SearchFilter.DeployableTypes = STRUCTURE_MARINE_TURRET; - SearchFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(8.0f); + SearchFilter.MaxSearchRadius = BALANCE_VAR(kTurretFactoryBuildDistance); NumTurrets = AITAC_GetNumDeployablesNearLocation(Structure.Location, &SearchFilter); diff --git a/main/source/mod/AvHAIConstants.h b/main/source/mod/AvHAIConstants.h index 6ecd84e7..00063062 100644 --- a/main/source/mod/AvHAIConstants.h +++ b/main/source/mod/AvHAIConstants.h @@ -788,6 +788,7 @@ typedef struct AVH_AI_PLAYER edict_t* LookTarget = nullptr; // Used to work out what view angle is needed to look at the desired entity Vector LookTargetLocation = g_vecZero; // This is the bot's current desired look target. Could be an enemy (see LookTarget), or point of interest Vector MoveLookLocation = g_vecZero; // If the bot has to look somewhere specific for movement (e.g. up for a ladder or wall-climb), this will override LookTargetLocation so the bot doesn't get distracted and mess the move up + bool bSnapView = false; // Use for rapid, precise snapping of the bot's view to the target. Useful if the bot requires more precise view angles for movement or other reasons float LastTargetTrackUpdate = 0.0f; // Add a delay to how frequently a bot can track a target's movements float ViewInterpolationSpeed = 0.0f; // How fast should the bot turn its view? Depends on distance to turn float ViewInterpStartedTime = 0.0f; // Used for interpolation diff --git a/main/source/mod/AvHAINavigation.cpp b/main/source/mod/AvHAINavigation.cpp index 6a351cdd..ae7d9c2d 100644 --- a/main/source/mod/AvHAINavigation.cpp +++ b/main/source/mod/AvHAINavigation.cpp @@ -3702,7 +3702,10 @@ void FallMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint) pBot->desiredMovementDir = vBotOrientation; } - + if (vIsZero(pBot->LookTargetLocation)) + { + BotLookAt(pBot, EndPoint + (vBotOrientation * 100.0f)); + } } @@ -4890,6 +4893,30 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP } Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint); + Vector ClimbAngle = UTIL_GetVectorNormal(Vector(EndPoint.x, EndPoint.y, RequiredClimbHeight) - pBot->Edict->v.origin); + + TraceResult SurfaceCheck; + + UTIL_TraceHull(pBot->Edict->v.origin - Vector(0.0f, 0.0f, 1.0f), pBot->Edict->v.origin + Vector(0.0f, 0.0f, 5.0f), ignore_monsters, head_hull, pBot->Edict->v.pContainingEntity, &SurfaceCheck); + + Vector CeilNormal = SurfaceCheck.vecPlaneNormal; + + bool bIsUnderClimbing = false; + + bool bClimbingUnderway = ((pBot->CollisionHullBottomLocation.z - StartPoint.z) >= 32.0f) && IsPlayerClimbingWall(pBot->Edict); + + if (pEdict->v.origin.z < (RequiredClimbHeight - 10.0f) && !(pEdict->v.flags & FL_ONGROUND) && bClimbingUnderway) + { + bIsUnderClimbing = (SurfaceCheck.flFraction < 1.0f && UTIL_GetDotProduct(ClimbAngle, CeilNormal) < 0.0f); + } + + if (bIsUnderClimbing) + { + pBot->Button |= IN_WALK; + vForward = (UTIL_GetDotProduct2D(vForward, CeilNormal) > 0.0f) ? vForward : -vForward; + + } + Vector vRight = UTIL_GetVectorNormal(UTIL_GetCrossProduct(vForward, UP_VECTOR)); pBot->desiredMovementDir = vForward; @@ -4912,14 +4939,10 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP pBot->Button &= ~IN_DUCK; } - - float ZDiff = fabs(pEdict->v.origin.z - RequiredClimbHeight); Vector AdjustedTargetLocation = EndPoint + (UTIL_GetVectorNormal2D(EndPoint - StartPoint) * 1000.0f); Vector DirectAheadView = pBot->CurrentEyePosition + (UTIL_GetVectorNormal2D(AdjustedTargetLocation - pBot->CurrentEyePosition) * 100.0f); - - Vector ClimbSurfaceNormal = UTIL_GetVectorNormal(EndPoint - StartPoint); - + Vector LookLocation = g_vecZero; if (ZDiff < 1.0f) @@ -4931,10 +4954,10 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP // Don't look up/down quite so much as we reach the desired height so we slow down a bit, reduces the chance of over-shooting and climbing right over a vent if (pEdict->v.origin.z > RequiredClimbHeight) { - if (ZDiff > 32.0f) + if (ZDiff > 16.0f) { - ClimbSurfaceNormal = ClimbSurfaceNormal - (2.0f * (UTIL_GetDotProduct(ClimbSurfaceNormal, UP_VECTOR) * ClimbSurfaceNormal)); - LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); + ClimbAngle = ClimbAngle - (2.0f * (UTIL_GetDotProduct(ClimbAngle, UP_VECTOR) * ClimbAngle)); + LookLocation = pBot->CurrentEyePosition + (ClimbAngle * 100.0f); } else { @@ -4943,12 +4966,27 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP } else { - ClimbSurfaceNormal = ClimbSurfaceNormal; - LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); + if (bIsUnderClimbing) + { + LookLocation = pBot->CurrentEyePosition + vForward; + LookLocation.z = EndPoint.z + 100.0f; + } + else + { + if (bClimbingUnderway) + { + LookLocation = pBot->CurrentEyePosition + (ClimbAngle * 100.0f); + } + else + { + LookLocation = pBot->CurrentEyePosition + vForward; + LookLocation.z = RequiredClimbHeight; + } + } } } - if (IsPlayerClimbingWall(pBot->Edict)) + if (IsPlayerClimbingWall(pBot->Edict) && !bIsUnderClimbing) { Vector RightDir = UTIL_GetCrossProduct(vForward, UP_VECTOR); @@ -4971,7 +5009,8 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP } } - BotMoveLookAt(pBot, LookLocation); + BotMoveLookAt(pBot, LookLocation, true); + //BotDirectLookAt(pBot, LookLocation); } @@ -7937,6 +7976,11 @@ void BotMovementInputs(AvHAIPlayer* pBot) float botSpeed = (pBot->BotNavInfo.bShouldWalk) ? (pBot->Edict->v.maxspeed * 0.4f) : pBot->Edict->v.maxspeed; + if (pBot->BotNavInfo.bShouldWalk) + { + pBot->Button |= IN_WALK; + } + if (angleDelta < -180.0f) { angleDelta += 360.0f; @@ -9690,6 +9734,11 @@ bool NAV_IsMovementTaskStillValid(AvHAIPlayer* pBot) if (MoveTask->TaskType == MOVE_TASK_TOUCH) { + if (pBot->BotNavInfo.IsOnGround) + { + if (vDist2DSq(pBot->Edict->v.origin, MoveTask->TaskLocation) < sqrf(8.0f) && fabs(pBot->Edict->v.origin.z - MoveTask->TaskLocation.z) < 32.0f) { return false; } + } + return (!FNullEnt(MoveTask->TaskTarget) && !IsPlayerTouchingEntity(pBot->Edict, MoveTask->TaskTarget)); } diff --git a/main/source/mod/AvHAIPlayer.cpp b/main/source/mod/AvHAIPlayer.cpp index bea5861a..4e62f51f 100644 --- a/main/source/mod/AvHAIPlayer.cpp +++ b/main/source/mod/AvHAIPlayer.cpp @@ -59,17 +59,18 @@ void BotSuicide(AvHAIPlayer* pBot) } /* Makes the bot look at the specified position */ -void BotLookAt(AvHAIPlayer* pBot, const Vector target) +void BotLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap) { - + pBot->bSnapView = bSnap; pBot->LookTargetLocation.x = target.x; pBot->LookTargetLocation.y = target.y; pBot->LookTargetLocation.z = target.z; } -void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target) +void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap) { + pBot->bSnapView = bSnap; pBot->MoveLookLocation.x = target.x; pBot->MoveLookLocation.y = target.y; pBot->MoveLookLocation.z = target.z; @@ -126,10 +127,12 @@ enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target) return nullptr; } -void BotLookAt(AvHAIPlayer* pBot, edict_t* target) +void BotLookAt(AvHAIPlayer* pBot, edict_t* target, bool bSnap) { if (FNullEnt(target)) { return; } + pBot->bSnapView = bSnap; + pBot->LookTarget = target; // For team mates we don't track enemy refs, so just look at the friendly player @@ -1152,6 +1155,14 @@ void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot) if (pBot->DesiredLookDirection.x > 180) pBot->DesiredLookDirection.x -= 360; + if (pBot->bSnapView) + { + pBot->ViewInterpolationSpeed = 1000.0f; + pBot->ViewInterpStartedTime = gpGlobals->time; + + return; + } + // Now figure out how far we have to turn to reach our desired target float yDelta = pBot->DesiredLookDirection.y - pBot->InterpolatedLookDirection.y; float xDelta = pBot->DesiredLookDirection.x - pBot->InterpolatedLookDirection.x; @@ -1194,8 +1205,6 @@ void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot) yOffset *= -1.0f; } - - pBot->DesiredLookDirection.x += xOffset; pBot->DesiredLookDirection.y += yOffset; } @@ -5741,7 +5750,7 @@ void TestNavThink(AvHAIPlayer* pBot) pBot->CurrentTask = &pBot->PrimaryBotTask; - if (IsPlayerAlien(pBot->Edict) && IsPlayerSkulk(pBot->Edict)) + if (IsPlayerAlien(pBot->Edict) && IsPlayerSkulk(pBot->Edict) && AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER1, pBot->Edict) > 0) { if (AITAC_GetNumPlayersOnTeamOfClass(pBot->Player->GetTeam(), AVH_USER3_ALIEN_PLAYER2, pBot->Edict) == 0) { diff --git a/main/source/mod/AvHAIPlayer.h b/main/source/mod/AvHAIPlayer.h index 15b2cdf0..9233d151 100644 --- a/main/source/mod/AvHAIPlayer.h +++ b/main/source/mod/AvHAIPlayer.h @@ -26,9 +26,9 @@ static const float f_ffwidth = f_ffheight * BOT_ASPECT_RATIO; void BotJump(AvHAIPlayer* pBot); void BotSuicide(AvHAIPlayer* pBot); -void BotLookAt(AvHAIPlayer* pBot, Vector NewLocation); -void BotLookAt(AvHAIPlayer* pBot, edict_t* target); -void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target); +void BotLookAt(AvHAIPlayer* pBot, Vector NewLocation, bool bSnap = false); +void BotLookAt(AvHAIPlayer* pBot, edict_t* target, bool bSnap = false); +void BotMoveLookAt(AvHAIPlayer* pBot, const Vector target, bool bSnap = false); void BotDirectLookAt(AvHAIPlayer* pBot, Vector target); bool BotUseObject(AvHAIPlayer* pBot, edict_t* Target, bool bContinuous); diff --git a/main/source/mod/AvHAITactical.cpp b/main/source/mod/AvHAITactical.cpp index 5744488a..b855dead 100644 --- a/main/source/mod/AvHAITactical.cpp +++ b/main/source/mod/AvHAITactical.cpp @@ -2585,7 +2585,7 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure) if (NearestHive) { - if (NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f))) + if (NearestHive->Status == HIVE_STATUS_UNBUILT && vDist2DSq(NearestHive->FloorLocation, StructureRef->Location) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { StructureRef->Purpose = STRUCTURE_PURPOSE_FORTIFY; } @@ -5520,9 +5520,10 @@ bool AITAC_IsBotPursuingSquadObjective(AvHAIPlayer* pBot, AvHAISquad* Squad) if (!pBot->CurrentTask || pBot->CurrentTask == &pBot->PrimaryBotTask) { return true; } // Bot isn't currently pursuing squad objective, so check if it's doing something in the vicinity - Vector TaskLocation = (!FNullEnt(pBot->CurrentTask->TaskTarget)) ? pBot->CurrentTask->TaskTarget->v.origin : pBot->CurrentTask->TaskLocation; + Vector BotTaskLocation = (!FNullEnt(pBot->CurrentTask->TaskTarget)) ? pBot->CurrentTask->TaskTarget->v.origin : pBot->CurrentTask->TaskLocation; + Vector SquadTaskLocation = (!FNullEnt(Squad->SquadTarget)) ? Squad->SquadTarget->v.origin : Squad->ObjectiveLocation; - return vDist2DSq(TaskLocation, Squad->SquadTarget->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)); + return vDist2DSq(BotTaskLocation, SquadTaskLocation) < sqrf(UTIL_MetresToGoldSrcUnits(10.0f)); } void AITAC_ManageSquads() diff --git a/main/source/mod/AvHAITask.cpp b/main/source/mod/AvHAITask.cpp index 3c186222..4e1217da 100644 --- a/main/source/mod/AvHAITask.cpp +++ b/main/source/mod/AvHAITask.cpp @@ -2814,11 +2814,14 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) StructureFilter.ReachabilityTeam = BotTeam; StructureFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_FORTIFY; vector BuildableStructures = AITAC_FindAllDeployables(Hive->FloorLocation, &StructureFilter); bool bKeyStructureBuilt = false; + AvHAIBuildableStructure KeyOutpostStructure; + AvHAIBuildableStructure StructureToBuild; float MinDist = 0.0f; @@ -2828,6 +2831,7 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) if ((ThisStructure.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED) && (ThisStructure.StructureType & (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE))) { + KeyOutpostStructure = ThisStructure; bKeyStructureBuilt = true; } @@ -2904,8 +2908,9 @@ void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) } } + Vector OutpostLocation = (KeyOutpostStructure.IsValid()) ? KeyOutpostStructure.Location : Task->TaskLocation; - BotGuardLocation(pBot, Task->TaskLocation); + BotGuardLocation(pBot, OutpostLocation); }