New base building setup

* Improved base strategy and planning
* Bot is better at taking over from a human - less likely to sell up stuff unnecessarily
This commit is contained in:
RGreenlees 2024-06-12 20:21:55 +01:00 committed by pierow
parent 0897cf15a0
commit 6f263d400b
7 changed files with 2105 additions and 899 deletions

File diff suppressed because it is too large Load diff

View file

@ -27,15 +27,17 @@ bool AICOMM_IssueSiegeHiveOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvH
bool AICOMM_IssueSecureResNodeOrder(AvHAIPlayer* pBot, edict_t* Recipient, const AvHAIResourceNode* ResNode);
void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
void AICOMM_AssignNewPlayerOrder(AvHAIPlayer* pBot, edict_t* Assignee, Vector OrderLocation, AvHAIOrderPurpose OrderPurpose);
int AICOMM_GetNumPlayersAssignedToOrder(AvHAIPlayer* pBot, edict_t* TargetEntity, AvHAIOrderPurpose OrderPurpose);
int AICOMM_GetNumPlayersAssignedToOrderType(AvHAIPlayer* pBot, AvHAIOrderPurpose OrderPurpose);
int AICOMM_GetNumPlayersAssignedToOrderLocation(AvHAIPlayer* pBot, Vector OrderLocation, AvHAIOrderPurpose OrderPurpose);
bool AICOMM_IsOrderStillValid(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_UpdatePlayerOrders(AvHAIPlayer* pBot);
edict_t* AICOMM_GetPlayerWithNoOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation);
edict_t* AICOMM_GetPlayerWithoutSpecificOrderNearestLocation(AvHAIPlayer* pBot, Vector SearchLocation, AvHAIOrderPurpose OrderPurpose);
bool AICOMM_DoesPlayerOrderNeedReminder(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_IssueOrderForAssignedJob(AvHAIPlayer* pBot, ai_commander_order* Order);
void AICOMM_ClearAction(commander_action* Action);
bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupportAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot);
@ -43,10 +45,7 @@ bool AICOMM_CheckForNextResearchAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextSupplyAction(AvHAIPlayer* pBot);
bool AICOMM_CheckForNextRelocationAction(AvHAIPlayer* pBot);
void AICOMM_SetDropHealthAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDropAmmoAction(AvHAIPlayer* pBot, commander_action* Action, edict_t* Recipient);
void AICOMM_SetDeployStructureAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableStructureType StructureToBuild, const Vector Location, bool bIsUrgent);
void AICOMM_SetDeployItemAction(AvHAIPlayer* pBot, commander_action* Action, AvHAIDeployableItemType ItemToBuild, const Vector Location, bool bIsUrgent);
Vector AICOMM_GetNextScanLocation(AvHAIPlayer* pBot);
void AICOMM_CommanderThink(AvHAIPlayer* pBot);
@ -76,11 +75,26 @@ bool AICOMM_ShouldCommanderRelocate(AvHAIPlayer* pBot);
bool AICOMM_GetRelocationMessage(Vector RelocationPoint, char* MessageBuffer);
void AICOMM_AddNewBase(AvHAIPlayer* pBot, Vector NewBaseLocation, MarineBaseType NewBaseType);
AvHAIMarineBase* AICOMM_AddNewBase(AvHAIPlayer* pBot, Vector NewBaseLocation, MarineBaseType NewBaseType);
bool AICOMM_AddStructureToBase(AvHAIPlayer* pBot, AvHAIDeployableStructureType StructureToDeploy, Vector BuildLocation, AvHAIMarineBase* BaseToAdd);
void AICOMM_ManageActiveBases(AvHAIPlayer* pBot);
bool AICOMM_IsMarineBaseValid(AvHAIMarineBase* Base);
void AICOMM_DeployBases(AvHAIPlayer* pBot);
vector<AvHAIBuildableStructure> AICOMM_GetBaseStructures(AvHAIMarineBase* Base);
void AICOMM_UpdateBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base);
void AICOMM_UpdateSiegeBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base);
void AICOMM_UpdateOutpostStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base);
void AICOMM_UpdateGuardpostStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base);
void AICOMM_UpdateMainBaseStatus(AvHAIPlayer* pBot, AvHAIMarineBase* Base);
bool AICOMM_BuildOutBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut);
bool AICOMM_BuildOutMainBase(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut);
bool AICOMM_BuildOutOutpost(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut);
bool AICOMM_BuildOutSiege(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut);
bool AICOMM_BuildOutGuardPost(AvHAIPlayer* pBot, AvHAIMarineBase* BaseToBuildOut);
AvHAIMarineBase* AICOMM_GetNearestBaseToLocation(AvHAIPlayer* pBot, Vector SearchLocation);
#endif // AVH_AI_COMMANDER_H

View file

@ -274,7 +274,7 @@ typedef struct _HIVE_DEFINITION_T
AvHTeamNumber OwningTeam = TEAM_IND; // Which team owns this hive currently (TEAM_IND if empty)
unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; // Who on team A can reach this node?
unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; // Who on team B can reach this node?
char HiveName[64];
char HiveName[64] = {'\0'};
} AvHAIHiveDefinition;
// A nav profile combines a nav mesh reference (indexed into NavMeshes) and filters to determine how a bot should find paths
@ -479,7 +479,8 @@ enum MarineBaseType
{
MARINE_BASE_MAINBASE, // The main marine base, where the CC, infantry portals and stuff like arms labs go
MARINE_BASE_OUTPOST, // A permanent outpost designed to control an area of the map, but not the main marine base
MARINE_BASE_SIEGE // A siege base designed to take down an enemy base
MARINE_BASE_SIEGE, // A siege base designed to take down an enemy base
MARINE_BASE_GUARDPOST // A cut-down version of an outpost with just sentry turrets and an observatory
};
typedef struct _AI_MARINE_BASE
@ -488,8 +489,13 @@ typedef struct _AI_MARINE_BASE
MarineBaseType BaseType = MARINE_BASE_OUTPOST; // The purpose of the base. Determines what structures the commander will place
Vector BaseLocation = ZERO_VECTOR; // Where the base should be located. The base will be grown around this location
vector<int> PlacedStructures; // Which structures are part of this base.
int NumBuilders = 0; // How many potential builders are there, able to construct stuff?
int NumEnemies = 0; // How many enemies are in and around the base?
bool bRecycleBase = false; // Should the commander pack up and remove this base?
bool bIsActive = true; // Should the commander actively build and maintain this base?
bool bBaseInitialised = false; // Has the commander started building this base? Will be true once a structure has been placed
bool bCanBeBuiltOut = false; // Can this base be built out currently?
bool bIsBaseEstablished = false; // Have enough key structures been placed to consider this "established", even if it's not finished yet?
} AvHAIMarineBase;
// Bot path node. A path will be several of these strung together to lead the bot to its destination
@ -658,61 +664,16 @@ typedef struct _NAV_STATUS
AvHAIPlayerMoveTask MovementTask;
} nav_status;
// Type of goal the commander wants to achieve
typedef enum _COMMANDERACTIONTYPE
{
ACTION_NONE = 0,
ACTION_UPGRADE,
ACTION_RESEARCH,
ACTION_RECYCLE,
ACTION_GIVEORDER,
ACTION_DEPLOY // Deploy a structure or item into the map
} CommanderActionType;
// Some commander actions are multi-step (e.g. click to select building, release to complete selection, input recycle command etc). Tracks where the commander is in the process
typedef enum _COMMANDERACTIONSTEP
{
ACTION_STEP_NONE = 0,
ACTION_STEP_BEGIN_SELECT, // Click mouse button down to start select
ACTION_STEP_END_SELECT, // Release mouse button to complete select
} CommanderActionStep;
// Used by the AI commander instead of bot_task. Has data specifically to handle commander-specific stuff
typedef struct _COMMANDER_ACTION
{
bool bIsActive = false;
CommanderActionType ActionType = ACTION_NONE; // What action to perform (e.g. build, recycle, drop item etc)
CommanderActionStep ActionStep = ACTION_STEP_NONE; // Used for multi-stage processes such as selecting a building, issuing recycle command etc.
AvHAIDeployableStructureType StructureToBuild = STRUCTURE_NONE; // What structure to build if build action
AvHAIDeployableItemType ItemToPlace = DEPLOYABLE_ITEM_NONE;
int NumInstances = 0;
int NumDesiredInstances = 0;
StructurePurpose ActionPurpose = STRUCTURE_PURPOSE_NONE;
Vector BuildLocation = g_vecZero; // Where to build the structure
Vector DesiredCommanderLocation = g_vecZero; // To perform this action, where does the commander's view need to be? For building, usually directly above location, but could be off to side if obstructed by geometry
Vector LastAttemptedCommanderLocation = g_vecZero; // The position of the commander's view at the last action attempt
Vector LastAttemptedCommanderAngle = g_vecZero; // The click angle of the last action attempt
int AssignedPlayer = 0; // Which player index is assigned to perform the action (e.g. build structure)? Will send orders to that player (move here, build this structure etc.)
edict_t* StructureOrItem = nullptr; // Reference the structure edict. If a structure has been successfully placed but not yet fully built, it will be referenced here
edict_t* ActionTarget = nullptr; // Mostly used for dropping health packs and ammo for players where the drop location might be moving around
bool bHasAttemptedAction = false; // Has the commander tried placing a structure or item at the build location? If so, and it didn't appear, will try to adjust view around until it works
float StructureBuildAttemptTime = 0.0f; // When the commander tried placing a structure. Commander will wait a short while to confirm if the building appeared or if it should try again
int NumActionAttempts = 0; // Commander will give up after a certain number of attempts to place structure/item
AvHMessageID ResearchId = MESSAGE_NULL; // What research to perform if research action
bool bIsAwaitingBuildLink = false; // The AI has tried placing a structure or item and is waiting to confirm it worked or not
bool bIsActionUrgent = false;
} commander_action;
typedef enum
{
ORDERPURPOSE_NONE,
ORDERPURPOSE_SECURE_HIVE,
ORDERPURPOSE_SIEGE_HIVE,
ORDERPURPOSE_SECURE_RESNODE
ORDERPURPOSE_SECURE_RESNODE,
ORDERPURPOSE_BUILD_MAINBASE,
ORDERPURPOSE_BUILD_SIEGE,
ORDERPURPOSE_BUILD_OUTPOST,
ORDERPURPOSE_BUILD_GUARDPOST
} AvHAIOrderPurpose;
typedef struct _AI_COMMANDER_ORDER

View file

@ -1897,7 +1897,7 @@ void CustomThink(AvHAIPlayer* pBot)
for (auto it = pBot->Bases.begin(); it != pBot->Bases.end(); it++)
{
if (AITAC_CanBuildOutBase(&(*it)))
if (it->bCanBeBuiltOut)
{
if (AICOMM_BuildOutBase(pBot, &(*it))) { return; }
}
@ -5718,6 +5718,8 @@ void AIPlayerDMThink(AvHAIPlayer* pBot)
void AIPlayerThink(AvHAIPlayer* pBot)
{
StartNewBotFrame(pBot);
#ifdef BOTDEBUG
for (int i = 0; i < gpGlobals->maxClients; i++)
{
@ -5752,9 +5754,7 @@ void AIPlayerThink(AvHAIPlayer* pBot)
ClearBotInputs(pBot);
pBot->bIsInactive = true;
return;
}
StartNewBotFrame(pBot);
}
if (avh_botdebugmode.value == 1)
{
@ -7477,19 +7477,19 @@ void AIPlayerSetWantsAndNeedsAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_DEFENCE) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_DEFENCE))
{
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_DEFENCE);
pBot->Impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_DEFENCE);
return;
}
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_MOVEMENT) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_MOVEMENT))
{
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_MOVEMENT);
pBot->Impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_MOVEMENT);
return;
}
if (!PlayerHasAlienUpgradeOfType(pBot->Edict, HIVE_TECH_SENSORY) && AITAC_IsAlienUpgradeAvailableForTeam(pBot->Player->GetTeam(), HIVE_TECH_SENSORY))
{
pBot->Edict->v.impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_SENSORY);
pBot->Impulse = AlienGetDesiredUpgrade(pBot, HIVE_TECH_SENSORY);
return;
}
}

View file

@ -6040,7 +6040,7 @@ void AITAC_AddNewBase(AvHTeamNumber Team, Vector NewBaseLocation, MarineBaseType
bool AITAC_CanBuildOutBase(const AvHAIMarineBase* Base)
{
if (!Base) { return false; }
if (!Base || Base->bRecycleBase || !Base->bIsActive) { return false; }
switch (Base->BaseType)
{
@ -6050,6 +6050,8 @@ bool AITAC_CanBuildOutBase(const AvHAIMarineBase* Base)
return AITAC_CanBuildOutOutpost(Base);
case MARINE_BASE_SIEGE:
return AITAC_CanBuildOutSiege(Base);
case MARINE_BASE_GUARDPOST:
return AITAC_CanBuildOutGuardPost(Base);
}
return false;
@ -6125,7 +6127,7 @@ bool AITAC_CanBuildOutMainBase(const AvHAIMarineBase* Base)
|| (!bHasArmsLab && bArmouryCompleted)
|| (!bHasProtoLab && bHasAdvArmoury && bArmsLabCompleted)
|| (!bHasObs && bArmouryCompleted)
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_PHASE_GATE))
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_RESEARCH_PHASETECH))
|| !bHasTF
|| NumTurrets < 5);
@ -6171,7 +6173,7 @@ bool AITAC_CanBuildOutOutpost(const AvHAIMarineBase* Base)
return (!bHasArmoury
|| !bHasObs
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_PHASE_GATE))
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_RESEARCH_PHASETECH))
|| !bHasTF
|| NumTurrets < 5);
@ -6216,12 +6218,46 @@ bool AITAC_CanBuildOutSiege(const AvHAIMarineBase* Base)
return (!bHasArmoury
|| !bHasObs
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_PHASE_GATE))
|| (!bHasPhase && AITAC_ResearchIsComplete(Base->BaseTeam, TECH_RESEARCH_PHASETECH))
|| !bHasTF
|| NumTurrets < 3);
}
bool AITAC_CanBuildOutGuardPost(const AvHAIMarineBase* Base)
{
bool bHasObs = false;
bool bHasTF = false;
int NumTurrets = 0;
std::unordered_map<int, AvHAIBuildableStructure>& BuildingMap = (Base->BaseTeam == AIMGR_GetTeamANumber()) ? TeamAStructureMap : TeamBStructureMap;
for (auto it = Base->PlacedStructures.begin(); it != Base->PlacedStructures.end(); it++)
{
AvHAIBuildableStructure StructureRef = BuildingMap[*it];
switch (StructureRef.StructureType)
{
case STRUCTURE_MARINE_OBSERVATORY:
bHasObs = true;
break;
case STRUCTURE_MARINE_TURRETFACTORY:
case STRUCTURE_MARINE_ADVTURRETFACTORY:
bHasTF = true;
break;
case STRUCTURE_MARINE_TURRET:
NumTurrets++;
break;
default:
break;
}
}
return (!bHasObs
|| !bHasTF
|| NumTurrets < 5);
}
vector<AvHAIMarineBase>& AITAC_GetTeamBases(AvHTeamNumber Team)
{
return (Team == AIMGR_GetTeamANumber()) ? ActiveTeamABases : ActiveTeamBBases;

View file

@ -229,6 +229,7 @@ bool AITAC_CanBuildOutBase(const AvHAIMarineBase* Base);
bool AITAC_CanBuildOutMainBase(const AvHAIMarineBase* Base);
bool AITAC_CanBuildOutOutpost(const AvHAIMarineBase* Base);
bool AITAC_CanBuildOutSiege(const AvHAIMarineBase* Base);
bool AITAC_CanBuildOutGuardPost(const AvHAIMarineBase* Base);
vector<AvHAIMarineBase>& AITAC_GetTeamBases(AvHTeamNumber Team);

View file

@ -1351,7 +1351,11 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
if (FNullEnt(Task->TaskTarget)) { return; }
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return; }
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING)
{
pBot->Impulse = 0;
return;
}
// We had a go, whether it succeeded or not we should try a new location
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_FAILED || Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_SUCCESS)
@ -1361,7 +1365,7 @@ void BotProgressReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
Task->ActiveBuildInfo.BuildStatus = BUILD_ATTEMPT_NONE;
}
if (gpGlobals->time - Task->TaskStartedTime < 1.0f) { return; }
if (gpGlobals->time - Task->TaskStartedTime < 0.5f) { return; }
AvHTeamNumber BotTeam = pBot->Player->GetTeam();
@ -2160,6 +2164,7 @@ void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
// We tried and failed to place the structure
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING)
{
pBot->Impulse = 0;
return;
}
@ -2173,6 +2178,8 @@ void AlienProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
edict_t* LinkedEdict = Task->ActiveBuildInfo.LinkedStructure->edict;
Task->TaskTarget = LinkedEdict;
if (UTIL_StructureIsFullyBuilt(LinkedEdict)) { return; }
if (IsPlayerInUseRange(pBot->Edict, LinkedEdict))
@ -2240,7 +2247,11 @@ void BotAlienPlaceChamber(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, AvHAIDeploya
{
if (vIsZero(Task->TaskLocation) || DesiredStructure == STRUCTURE_NONE) { return; }
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING) { return; }
if (Task->ActiveBuildInfo.BuildStatus == BUILD_ATTEMPT_PENDING)
{
pBot->Impulse = 0;
return;
}
float DistFromBuildLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskLocation);