mirror of
https://github.com/ENSL/NS.git
synced 2024-11-24 21:41:17 +00:00
Further bot enhancements
* Improved jump and blink movement * Hopefully fixed bug with bots trying to walk between phase gates * Fixed issue with bots constantly switching weapons when trying to reload * Added a max AI time for a match, with bots throwing the game if there are no humans and it goes on too long (configurable in nsbots.ini, default is 90 minutes)
This commit is contained in:
parent
eba41379e3
commit
d87bb3d600
8 changed files with 217 additions and 49 deletions
|
@ -960,7 +960,11 @@ bool AICOMM_IsRequestValid(ai_commander_request* Request)
|
|||
case BUILD_TURRET_FACTORY:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
case BUILD_ARMORY:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
case BUILD_COMMANDSTATION:
|
||||
return !AITAC_IsStructureOfTypeNearLocation(RequestorTeam, STRUCTURE_MARINE_COMMCHAIR, Requestor->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
|
||||
case BUILD_SCAN:
|
||||
return !AITAC_ItemExistsInLocation(Requestor->v.origin, DEPLOYABLE_ITEM_SCAN, RequestorTeam, AI_REACHABILITY_NONE, 0.0f, UTIL_MetresToGoldSrcUnits(5.0f), false);
|
||||
default:
|
||||
return true;
|
||||
}
|
||||
|
@ -2843,6 +2847,86 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
return true;
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_SCAN)
|
||||
{
|
||||
DeployableSearchFilter ObsFilter;
|
||||
ObsFilter.DeployableTeam = CommanderTeam;
|
||||
ObsFilter.DeployableTypes = STRUCTURE_MARINE_OBSERVATORY;
|
||||
ObsFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
|
||||
ObsFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
|
||||
|
||||
AvHAIBuildableStructure NearestObservatory = AITAC_FindClosestDeployableToLocation(Requestor->v.origin, &ObsFilter);
|
||||
|
||||
if (!NearestObservatory.IsValid())
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "We don't have an observatory yet %s, ask again later.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
Vector DeployLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(1.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
DeployLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), Requestor->v.origin, UTIL_MetresToGoldSrcUnits(5.0f));
|
||||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_SCAN, DeployLocation);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
NextRequest->bResponded = true;
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good scan spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
char msg[128];
|
||||
sprintf(msg, "I can't find a good scan spot, %s. Try again elsewhere.", STRING(Requestor->v.netname));
|
||||
BotSay(pBot, true, 0.5f, msg);
|
||||
NextRequest->bResponded = true;
|
||||
return false;
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_PHASEGATE)
|
||||
{
|
||||
if (!AITAC_ResearchIsComplete(CommanderTeam, TECH_RESEARCH_PHASETECH))
|
||||
|
@ -2927,9 +3011,11 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
|
||||
}
|
||||
|
||||
if (NextRequest->RequestType == BUILD_ARMORY)
|
||||
if (NextRequest->RequestType == BUILD_ARMORY || NextRequest->RequestType == BUILD_COMMANDSTATION)
|
||||
{
|
||||
if (pBot->Player->GetResources() < BALANCE_VAR(kArmoryCost))
|
||||
float RequiredRes = (NextRequest->RequestType == BUILD_ARMORY) ? BALANCE_VAR(kArmoryCost) : BALANCE_VAR(kCommandStationCost);
|
||||
|
||||
if (pBot->Player->GetResources() < RequiredRes)
|
||||
{
|
||||
if (!NextRequest->bAcknowledged)
|
||||
{
|
||||
|
@ -2945,9 +3031,11 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
Vector IdealDeployLocation = Requestor->v.origin + (UTIL_GetForwardVector2D(Requestor->v.angles) * 75.0f);
|
||||
Vector ProjectedDeployLocation = AdjustPointForPathfinding(IdealDeployLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
|
||||
|
||||
AvHAIDeployableStructureType StructureToDeploy = (NextRequest->RequestType == BUILD_ARMORY) ? STRUCTURE_MARINE_ARMOURY : STRUCTURE_MARINE_COMMCHAIR;
|
||||
|
||||
if (!vIsZero(ProjectedDeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, ProjectedDeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
|
@ -2960,7 +3048,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
|
@ -2973,7 +3061,7 @@ bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot)
|
|||
|
||||
if (!vIsZero(DeployLocation))
|
||||
{
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
bool bSuccess = AICOMM_DeployStructure(pBot, StructureToDeploy, DeployLocation, STRUCTURE_PURPOSE_NONE);
|
||||
|
||||
if (bSuccess)
|
||||
{
|
||||
|
|
|
@ -8,6 +8,8 @@
|
|||
|
||||
BotFillTiming CurrentBotFillTiming = FILLTIMING_ALLHUMANS;
|
||||
|
||||
float MaxAIMatchTimeMinutes = 90.0f;
|
||||
|
||||
std::unordered_map<std::string, TeamSizeDefinitions> TeamSizeMap;
|
||||
|
||||
bot_skill BotSkillLevels[4];
|
||||
|
@ -54,6 +56,11 @@ float CONFIG_GetMaxStuckTime()
|
|||
return avh_botmaxstucktime.value;
|
||||
}
|
||||
|
||||
float CONFIG_GetMaxAIMatchTimeMinutes()
|
||||
{
|
||||
return MaxAIMatchTimeMinutes;
|
||||
}
|
||||
|
||||
string CONFIG_GetBotPrefix()
|
||||
{
|
||||
return string(BotPrefix);
|
||||
|
@ -208,6 +215,14 @@ void CONFIG_ParseConfigFile()
|
|||
continue;
|
||||
}
|
||||
|
||||
if (!stricmp(keyChar, "MaxAIMatchTime"))
|
||||
{
|
||||
float MaxMinutes = std::stof(value.c_str());
|
||||
MaxAIMatchTimeMinutes = MaxMinutes;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!stricmp(keyChar, "BotFillTiming"))
|
||||
{
|
||||
int FillSetting = atoi(value.c_str());
|
||||
|
@ -550,6 +565,11 @@ void CONFIG_RegenerateIniFile()
|
|||
fprintf(NewConfigFile, "# What prefix to put in front of a bot's name (can leave blank)\n");
|
||||
fprintf(NewConfigFile, "Prefix=[BOT]\n\n");
|
||||
|
||||
fprintf(NewConfigFile, "# After this many minutes into a match, the bots will leave the game if there are no humans playing\n");
|
||||
fprintf(NewConfigFile, "# Helps prevent stalemates and bugs that happen after extremely long matches\n");
|
||||
fprintf(NewConfigFile, "# Default = 90 minutes\n");
|
||||
fprintf(NewConfigFile, "MaxAIMatchTime=90\n\n");
|
||||
|
||||
fprintf(NewConfigFile, "# When should the server start adding bots? Note: bots will always be added after round start regardless\n");
|
||||
fprintf(NewConfigFile, "# 0 = On map load (after 5 second grace period)\n");
|
||||
fprintf(NewConfigFile, "# 1 = When all humans have joined a team (i.e. no more humans left in ready room)\n");
|
||||
|
|
|
@ -46,6 +46,8 @@ bool CONFIG_IsOnosAllowed();
|
|||
// Returns the max time a bot is allowed to be stuck before suiciding (0 means forever)
|
||||
float CONFIG_GetMaxStuckTime();
|
||||
|
||||
float CONFIG_GetMaxAIMatchTimeMinutes();
|
||||
|
||||
// Returns the desired marine team size for the given map, indexes into TeamSizeMap
|
||||
int CONFIG_GetTeamASizeForMap(const char* MapName);
|
||||
// Returns the desired alien team size for the given map, indexes into TeamSizeMap
|
||||
|
|
|
@ -3530,6 +3530,25 @@ void BlockedMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoi
|
|||
|
||||
pBot->desiredMovementDir = vForward;
|
||||
|
||||
Vector CurrVelocity = UTIL_GetVectorNormal2D(pBot->Edict->v.velocity);
|
||||
|
||||
float Dot = UTIL_GetDotProduct2D(vForward, CurrVelocity);
|
||||
|
||||
Vector FaceDir = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
|
||||
float FaceDot = UTIL_GetDotProduct2D(FaceDir, vForward);
|
||||
|
||||
// Yes this is cheating, but is it not cheating for humans to have millions of years of evolution
|
||||
// driving their ability to judge a jump, while the bots have a single year of coding from a moron?
|
||||
if (FaceDot < 0.95f)
|
||||
{
|
||||
float MoveSpeed = vSize2D(pBot->Edict->v.velocity);
|
||||
Vector NewVelocity = vForward * MoveSpeed;
|
||||
NewVelocity.z = pBot->Edict->v.velocity.z;
|
||||
|
||||
pBot->Edict->v.velocity = NewVelocity;
|
||||
}
|
||||
|
||||
BotJump(pBot);
|
||||
}
|
||||
|
||||
|
@ -3544,6 +3563,25 @@ void JumpMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndPoint)
|
|||
|
||||
pBot->desiredMovementDir = vForward;
|
||||
|
||||
Vector CurrVelocity = UTIL_GetVectorNormal2D(pBot->Edict->v.velocity);
|
||||
|
||||
float Dot = UTIL_GetDotProduct2D(vForward, CurrVelocity);
|
||||
|
||||
Vector FaceDir = UTIL_GetForwardVector2D(pBot->Edict->v.angles);
|
||||
|
||||
float FaceDot = UTIL_GetDotProduct2D(FaceDir, vForward);
|
||||
|
||||
// Yes this is cheating, but is it not cheating for humans to have millions of years of evolution
|
||||
// driving their ability to judge a jump, while the bots have a single year of coding from a moron?
|
||||
if (FaceDot < 0.95f)
|
||||
{
|
||||
float MoveSpeed = vSize2D(pBot->Edict->v.velocity);
|
||||
Vector NewVelocity = vForward * MoveSpeed;
|
||||
NewVelocity.z = pBot->Edict->v.velocity.z;
|
||||
|
||||
pBot->Edict->v.velocity = NewVelocity;
|
||||
}
|
||||
|
||||
BotJump(pBot);
|
||||
|
||||
bool bCanDuck = (IsPlayerMarine(pBot->Edict) || IsPlayerFade(pBot->Edict) || IsPlayerOnos(pBot->Edict));
|
||||
|
@ -4315,10 +4353,10 @@ bool IsBotOffPath(const AvHAIPlayer* pBot)
|
|||
case SAMPLE_POLYFLAGS_LIFT:
|
||||
return IsBotOffLiftNode(pBot, MoveFrom, MoveTo, NextMoveLocation, NextMoveFlag);
|
||||
default:
|
||||
return IsBotOffWalkNode(pBot, MoveFrom, MoveTo, NextMoveLocation, NextMoveFlag);
|
||||
return IsBotOffFallNode(pBot, MoveFrom, MoveTo, NextMoveLocation, NextMoveFlag);
|
||||
}
|
||||
|
||||
return IsBotOffWalkNode(pBot, MoveFrom, MoveTo, NextMoveLocation, NextMoveFlag);
|
||||
return IsBotOffFallNode(pBot, MoveFrom, MoveTo, NextMoveLocation, NextMoveFlag);
|
||||
|
||||
}
|
||||
|
||||
|
@ -4341,6 +4379,9 @@ bool IsBotOffWalkNode(const AvHAIPlayer* pBot, Vector MoveStart, Vector MoveEnd,
|
|||
{
|
||||
if (!pBot->BotNavInfo.IsOnGround) { return false; }
|
||||
|
||||
// This shouldn't happen... but does occasionally. Walk moves should always be directly reachable from start to end
|
||||
if (!UTIL_PointIsDirectlyReachable(MoveStart, MoveEnd)) { return true; }
|
||||
|
||||
Vector NearestPointOnLine = vClosestPointOnLine2D(MoveStart, MoveEnd, pBot->Edict->v.origin);
|
||||
|
||||
if (vDist2DSq(pBot->Edict->v.origin, NearestPointOnLine) > sqrf(GetPlayerRadius(pBot->Edict) * 3.0f)) { return true; }
|
||||
|
@ -4464,18 +4505,10 @@ void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector End
|
|||
if (GetPlayerCurrentWeapon(pBot->Player) != WEAPON_FADE_BLINK) { return; }
|
||||
|
||||
// Only blink if we're below the target climb height
|
||||
if (pEdict->v.origin.z < RequiredClimbHeight)
|
||||
if (pEdict->v.origin.z < RequiredClimbHeight + 32.0f)
|
||||
{
|
||||
float HeightToClimb = (fabsf(pEdict->v.origin.z - RequiredClimbHeight));
|
||||
|
||||
if (HeightToClimb > (GetPlayerHeight(pBot->Edict, false) * 2.0f))
|
||||
{
|
||||
if (GetPlayerEnergy(pBot->Edict) < 0.15f && pBot->BotNavInfo.IsOnGround)
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
Vector CurrVelocity = UTIL_GetVectorNormal2D(pBot->Edict->v.velocity);
|
||||
|
||||
float Dot = UTIL_GetDotProduct2D(MoveDir, CurrVelocity);
|
||||
|
@ -4484,30 +4517,37 @@ void BlinkClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector End
|
|||
|
||||
float FaceDot = UTIL_GetDotProduct2D(FaceDir, MoveDir);
|
||||
|
||||
// Don't start blinking unless we're already in the air, or we're moving in the correct direction. Stops fade shooting off sideways when approaching a climb point from the side
|
||||
if (FaceDot > 0.9f)
|
||||
// Yes this is cheating, but the fades were struggling with zipping off-target when trying to blink
|
||||
// Better this than fades getting constantly chewed up by marines because they can't escape properly
|
||||
if (FaceDot < 0.95f)
|
||||
{
|
||||
float ZDiff = fabs(pEdict->v.origin.z - RequiredClimbHeight);
|
||||
float MoveSpeed = vSize2D(pBot->Edict->v.velocity);
|
||||
Vector NewVelocity = MoveDir * MoveSpeed;
|
||||
NewVelocity.z = pBot->Edict->v.velocity.z;
|
||||
|
||||
// We don't want to blast off like a rocket, so only apply enough blink until our upwards velocity is enough to carry us to the desired height
|
||||
float DesiredZVelocity = sqrtf(2.0f * GOLDSRC_GRAVITY * (ZDiff + 10.0f));
|
||||
pBot->Edict->v.velocity = NewVelocity;
|
||||
}
|
||||
|
||||
if (pBot->Edict->v.velocity.z < DesiredZVelocity || pBot->Edict->v.velocity.z < 300.0f)
|
||||
float ZDiff = fabs(pEdict->v.origin.z - (RequiredClimbHeight + 72.0f));
|
||||
|
||||
// We don't want to blast off like a rocket, so only apply enough blink until our upwards velocity is enough to carry us to the desired height
|
||||
float DesiredZVelocity = sqrtf(2.0f * GOLDSRC_GRAVITY * (ZDiff + 10.0f));
|
||||
|
||||
if (pBot->Edict->v.velocity.z < DesiredZVelocity || pBot->Edict->v.velocity.z < 300.0f)
|
||||
{
|
||||
// We're going to cheat and give the bot the necessary energy to make the move. Better the fade cheats a bit than gets stuck somewhere
|
||||
if (GetPlayerEnergy(pBot->Edict) < 0.1f)
|
||||
{
|
||||
// We're going to cheat and give the bot the necessary energy to make the move. Better the fade cheats a bit than gets stuck somewhere
|
||||
if (GetPlayerEnergy(pBot->Edict) < 0.1f)
|
||||
{
|
||||
pBot->Player->Energize(0.1f);
|
||||
}
|
||||
BotMoveLookAt(pBot, EndPoint + Vector(0.0f, 0.0f, 100.0f));
|
||||
pBot->Button |= IN_ATTACK2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector LookAtTarget = EndPoint;
|
||||
LookAtTarget.z = pBot->CurrentEyePosition.z;
|
||||
BotMoveLookAt(pBot, LookAtTarget);
|
||||
pBot->Player->Energize(0.1f);
|
||||
}
|
||||
BotMoveLookAt(pBot, EndPoint + Vector(0.0f, 0.0f, 100.0f));
|
||||
pBot->Button |= IN_ATTACK2;
|
||||
}
|
||||
else
|
||||
{
|
||||
Vector LookAtTarget = EndPoint;
|
||||
LookAtTarget.z = pBot->CurrentEyePosition.z;
|
||||
BotMoveLookAt(pBot, LookAtTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -8770,7 +8810,7 @@ void NAV_ProgressMovementTask(AvHAIPlayer* pBot)
|
|||
}
|
||||
|
||||
BotMoveLookAt(pBot, AimLocation);
|
||||
pBot->DesiredCombatWeapon = WEAPON_MARINE_WELDER;
|
||||
pBot->DesiredMoveWeapon = WEAPON_MARINE_WELDER;
|
||||
|
||||
if (GetPlayerCurrentWeapon(pBot->Player) != WEAPON_MARINE_WELDER)
|
||||
{
|
||||
|
|
|
@ -379,7 +379,13 @@ void BotSay(AvHAIPlayer* pBot, bool bTeamSay, float Delay, char* textToSay)
|
|||
bool BotReloadWeapons(AvHAIPlayer* pBot)
|
||||
{
|
||||
// Aliens and commander don't reload
|
||||
if (!IsPlayerMarine(pBot->Edict) || !IsPlayerActiveInGame(pBot->Edict) || IsPlayerReloading(pBot->Player)) { return false; }
|
||||
if (!IsPlayerMarine(pBot->Edict) || !IsPlayerActiveInGame(pBot->Edict)) { return false; }
|
||||
|
||||
if (IsPlayerReloading(pBot->Player))
|
||||
{
|
||||
pBot->DesiredCombatWeapon = GetPlayerCurrentWeapon(pBot->Player);
|
||||
}
|
||||
|
||||
|
||||
AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
AvHAIWeapon SecondaryWeapon = GetBotMarineSecondaryWeapon(pBot);
|
||||
|
@ -791,12 +797,14 @@ void BotShootTarget(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, edict_t* Target
|
|||
// We can be less accurate with spores and umbra since they have AoE effects
|
||||
float MinAcceptableAccuracy = 0.9f;
|
||||
|
||||
Vector GetPosition = pBot->Player->GetGunPosition();
|
||||
|
||||
bWillHit = (AimDot >= MinAcceptableAccuracy);
|
||||
|
||||
if (!bWillHit && IsHitscanWeapon(CurrentWeapon))
|
||||
{
|
||||
|
||||
edict_t* HitEntity = UTIL_TraceEntity(pBot->Edict, pBot->CurrentEyePosition, pBot->CurrentEyePosition + (AimDir * GetMaxIdealWeaponRange(CurrentWeapon)));
|
||||
edict_t* HitEntity = UTIL_TraceEntity(pBot->Edict, pBot->Player->GetGunPosition(), pBot->Player->GetGunPosition() + (AimDir * GetMaxIdealWeaponRange(CurrentWeapon)));
|
||||
|
||||
bWillHit = (HitEntity == Target);
|
||||
}
|
||||
|
@ -871,7 +879,7 @@ void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector
|
|||
return;
|
||||
}
|
||||
|
||||
if (CurrentWeapon == WEAPON_NONE) { return; }
|
||||
if (CurrentWeapon == WEAPON_INVALID) { return; }
|
||||
|
||||
if (CurrentWeapon == WEAPON_SKULK_XENOCIDE)
|
||||
{
|
||||
|
@ -2820,7 +2828,7 @@ void AIPlayerNSMarineThink(AvHAIPlayer* pBot)
|
|||
BotProgressTask(pBot, pBot->CurrentTask);
|
||||
}
|
||||
|
||||
if (pBot->DesiredCombatWeapon == WEAPON_NONE)
|
||||
if (pBot->DesiredCombatWeapon == WEAPON_INVALID)
|
||||
{
|
||||
pBot->DesiredCombatWeapon = BotMarineChooseBestWeapon(pBot, nullptr);
|
||||
}
|
||||
|
@ -4245,7 +4253,7 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
|
|||
BotProgressTask(pBot, pBot->CurrentTask);
|
||||
}
|
||||
|
||||
if (pBot->DesiredCombatWeapon == WEAPON_NONE)
|
||||
if (pBot->DesiredCombatWeapon == WEAPON_INVALID)
|
||||
{
|
||||
pBot->DesiredCombatWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
|
||||
}
|
||||
|
|
|
@ -12,8 +12,6 @@
|
|||
#include "../dlls/client.h"
|
||||
#include <time.h>
|
||||
|
||||
float MAX_MATCH_TIME = 7200.0f;
|
||||
|
||||
double last_think_time = 0.0;
|
||||
|
||||
vector<AvHAIPlayer> ActiveAIPlayers;
|
||||
|
@ -129,9 +127,14 @@ void AIMGR_UpdateAIPlayerCounts()
|
|||
|
||||
LastAIPlayerCountUpdate = gpGlobals->time;
|
||||
|
||||
bool bMatchExceededMaxLength = (gpGlobals->time - AIStartedTime) > MAX_MATCH_TIME;
|
||||
float MaxMinutes = CONFIG_GetMaxAIMatchTimeMinutes();
|
||||
float MaxSeconds = MaxMinutes * 60.0f;
|
||||
|
||||
// If bots are disabled, ensure we've removed all bots from the game
|
||||
bool bMatchExceededMaxLength = (GetGameRules()->GetGameTime() > MaxSeconds);
|
||||
|
||||
// If bots are disabled or we've exceeded max AI time and no humans are playing, ensure we've removed all bots from the game
|
||||
// Max AI time is configurable in nsbots.ini, and helps prevent infinite stalemates
|
||||
// Default time is 90 minutes before bots start leaving to let the map cycle
|
||||
if (!AIMGR_IsBotEnabled() || (bMatchExceededMaxLength && AIMGR_GetNumActiveHumanPlayers() == 0))
|
||||
{
|
||||
if (AIMGR_GetNumAIPlayers() > 0)
|
||||
|
|
|
@ -563,9 +563,10 @@ bool AITASK_IsResupplyTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
|
|||
|
||||
if (!UTIL_StructureIsFullyBuilt(Task->TaskTarget) || UTIL_StructureIsRecycling(Task->TaskTarget)) { return false; }
|
||||
|
||||
return ((pBot->Edict->v.health < pBot->Edict->v.max_health)
|
||||
|| (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player))
|
||||
|| (BotGetSecondaryWeaponAmmoReserve(pBot) < BotGetSecondaryWeaponMaxAmmoReserve(pBot))
|
||||
return (
|
||||
(pBot->Edict->v.health < pBot->Edict->v.max_health)
|
||||
|| (UTIL_GetPlayerPrimaryAmmoReserve(pBot->Player) < UTIL_GetPlayerPrimaryMaxAmmoReserve(pBot->Player))
|
||||
|| (UTIL_GetPlayerSecondaryAmmoReserve(pBot->Player) < UTIL_GetPlayerSecondaryMaxAmmoReserve(pBot->Player))
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -1977,6 +1977,12 @@ void AvHCommandStation::CommandUse( CBaseEntity* pActivator, CBaseEntity* pCalle
|
|||
this->mTimeToPlayOnlineSound = this->GetTimeAnimationDone();
|
||||
|
||||
GetGameRules()->MarkDramaticEvent(kCCNewCommanderPriority, thePlayer, this);
|
||||
|
||||
// A human used the comm chair, let the AI know to keep away
|
||||
if (!(thePlayer->pev->flags & FL_FAKECLIENT))
|
||||
{
|
||||
AIMGR_SetCommanderAllowedTime(theStationTeamNumber, gpGlobals->time + 20.0f);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
Loading…
Reference in a new issue