mirror of
https://github.com/ENSL/NS.git
synced 2024-11-24 21:41:17 +00:00
Door and movement improvements
* Fixed buttons having overly long wait times with bots * Fixed issue with climbing walls in certain situations
This commit is contained in:
parent
3daa2f32e4
commit
f391176841
4 changed files with 269 additions and 67 deletions
|
@ -276,7 +276,7 @@ void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Orde
|
|||
}
|
||||
else
|
||||
{
|
||||
Vector MoveLoc = (bIsSiegeHiveOrder) ? UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Hive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(25.0f)) : Hive->FloorLocation;
|
||||
Vector MoveLoc = (bIsSiegeHiveOrder) ? UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Hive->FloorLocation, UTIL_MetresToGoldSrcUnits(10.0f), UTIL_MetresToGoldSrcUnits(20.0f)) : Hive->FloorLocation;
|
||||
|
||||
AICOMM_IssueMovementOrder(pBot, Order->Assignee, MoveLoc);
|
||||
Order->LastReminderTime = gpGlobals->time;
|
||||
|
@ -1972,7 +1972,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
{
|
||||
Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f));
|
||||
|
||||
if (!vIsZero(NextBuildPosition))
|
||||
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
|
||||
|
||||
|
@ -1981,7 +1981,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(NextBuildPosition))
|
||||
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
|
||||
|
||||
|
@ -1990,7 +1990,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit
|
|||
|
||||
NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(NextBuildPosition))
|
||||
if (!vIsZero(NextBuildPosition) && vDist2DSq(NextBuildPosition, HiveToSiege->Location) < sqrf(UTIL_MetresToGoldSrcUnits(22.0f)))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, NextStructure, NextBuildPosition, STRUCTURE_PURPOSE_SIEGE);
|
||||
|
||||
|
|
|
@ -159,7 +159,7 @@ void CONFIG_ParseConfigFile()
|
|||
|
||||
while (getline(cFile, line))
|
||||
{
|
||||
line.erase(std::remove_if(line.begin(), line.end(), ::isspace),
|
||||
line.erase(std::remove_if(line.begin(), line.end(), isspace),
|
||||
line.end());
|
||||
if (line[0] == '#' || line.empty())
|
||||
continue;
|
||||
|
@ -468,8 +468,8 @@ void CONFIG_RegenerateIniFile()
|
|||
|
||||
fprintf(NewConfigFile, "### Skill Settings ###\n\n");
|
||||
|
||||
fprintf(NewConfigFile, "# Bot skill settings. You can define as many settings as you like and reference them by name\n");
|
||||
fprintf(NewConfigFile, "# Format is BotSkillName = name, followed by one of the following:\n");
|
||||
fprintf(NewConfigFile, "# Bot skill settings. There are 4 settings from 0 - 3 for easiest - hardest\n");
|
||||
fprintf(NewConfigFile, "# Use BotSkillName=<index> to start defining a skill level, then the following:\n");
|
||||
fprintf(NewConfigFile, "# ReactionTime = How quickly in seconds the bot will react to sighting enemies\n");
|
||||
fprintf(NewConfigFile, "# AimSkill = How accurately the bot can lock sights on you after seeing you (0.0 - 1.0)\n");
|
||||
fprintf(NewConfigFile, "# MovementTracking = How accurately the bot can follow a moving target (0.0 - 1.0)\n");
|
||||
|
@ -514,7 +514,8 @@ void CONFIG_RegenerateIniFile()
|
|||
fprintf(NewConfigFile, "AlienReactionTime=0.1\n");
|
||||
fprintf(NewConfigFile, "AlienAimSkill=1.0\n");
|
||||
fprintf(NewConfigFile, "AlienMovementTracking=1.0\n");
|
||||
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n");
|
||||
fprintf(NewConfigFile, "AlienViewSpeed=2.0\n\n\n");
|
||||
|
||||
|
||||
fprintf(NewConfigFile, "# Desired team sizes. Only used if bot fill mode is 'fillteams'\n");
|
||||
fprintf(NewConfigFile, "# Format is TeamSize=mapname:nummarines/numaliens\n");
|
||||
|
|
|
@ -2326,7 +2326,9 @@ bool HasBotCompletedClimbMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector
|
|||
{
|
||||
Vector PositionInMove = vClosestPointOnLine2D(MoveStart, MoveEnd, pBot->Edict->v.origin);
|
||||
|
||||
if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
|
||||
if (!vEquals2D(PositionInMove, MoveEnd, 4.0f)) { return false; }
|
||||
|
||||
/*if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
|
||||
{
|
||||
Vector ThisMoveDir = UTIL_GetVectorNormal2D(MoveEnd - MoveStart);
|
||||
Vector NextMoveDir = UTIL_GetVectorNormal2D(NextMoveDestination - MoveEnd);
|
||||
|
@ -2344,11 +2346,40 @@ bool HasBotCompletedClimbMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector
|
|||
}
|
||||
}
|
||||
}
|
||||
}*/
|
||||
|
||||
if (pBot->BotNavInfo.IsOnGround)
|
||||
{
|
||||
return UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (NextMoveFlag != SAMPLE_POLYFLAGS_DISABLED)
|
||||
{
|
||||
Vector ThisMoveDir = UTIL_GetVectorNormal2D(MoveEnd - MoveStart);
|
||||
Vector NextMoveDir = UTIL_GetVectorNormal2D(NextMoveDestination - MoveEnd);
|
||||
|
||||
float MoveDot = UTIL_GetDotProduct2D(ThisMoveDir, NextMoveDir);
|
||||
|
||||
if (MoveDot > 0.0f)
|
||||
{
|
||||
if (pBot->Edict->v.origin.z >= RequiredClimbHeight && !pBot->BotNavInfo.IsOnGround)
|
||||
{
|
||||
if (UTIL_QuickTrace(pBot->Edict, pBot->Edict->v.origin, NextMoveDestination)
|
||||
&& fabsf(pBot->CollisionHullBottomLocation.z - MoveEnd.z) < 100.0f)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!vEquals2D(PositionInMove, MoveEnd, 2.0f)) { return false; }
|
||||
|
||||
return vPointOverlaps3D(MoveEnd, pBot->Edict->v.absmin, pBot->Edict->v.absmax) && UTIL_PointIsDirectlyReachable(pBot->CurrentFloorPosition, NextMoveDestination);
|
||||
}
|
||||
|
||||
bool HasBotCompletedJumpMove(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd, Vector NextMoveDestination, SamplePolyFlags NextMoveFlag)
|
||||
|
@ -4081,7 +4112,7 @@ void LiftMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
|||
if (NearestLiftTrigger)
|
||||
{
|
||||
// If the trigger is on cooldown, or the door/train is designed to automatically return without being summoned, then just wait for it to come back
|
||||
if (gpGlobals->time < NearestLiftTrigger->NextActivationTime || (NearestLift->DoorType == DOORTYPE_TRAIN && !(NearestLift->DoorEdict->v.spawnflags & SF_TRAIN_WAIT_RETRIGGER)) || (NearestLift->DoorType == DOORTYPE_DOOR && NearestLift->DoorEntity && NearestLift->DoorEntity->GetToggleState() == TS_AT_TOP && NearestLift->DoorEntity->m_flWait > 0.0f && !(NearestLift->DoorEdict->v.spawnflags & SF_DOOR_NO_AUTO_RETURN)))
|
||||
if (gpGlobals->time < NearestLiftTrigger->NextActivationTime || (NearestLift->DoorType == DOORTYPE_TRAIN && !(NearestLift->DoorEdict->v.spawnflags & SF_TRAIN_WAIT_RETRIGGER)) || (NearestLift->DoorType == DOORTYPE_DOOR && NearestLift->DoorEntity && NearestLift->DoorEntity->GetToggleState() == TS_AT_TOP && NearestLift->DoorEntity->m_flWait > 0.0f && !FBitSet(NearestLift->DoorEdict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN)))
|
||||
{
|
||||
if (!bIsOnLift && !bIsLiftAtOrNearStart)
|
||||
{
|
||||
|
@ -4462,6 +4493,25 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP
|
|||
{
|
||||
edict_t* pEdict = pBot->Edict;
|
||||
|
||||
if (UTIL_PointIsDirectlyReachable(pBot->BotNavInfo.NavProfile, pBot->CurrentFloorPosition, EndPoint))
|
||||
{
|
||||
Vector PointOnMoveLine = vClosestPointOnLine2D(StartPoint, EndPoint, pBot->Edict->v.origin);
|
||||
|
||||
if (vEquals2D(PointOnMoveLine, EndPoint, 4.0f))
|
||||
{
|
||||
|
||||
// Stop holding crouch if we're a skulk so we can actually climb
|
||||
if (IsPlayerSkulk(pBot->Edict))
|
||||
{
|
||||
pBot->Button &= ~IN_DUCK;
|
||||
}
|
||||
|
||||
pBot->desiredMovementDir = UTIL_GetVectorNormal2D(EndPoint - pBot->CurrentFloorPosition);
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector vForward = UTIL_GetVectorNormal2D(EndPoint - StartPoint);
|
||||
Vector vRight = UTIL_GetVectorNormal(UTIL_GetCrossProduct(vForward, UP_VECTOR));
|
||||
|
||||
|
@ -4499,6 +4549,8 @@ 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);
|
||||
|
@ -8014,18 +8066,30 @@ void UTIL_UpdateDoorTriggers(nav_door* Door)
|
|||
}
|
||||
}
|
||||
|
||||
float BaseTriggerDelay = (it->ToggleEnt) ? it->ToggleEnt->m_flDelay : 0.0f;
|
||||
float DoorDelay = Door->DoorEntity->GetDelay();
|
||||
it->ActivationDelay = BaseTriggerDelay + DoorDelay + 1.0f;
|
||||
float BaseTriggerDelay = 0.0f;
|
||||
float BaseTriggerResetTime = 0.0f;
|
||||
|
||||
bool bButtonIsToggle = FBitSet(it->Edict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN);
|
||||
|
||||
if (it->ToggleEnt)
|
||||
{
|
||||
BaseTriggerDelay = it->ToggleEnt->m_flDelay;
|
||||
BaseTriggerResetTime = (bButtonIsToggle) ? 1.0f : it->ToggleEnt->GetDelay();
|
||||
}
|
||||
|
||||
float DoorDelay = (FBitSet(Door->DoorEdict->v.spawnflags, SF_DOOR_NO_AUTO_RETURN)) ? 0.0f : Door->DoorEntity->GetDelay();
|
||||
it->ActivationDelay = fmaxf(BaseTriggerDelay, BaseTriggerResetTime) + DoorDelay + 1.0f;
|
||||
|
||||
if (it->ToggleEnt && it->ToggleEnt->GetToggleState() != it->LastToggleState)
|
||||
{
|
||||
if (it->LastToggleState != TS_GOING_UP && it->LastToggleState != TS_GOING_DOWN)
|
||||
TOGGLE_STATE NewState = (TOGGLE_STATE)it->ToggleEnt->GetToggleState();
|
||||
|
||||
if (it->LastToggleState == TS_AT_BOTTOM || (bButtonIsToggle && it->LastToggleState == TS_AT_TOP))
|
||||
{
|
||||
it->NextActivationTime = gpGlobals->time + fmaxf(it->ActivationDelay + 1.0f, 1.0f);
|
||||
it->NextActivationTime = gpGlobals->time + fmaxf(it->ActivationDelay, 1.0f);
|
||||
}
|
||||
|
||||
it->LastToggleState = (TOGGLE_STATE)it->ToggleEnt->GetToggleState();
|
||||
it->LastToggleState = NewState;
|
||||
}
|
||||
|
||||
it++;
|
||||
|
|
|
@ -4655,6 +4655,7 @@ void AIPlayerCOAlienThink(AvHAIPlayer* pBot)
|
|||
|
||||
void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
{
|
||||
|
||||
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
|
||||
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
|
||||
|
||||
|
@ -4666,10 +4667,11 @@ void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter);
|
||||
|
||||
edict_t* StructureToAttack = nullptr;
|
||||
|
||||
if (EnemyStructure.IsValid())
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, EnemyStructure.edict, false);
|
||||
return;
|
||||
StructureToAttack = EnemyStructure.edict;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -4679,40 +4681,73 @@ void AIPlayerSetPrimaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (EnemyHive)
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, EnemyHive->HiveEdict, false);
|
||||
return;
|
||||
StructureToAttack = EnemyHive->HiveEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
|
||||
edict_t* TargetPlayer = nullptr;
|
||||
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
|
||||
// Nothing to attack, just hunt down remaining enemy players. Shouldn't happen in vanilla combat mode, but a plugin might change behaviour
|
||||
if (FNullEnt(StructureToAttack))
|
||||
{
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
|
||||
if (!ThisPlayer) { continue; }
|
||||
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
|
||||
edict_t* TargetPlayer = nullptr;
|
||||
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
float MinDist = 0.0f;
|
||||
|
||||
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
|
||||
|
||||
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
|
||||
|
||||
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
|
||||
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
|
||||
{
|
||||
TargetPlayer = PlayerEdict;
|
||||
MinDist = ThisDist;
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
|
||||
if (!ThisPlayer) { continue; }
|
||||
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
|
||||
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
|
||||
|
||||
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
|
||||
|
||||
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
|
||||
{
|
||||
TargetPlayer = PlayerEdict;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FNullEnt(TargetPlayer))
|
||||
{
|
||||
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're close to the enemy base then just attack. We don't want bots marching through the enemy base and ignoring the hive/comm chair
|
||||
if (vDist2DSq(pBot->Edict->v.origin, StructureToAttack->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we already know what we want to do, just crack on
|
||||
if (Task->TaskType != TASK_NONE) { return; }
|
||||
|
||||
// Decide if we're going to attack right away, or take a little detour first. Helps mix things up and prevents all bots just gang-rushing the base endlessly
|
||||
|
||||
if (randbool())
|
||||
{
|
||||
Vector RandomVisitPoint = UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, StructureToAttack->v.origin, UTIL_MetresToGoldSrcUnits(20.0f), UTIL_MetresToGoldSrcUnits(40.0f));
|
||||
|
||||
if (!vIsZero(RandomVisitPoint))
|
||||
{
|
||||
AITASK_SetMoveTask(pBot, Task, RandomVisitPoint, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FNullEnt(TargetPlayer))
|
||||
{
|
||||
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
|
||||
}
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
|
||||
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -4765,6 +4800,51 @@ void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
|
||||
{
|
||||
vector<AvHPlayer*> NearbyPlayers = AITAC_GetAllPlayersOfTeamInArea(BotTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
|
||||
AvHPlayer* NearestWeldablePlayer = nullptr;
|
||||
AvHPlayer* NearestBadlyDamagedPlayer = nullptr;
|
||||
float MinDist = 0.0f;
|
||||
float MinBadDist = 0.0f;
|
||||
|
||||
for (auto it = NearbyPlayers.begin(); it != NearbyPlayers.end(); it++)
|
||||
{
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
|
||||
float ArmourPercent = PlayerEdict->v.armorvalue / (float)GetPlayerMaxArmour(PlayerEdict);
|
||||
|
||||
if (ArmourPercent < 1.0f)
|
||||
{
|
||||
float ThisDist = vDist2DSq(pBot->Edict->v.origin, PlayerEdict->v.origin);
|
||||
|
||||
if (ArmourPercent < 0.75f)
|
||||
{
|
||||
if (!NearestBadlyDamagedPlayer || ThisDist < MinBadDist)
|
||||
{
|
||||
NearestBadlyDamagedPlayer = ThisPlayer;
|
||||
MinBadDist = ThisDist;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!NearestWeldablePlayer || ThisDist < MinDist)
|
||||
{
|
||||
NearestWeldablePlayer = ThisPlayer;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Basically, we won't prioritise welding players over structures unless they're low on armour, otherwise we prefer structures. This avoids
|
||||
// situations where the bot constantly keeps topping up nearby players when there are more important weld targets to worry about
|
||||
if (NearestBadlyDamagedPlayer)
|
||||
{
|
||||
AITASK_SetWeldTask(pBot, Task, NearestBadlyDamagedPlayer->edict(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
DeployableSearchFilter DamagedStructuresFilter;
|
||||
DamagedStructuresFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
|
||||
DamagedStructuresFilter.DeployableTeam = BotTeam;
|
||||
|
@ -4803,6 +4883,13 @@ void AIPlayerSetSecondaryCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
return;
|
||||
}
|
||||
|
||||
|
||||
if (NearestWeldablePlayer)
|
||||
{
|
||||
AITASK_SetWeldTask(pBot, Task, NearestWeldablePlayer->edict(), false);
|
||||
return;
|
||||
}
|
||||
|
||||
DeployableSearchFilter NearbyArmouryFilter;
|
||||
NearbyArmouryFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY);
|
||||
NearbyArmouryFilter.DeployableTeam = BotTeam;
|
||||
|
@ -4926,41 +5013,85 @@ void AIPlayerSetPrimaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &EnemyStuffFilter);
|
||||
|
||||
edict_t* StructureToAttack = nullptr;
|
||||
|
||||
if (EnemyStructure.IsValid())
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, EnemyStructure.edict, false);
|
||||
return;
|
||||
StructureToAttack = EnemyStructure.edict;
|
||||
}
|
||||
|
||||
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
|
||||
edict_t* TargetPlayer = nullptr;
|
||||
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
|
||||
else
|
||||
{
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
|
||||
if (!ThisPlayer) { continue; }
|
||||
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
|
||||
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
|
||||
|
||||
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
|
||||
|
||||
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
|
||||
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN)
|
||||
{
|
||||
TargetPlayer = PlayerEdict;
|
||||
MinDist = ThisDist;
|
||||
const AvHAIHiveDefinition* EnemyHive = AITAC_GetActiveHiveNearestLocation(EnemyTeam, pBot->Edict->v.origin);
|
||||
|
||||
if (EnemyHive)
|
||||
{
|
||||
StructureToAttack = EnemyHive->HiveEdict;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!FNullEnt(TargetPlayer))
|
||||
// Nothing to attack, just hunt down remaining enemy players. Shouldn't happen in vanilla combat mode, but a plugin might change behaviour
|
||||
if (FNullEnt(StructureToAttack))
|
||||
{
|
||||
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
|
||||
vector<AvHPlayer*> AllEnemyPlayers = AIMGR_GetAllPlayersOnTeam(EnemyTeam);
|
||||
edict_t* TargetPlayer = nullptr;
|
||||
|
||||
float MinDist = 0.0f;
|
||||
|
||||
for (auto it = AllEnemyPlayers.begin(); it != AllEnemyPlayers.end(); it++)
|
||||
{
|
||||
AvHPlayer* ThisPlayer = (*it);
|
||||
|
||||
if (!ThisPlayer) { continue; }
|
||||
|
||||
edict_t* PlayerEdict = ThisPlayer->edict();
|
||||
|
||||
if (!IsPlayerActiveInGame(PlayerEdict)) { continue; }
|
||||
|
||||
float ThisDist = vDist2DSq(PlayerEdict->v.origin, pBot->Edict->v.origin);
|
||||
|
||||
if (FNullEnt(TargetPlayer) || ThisDist < MinDist)
|
||||
{
|
||||
TargetPlayer = PlayerEdict;
|
||||
MinDist = ThisDist;
|
||||
}
|
||||
}
|
||||
|
||||
if (!FNullEnt(TargetPlayer))
|
||||
{
|
||||
MoveTo(pBot, UTIL_GetEntityGroundLocation(TargetPlayer), MOVESTYLE_NORMAL);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// If we're close to the enemy base then just attack. We don't want bots marching through the enemy base and ignoring the hive/comm chair
|
||||
if (vDist2DSq(pBot->Edict->v.origin, StructureToAttack->v.origin) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
|
||||
{
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
|
||||
return;
|
||||
}
|
||||
|
||||
// At this point we already know what we want to do, just crack on
|
||||
if (Task->TaskType != TASK_NONE) { return; }
|
||||
|
||||
// Decide if we're going to attack right away, or take a little detour first. Helps mix things up and prevents all bots just gang-rushing the base endlessly
|
||||
|
||||
if (randbool())
|
||||
{
|
||||
Vector RandomVisitPoint = UTIL_GetRandomPointOnNavmeshInDonut(pBot->BotNavInfo.NavProfile, StructureToAttack->v.origin, UTIL_MetresToGoldSrcUnits(20.0f), UTIL_MetresToGoldSrcUnits(40.0f));
|
||||
|
||||
if (!vIsZero(RandomVisitPoint))
|
||||
{
|
||||
AITASK_SetMoveTask(pBot, Task, RandomVisitPoint, false);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
AITASK_SetAttackTask(pBot, Task, StructureToAttack, false);
|
||||
|
||||
}
|
||||
|
||||
void AIPlayerSetSecondaryCOAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
||||
|
@ -5277,6 +5408,12 @@ void BotResumePlay(AvHAIPlayer* pBot)
|
|||
SetBaseNavProfile(pBot);
|
||||
|
||||
pBot->bIsInactive = false;
|
||||
|
||||
// Keep things nicely randomized in Combat mode
|
||||
if (GetGameRules()->GetMapMode() == MAP_MODE_CO)
|
||||
{
|
||||
AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask);
|
||||
}
|
||||
}
|
||||
|
||||
void UpdateCommanderOrders(AvHAIPlayer* pBot)
|
||||
|
|
Loading…
Reference in a new issue