Final fixes for next release

* Fixed some nav meshes so Onos can navigate better, and avoid bad structure placement
* Improved bot movement when not using the nav mesh (i.e. direct movement)
* Fixed player-built structures being duplicated if a commander takes over (i.e. AI commander ignoring player-placed structures and acting like they don't exist)
* Fixed marines just loitering in enemy hives doing nothing
* Fixed gorges sometimes not fully building structures and just leaving them to slowly grow
This commit is contained in:
RGreenlees 2024-05-10 20:49:15 +01:00 committed by pierow
parent 802174ddcd
commit 1363f48d96
12 changed files with 338 additions and 173 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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)
{

View File

@ -354,7 +354,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE
vector<AvHAIOffMeshConnection> 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; }

View File

@ -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<CBaseEntity*>
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);

View File

@ -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);

View File

@ -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<AvHAIHiveDefinition*> 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);
}
}
}

View File

@ -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);

View File

@ -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);

View File

@ -2413,24 +2413,26 @@ AvHAIBuildableStructure* AITAC_UpdateBuildableStructure(CBaseEntity* Structure)
std::unordered_map<int, AvHAIBuildableStructure>& 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<AvHBaseBuildable*>(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;
}

View File

@ -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<AvHAIBuildableStructure> 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);
}