Fix grenade throwing, better combat

This commit is contained in:
RGreenlees 2024-01-26 23:06:31 +00:00 committed by pierow
parent 3a1a92c505
commit a0700fcd4f
10 changed files with 503 additions and 40 deletions

View file

@ -791,10 +791,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (NumInfantryPortals < 2) if (NumInfantryPortals < 2)
{ {
if (AICOMM_BuildInfantryPortal(pBot, CommChair)) AICOMM_BuildInfantryPortal(pBot, CommChair);
{ return true;
return true;
}
} }
StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY; StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY;
@ -823,10 +821,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (!vIsZero(BuildLocation)) if (!vIsZero(BuildLocation))
{ {
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation)) AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation);
{ return true;
return true;
}
} }
} }
@ -858,8 +854,9 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
{ {
Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f));
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation)) if (!vIsZero(BuildLocation))
{ {
AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation);
return true; return true;
} }
} }
@ -869,10 +866,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot)
if (CappableNode) if (CappableNode)
{ {
if (AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location)) AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_RESTOWER, CappableNode->Location);
{ return true;
return true;
}
} }
const AvHAIHiveDefinition* HiveToSecure = AICOMM_GetEmptyHiveOpportunityNearestLocation(pBot, AITAC_GetCommChairLocation(TeamNumber)); const AvHAIHiveDefinition* HiveToSecure = AICOMM_GetEmptyHiveOpportunityNearestLocation(pBot, AITAC_GetCommChairLocation(TeamNumber));
@ -1973,6 +1968,7 @@ bool AICOMM_ShouldCommanderLeaveChair(AvHAIPlayer* pBot)
const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation) const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPlayer* CommanderBot, const Vector SearchLocation)
{ {
AvHTeamNumber CommanderTeam = CommanderBot->Player->GetTeam(); AvHTeamNumber CommanderTeam = CommanderBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(CommanderTeam);
const AvHAIHiveDefinition* Result = nullptr; const AvHAIHiveDefinition* Result = nullptr;
float MinDist = 0.0f; float MinDist = 0.0f;
@ -2009,6 +2005,27 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl
if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SecureLocation, MarineDist) == nullptr) { continue; } if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, SecureLocation, MarineDist) == nullptr) { continue; }
int NumEnemiesNearby = AITAC_GetNumPlayersOnTeamWithLOS(EnemyTeam, SecureLocation + Vector(0.0f, 0.0f, 10.0f), UTIL_MetresToGoldSrcUnits(15.0f), nullptr);
if (NumEnemiesNearby > 0) { continue; }
DeployableSearchFilter EnemyStuff;
EnemyStuff.DeployableTeam = EnemyTeam;
EnemyStuff.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStuff.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStuff.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
EnemyStuff.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
}
else
{
EnemyStuff.DeployableTypes = STRUCTURE_ALIEN_OFFENCECHAMBER;
}
if (AITAC_DeployableExistsAtLocation(SecureLocation, &EnemyStuff)) { continue; }
float ThisDist = vDist2DSq(Hive->FloorLocation, SearchLocation); float ThisDist = vDist2DSq(Hive->FloorLocation, SearchLocation);
if (!Result || ThisDist < MinDist) if (!Result || ThisDist < MinDist)

View file

@ -4933,7 +4933,7 @@ void UTIL_UpdateBotMovementStatus(AvHAIPlayer* pBot)
bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination) bool AbortCurrentMove(AvHAIPlayer* pBot, const Vector NewDestination)
{ {
if (pBot->BotNavInfo.CurrentPath.size() == 0) { return true; } if (pBot->BotNavInfo.CurrentPath.size() == 0 || pBot->BotNavInfo.NavProfile.bFlyingProfile) { return true; }
if (IsBotPermaStuck(pBot)) if (IsBotPermaStuck(pBot))
{ {
@ -5764,8 +5764,6 @@ void SkipAheadInFlightPath(AvHAIPlayer* pBot)
// Early exit if we don't have a path, or we're already on the last path point // Early exit if we don't have a path, or we're already on the last path point
if (BotNavInfo->CurrentPath.size() == 0 || BotNavInfo->CurrentPathPoint == prev(BotNavInfo->CurrentPath.end())) { return; } if (BotNavInfo->CurrentPath.size() == 0 || BotNavInfo->CurrentPathPoint == prev(BotNavInfo->CurrentPath.end())) { return; }
if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, prev(BotNavInfo->CurrentPath.end())->Location, head_hull)) if (UTIL_QuickHullTrace(pBot->Edict, pBot->Edict->v.origin, prev(BotNavInfo->CurrentPath.end())->Location, head_hull))
{ {
pBot->BotNavInfo.CurrentPathPoint = prev(BotNavInfo->CurrentPath.end()); pBot->BotNavInfo.CurrentPathPoint = prev(BotNavInfo->CurrentPath.end());
@ -5897,7 +5895,14 @@ void BotFollowFlightPath(AvHAIPlayer* pBot)
} }
} }
BotMoveLookAt(pBot, CurrentMoveDest); Vector LookLocation = CurrentMoveDest;
if (pEdict->v.origin.z < BotNavInfo->CurrentPathPoint->requiredZ)
{
LookLocation.z += 32.0f;
}
BotMoveLookAt(pBot, LookLocation);
pBot->desiredMovementDir = UTIL_GetForwardVector2D(pEdict->v.v_angle); pBot->desiredMovementDir = UTIL_GetForwardVector2D(pEdict->v.v_angle);

View file

@ -978,10 +978,22 @@ void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector
{ {
AvHBasePlayerWeapon* WeaponRef = dynamic_cast<AvHBasePlayerWeapon*>(pBot->Player->m_pActiveItem); AvHBasePlayerWeapon* WeaponRef = dynamic_cast<AvHBasePlayerWeapon*>(pBot->Player->m_pActiveItem);
if (!WeaponRef->GetMustPressTriggerForEachShot() || WeaponRef->m_flNextPrimaryAttack <= 0.0f) if (CurrentWeapon == WEAPON_MARINE_GRENADE)
{ {
pBot->Button |= IN_ATTACK; if (!WeaponRef->m_flStartThrow && WeaponRef->m_flReleaseThrow == -1)
{
pBot->Button |= IN_ATTACK;
}
} }
else
{
if (!WeaponRef->GetMustPressTriggerForEachShot() || WeaponRef->m_flNextPrimaryAttack <= 0.0f)
{
pBot->Button |= IN_ATTACK;
}
}
} }
} }
@ -1041,6 +1053,24 @@ void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessa
} }
} }
void BotEvolveUpgrade(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetUpgrade)
{
Vector EvolvePoint = UTIL_ProjectPointToNavmesh(DesiredEvolveLocation, GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE));
if (vIsZero(EvolvePoint))
{
EvolvePoint = DesiredEvolveLocation;
}
if (vDist2DSq(pBot->Edict->v.origin, EvolvePoint) > sqrf(32.0f))
{
MoveTo(pBot, EvolvePoint, MOVESTYLE_NORMAL);
return;
}
pBot->Impulse = TargetUpgrade;
}
void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot) void BotUpdateDesiredViewRotation(AvHAIPlayer* pBot)
{ {
// We always prioritise MoveLookLocation if it is set so the bot doesn't screw up wall climbing or ladder movement // We always prioritise MoveLookLocation if it is set so the bot doesn't screw up wall climbing or ladder movement
@ -1634,7 +1664,29 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
void CustomThink(AvHAIPlayer* pBot) void CustomThink(AvHAIPlayer* pBot)
{ {
if (IsPlayerMarine(pBot->Player)) { return; } if (IsPlayerMarine(pBot->Player))
{
if (PlayerHasWeapon(pBot->Player, WEAPON_MARINE_GRENADE))
{
if (!vIsZero(AIDEBUG_GetDebugVector1()))
{
BotThrowGrenadeAtTarget(pBot, AIDEBUG_GetDebugVector1());
}
}
else
{
pBot->DesiredCombatWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player);
}
AvHAIWeapon DesiredWeapon = (pBot->DesiredMoveWeapon != WEAPON_NONE) ? pBot->DesiredMoveWeapon : pBot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetPlayerCurrentWeapon(pBot->Player) != DesiredWeapon)
{
BotSwitchToWeapon(pBot, DesiredWeapon);
}
return;
}
if (!IsPlayerFade(pBot->Edict)) if (!IsPlayerFade(pBot->Edict))
{ {
@ -2455,11 +2507,11 @@ bool MarineCombatThink(AvHAIPlayer* pBot)
{ {
edict_t* pEdict = pBot->Edict; edict_t* pEdict = pBot->Edict;
if (pBot->CurrentEnemy < 0) { return false; }
edict_t* CurrentEnemy = pBot->TrackedEnemies[pBot->CurrentEnemy].EnemyEdict; edict_t* CurrentEnemy = pBot->TrackedEnemies[pBot->CurrentEnemy].EnemyEdict;
enemy_status* TrackedEnemyRef = &pBot->TrackedEnemies[pBot->CurrentEnemy]; enemy_status* TrackedEnemyRef = &pBot->TrackedEnemies[pBot->CurrentEnemy];
pBot->LastCombatTime = gpGlobals->time;
// ENEMY IS OUT OF SIGHT // ENEMY IS OUT OF SIGHT
if (!TrackedEnemyRef->bHasLOS) if (!TrackedEnemyRef->bHasLOS)
@ -2812,13 +2864,15 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
for (auto it = BuildableStructures.begin(); it != BuildableStructures.end(); it++) for (auto it = BuildableStructures.begin(); it != BuildableStructures.end(); it++)
{ {
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), (*it)->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER); float ThisDist = vDist2D((*it)->Location, pBot->Edict->v.origin);
int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(pBot->Player->GetTeam(), (*it)->Location, ThisDist - 5.0f, false, pBot->Edict, AVH_USER3_COMMANDER_PLAYER);
int NumDesiredBuilders = (vDist2DSq((*it)->Location, AITAC_GetCommChairLocation(pBot->Player->GetTeam())) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) ? 1 : 2; int NumDesiredBuilders = (vDist2DSq((*it)->Location, AITAC_GetCommChairLocation(pBot->Player->GetTeam())) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) ? 1 : 2;
if (NumBuilders < NumDesiredBuilders) if (NumBuilders < NumDesiredBuilders)
{ {
float ThisDist = vDist2DSq((*it)->Location, pBot->Edict->v.origin);
if (!NearestStructure || ThisDist < MinDist) if (!NearestStructure || ThisDist < MinDist)
{ {
NearestStructure = (*it); NearestStructure = (*it);
@ -2829,7 +2883,7 @@ void AIPlayerSetSecondaryMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (NearestStructure) if (NearestStructure)
{ {
AITASK_SetBuildTask(pBot, Task, NearestStructure->edict, false); AITASK_SetBuildTask(pBot, Task, NearestStructure->edict, true);
return; return;
} }
@ -2866,6 +2920,27 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
if (AlienCombatThink(pBot)) { return; } if (AlienCombatThink(pBot)) { return; }
} }
if (pBot->LastCombatTime > 5.0f)
{
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_DEFENCE) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_DEFENCE))
{
BotEvolveUpgrade(pBot, pBot->Edict->v.origin, AlienGetDesiredUpgrade(pBot, HIVE_TECH_DEFENCE));
return;
}
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_MOVEMENT) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_MOVEMENT))
{
BotEvolveUpgrade(pBot, pBot->Edict->v.origin, AlienGetDesiredUpgrade(pBot, HIVE_TECH_MOVEMENT));
return;
}
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_SENSORY) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_SENSORY))
{
BotEvolveUpgrade(pBot, pBot->Edict->v.origin, AlienGetDesiredUpgrade(pBot, HIVE_TECH_SENSORY));
return;
}
}
if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE) if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE)
{ {
BotProgressTask(pBot, pBot->CurrentTask); BotProgressTask(pBot, pBot->CurrentTask);
@ -2877,6 +2952,26 @@ void AIPlayerNSAlienThink(AvHAIPlayer* pBot)
} }
} }
AvHMessageID AlienGetDesiredUpgrade(AvHAIPlayer* pBot, HiveTechStatus DesiredTech)
{
if (DesiredTech == HIVE_TECH_DEFENCE)
{
return ALIEN_EVOLUTION_ONE;
}
if (DesiredTech == HIVE_TECH_MOVEMENT)
{
return ALIEN_EVOLUTION_SEVEN;
}
if (DesiredTech == HIVE_TECH_SENSORY)
{
return ALIEN_EVOLUTION_ELEVEN;
}
return MESSAGE_NULL;
}
void AIPlayerCOThink(AvHAIPlayer* pBot) void AIPlayerCOThink(AvHAIPlayer* pBot)
{ {
@ -3165,6 +3260,7 @@ void AIPlayerSetAlienBuilderPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
DeployableSearchFilter EnemyStructureFilter; DeployableSearchFilter EnemyStructureFilter;
EnemyStructureFilter.DeployableTeam = EnemyTeam; EnemyStructureFilter.DeployableTeam = EnemyTeam;
EnemyStructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; EnemyStructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStructureFilter.DeployableTypes = StructureTypes; EnemyStructureFilter.DeployableTypes = StructureTypes;
EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); EnemyStructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f);
@ -3343,6 +3439,37 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
AvHAIResourceNode* ThisNode = (*it); AvHAIResourceNode* ThisNode = (*it);
if (ThisNode->bIsBaseNode)
{
if (FNullEnt(ThisNode->ParentHive)) { continue; } // This node must belong to marine base, don't try to cap it
}
if (!FNullEnt(ThisNode->ParentHive))
{
AvHAIHiveDefinition* ParentHiveRef = AITAC_GetHiveFromEdict(ThisNode->ParentHive);
// Don't try to cap resource nodes in an enemy hive.
if (ParentHiveRef->OwningTeam == EnemyTeam) { continue; }
DeployableSearchFilter EnemyStructuresFilter;
EnemyStructuresFilter.DeployableTeam = EnemyTeam;
EnemyStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStructuresFilter.ExcludeStatusFlags = (IsPlayerSkulk(pBot->Edict)) ? (STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_ELECTRIFIED) : STRUCTURE_STATUS_RECYCLING;
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
EnemyStructuresFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
}
else
{
EnemyStructuresFilter.DeployableTypes = (STRUCTURE_ALIEN_OFFENCECHAMBER);
}
// Enemy has started fortifying the hive we want to build a RT in, don't try to cap it
if (AITAC_DeployableExistsAtLocation(ThisNode->Location, &EnemyStructuresFilter)) { continue; }
}
edict_t* ExistingBuilder = AITAC_GetNearestPlayerOfClassInArea(BotTeam, ThisNode->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2); edict_t* ExistingBuilder = AITAC_GetNearestPlayerOfClassInArea(BotTeam, ThisNode->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
if (!FNullEnt(ExistingBuilder) && vDist2DSq(ExistingBuilder->v.origin, ThisNode->Location) < vDist2DSq(pBot->Edict->v.origin, ThisNode->Location) && GetPlayerResources(ExistingBuilder) >= (BALANCE_VAR(kResourceTowerCost) * 0.8f)) { continue; } if (!FNullEnt(ExistingBuilder) && vDist2DSq(ExistingBuilder->v.origin, ThisNode->Location) < vDist2DSq(pBot->Edict->v.origin, ThisNode->Location) && GetPlayerResources(ExistingBuilder) >= (BALANCE_VAR(kResourceTowerCost) * 0.8f)) { continue; }
@ -3396,22 +3523,37 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
AvHAIResourceNode* ThisNode = (*it); AvHAIResourceNode* ThisNode = (*it);
// Don't attack nodes which are firmly owned by the enemy (i.e. in marine base, or part of an enemy alien team's active hive)
if (ThisNode->bIsBaseNode) if (ThisNode->bIsBaseNode)
{ {
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); if (FNullEnt(ThisNode->ParentHive)) { continue; } // This node must belong to marine base, leave that tower alone
if (AIMGR_GetEnemyTeamType(BotTeam) == AVH_CLASS_TYPE_MARINE) }
if (!FNullEnt(ThisNode->ParentHive))
{
AvHAIHiveDefinition* ParentHiveRef = AITAC_GetHiveFromEdict(ThisNode->ParentHive);
// Don't try to attack RTs inside enemy hives
if (ParentHiveRef->OwningTeam == EnemyTeam) { continue; }
// Don't attack an empty hive RT if the enemy has fortified the area
DeployableSearchFilter EnemyStructuresFilter;
EnemyStructuresFilter.DeployableTeam = EnemyTeam;
EnemyStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStructuresFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{ {
// Too close to the marine comm chair, don't touch this one // Don't attack if there are turrets or a phase gate in the area.
if (vDist2DSq(ThisNode->Location, AITAC_GetCommChairLocation(EnemyTeam)) < sqrf(UTIL_MetresToGoldSrcUnits(15.0f))) { continue; } EnemyStructuresFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
} }
else else
{ {
// The enemy alien team has a hive here, don't attack this res node or we'll get smooshed EnemyStructuresFilter.DeployableTypes = (STRUCTURE_ALIEN_OFFENCECHAMBER);
AvHAIHiveDefinition* ParentHive = AITAC_GetHiveFromEdict(ThisNode->ParentHive);
if (ParentHive && ParentHive->OwningTeam == EnemyTeam) { continue; }
} }
// Enemy has started fortifying the hive we want to build a RT in, leave that tower alone
if (AITAC_DeployableExistsAtLocation(ThisNode->Location, &EnemyStructuresFilter)) { continue; }
} }
float ThisDist = vDist2DSq(ThisNode->Location, AITAC_GetTeamStartingLocation(EnemyTeam)); float ThisDist = vDist2DSq(ThisNode->Location, AITAC_GetTeamStartingLocation(EnemyTeam));
@ -3430,6 +3572,7 @@ void AIPlayerSetAlienCapperPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
return; return;
} }
// If we have nothing to do as a capper, then revert to assault
AIPlayerSetAlienAssaultPrimaryTask(pBot, Task); AIPlayerSetAlienAssaultPrimaryTask(pBot, Task);
} }
@ -3462,6 +3605,10 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
{ {
EnemyStuffFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_COMMCHAIR); EnemyStuffFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_COMMCHAIR);
EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; EnemyStuffFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
if (IsPlayerSkulk(pBot->Edict) || IsPlayerLerk(pBot->Edict))
{
EnemyStuffFilter.ExcludeStatusFlags |= STRUCTURE_STATUS_RECYCLING;
}
} }
else else
{ {
@ -3598,7 +3745,7 @@ void AIPlayerSetAlienAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task
return; return;
} }
} }
// We're a skulk // We're a skulk or lerk
else else
{ {
if (HiveToGuard) if (HiveToGuard)
@ -3831,6 +3978,228 @@ void AIPlayerSetAlienHarasserPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas
void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{ {
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (IsPlayerGorge(pBot->Edict))
{
edict_t* TeamMateToHeal = nullptr;
vector<AvHPlayer*> AllNearbyTeammates = AITAC_GetAllPlayersOfTeamInArea(BotTeam, pBot->Edict->v.origin, UTIL_MetresToGoldSrcUnits(5.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
float MinDist = 0.0f;
for (auto it = AllNearbyTeammates.begin(); it != AllNearbyTeammates.end(); it++)
{
edict_t* ThisPlayer = (*it)->edict();
if (!FNullEnt(ThisPlayer) && IsPlayerActiveInGame(ThisPlayer) && GetPlayerOverallHealthPercent(ThisPlayer) < 0.99f)
{
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisPlayer->v.origin);
if (FNullEnt(TeamMateToHeal) || ThisDist < MinDist)
{
TeamMateToHeal = ThisPlayer;
}
}
}
if (!FNullEnt(TeamMateToHeal))
{
Task->TaskType = TASK_HEAL;
Task->TaskTarget = TeamMateToHeal;
Task->bTaskIsUrgent = true;
return;
}
DeployableSearchFilter DamagedStructuresFilter;
DamagedStructuresFilter.DeployableTypes = SEARCH_ALL_STRUCTURES;
DamagedStructuresFilter.DeployableTeam = BotTeam;
DamagedStructuresFilter.ReachabilityTeam = BotTeam;
DamagedStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
DamagedStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
DamagedStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
vector<AvHAIBuildableStructure*> AllNearbyStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &DamagedStructuresFilter);
edict_t* StructureToHeal = nullptr;
MinDist = 0.0f;
for (auto it = AllNearbyStructures.begin(); it != AllNearbyStructures.end(); it++)
{
AvHAIBuildableStructure* ThisStructure = (*it);
if (ThisStructure && ThisStructure->healthPercent < 0.99f)
{
float ThisDist = vDist2DSq(pBot->Edict->v.origin, ThisStructure->Location);
if (FNullEnt(StructureToHeal) || ThisDist < MinDist)
{
StructureToHeal = ThisStructure->edict;
MinDist = ThisDist;
}
}
}
if (!FNullEnt(StructureToHeal))
{
Task->TaskType = TASK_HEAL;
Task->TaskTarget = StructureToHeal;
Task->bTaskIsUrgent = true;
return;
}
AITASK_ClearBotTask(pBot, Task);
return;
}
vector<AvHAIHiveDefinition*> AllHives = AITAC_GetAllTeamHives(BotTeam, false);
AvHAIHiveDefinition* HiveToDefend = nullptr;
float MinDist = 0.0f;
for (auto it = AllHives.begin(); it != AllHives.end(); it++)
{
AvHAIHiveDefinition* ThisHive = (*it);
if (ThisHive && ThisHive->bIsUnderAttack)
{
int AttackerStrength = 0;
int DefenderStrength = 0;
vector<AvHPlayer*> AttackingPlayers = AITAC_GetAllPlayersOfTeamInArea(EnemyTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(15.0f), false, nullptr, AVH_USER3_NONE);
for (auto AttackerIt = AttackingPlayers.begin(); AttackerIt != AttackingPlayers.end(); AttackerIt++)
{
AvHPlayer* ThisPlayer = (*AttackerIt);
edict_t* ThisPlayerEdict = ThisPlayer->edict();
int ThisAttackerStrength = 1;
if (PlayerHasWeapon(ThisPlayer, WEAPON_MARINE_HMG) || PlayerHasHeavyArmour(ThisPlayerEdict))
{
ThisAttackerStrength = 2;
}
if (IsPlayerFade(ThisPlayerEdict))
{
ThisAttackerStrength = 2;
}
if (IsPlayerOnos(ThisPlayerEdict))
{
ThisAttackerStrength = 3;
}
AttackerStrength += ThisAttackerStrength;
}
vector<AvHPlayer*> DefendingPlayers = AITAC_GetAllPlayersOfTeamInArea(EnemyTeam, ThisHive->FloorLocation, UTIL_MetresToGoldSrcUnits(15.0f), false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
for (auto DefenderIt = DefendingPlayers.begin(); DefenderIt != DefendingPlayers.end(); DefenderIt++)
{
AvHPlayer* ThisPlayer = (*DefenderIt);
edict_t* ThisPlayerEdict = ThisPlayer->edict();
int ThisDefenderStrength = 1;
if (IsPlayerFade(ThisPlayerEdict))
{
ThisDefenderStrength = 2;
}
if (IsPlayerOnos(ThisPlayerEdict))
{
ThisDefenderStrength = 3;
}
DefenderStrength += ThisDefenderStrength;
}
vector<AvHAIPlayer*> AllOtherBots = AIMGR_GetAIPlayersOnTeam(BotTeam);
for (auto BotIt = AllOtherBots.begin(); BotIt != AllOtherBots.end(); BotIt++)
{
AvHAIPlayer* ThisBot = (*BotIt);
if (ThisBot != pBot && IsPlayerActiveInGame(ThisBot->Edict) && !IsPlayerGorge(ThisBot->Edict) && vDist2DSq(ThisBot->Edict->v.origin, ThisHive->FloorLocation) > sqrf(UTIL_MetresToGoldSrcUnits(15.0f)))
{
if (ThisBot->SecondaryBotTask.TaskType == TASK_DEFEND && ThisBot->SecondaryBotTask.TaskTarget == ThisHive->HiveEntity->edict())
{
int ThisDefenderStrength = 1;
if (IsPlayerFade(ThisBot->Edict))
{
ThisDefenderStrength = 2;
}
if (IsPlayerOnos(ThisBot->Edict))
{
ThisDefenderStrength = 3;
}
DefenderStrength += ThisDefenderStrength;
}
}
}
if (AttackerStrength >= DefenderStrength)
{
float ThisDist = vDist2DSq(ThisHive->FloorLocation, pBot->Edict->v.origin);
if (!HiveToDefend || ThisDist < MinDist)
{
HiveToDefend = ThisHive;
MinDist = ThisDist;
}
}
}
}
if (HiveToDefend)
{
AITASK_SetDefendTask(pBot, Task, HiveToDefend->HiveEntity->edict(), true);
return;
}
DeployableSearchFilter AttackedStructuresFilter;
AttackedStructuresFilter.DeployableTypes = (IsPlayerLerk(pBot->Edict)) ? SEARCH_ALL_STRUCTURES : STRUCTURE_ALIEN_RESTOWER;
AttackedStructuresFilter.DeployableTeam = BotTeam;
AttackedStructuresFilter.ReachabilityTeam = BotTeam;
AttackedStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
AttackedStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_UNDERATTACK;
AttackedStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(30.0f);
vector<AvHAIBuildableStructure*> AllAttackedStructures = AITAC_FindAllDeployables(pBot->Edict->v.origin, &AttackedStructuresFilter);
AvHAIBuildableStructure* StructureToDefend = nullptr;
MinDist = 0.0f;
for (auto it = AllAttackedStructures.begin(); it != AllAttackedStructures.end(); it++)
{
AvHAIBuildableStructure* ThisStructure = (*it);
float ThisDist = vDist2D(pBot->Edict->v.origin, ThisStructure->edict->v.origin);
int NumExistingDefenders = AITAC_GetNumPlayersOfTeamInArea(BotTeam, ThisStructure->Location, ThisDist - 10.0f, false, pBot->Edict, AVH_USER3_ALIEN_PLAYER2);
if (NumExistingDefenders < 2)
{
if (!StructureToDefend || ThisDist < MinDist)
{
StructureToDefend = ThisStructure;
MinDist = ThisDist;
}
}
}
if (StructureToDefend)
{
AITASK_SetDefendTask(pBot, Task, StructureToDefend->edict, true);
return;
}
AITASK_ClearBotTask(pBot, Task);
} }
@ -3844,6 +4213,8 @@ bool AlienCombatThink(AvHAIPlayer* pBot)
if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_IGNORE) { return false; } if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_IGNORE) { return false; }
pBot->LastCombatTime = gpGlobals->time;
switch (pBot->Player->GetUser3()) switch (pBot->Player->GetUser3())
{ {
case AVH_USER3_ALIEN_PLAYER1: case AVH_USER3_ALIEN_PLAYER1:
@ -4166,7 +4537,6 @@ bool LerkCombatThink(AvHAIPlayer* pBot)
{ {
BotShootLocation(pBot, WEAPON_LERK_SPORES, SporeLocation); BotShootLocation(pBot, WEAPON_LERK_SPORES, SporeLocation);
} }
} }
else else
{ {

View file

@ -37,6 +37,7 @@ void BotShootLocation(AvHAIPlayer* pBot, AvHAIWeapon AttackWeapon, const Vector
void BombardierAttackTarget(AvHAIPlayer* pBot, edict_t* Target); void BombardierAttackTarget(AvHAIPlayer* pBot, edict_t* Target);
void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetLifeform); void BotEvolveLifeform(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetLifeform);
void BotEvolveUpgrade(AvHAIPlayer* pBot, Vector DesiredEvolveLocation, AvHMessageID TargetUpgrade);
enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target); enemy_status* GetTrackedEnemyRefForTarget(AvHAIPlayer* pBot, edict_t* Target);
@ -106,6 +107,8 @@ bool ShouldAIPlayerTakeCommand(AvHAIPlayer* pBot);
int BotGetNextEnemyTarget(AvHAIPlayer* pBot); int BotGetNextEnemyTarget(AvHAIPlayer* pBot);
AvHMessageID AlienGetDesiredUpgrade(AvHAIPlayer* pBot, HiveTechStatus DesiredTech);
AvHAICombatStrategy GetBotCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy); AvHAICombatStrategy GetBotCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetAlienCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy); AvHAICombatStrategy GetAlienCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);
AvHAICombatStrategy GetSkulkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy); AvHAICombatStrategy GetSkulkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_status* CurrentEnemy);

View file

@ -593,7 +593,7 @@ void AIMGR_UpdateAIPlayers()
UpdateBotChat(bot); UpdateBotChat(bot);
AIPlayerThink(bot); CustomThink(bot);
BotUpdateDesiredViewRotation(bot); BotUpdateDesiredViewRotation(bot);
} }

View file

@ -641,6 +641,13 @@ bool PlayerHasWeapon(const AvHPlayer* Player, const AvHAIWeapon DesiredCombatWea
// Marines don't have a fixed inventory, so we can just do a simple check for them. Same goes to confirm the alien has the weapon in their inventory // Marines don't have a fixed inventory, so we can just do a simple check for them. Same goes to confirm the alien has the weapon in their inventory
if (IsPlayerMarine(Player) || !HasWeaponInInventory) if (IsPlayerMarine(Player) || !HasWeaponInInventory)
{ {
if (DesiredCombatWeapon == WEAPON_MARINE_GRENADE && HasWeaponInInventory)
{
AvHBasePlayerWeapon* Weapon = dynamic_cast<AvHBasePlayerWeapon*>(Player->m_rgpPlayerItems[5]);
return Weapon->m_iClip > 0;
}
return HasWeaponInInventory; return HasWeaponInInventory;
} }

View file

@ -4200,3 +4200,30 @@ edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLo
return (!FNullEnt(FriendlyGorge) ? FriendlyGorge : Result); return (!FNullEnt(FriendlyGorge) ? FriendlyGorge : Result);
} }
bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus DesiredTech)
{
AvHAIDeployableStructureType SearchType;
switch (DesiredTech)
{
case HIVE_TECH_DEFENCE:
SearchType = STRUCTURE_ALIEN_DEFENCECHAMBER;
break;
case HIVE_TECH_MOVEMENT:
SearchType = STRUCTURE_ALIEN_MOVEMENTCHAMBER;
break;
case HIVE_TECH_SENSORY:
SearchType = STRUCTURE_ALIEN_SENSORYCHAMBER;
break;
default:
return false;
}
DeployableSearchFilter ChamberFilter;
ChamberFilter.DeployableTeam = Team;
ChamberFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
ChamberFilter.DeployableTypes = SearchType;
return (AITAC_DeployableExistsAtLocation(ZERO_VECTOR, &ChamberFilter));
}

View file

@ -175,4 +175,6 @@ void AITAC_OnTeamStartsModified();
edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLocation, edict_t* SearchingPlayer, bool bIncludeGorges); edict_t* AITAC_AlienFindNearestHealingSource(AvHTeamNumber Team, Vector SearchLocation, edict_t* SearchingPlayer, bool bIncludeGorges);
bool AITAC_IsAlienUpgradeAvailableForTeam(AvHTeamNumber Team, HiveTechStatus DesiredTech);
#endif #endif

View file

@ -634,13 +634,24 @@ bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
} }
AvHTeamNumber BotTeam = pBot->Player->GetTeam(); AvHTeamNumber BotTeam = pBot->Player->GetTeam();
AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam);
if (ResNodeIndex->bIsBaseNode)
{
if (FNullEnt(ResNodeIndex->ParentHive)) { return false; } // This is the marine base res node, leave it alone
AvHAIHiveDefinition* ParentHive = AITAC_GetHiveFromEdict(ResNodeIndex->ParentHive);
// An enemy is now building the hive that this res node belongs to, abort
if (ParentHive && ParentHive->OwningTeam == EnemyTeam) { return false; }
}
// Don't waste resources switching down to gorge if we're a lerk, fade or onos // Don't waste resources switching down to gorge if we're a lerk, fade or onos
// but we can still clear the area of enemy structures // but we can still clear the area of enemy structures
if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict)) if (!IsPlayerSkulk(pBot->Edict) && !IsPlayerGorge(pBot->Edict))
{ {
DeployableSearchFilter EnemyStructuresFilter; DeployableSearchFilter EnemyStructuresFilter;
EnemyStructuresFilter.DeployableTeam = AIMGR_GetEnemyTeam(BotTeam); EnemyStructuresFilter.DeployableTeam = EnemyTeam;
EnemyStructuresFilter.ReachabilityTeam = BotTeam; EnemyStructuresFilter.ReachabilityTeam = BotTeam;
EnemyStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; EnemyStructuresFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag;
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f);
@ -648,6 +659,27 @@ bool AITASK_IsAlienCapResNodeTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask*
return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter); return AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter);
} }
if (!FNullEnt(ResNodeIndex->ParentHive))
{
DeployableSearchFilter EnemyStructuresFilter;
EnemyStructuresFilter.DeployableTeam = EnemyTeam;
EnemyStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED;
EnemyStructuresFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING;
EnemyStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f);
if (AIMGR_GetTeamType(EnemyTeam) == AVH_CLASS_TYPE_MARINE)
{
EnemyStructuresFilter.DeployableTypes = (STRUCTURE_MARINE_PHASEGATE | STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY);
}
else
{
EnemyStructuresFilter.DeployableTypes = (STRUCTURE_ALIEN_OFFENCECHAMBER);
}
// Enemy has started fortifying the hive we want to build a RT in, abort
if (AITAC_DeployableExistsAtLocation(ResNodeIndex->Location, &EnemyStructuresFilter)) { return false; }
}
// We can attack structures basically if we aren't stuck with Gorge's spit attack // We can attack structures basically if we aren't stuck with Gorge's spit attack
bool bCanAttackStructures = (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB)); bool bCanAttackStructures = (!IsPlayerGorge(pBot->Edict) || PlayerHasWeapon(pBot->Player, WEAPON_GORGE_BILEBOMB));
@ -962,7 +994,7 @@ bool AITASK_IsAlienHealTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
// If our target is a player, give up if they are too far away. I'm not going to waste time chasing you around the map! // If our target is a player, give up if they are too far away. I'm not going to waste time chasing you around the map!
float MaxHealRelevant = sqrf(UTIL_MetresToGoldSrcUnits(5.0f)); float MaxHealRelevant = sqrf(UTIL_MetresToGoldSrcUnits(5.0f));
return (vDist2DSq(pBot->CurrentFloorPosition, Task->TaskTarget->v.origin) <= MaxHealRelevant); return (IsPlayerActiveInGame(Task->TaskTarget) && vDist2DSq(pBot->CurrentFloorPosition, Task->TaskTarget->v.origin) <= MaxHealRelevant);
} }
bool AITASK_IsUseTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) bool AITASK_IsUseTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)

View file

@ -182,7 +182,7 @@ typedef enum
ALIEN_EVOLUTION_EIGHT = 108, // Adrenaline ALIEN_EVOLUTION_EIGHT = 108, // Adrenaline
ALIEN_EVOLUTION_NINE = 109, // Silence ALIEN_EVOLUTION_NINE = 109, // Silence
ALIEN_EVOLUTION_TEN = 110, // Cloaking ALIEN_EVOLUTION_TEN = 110, // Cloaking
ALIEN_EVOLUTION_ELEVEN = 111, // Pheromones ALIEN_EVOLUTION_ELEVEN = 111, // Focus
ALIEN_EVOLUTION_TWELVE = 112, // Scent of fear ALIEN_EVOLUTION_TWELVE = 112, // Scent of fear
// Alien lifeforms // Alien lifeforms