diff --git a/main/source/mod/AIPlayers/AvHAICommander.cpp b/main/source/mod/AIPlayers/AvHAICommander.cpp index 8aed44e0..7d7be8fe 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.cpp +++ b/main/source/mod/AIPlayers/AvHAICommander.cpp @@ -16,14 +16,27 @@ bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureTyp AvHMessageID StructureID = UTIL_StructureTypeToImpulseCommand(StructureToDeploy); Vector BuildLocation = Location; + BuildLocation.z += 4.0f; + + UTIL_DrawLine(INDEXENT(1), INDEXENT(1)->v.origin, Location, 10.0f); if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; } - CBaseEntity* NewBuilding = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player); + string theErrorMessage; + int theCost = 0; + bool thePurchaseAllowed = pBot->Player->GetPurchaseAllowed(StructureID, theCost, &theErrorMessage); + + if (!thePurchaseAllowed) { return false; } + + bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL); + + if (!theSuccess) { return false; } + + pBot->Player->PayPurchaseCost(theCost); pBot->next_commander_action_time = gpGlobals->time + 1.0f; - return NewBuilding != nullptr; + return true; } bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDeploy, const Vector Location) @@ -32,25 +45,38 @@ bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDe Vector BuildLocation = Location; + string theErrorMessage; + int theCost = 0; + bool thePurchaseAllowed = pBot->Player->GetPurchaseAllowed(StructureID, theCost, &theErrorMessage); + + if (!thePurchaseAllowed) { return false; } + if (!AvHSHUGetIsSiteValidForBuild(StructureID, &BuildLocation)) { return false; } - CBaseEntity* NewBuilding = AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player); + bool theSuccess = (AvHSUBuildTechForPlayer(StructureID, BuildLocation, pBot->Player) != NULL); + + if (!theSuccess) { return false; } + + pBot->Player->PayPurchaseCost(theCost); pBot->next_commander_action_time = gpGlobals->time + 0.2f; - return NewBuilding != nullptr; + return true; } bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research) { if (FNullEnt(StructureToResearch->edict)) { return false; } - if (StructureToResearch->EntityRef->GetIsRecycling() || StructureToResearch->EntityRef->GetIsResearching()) { return false; } + // Don't do anything if the structure is being recycled, or we DON'T want to recycle but the structure is already busy + if (StructureToResearch->EntityRef->GetIsRecycling() || (Research != BUILD_RECYCLE && StructureToResearch->EntityRef->GetIsResearching())) { return false; } int StructureIndex = ENTINDEX(StructureToResearch->edict); if (StructureIndex < 0) { return false; } + if (!StructureToResearch->EntityRef->GetIsTechnologyAvailable(Research)) { return false; } + pBot->Player->SetSelection(StructureIndex, true); pBot->Button |= IN_ATTACK2; @@ -72,6 +98,7 @@ bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* Structu break; case STRUCTURE_MARINE_TURRETFACTORY: UpgradeImpulse = TURRET_FACTORY_UPGRADE; + break; default: return false; } @@ -376,7 +403,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) StructureFilter.DeployableTeam = TeamNumber; StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; StructureFilter.ReachabilityTeam = TeamNumber; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); int NumInfantryPortals = AITAC_GetNumDeployablesNearLocation(CommChair->v.origin, &StructureFilter); @@ -389,7 +416,6 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) } StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); AvHAIBuildableStructure* BaseArmoury = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &StructureFilter); @@ -405,12 +431,12 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (NearestInfantryPortal) { - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestInfantryPortal->Location, UTIL_MetresToGoldSrcUnits(5.0f)); + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestInfantryPortal->Location, UTIL_MetresToGoldSrcUnits(5.0f)); } if (vIsZero(BuildLocation)) { - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); } if (!vIsZero(BuildLocation)) @@ -430,7 +456,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (!bAlreadyScanning) { - Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(ALL_NAV_PROFILE), HiveUnderSiege->FloorLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), HiveUnderSiege->FloorLocation, UTIL_MetresToGoldSrcUnits(3.0f)); if (AICOMM_DeployItem(pBot, DEPLOYABLE_ITEM_SCAN, BuildLocation)) { @@ -448,7 +474,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (!bPhaseNearBase) { - Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_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)) { @@ -484,7 +510,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (!bHasArmsLab) { - Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_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 (!vIsZero(BuildLocation)) { @@ -501,7 +527,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (!bHasObservatory) { - Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_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 (!vIsZero(BuildLocation)) { @@ -564,7 +590,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action) if (!bHasPrototypeLab && bHasAdvArmoury) { - Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInDonut(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), BaseArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f), UTIL_MetresToGoldSrcUnits(5.0f)); + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), BaseArmoury->Location, UTIL_MetresToGoldSrcUnits(3.0f), UTIL_MetresToGoldSrcUnits(5.0f)); if (!vIsZero(BuildLocation)) { @@ -775,35 +801,39 @@ const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPl bool bPhaseGatesAvailable = AITAC_PhaseGatesAvailable(CommanderTeam); + // Only siege if we have phase gates available if (!bPhaseGatesAvailable) { return nullptr; } const AvHAIHiveDefinition* Result = nullptr; float MinDist = 0.0f; - const vector Hives = AITAC_GetAllHives(); + const vector Hives = AITAC_GetAllHives(); for (auto it = Hives.begin(); it != Hives.end(); it++) { - const AvHAIHiveDefinition* Hive = &(*it); + const AvHAIHiveDefinition* Hive = (*it); - if (it->Status == HIVE_STATUS_UNBUILT) { continue; } - - if (AITAC_GetMarineEligibleToBuildSiege(CommanderTeam, Hive) == nullptr) { continue; } + if (Hive->Status == HIVE_STATUS_UNBUILT) { continue; } DeployableSearchFilter StructureFilter; StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE; StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); - StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_COMPLETED; + StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; AvHAIBuildableStructure* BuiltPhaseGate = AITAC_FindClosestDeployableToLocation(Hive->Location, &StructureFilter); + // If we have a phase gate already in place, then keep building as long as someone is there. If we don't have a phase gate, only build if there is a marine who isn't sighted by the enemy (to allow element of surprise) if (BuiltPhaseGate) { int NumBuilders = AITAC_GetNumPlayersOfTeamInArea(CommanderTeam, BuiltPhaseGate->Location, UTIL_MetresToGoldSrcUnits(5.0f), false, CommanderBot->Edict, AVH_USER3_COMMANDER_PLAYER); if (NumBuilders == 0) { continue; } } + else + { + if (AITAC_GetMarineEligibleToBuildSiege(CommanderTeam, Hive) == nullptr) { continue; } + } float ThisDist = vDist2DSq(Hive->FloorLocation, SearchLocation); @@ -820,22 +850,24 @@ const AvHAIHiveDefinition* AICOMM_GetHiveSiegeOpportunityNearestLocation(AvHAIPl const AvHAIResourceNode* AICOMM_GetNearestResourceNodeCapOpportunity(const AvHTeamNumber Team, const Vector SearchLocation) { - vector AllNodes = AITAC_GetAllResourceNodes(); + vector AllNodes = AITAC_GetAllResourceNodes(); AvHAIResourceNode* Result = nullptr; float MinDist = 0.0f; for (auto it = AllNodes.begin(); it != AllNodes.end(); it++) { - if (it->bIsOccupied) { continue; } + AvHAIResourceNode* ResNode = (*it); - if (!AITAC_AnyPlayerOnTeamWithLOS(Team, (it->Location + Vector(0.0f, 0.0f, 32.0f)), UTIL_MetresToGoldSrcUnits(5.0f))) { continue; } + if (ResNode->bIsOccupied) { continue; } - float ThisDist = vDist2DSq(it->Location, SearchLocation); + if (!AITAC_AnyPlayerOnTeamWithLOS(Team, (ResNode->Location + Vector(0.0f, 0.0f, 32.0f)), UTIL_MetresToGoldSrcUnits(5.0f))) { continue; } + + float ThisDist = vDist2DSq(ResNode->Location, SearchLocation); if (!Result || ThisDist < MinDist) { - Result = &(*it); + Result = ResNode; MinDist = ThisDist; } } @@ -855,7 +887,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); - Vector SiegeLocation; + Vector SiegeLocation = ZERO_VECTOR; AvHAIBuildableStructure* ExistingPG = nullptr; if (AITAC_PhaseGatesAvailable(CommanderTeam)) @@ -895,7 +927,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit if (FNullEnt(NearestBuilder)) { return false; } - Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(BUILDING_NAV_MESH), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + Vector NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); if (!ExistingPG) { @@ -917,7 +949,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, NextBuildPosition); } - if (ExistingTF->StructureType != STRUCTURE_MARINE_ADVARMOURY) + if (ExistingTF->StructureType != STRUCTURE_MARINE_ADVTURRETFACTORY) { return AICOMM_UpgradeStructure(pBot, ExistingTF); } @@ -931,7 +963,7 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit { SiegeLocation = ExistingTF->Location; - NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(BUILDING_NAV_MESH), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); + NextBuildPosition = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), SiegeLocation, UTIL_MetresToGoldSrcUnits(3.0f)); return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_SIEGETURRET, NextBuildPosition); } @@ -945,8 +977,98 @@ bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinit } -bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action) +bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action) { + DeployableSearchFilter StructureFilter; + StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY | STRUCTURE_MARINE_PHASEGATE; + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + StructureFilter.DeployableTeam = pBot->Player->GetTeam(); + StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; + StructureFilter.ReachabilityTeam = pBot->Player->GetTeam(); + StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + + AvHAIBuildableStructure* ExistingStructure = AITAC_FindClosestDeployableToLocation(HiveToSecure->FloorLocation, &StructureFilter); + AvHAIBuildableStructure* ExistingPG = nullptr; + AvHAIBuildableStructure* ExistingTF = nullptr; + + Vector OutpostLocation = (ExistingStructure) ? ExistingStructure->Location : HiveToSecure->FloorLocation; + + if (ExistingStructure) + { + if (ExistingStructure->StructureType == STRUCTURE_MARINE_PHASEGATE) + { + ExistingPG = ExistingStructure; + } + else + { + ExistingTF = ExistingStructure; + } + } + + if (AITAC_PhaseGatesAvailable(pBot->Player->GetTeam())) + { + if (!ExistingPG) + { + StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE; + + ExistingPG = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter); + + if (!ExistingPG) + { + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f)); + + if (!vIsZero(BuildLocation)) + { + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation); + } + + return false; + } + } + } + + if (!ExistingTF) + { + StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRETFACTORY; + + ExistingTF = AITAC_FindClosestDeployableToLocation(OutpostLocation, &StructureFilter); + + if (!ExistingTF) + { + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), OutpostLocation, UTIL_MetresToGoldSrcUnits(5.0f)); + + if (!vIsZero(BuildLocation)) + { + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation); + } + + return false; + } + } + + StructureFilter.DeployableTypes = STRUCTURE_MARINE_TURRET; + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); + + int NumTurrets = AITAC_GetNumDeployablesNearLocation(ExistingTF->Location, &StructureFilter); + + if (NumTurrets < 5) + { + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingTF->Location, UTIL_MetresToGoldSrcUnits(3.0f)); + + if (!vIsZero(BuildLocation)) + { + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation); + } + + return false; + } + + if (!UTIL_IsStructureElectrified(ExistingTF->edict)) + { + return AICOMM_ResearchTech(pBot, ExistingTF, RESEARCH_ELECTRICAL); + } + + return false; } @@ -958,7 +1080,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander DeployableSearchFilter ExistingPortalFilter; ExistingPortalFilter.DeployableTypes = STRUCTURE_MARINE_INFANTRYPORTAL; - ExistingPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(5.0f); + ExistingPortalFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(10.0f); ExistingPortalFilter.DeployableTeam = pBot->Player->GetTeam(); ExistingPortalFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; ExistingPortalFilter.ReachabilityTeam = pBot->Player->GetTeam(); @@ -968,7 +1090,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander // First see if we can place the next infantry portal next to the first one if (ExistingInfantryPortal) { - BuildLocation = UTIL_GetRandomPointOnNavmeshInDonut(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), ExistingInfantryPortal->edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f)); + BuildLocation = UTIL_GetRandomPointOnNavmeshInDonutIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), ExistingInfantryPortal->edict->v.origin, UTIL_MetresToGoldSrcUnits(2.0f), UTIL_MetresToGoldSrcUnits(3.0f)); } if (vIsZero(BuildLocation)) @@ -997,12 +1119,12 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander float Distance = vDist2D(NearestPointToChair, CommChair->v.origin); float RandomDist = UTIL_MetresToGoldSrcUnits(5.0f) - Distance; - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist); + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), NearestPointToChair, RandomDist); } else { - BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(5.0f)); } } @@ -1011,6 +1133,25 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); } +bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot) +{ + DeployableSearchFilter UnreachableFilter; + UnreachableFilter.DeployableTeam = pBot->Player->GetTeam(); + UnreachableFilter.ReachabilityTeam = pBot->Player->GetTeam(); + UnreachableFilter.ReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + UnreachableFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_RESEARCHING; + + AvHAIBuildableStructure* UnreachableStructure = AITAC_FindClosestDeployableToLocation(AITAC_GetCommChairLocation(pBot->Player->GetTeam()), &UnreachableFilter); + + // Recycle any structures which are unreachable (e.g. sunk below the map) + if (UnreachableStructure) + { + return AICOMM_RecycleStructure(pBot, UnreachableStructure); + } + + return false; +} + bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot) { AICOMM_CheckNewRequests(pBot); @@ -1245,6 +1386,7 @@ void AICOMM_CommanderThink(AvHAIPlayer* pBot) if (gpGlobals->time < pBot->next_commander_action_time) { return; } + if (AICOMM_CheckForNextRecycleAction(pBot)) { return; } if (AICOMM_CheckForNextSupportAction(pBot)) { return; } if (AICOMM_CheckForNextBuildAction(pBot, &pBot->BuildAction)) { return; } if (AICOMM_CheckForNextResearchAction(pBot, &pBot->ResearchAction)) { return; } @@ -1320,11 +1462,11 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl const AvHAIHiveDefinition* Result = nullptr; float MinDist = 0.0f; - const vector Hives = AITAC_GetAllHives(); + const vector Hives = AITAC_GetAllHives(); for (auto it = Hives.begin(); it != Hives.end(); it++) { - const AvHAIHiveDefinition* Hive = &(*it); + const AvHAIHiveDefinition* Hive = (*it); if (Hive->Status != HIVE_STATUS_UNBUILT) { continue; } @@ -1332,7 +1474,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl if (AITAC_GetNearestHiddenPlayerInLocation(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f)) == nullptr) { continue; } - if (!AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f))) + if (AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, Hive->Location, UTIL_MetresToGoldSrcUnits(10.0f))) { DeployableSearchFilter StructureFilter; StructureFilter.DeployableTeam = CommanderTeam; @@ -1345,7 +1487,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl AvHAIBuildableStructure* PG = AITAC_FindClosestDeployableToLocation(Hive->FloorLocation, &StructureFilter); - bool bCanSeePG = (PG && AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(PG->edict), UTIL_MetresToGoldSrcUnits(10.0f))); + bool bCanSeePG = (!PG || AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(PG->edict), UTIL_MetresToGoldSrcUnits(10.0f))); if (!bCanSeePG) { @@ -1363,7 +1505,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl bNeedsElectrifying = (UTIL_StructureIsFullyBuilt(TF->edict) && !UTIL_IsStructureElectrified(TF->edict) && AITAC_DeployableExistsAtLocation(TF->Location, &StructureFilter)); } - bool bCanSeeTF = (TF && AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(TF->edict), UTIL_MetresToGoldSrcUnits(10.0f))); + bool bCanSeeTF = (!TF || AITAC_AnyPlayerOnTeamWithLOS(CommanderTeam, UTIL_GetCentreOfEntity(TF->edict), UTIL_MetresToGoldSrcUnits(10.0f))); if (!bNeedsElectrifying && !bCanSeePG && !bCanSeeTF) { continue; } } diff --git a/main/source/mod/AIPlayers/AvHAICommander.h b/main/source/mod/AIPlayers/AvHAICommander.h index b1f5cb4b..66fa3e53 100644 --- a/main/source/mod/AIPlayers/AvHAICommander.h +++ b/main/source/mod/AIPlayers/AvHAICommander.h @@ -27,6 +27,7 @@ bool AICOMM_IssueSecureResNodeOrder(AvHAIPlayer* pBot, edict_t* Recipient, const void AICOMM_ClearAction(commander_action* Action); bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot, commander_action* Action); bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot); +bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot); bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot, commander_action* Action); void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient); void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient); @@ -39,7 +40,7 @@ const AvHAIHiveDefinition* AICOMM_GetEmptyHiveOpportunityNearestLocation(AvHAIPl bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair, commander_action* Action); bool AICOMM_PerformNextSiegeHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action); -bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSiege, commander_action* Action); +bool AICOMM_PerformNextSecureHiveAction(AvHAIPlayer* pBot, const AvHAIHiveDefinition* HiveToSecure, commander_action* Action); ai_commander_request* AICOMM_GetExistingRequestForPlayer(AvHAIPlayer* pBot, edict_t* Requestor); void AICOMM_CheckNewRequests(AvHAIPlayer* pBot); diff --git a/main/source/mod/AIPlayers/AvHAIConstants.h b/main/source/mod/AIPlayers/AvHAIConstants.h index 1077c3e9..113f913c 100644 --- a/main/source/mod/AIPlayers/AvHAIConstants.h +++ b/main/source/mod/AIPlayers/AvHAIConstants.h @@ -89,6 +89,7 @@ typedef enum _AI_REACHABILITY_STATUS AI_REACHABILITY_GORGE = 1u << 2, AI_REACHABILITY_ONOS = 1u << 3, AI_REACHABILITY_WELDER = 1u << 4, + AI_REACHABILITY_UNREACHABLE = 1u << 5, AI_REACHABILITY_ALL = -1 } AvHAIReachabilityStatus; @@ -179,6 +180,12 @@ typedef struct _OFF_MESH_CONN edict_t* TargetObject = nullptr; } AvHAIOffMeshConnection; +typedef struct _STRUCTURE_OBSTACLE +{ + unsigned int NavMeshIndex = 0; + unsigned int ObstacleRef = 0; +} AvHAITempObstacle; + // Data structure used to track resource nodes in the map typedef struct _RESOURCE_NODE { @@ -266,7 +273,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the building has been removed from play - unsigned int ObstacleRefs[MAX_NAV_MESHES]; // References to this structure's obstacles across each nav mesh + vector< AvHAITempObstacle> Obstacles; vector 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 diff --git a/main/source/mod/AIPlayers/AvHAINavigation.cpp b/main/source/mod/AIPlayers/AvHAINavigation.cpp index d49d12c7..90894570 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.cpp +++ b/main/source/mod/AIPlayers/AvHAINavigation.cpp @@ -418,31 +418,97 @@ Vector UTIL_GetNearestPointOnNavWall(const nav_profile &NavProfile, const Vector return g_vecZero; } -unsigned int UTIL_AddTemporaryObstacle(const Vector Location, float Radius, float Height, int area) +unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector Location, float Radius, float Height, int area) { unsigned int ObstacleNum = 0; - - float Pos[3] = { Location.x, Location.z - (Height * 0.5f), -Location.y }; - - for (int i = 0; i < MAX_NAV_MESHES; i++) + + if (NavMeshes[NavMeshIndex].tileCache) { - if (NavMeshes[i].tileCache) + float Pos[3] = { Location.x, Location.z - (Height * 0.5f), -Location.y }; + + dtObstacleRef ObsRef = 0; + NavMeshes[NavMeshIndex].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef); + + ObstacleNum = (unsigned int)ObsRef; + + if (ObstacleNum > 0) { - dtObstacleRef ObsRef = 0; - NavMeshes[i].tileCache->addObstacle(Pos, Radius, Height, area, &ObsRef); - - ObstacleNum = (unsigned int)ObsRef; - - if (area == DT_TILECACHE_NULL_AREA || area == DT_TILECACHE_WELD_AREA) - { - bNavMeshModified = true; - } + bNavMeshModified = true; } } return ObstacleNum; } +void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure) +{ + bool bCollideWithPlayers = UTIL_ShouldStructureCollide(Structure->StructureType); + + float Radius = UTIL_GetStructureRadiusForObstruction(Structure->StructureType); + + // Not all structures collide with players (e.g. phase gate) + if (bCollideWithPlayers) + { + unsigned int area = UTIL_GetAreaForObstruction(Structure->StructureType, Structure->edict); + + // We add an obstacle for the building nav mesh below + for (int i = 0; i < BUILDING_NAV_MESH; i++) + { + unsigned int NewObstacleRef = UTIL_AddTemporaryObstacle(i, UTIL_GetCentreOfEntity(Structure->edict), Radius, 100.0f, area); + + if (NewObstacleRef > 0) + { + AvHAITempObstacle NewObstacle; + NewObstacle.NavMeshIndex = i; + NewObstacle.ObstacleRef = NewObstacleRef; + + Structure->Obstacles.push_back(NewObstacle); + } + } + } + + // Always cut a hole in the building nav mesh so we don't try to place anything on top of this structure in future + unsigned int NewObstacleRef = UTIL_AddTemporaryObstacle(BUILDING_NAV_MESH, UTIL_GetCentreOfEntity(Structure->edict), Radius * 1.5f, 100.0f, DT_TILECACHE_NULL_AREA); + + if (NewObstacleRef > 0) + { + AvHAITempObstacle NewObstacle; + NewObstacle.NavMeshIndex = BUILDING_NAV_MESH; + NewObstacle.ObstacleRef = NewObstacleRef; + + Structure->Obstacles.push_back(NewObstacle); + } + +} + +void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure) +{ + for (auto it = Structure->Obstacles.begin(); it != Structure->Obstacles.end();) + { + int NavMeshIndex = it->NavMeshIndex; + + if (NavMeshes[NavMeshIndex].tileCache) + { + const dtTileCacheObstacle* ObstacleToRemove = NavMeshes[NavMeshIndex].tileCache->getObstacleByRef((dtObstacleRef)it->ObstacleRef); + + if (ObstacleToRemove) + { + dtStatus RemovalStatus = NavMeshes[NavMeshIndex].tileCache->removeObstacle((dtObstacleRef)it->ObstacleRef); + + if (dtStatusSucceed(RemovalStatus)) + { + bNavMeshModified = true; + } + } + + + } + + it = Structure->Obstacles.erase(it); + } + +} + void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray) { unsigned int ObstacleNum = 0; @@ -460,7 +526,7 @@ void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Heigh ObstacleRefArray[i] = (unsigned int)ObsRef; - if (area == DT_TILECACHE_NULL_AREA || area == DT_TILECACHE_WELD_AREA) + if (ObstacleNum > 0) { bNavMeshModified = true; } diff --git a/main/source/mod/AIPlayers/AvHAINavigation.h b/main/source/mod/AIPlayers/AvHAINavigation.h index 973a847d..b5d9cbc9 100644 --- a/main/source/mod/AIPlayers/AvHAINavigation.h +++ b/main/source/mod/AIPlayers/AvHAINavigation.h @@ -154,9 +154,9 @@ static const float pReachableExtents[3] = { max_ai_use_reach, max_ai_use_reach, static const int MAX_NAV_PROFILES = 16; // Max number of possible nav profiles. Currently 9 are used (see top of this header file) -static const int REGULAR_NAV_MESH = 0; -static const int ONOS_NAV_MESH = 1; -static const int BUILDING_NAV_MESH = 2; +static const int REGULAR_NAV_MESH = 0; // Nav mesh used by all players except Onos and the AI commander +static const int ONOS_NAV_MESH = 1; // Nav mesh used by Onos (due to larger hitbox) +static const int BUILDING_NAV_MESH = 2; // Nav mesh used by commander for building placement. Must be the last nav mesh index (see UTIL_AddStructureTemporaryObstacles) static const int DT_AREA_NULL = 0; // Represents a null area on the nav mesh. Not traversable and considered not on the nav mesh static const int DT_AREA_BLOCKED = 3; // Area occupied by an obstruction (e.g. building). Not traversable, but considered to be on the nav mesh @@ -302,9 +302,10 @@ Vector UTIL_GetNearestPointOnNavWall(const nav_profile& NavProfile, const Vector An example use case is to place an obstacle of area type SAMPLE_POLYAREA_OBSTRUCTION to mark where buildings are. Using DT_AREA_NULL will effectively cut a hole in the nav mesh, meaning it's no longer considered a valid mesh position. */ -unsigned int UTIL_AddTemporaryObstacle(const Vector Location, float Radius, float Height, int area); +unsigned int UTIL_AddTemporaryObstacle(unsigned int NavMeshIndex, const Vector Location, float Radius, float Height, int area); void UTIL_AddTemporaryObstacles(const Vector Location, float Radius, float Height, int area, unsigned int* ObstacleRefArray); - +void UTIL_AddStructureTemporaryObstacles(AvHAIBuildableStructure* Structure); +void UTIL_RemoveStructureTemporaryObstacles(AvHAIBuildableStructure* Structure); unsigned int UTIL_AddTemporaryBoxObstacle(Vector bMin, Vector bMax, int area); diff --git a/main/source/mod/AIPlayers/AvHAIPlayer.cpp b/main/source/mod/AIPlayers/AvHAIPlayer.cpp index 479603ff..ea2c3a83 100644 --- a/main/source/mod/AIPlayers/AvHAIPlayer.cpp +++ b/main/source/mod/AIPlayers/AvHAIPlayer.cpp @@ -1597,6 +1597,9 @@ void BotStopCommanderMode(AvHAIPlayer* pBot) if (IsPlayerCommander(pBot->Edict)) { - CLIENT_COMMAND(pBot->Edict, "stopcommandermode"); + pBot->Player->SetUser3(AVH_USER3_MARINE_PLAYER); + + // Cheesy way to make sure player class change is sent to everyone + pBot->Player->EffectivePlayerClassChanged(); } } diff --git a/main/source/mod/AIPlayers/AvHAITactical.cpp b/main/source/mod/AIPlayers/AvHAITactical.cpp index 61b869e1..069029fb 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.cpp +++ b/main/source/mod/AIPlayers/AvHAITactical.cpp @@ -811,8 +811,8 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item) if (!bOnNavMesh) { - Item->TeamAReachabilityFlags = AI_REACHABILITY_NONE; - Item->TeamBReachabilityFlags = AI_REACHABILITY_NONE; + Item->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + Item->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; return; } @@ -838,6 +838,10 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item) { Item->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + Item->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } @@ -863,6 +867,10 @@ void AITAC_RefreshReachabilityForItem(AvHAIDroppedItem* Item) { Item->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + Item->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } } @@ -896,8 +904,8 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) if (!bOnNavMesh) { - ResNode->TeamAReachabilityFlags = AI_REACHABILITY_NONE; - ResNode->TeamBReachabilityFlags = AI_REACHABILITY_NONE; + ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; return; } @@ -926,6 +934,10 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) { ResNode->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } else @@ -948,6 +960,11 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) { ResNode->TeamAReachabilityFlags |= AI_REACHABILITY_ONOS; } + + if (ResNode->TeamAReachabilityFlags == AI_REACHABILITY_NONE) + { + ResNode->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE) @@ -972,6 +989,10 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) { ResNode->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } else @@ -994,6 +1015,11 @@ void AITAC_RefreshReachabilityForResNode(AvHAIResourceNode* ResNode) { ResNode->TeamBReachabilityFlags |= AI_REACHABILITY_ONOS; } + + if (ResNode->TeamBReachabilityFlags == AI_REACHABILITY_NONE) + { + ResNode->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } @@ -1274,7 +1300,6 @@ void AITAC_RefreshBuildableStructures() if (it->second.LastSeen < StructureRefreshFrame) { AITAC_OnStructureDestroyed(&it->second); - UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs); it = TeamAStructureMap.erase(it); } else @@ -1293,7 +1318,6 @@ void AITAC_RefreshBuildableStructures() if (it->second.LastSeen < StructureRefreshFrame) { AITAC_OnStructureDestroyed(&it->second); - UTIL_RemoveTemporaryObstacles(it->second.ObstacleRefs); it = TeamBStructureMap.erase(it); } else @@ -1476,12 +1500,15 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) Structure->bReachabilityMarkedDirty = false; + Structure->TeamAReachabilityFlags = AI_REACHABILITY_NONE; + Structure->TeamBReachabilityFlags = AI_REACHABILITY_NONE; + bool bIsOnNavMesh = UTIL_PointIsOnNavmesh(BaseNavProfiles[MARINE_BASE_NAV_PROFILE], UTIL_GetEntityGroundLocation(Structure->edict), Vector(max_player_use_reach, max_player_use_reach, max_player_use_reach)); if (!bIsOnNavMesh) { - Structure->TeamAReachabilityFlags = AI_REACHABILITY_NONE; - Structure->TeamBReachabilityFlags = AI_REACHABILITY_NONE; + Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; return; } @@ -1508,6 +1535,10 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) { Structure->TeamAReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } else @@ -1530,6 +1561,11 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) { Structure->TeamAReachabilityFlags |= AI_REACHABILITY_ONOS; } + + if (Structure->TeamAReachabilityFlags == AI_REACHABILITY_NONE) + { + Structure->TeamAReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } if (GetGameRules()->GetTeamB()->GetTeamType() == AVH_CLASS_TYPE_MARINE) @@ -1555,6 +1591,10 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) { Structure->TeamBReachabilityFlags |= AI_REACHABILITY_WELDER; } + else + { + Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } else @@ -1577,6 +1617,11 @@ void AITAC_RefreshReachabilityForStructure(AvHAIBuildableStructure* Structure) { Structure->TeamBReachabilityFlags |= AI_REACHABILITY_ONOS; } + + if (Structure->TeamBReachabilityFlags == AI_REACHABILITY_NONE) + { + Structure->TeamBReachabilityFlags = AI_REACHABILITY_UNREACHABLE; + } } } @@ -1602,29 +1647,16 @@ void AITAC_UpdateBuildableStructure(CBaseEntity* Structure) std::unordered_map& BuildingMap = (BaseBuildable->GetTeamNumber() == TeamANumber) ? TeamAStructureMap : TeamBStructureMap; + // This is the first time we've seen this structure, so it must be new if (BuildingMap[EntIndex].LastSeen == 0) { BuildingMap[EntIndex].EntityRef = BaseBuildable; BuildingMap[EntIndex].edict = BuildingEdict; BuildingMap[EntIndex].OffMeshConnections.clear(); + BuildingMap[EntIndex].Obstacles.clear(); - memset(&BuildingMap[EntIndex].ObstacleRefs, 0, sizeof(BuildingMap[EntIndex].ObstacleRefs)); - - bool bShouldCollide = UTIL_ShouldStructureCollide(StructureType); - - if (bShouldCollide) - { - unsigned int area = UTIL_GetAreaForObstruction(StructureType, BuildingEdict); - float Radius = UTIL_GetStructureRadiusForObstruction(StructureType); - UTIL_AddTemporaryObstacles(UTIL_GetCentreOfEntity(BuildingMap[EntIndex].edict), Radius, 100.0f, area, BuildingMap[EntIndex].ObstacleRefs); - } - else - { - memset(BuildingMap[EntIndex].ObstacleRefs, 0, sizeof(unsigned int) * MAX_NAV_MESHES); - } - - BuildingMap[EntIndex].Location = g_vecZero; + BuildingMap[EntIndex].Location = g_vecZero; // We set this just below after calculating reachability AITAC_OnStructureCreated(&BuildingMap[EntIndex]); } @@ -1695,8 +1727,12 @@ void AITAC_OnStructureCreated(AvHAIBuildableStructure* NewStructure) { if (!GetGameRules()->GetGameStarted()) { return; } + UTIL_AddStructureTemporaryObstacles(NewStructure); + AvHTeamNumber StructureTeam = NewStructure->EntityRef->GetTeamNumber(); + AITAC_RefreshReachabilityForStructure(NewStructure); + if (StructureTeam == TEAM_IND) { return; } AvHTeam* Team = GetGameRules()->GetTeam(StructureTeam); @@ -1791,6 +1827,8 @@ void AITAC_OnStructureBeginRecycling(AvHAIBuildableStructure* RecyclingStructure void AITAC_OnStructureDestroyed(AvHAIBuildableStructure* DestroyedStructure) { + UTIL_RemoveStructureTemporaryObstacles(DestroyedStructure); + if (DestroyedStructure->StructureType == STRUCTURE_MARINE_PHASEGATE) { // Eliminate all connections from this phase gate @@ -3050,14 +3088,28 @@ vector AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team) return Result; } -const vector& AITAC_GetAllResourceNodes() +const vector AITAC_GetAllResourceNodes() { - return ResourceNodes; + vector Results; + + for (auto it = ResourceNodes.begin(); it != ResourceNodes.end(); it++) + { + Results.push_back(&(*it)); + } + + return Results; } -const vector& AITAC_GetAllHives() +const vector AITAC_GetAllHives() { - return Hives; + vector Results; + + for (auto it = Hives.begin(); it != Hives.end(); it++) + { + Results.push_back(&(*it)); + } + + return Results; } bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius) diff --git a/main/source/mod/AIPlayers/AvHAITactical.h b/main/source/mod/AIPlayers/AvHAITactical.h index 0b892c84..f2ed712d 100644 --- a/main/source/mod/AIPlayers/AvHAITactical.h +++ b/main/source/mod/AIPlayers/AvHAITactical.h @@ -145,8 +145,8 @@ edict_t* AITAC_GetMarineEligibleToBuildSiege(AvHTeamNumber Team, const AvHAIHive vector AITAC_GetAllPlayersOnTeam(AvHTeamNumber Team); edict_t* AITAC_GetNearestHiddenPlayerInLocation(AvHTeamNumber Team, const Vector Location, const float MaxRadius); -const vector& AITAC_GetAllResourceNodes(); -const vector& AITAC_GetAllHives(); +const vector AITAC_GetAllResourceNodes(); +const vector AITAC_GetAllHives(); bool AITAC_AnyPlayerOnTeamWithLOS(AvHTeamNumber Team, const Vector& Location, float SearchRadius); diff --git a/main/source/mod/AvHPlayer.h b/main/source/mod/AvHPlayer.h index 644fbd42..2c9323be 100644 --- a/main/source/mod/AvHPlayer.h +++ b/main/source/mod/AvHPlayer.h @@ -487,6 +487,8 @@ public: bool GetHasSeenATeam(); + bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const; + void GiveOrderToSelection(AvHOrder& inOrder); private: void AcquireOverwatchTarget(); @@ -505,7 +507,7 @@ private: bool QueryEnemySighted(CBaseEntity* inEntity); bool GetHasActiveAlienWeaponWithImpulse(AvHMessageID inMessageID) const; bool GetRandomGameStartedTick(float inApproximateFrameRate); - bool GetPurchaseAllowed(AvHMessageID inUpgrade, int& outCost, string* outErrorMessage = NULL) const; + int GetRelevantWeight(void) const; int GetRelevantWeightForWeapon(AvHBasePlayerWeapon* inWeapon) const; void GetSpeeds(int& outBaseSpeed, int& outUnemcumberedSpeed) const;