diff --git a/main/source/mod/AvHAICommander.cpp b/main/source/mod/AvHAICommander.cpp index 67d6b10b..aa9f63de 100644 --- a/main/source/mod/AvHAICommander.cpp +++ b/main/source/mod/AvHAICommander.cpp @@ -988,6 +988,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) StructureFilter.ReachabilityFlags = AI_REACHABILITY_MARINE; StructureFilter.ReachabilityTeam = TeamNumber; StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; edict_t* BaseBuilder = AITAC_GetNearestHiddenPlayerInLocation(TeamNumber, CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); @@ -1002,6 +1003,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) } StructureFilter.DeployableTypes = (STRUCTURE_MARINE_ARMOURY | STRUCTURE_MARINE_ADVARMOURY); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; AvHAIBuildableStructure BaseArmoury = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &StructureFilter); @@ -1015,7 +1017,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1035,7 +1037,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1050,7 +1052,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMOURY, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1079,7 +1081,8 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (AITAC_PhaseGatesAvailable(TeamNumber)) { StructureFilter.DeployableTypes = STRUCTURE_MARINE_PHASEGATE; - StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; bool bPhaseNearBase = AITAC_DeployableExistsAtLocation(CommChair->v.origin, &StructureFilter); @@ -1094,7 +1097,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1106,7 +1109,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) { bool bEnemyInBase = AITAC_AnyPlayerOnTeamWithLOS(AIMGR_GetEnemyTeam(TeamNumber), BuildLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_MetresToGoldSrcUnits(10.0f)); - bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation); + bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PHASEGATE, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kPhaseGateCost) + 10) { return true; } } } @@ -1133,8 +1136,84 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (AICOMM_ShouldCommanderPrioritiseNodes(pBot) && pBot->Player->GetResources() < 30) { return false; } + StructureFilter.DeployableTypes = (STRUCTURE_MARINE_TURRETFACTORY | STRUCTURE_MARINE_ADVTURRETFACTORY); + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; + + AvHAIBuildableStructure TF = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &StructureFilter); + + if (!TF.IsValid()) + { + Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_TURRETFACTORY, CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); + + if (!vIsZero(BuildLocation)) + { + bool bEnemyInBase = AITAC_AnyPlayerOnTeamWithLOS(AIMGR_GetEnemyTeam(TeamNumber), BuildLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_MetresToGoldSrcUnits(10.0f)); + + if (!bEnemyInBase) + { + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_BASE); + + if (bSuccess) { return true; } + } + } + + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), CommChair->v.origin, UTIL_MetresToGoldSrcUnits(10.0f)); + + if (!vIsZero(BuildLocation)) + { + bool bEnemyInBase = AITAC_AnyPlayerOnTeamWithLOS(AIMGR_GetEnemyTeam(TeamNumber), BuildLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_MetresToGoldSrcUnits(10.0f)); + + bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRETFACTORY, BuildLocation, STRUCTURE_PURPOSE_BASE); + + if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kTurretFactoryCost) + 5) { return true; } + + } + } + + if (TF.IsValid() && (TF.StructureStatusFlags & STRUCTURE_STATUS_COMPLETED)) + { + StructureFilter.DeployableTypes = (STRUCTURE_MARINE_TURRET); + StructureFilter.MaxSearchRadius = BALANCE_VAR(kTurretFactoryBuildDistance); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; + + int NumTurrets = AITAC_GetNumDeployablesNearLocation(TF.Location, &StructureFilter); + + if (NumTurrets < 5) + { + Vector BuildLocation = UTIL_GetRandomPointOnNavmeshInRadiusIgnoreReachability(GetBaseNavProfile(STRUCTURE_BASE_NAV_PROFILE), TF.Location, (BALANCE_VAR(kTurretFactoryBuildDistance) * 0.8f)); + + if (!vIsZero(BuildLocation)) + { + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_BASE); + + if (bSuccess) { return true; } + } + + BuildLocation = UTIL_GetRandomPointOnNavmeshInRadius(GetBaseNavProfile(MARINE_BASE_NAV_PROFILE), TF.Location, (BALANCE_VAR(kTurretFactoryBuildDistance) * 0.8f)); + + if (!vIsZero(BuildLocation)) + { + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_TURRET, BuildLocation, STRUCTURE_PURPOSE_BASE); + + if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kSentryCost) + 5) { return true; } + } + + } + else + { + if (!(TF.StructureStatusFlags & STRUCTURE_STATUS_ELECTRIFIED) && pBot->Player->GetResources() > 50) + { + bool bSuccess = AICOMM_ResearchTech(pBot, &TF, RESEARCH_ELECTRICAL); + + if (bSuccess) { return true; } + } + } + } + StructureFilter.DeployableTypes = STRUCTURE_MARINE_ARMSLAB; StructureFilter.MaxSearchRadius = 0.0f; + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; bool bHasArmsLab = AITAC_DeployableExistsAtLocation(CommChair->v.origin, &StructureFilter); @@ -1148,7 +1227,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1160,20 +1239,20 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) { bool bEnemyInBase = AITAC_AnyPlayerOnTeamWithLOS(AIMGR_GetEnemyTeam(TeamNumber), BuildLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_MetresToGoldSrcUnits(10.0f)); - bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation); + bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_ARMSLAB, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kArmsLabCost) + 10) { return true; } } } StructureFilter.DeployableTypes = STRUCTURE_MARINE_OBSERVATORY; + StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; bool bHasObservatory = AITAC_DeployableExistsAtLocation(CommChair->v.origin, &StructureFilter); if (!bHasObservatory && !FNullEnt(BaseBuilder)) { - - Vector BuildLocation = AITAC_GetRandomBuildHintInLocation(STRUCTURE_MARINE_OBSERVATORY, CommChair->v.origin, UTIL_MetresToGoldSrcUnits(15.0f)); if (!vIsZero(BuildLocation)) @@ -1182,7 +1261,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1194,7 +1273,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) { bool bEnemyInBase = AITAC_AnyPlayerOnTeamWithLOS(AIMGR_GetEnemyTeam(TeamNumber), BuildLocation + Vector(0.0f, 0.0f, 32.0f), UTIL_MetresToGoldSrcUnits(10.0f)); - bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation); + bool bSuccess = !bEnemyInBase && AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_OBSERVATORY, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kObservatoryCost) + 10) { return true; } @@ -1214,7 +1293,9 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (bSuccess || pBot->Player->GetResources() <= BALANCE_VAR(kTurretFactoryCost) + 5) { return true; } } + StructureFilter.MaxSearchRadius = 0.0f; StructureFilter.DeployableTypes = STRUCTURE_MARINE_ADVARMOURY; + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; bool bHasAdvArmoury = AITAC_DeployableExistsAtLocation(CommChair->v.origin, &StructureFilter); bool bIsResearchingArmoury = false; @@ -1225,7 +1306,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_RESEARCHING; - + AvHAIBuildableStructure NearestArmoury = AITAC_FindClosestDeployableToLocation(CommChair->v.origin, &StructureFilter); if (NearestArmoury.IsValid()) @@ -1246,6 +1327,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) StructureFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(20.0f); StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_NONE; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_BASE; bool bHasPrototypeLab = AITAC_DeployableExistsAtLocation(CommChair->v.origin, &StructureFilter); @@ -1259,7 +1341,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1273,7 +1355,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -1287,7 +1369,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) if (!bEnemyInBase) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_PROTOTYPELAB, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess || pBot->Player->GetResources() < BALANCE_VAR(kPrototypeLabCost) + 20) { return true; } } @@ -1302,6 +1384,7 @@ bool AICOMM_CheckForNextBuildAction(AvHAIPlayer* pBot) StructureFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; StructureFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING | STRUCTURE_STATUS_ELECTRIFIED; StructureFilter.MaxSearchRadius = 0.0f; + StructureFilter.PurposeFlags = STRUCTURE_PURPOSE_ANY; AvHAIBuildableStructure ResTower = AITAC_FindFurthestDeployableFromLocation(CommChair->v.origin, &StructureFilter); @@ -2238,7 +2321,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair) if (!vIsZero(BuildLocation)) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -2272,7 +2355,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair) if (!vIsZero(BuildLocation)) { - bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); + bool bSuccess = AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE); if (bSuccess) { return true; } } @@ -2284,7 +2367,7 @@ bool AICOMM_BuildInfantryPortal(AvHAIPlayer* pBot, edict_t* CommChair) if (vIsZero(BuildLocation)) { return false; } - return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation); + return AICOMM_DeployStructure(pBot, STRUCTURE_MARINE_INFANTRYPORTAL, BuildLocation, STRUCTURE_PURPOSE_BASE); } bool AICOMM_CheckForNextRecycleAction(AvHAIPlayer* pBot) diff --git a/main/source/mod/AvHAICommander.h b/main/source/mod/AvHAICommander.h index c95216b5..efa22f1b 100644 --- a/main/source/mod/AvHAICommander.h +++ b/main/source/mod/AvHAICommander.h @@ -14,7 +14,7 @@ static const float MIN_COMMANDER_REMIND_TIME = 20.0f; // How frequently the commander can nag a player to do something, if they don't think they're doing it -bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location, StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE); +bool AICOMM_DeployStructure(AvHAIPlayer* pBot, const AvHAIDeployableStructureType StructureToDeploy, const Vector Location, StructurePurpose Purpose = STRUCTURE_PURPOSE_GENERAL); bool AICOMM_DeployItem(AvHAIPlayer* pBot, const AvHAIDeployableItemType ItemToDeploy, const Vector Location); bool AICOMM_UpgradeStructure(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToUpgrade); bool AICOMM_ResearchTech(AvHAIPlayer* pBot, AvHAIBuildableStructure* StructureToResearch, AvHMessageID Research); diff --git a/main/source/mod/AvHAIConstants.h b/main/source/mod/AvHAIConstants.h index 1eadbab1..34629fe2 100644 --- a/main/source/mod/AvHAIConstants.h +++ b/main/source/mod/AvHAIConstants.h @@ -169,8 +169,11 @@ typedef enum typedef enum _STRUCTUREPURPOSE { STRUCTURE_PURPOSE_NONE = 0, - STRUCTURE_PURPOSE_SIEGE, - STRUCTURE_PURPOSE_FORTIFY + STRUCTURE_PURPOSE_GENERAL = 1u, + STRUCTURE_PURPOSE_SIEGE = 1u << 1, + STRUCTURE_PURPOSE_FORTIFY = 1u << 2, + STRUCTURE_PURPOSE_BASE = 1u << 3, + STRUCTURE_PURPOSE_ANY = -1 } StructurePurpose; @@ -308,6 +311,7 @@ typedef struct _DEPLOYABLE_SEARCH_FILTER bool bConsiderPhaseDistance = false; AvHTeamNumber DeployableTeam = TEAM_IND; AvHTeamNumber ReachabilityTeam = TEAM_IND; + unsigned int PurposeFlags = STRUCTURE_PURPOSE_ANY; } DeployableSearchFilter; // Pending message a bot wants to say. Allows for a delay in sending a message to simulate typing, or prevent too many messages on the same frame @@ -350,7 +354,7 @@ typedef struct _AVH_AI_BUILDABLE_STRUCTURE 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 - StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE; + StructurePurpose Purpose = STRUCTURE_PURPOSE_GENERAL; bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; } @@ -489,7 +493,7 @@ typedef struct _ENEMY_STATUS float LastDetectedTime = 0.0f; // When the bot last saw the enemy or they pinged on motion tracking float InitialAwarenessTime = 0.0f; // When the bot first became aware of the enemy float LastVisibleTime = 0.0f; // Last time the bot actually saw the enemy - float EnemyThreatLevel = 0.0f; + float EnemyThreatLevel = 0.0f; // Generally, >=3.0 means actively fighting them, >=2.0 means visible and close, >=1.0 means not visible but close and <1.0 means they can be heard but not close bool bHasLOS = false; // Does the bot has LOS of the enemy? bool bEnemyHasLOS = false; diff --git a/main/source/mod/AvHAIHelper.cpp b/main/source/mod/AvHAIHelper.cpp index bc3da404..0c7ab75c 100644 --- a/main/source/mod/AvHAIHelper.cpp +++ b/main/source/mod/AvHAIHelper.cpp @@ -554,7 +554,7 @@ void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned WRITE_BYTE(1); // effect ALPHA WRITE_SHORT(0); // fade-in time in seconds * 256 WRITE_SHORT(0); // fade-out time in seconds * 256 - WRITE_SHORT(5); // hold time in seconds * 256 + WRITE_SHORT(20); // hold time in seconds * 256 WRITE_STRING(string);//string); // send the string MESSAGE_END(); // end @@ -646,4 +646,78 @@ void UTIL_LocalizeText(const char* InputText, string& OutputText) LocalizedLocationsMap[InputText] = OutputText; +} + +char* UTIL_TaskTypeToChar(const BotTaskType TaskType) +{ + switch (TaskType) + { + case TASK_ATTACK: + return "Attack"; + case TASK_BUILD: + return "Build"; + case TASK_CAP_RESNODE: + return "Cap Res Node"; + case TASK_COMMAND: + return "Take Command"; + case TASK_DEFEND: + return "Defend Structure"; + case TASK_EVOLVE: + return "Evolve"; + case TASK_GET_AMMO: + return "Get Ammo Pack"; + case TASK_GET_EQUIPMENT: + return "Get Equipment"; + case TASK_GET_HEALTH: + return "Get Health Pack"; + case TASK_GET_WEAPON: + return "Get Weapon"; + case TASK_GUARD: + return "Guard"; + case TASK_HEAL: + return "Heal Target"; + case TASK_MOVE: + return "Move to Location"; + case TASK_PLACE_MINE: + return "Place Mine"; + case TASK_REINFORCE_STRUCTURE: + return "Reinforce Structure"; + case TASK_RESUPPLY: + return "Resupply"; + case TASK_SECURE_HIVE: + return "Secure Hive"; + case TASK_TOUCH: + return "Touch Trigger"; + case TASK_WELD: + return "Weld Target"; + default: + return "None"; + } + + return "None"; +} + +char* UTIL_BotRoleToChar(const AvHAIBotRole Role) +{ + switch (Role) + { + case BOT_ROLE_ASSAULT: + return "Assault"; + case BOT_ROLE_BOMBARDIER: + return "Bombardier"; + case BOT_ROLE_BUILDER: + return "Builder"; + case BOT_ROLE_COMMAND: + return "Commander"; + case BOT_ROLE_FIND_RESOURCES: + return "Res Capper"; + case BOT_ROLE_HARASS: + return "Harrasser"; + case BOT_ROLE_SWEEPER: + return "Sweeper"; + default: + return "None"; + } + + return "None"; } \ No newline at end of file diff --git a/main/source/mod/AvHAIHelper.h b/main/source/mod/AvHAIHelper.h index 1bc5c55a..a1654393 100644 --- a/main/source/mod/AvHAIHelper.h +++ b/main/source/mod/AvHAIHelper.h @@ -59,4 +59,8 @@ void UTIL_DrawHUDText(edict_t* pEntity, char channel, float x, float y, unsigned void UTIL_ClearLocalizations(); void UTIL_LocalizeText(const char* InputText, string& OutputText); +char* UTIL_TaskTypeToChar(const BotTaskType TaskType); + +char* UTIL_BotRoleToChar(const AvHAIBotRole Role); + #endif \ No newline at end of file diff --git a/main/source/mod/AvHAINavigation.cpp b/main/source/mod/AvHAINavigation.cpp index 31314982..7a81c064 100644 --- a/main/source/mod/AvHAINavigation.cpp +++ b/main/source/mod/AvHAINavigation.cpp @@ -2069,7 +2069,17 @@ dtStatus FindPathClosestToPoint(AvHAIPlayer* pBot, const BotMoveStyle MoveStyle, Vector FromLocation = pBot->CurrentFloorPosition; - Vector FromFloorLocation = AdjustPointForPathfinding(FromLocation); + // Add a slight bias towards trying to move forward if on a railing or other narrow bit of navigable terrain + // rather than potentially dropping back off it the wrong way + Vector GeneralDir = UTIL_GetVectorNormal2D(ToLocation - pBot->CurrentFloorPosition); + Vector CheckLocation = FromLocation + (GeneralDir * 16.0f); + + Vector FromFloorLocation = AdjustPointForPathfinding(CheckLocation); + + if (vIsZero(FromFloorLocation)) + { + FromFloorLocation = AdjustPointForPathfinding(FromLocation); + } nav_door* LiftReference = UTIL_GetLiftReferenceByEdict(pBot->Edict->v.groundentity); bool bMustDisembarkLiftFirst = false; @@ -4852,20 +4862,6 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP pBot->desiredMovementDir = UTIL_GetVectorNormal2D(pBot->desiredMovementDir + (vRight * modifier)); } - // Jump if we're on the floor, to give ourselves a boost and remove that momentary pause while "wall-sticking" mode activates if skulk - if ((pEdict->v.flags & FL_ONGROUND) && !IsPlayerClimbingWall(pBot->Edict)) - { - Vector CurrentVelocity = UTIL_GetVectorNormal2D(pBot->Edict->v.velocity); - - float VelocityDot = UTIL_GetDotProduct2D(vForward, CurrentVelocity); - - if (VelocityDot > 0.7f) - { - // This was causing issues in tight areas, rethink this - //BotJump(pBot); - } - } - // Stop holding crouch if we're a skulk so we can actually climb if (IsPlayerSkulk(pBot->Edict)) { @@ -4899,20 +4895,12 @@ void WallClimbMove(AvHAIPlayer* pBot, const Vector StartPoint, const Vector EndP else { LookLocation = DirectAheadView - Vector(0.0f, 0.0f, 20.0f); - //LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); } } else { - if (ZDiff > 16.0f) - { - ClimbSurfaceNormal = ClimbSurfaceNormal; - LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); - } - else - { - LookLocation = DirectAheadView + Vector(0.0f, 0.0f, 20.0f); - } + ClimbSurfaceNormal = ClimbSurfaceNormal; + LookLocation = pBot->CurrentEyePosition + (ClimbSurfaceNormal * 100.0f); } } @@ -6437,12 +6425,12 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move bool bSucceeded = NAV_GenerateNewBasePath(pBot, Destination, MoveStyle, MaxAcceptableDist); if (!bSucceeded) - { - if (pBot->BotNavInfo.CurrentPath.size() == 0) - { - pBot->BotNavInfo.StuckInfo.bPathFollowFailed = true; + { + pBot->BotNavInfo.StuckInfo.bPathFollowFailed = true; - if (!UTIL_PointIsOnNavmesh(pBot->CollisionHullBottomLocation, pBot->BotNavInfo.NavProfile) && !vIsZero(BotNavInfo->LastNavMeshPosition)) + if (!UTIL_PointIsOnNavmesh(pBot->CollisionHullBottomLocation, pBot->BotNavInfo.NavProfile)) + { + if (!vIsZero(BotNavInfo->LastNavMeshPosition)) { MoveDirectlyTo(pBot, BotNavInfo->LastNavMeshPosition); @@ -6451,7 +6439,7 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move BotNavInfo->LastNavMeshPosition = g_vecZero; } - return true; + return false; } else { @@ -6462,19 +6450,20 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move if (vIsZero(BotNavInfo->UnstuckMoveLocation)) { - BotNavInfo->UnstuckMoveLocation = FindClosestPointBackOnPath(pBot); + BotNavInfo->UnstuckMoveLocation = FindClosestPointBackOnPath(pBot, Destination); } if (!vIsZero(BotNavInfo->UnstuckMoveLocation)) { MoveDirectlyTo(pBot, BotNavInfo->UnstuckMoveLocation); - return true; + return false; } } } else { - BotFollowPath(pBot); + MoveDirectlyTo(pBot, Destination); + return false; } return false; @@ -6486,17 +6475,6 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move if (!bSucceeded) { - if (!FNullEnt(pBot->Edict->v.groundentity)) - { - nav_door* Door = UTIL_GetNavDoorByEdict(pBot->Edict->v.groundentity); - - if (Door) - { - - } - } - - if (!FNullEnt(BotNavInfo->MovementTask.TaskTarget)) { CBaseEntity* TargetEntity = CBaseEntity::Instance(BotNavInfo->MovementTask.TaskTarget); @@ -6587,26 +6565,33 @@ bool MoveTo(AvHAIPlayer* pBot, const Vector Destination, const BotMoveStyle Move } -Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot) +Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot, Vector Destination) { + Vector GeneralMoveDir = UTIL_GetVectorNormal2D(Destination - pBot->Edict->v.origin); + Vector CheckDir = pBot->Edict->v.origin + (GeneralMoveDir * 16.0f); - DeployableSearchFilter ResNodeFilter; - ResNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; - - AvHAIResourceNode* NearestResNode = AITAC_FindNearestResourceNodeToLocation(pBot->Edict->v.origin, &ResNodeFilter); - - Vector ValidNavmeshPoint = AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()); - - if (NearestResNode && vDist2D(pBot->Edict->v.origin, NearestResNode->Location) < vDist2D(pBot->Edict->v.origin, ValidNavmeshPoint)) - { - ValidNavmeshPoint = NearestResNode->Location; - } - - ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(ValidNavmeshPoint, pBot->BotNavInfo.NavProfile); + Vector ValidNavmeshPoint = AdjustPointForPathfinding(CheckDir); if (vIsZero(ValidNavmeshPoint)) { - return g_vecZero; + DeployableSearchFilter ResNodeFilter; + ResNodeFilter.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + + AvHAIResourceNode* NearestResNode = AITAC_FindNearestResourceNodeToLocation(pBot->Edict->v.origin, &ResNodeFilter); + + Vector ValidNavmeshPoint = AITAC_GetTeamStartingLocation(pBot->Player->GetTeam()); + + if (NearestResNode && vDist2D(pBot->Edict->v.origin, NearestResNode->Location) < vDist2D(pBot->Edict->v.origin, ValidNavmeshPoint)) + { + ValidNavmeshPoint = NearestResNode->Location; + } + + ValidNavmeshPoint = UTIL_ProjectPointToNavmesh(ValidNavmeshPoint, pBot->BotNavInfo.NavProfile); + + if (vIsZero(ValidNavmeshPoint)) + { + return g_vecZero; + } } vector BackwardsPath; @@ -6829,9 +6814,6 @@ LerkFlightBehaviour BotFlightClimbMove(AvHAIPlayer* pBot, Vector FromLocation, V pBot->Edict->v.velocity.z = fmaxf(pBot->Edict->v.velocity.z, 10.0f); } - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->Edict->v.origin + UTIL_GetVectorNormal(pBot->Edict->v.velocity) * 100.0f, 255, 0, 0); - - return FLIGHT_FLAP; } diff --git a/main/source/mod/AvHAINavigation.h b/main/source/mod/AvHAINavigation.h index c674bf48..9b462dd3 100644 --- a/main/source/mod/AvHAINavigation.h +++ b/main/source/mod/AvHAINavigation.h @@ -371,7 +371,7 @@ dtStatus FindPathClosestToPoint(const nav_profile& NavProfile, const Vector From dtStatus DEBUG_TestFindPath(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, vector& path, float MaxAcceptableDistance); // If the bot is stuck and off the path or nav mesh, this will try to find a point it can directly move towards to get it back on track -Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot); +Vector FindClosestPointBackOnPath(AvHAIPlayer* pBot, Vector Destination); Vector FindClosestNavigablePointToDestination(const nav_profile& NavProfile, const Vector FromLocation, const Vector ToLocation, float MaxAcceptableDistance); diff --git a/main/source/mod/AvHAIPlayer.cpp b/main/source/mod/AvHAIPlayer.cpp index 1cd9bffd..841e59b3 100644 --- a/main/source/mod/AvHAIPlayer.cpp +++ b/main/source/mod/AvHAIPlayer.cpp @@ -392,9 +392,9 @@ bool BotReloadWeapons(AvHAIPlayer* pBot) if (IsPlayerReloading(pBot->Player)) { pBot->DesiredCombatWeapon = GetPlayerCurrentWeapon(pBot->Player); + return true; } - AvHAIWeapon PrimaryWeapon = UTIL_GetPlayerPrimaryWeapon(pBot->Player); AvHAIWeapon SecondaryWeapon = GetBotMarineSecondaryWeapon(pBot); AvHAIWeapon CurrentWeapon = GetPlayerCurrentWeapon(pBot->Player); @@ -2172,8 +2172,25 @@ void UpdateAIMarinePlayerNSRole(AvHAIPlayer* pBot) int NumSweeperBots = AIMGR_GetNumAIPlayersWithRoleOnTeam(BotTeamNumber, BOT_ROLE_SWEEPER, pBot); + int NumDesiredSweeperBots = 1; + + if (NumSweeperBots < 2) + { + DeployableSearchFilter DefenceStructuresFilter; + DefenceStructuresFilter.DeployableTeam = BotTeamNumber; + DefenceStructuresFilter.DeployableTypes = (STRUCTURE_MARINE_TURRET | STRUCTURE_MARINE_OBSERVATORY); + DefenceStructuresFilter.IncludeStatusFlags = STRUCTURE_STATUS_COMPLETED; + DefenceStructuresFilter.ExcludeStatusFlags = STRUCTURE_STATUS_RECYCLING; + DefenceStructuresFilter.MaxSearchRadius = UTIL_MetresToGoldSrcUnits(15.0f); + + if (!AITAC_DeployableExistsAtLocation(AITAC_GetTeamStartingLocation(BotTeamNumber), &DefenceStructuresFilter)) + { + NumDesiredSweeperBots = 2; + } + } + // Always have a sweeper - if (NumSweeperBots < 1) + if (NumSweeperBots < NumDesiredSweeperBots) { SetNewAIPlayerRole(pBot, BOT_ROLE_SWEEPER); return; @@ -2208,18 +2225,18 @@ void AIPlayerNSThink(AvHAIPlayer* pBot) { AvHTeam* BotTeam = GetGameRules()->GetTeam(pBot->Player->GetTeam()); - if (!BotTeam) { return; } +if (!BotTeam) { return; } - pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot); +pBot->CurrentEnemy = BotGetNextEnemyTarget(pBot); - if (BotTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) - { - AIPlayerNSMarineThink(pBot); - } - else - { - AIPlayerNSAlienThink(pBot); - } +if (BotTeam->GetTeamType() == AVH_CLASS_TYPE_MARINE) +{ + AIPlayerNSMarineThink(pBot); +} +else +{ + AIPlayerNSAlienThink(pBot); +} } int BotGetNextEnemyTarget(AvHAIPlayer* pBot) @@ -2266,18 +2283,18 @@ AvHAICombatStrategy GetAlienCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_sta switch (PlayerUser3) { - case AVH_USER3_ALIEN_PLAYER1: - return GetSkulkCombatStrategyForTarget(pBot, CurrentEnemy); - case AVH_USER3_ALIEN_PLAYER2: - return GetGorgeCombatStrategyForTarget(pBot, CurrentEnemy); - case AVH_USER3_ALIEN_PLAYER3: - return GetLerkCombatStrategyForTarget(pBot, CurrentEnemy); - case AVH_USER3_ALIEN_PLAYER4: - return GetFadeCombatStrategyForTarget(pBot, CurrentEnemy); - case AVH_USER3_ALIEN_PLAYER5: - return GetOnosCombatStrategyForTarget(pBot, CurrentEnemy); - default: - return COMBAT_STRATEGY_IGNORE; + case AVH_USER3_ALIEN_PLAYER1: + return GetSkulkCombatStrategyForTarget(pBot, CurrentEnemy); + case AVH_USER3_ALIEN_PLAYER2: + return GetGorgeCombatStrategyForTarget(pBot, CurrentEnemy); + case AVH_USER3_ALIEN_PLAYER3: + return GetLerkCombatStrategyForTarget(pBot, CurrentEnemy); + case AVH_USER3_ALIEN_PLAYER4: + return GetFadeCombatStrategyForTarget(pBot, CurrentEnemy); + case AVH_USER3_ALIEN_PLAYER5: + return GetOnosCombatStrategyForTarget(pBot, CurrentEnemy); + default: + return COMBAT_STRATEGY_IGNORE; } } @@ -2305,7 +2322,15 @@ AvHAICombatStrategy GetSkulkCombatStrategyForTarget(AvHAIPlayer* pBot, enemy_sta return COMBAT_STRATEGY_ATTACK; } - if (CurrentEnemy->EnemyThreatLevel < 2.0f && (gpGlobals->time - CurrentEnemy->LastVisibleTime > 5.0f)) + if (pBot->CurrentTask->TaskType == TASK_EVOLVE) + { + if (CurrentEnemy->EnemyThreatLevel < 3.0f || !CurrentEnemy->bHasLOS) + { + return COMBAT_STRATEGY_IGNORE; + } + } + + if (CurrentEnemy->EnemyThreatLevel < 2.0f && gpGlobals->time - CurrentEnemy->LastVisibleTime > 5.0f) { return COMBAT_STRATEGY_IGNORE; } @@ -3698,10 +3723,43 @@ void AIPlayerSetMarineAssaultPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Tas { DeployableSearchFilter AnyEnemyStuff; AnyEnemyStuff.DeployableTeam = EnemyTeam; - AnyEnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES; + AnyEnemyStuff.DeployableTypes = SEARCH_ANY_RES_TOWER; AnyEnemyStuff.ReachabilityTeam = BotTeam; AnyEnemyStuff.ReachabilityFlags = pBot->BotNavInfo.NavProfile.ReachabilityFlag; + AvHAIBuildableStructure EnemyRT = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &AnyEnemyStuff); + + if (EnemyRT.IsValid()) + { + AITASK_SetAttackTask(pBot, Task, EnemyRT.edict, false); + return; + } + + if (AIMGR_GetEnemyTeamType(EnemyTeam) == AVH_CLASS_TYPE_ALIEN) + { + const AvHAIHiveDefinition* ActiveHive = AITAC_GetActiveHiveNearestLocation(AIMGR_GetEnemyTeam(BotTeam), pBot->Edict->v.origin); + + if (ActiveHive) + { + AITASK_SetAttackTask(pBot, Task, ActiveHive->HiveEdict, false); + return; + } + } + else + { + AnyEnemyStuff.DeployableTypes = STRUCTURE_MARINE_COMMCHAIR; + + AvHAIBuildableStructure EnemyCommChair = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &AnyEnemyStuff); + + if (EnemyCommChair.IsValid()) + { + AITASK_SetAttackTask(pBot, Task, EnemyCommChair.edict, false); + return; + } + } + + AnyEnemyStuff.DeployableTypes = SEARCH_ALL_STRUCTURES; + AvHAIBuildableStructure EnemyStructure = AITAC_FindClosestDeployableToLocation(pBot->Edict->v.origin, &AnyEnemyStuff); if (EnemyStructure.IsValid()) @@ -3723,10 +3781,6 @@ void AIPlayerSetMarineBombardierPrimaryTask(AvHAIPlayer* pBot, AvHAIPlayerTask* void AIPlayerSetWantsAndNeedsCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { - if (gpGlobals->time - pBot->LastCombatTime > 5.0f) - { - if (BotReloadWeapons(pBot)) { return; } - } if (Task->TaskType == TASK_RESUPPLY) { return; } @@ -3774,10 +3828,6 @@ void AIPlayerSetWantsAndNeedsCOMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Ta void AIPlayerSetWantsAndNeedsMarineTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) { - if (gpGlobals->time - pBot->LastCombatTime > 5.0f) - { - if (BotReloadWeapons(pBot)) { return; } - } if (Task->TaskType == TASK_RESUPPLY || Task->TaskType == TASK_GET_HEALTH || Task->TaskType == TASK_GET_AMMO) { return; } @@ -5479,25 +5529,8 @@ void AIPlayerThink(AvHAIPlayer* pBot) AIDEBUG_DrawBotPath(pBot); - if (pBot->BotNavInfo.CurrentPath.size() > 0 && pBot->BotNavInfo.CurrentPathPoint < pBot->BotNavInfo.CurrentPath.size()) - { - bot_path_node CurrentPathNode = pBot->BotNavInfo.CurrentPath[pBot->BotNavInfo.CurrentPathPoint]; - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, CurrentPathNode.FromLocation, 255, 0, 0); - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, CurrentPathNode.Location, 0, 128, 0); - } - - if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE) - { - if (!FNullEnt(pBot->CurrentTask->TaskTarget)) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskTarget->v.origin, 255, 0, 0); - } - - if (!vIsZero(pBot->CurrentTask->TaskLocation)) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskLocation, 255, 0, 0); - } - } + DEBUG_PrintTaskInfo(pBot); + //DEBUG_PrintCombatInfo(pBot); } #endif @@ -6987,6 +7020,12 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) return; } + if (pBot->PrimaryBotTask.TaskType == TASK_EVOLVE) + { + AITASK_ClearBotTask(pBot, &pBot->SecondaryBotTask); + return; + } + vector AllHives = AITAC_GetAllTeamHives(BotTeam, false); AvHAIHiveDefinition* HiveToDefend = nullptr; @@ -7105,6 +7144,21 @@ void AIPlayerSetSecondaryAlienTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task) } } + if (pBot->PrimaryBotTask.TaskType == TASK_CAP_RESNODE) + { + float ResourceCost = BALANCE_VAR(kResourceTowerCost); + if (!IsPlayerGorge(pBot->Edict)) + { + ResourceCost += BALANCE_VAR(kGorgeCost); + } + + if (pBot->Player->GetResources() >= ResourceCost) + { + AITASK_ClearBotTask(pBot, &pBot->SecondaryBotTask); + return; + } + } + DeployableSearchFilter AttackedStructuresFilter; AttackedStructuresFilter.DeployableTypes = (IsPlayerLerk(pBot->Edict)) ? SEARCH_ALL_STRUCTURES : STRUCTURE_ALIEN_RESTOWER; AttackedStructuresFilter.DeployableTeam = BotTeam; @@ -7612,6 +7666,8 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) float CurrentHealthPercent = GetPlayerOverallHealthPercent(pBot->Edict); + bool bPlayerCloaked = UTIL_IsCloakedPlayerInvisible(CurrentEnemy, pBot->Player); + if (pBot->CurrentCombatStrategy == COMBAT_STRATEGY_RETREAT) { BotAttackResult AttackResult = PerformAttackLOSCheck(pBot, WEAPON_GORGE_SPIT, CurrentEnemy); @@ -7631,19 +7687,24 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) if (!bInHealingRange) { + pBot->BotNavInfo.bShouldWalk = bPlayerCloaked && TrackedEnemyRef->bEnemyHasLOS; + MoveTo(pBot, UTIL_GetEntityGroundLocation(NearestHealingSource), MOVESTYLE_NORMAL, DesiredDistFromHealingSource); - if (CurrentHealthPercent > 0.5f && AttackResult == ATTACK_SUCCESS) + if (!bPlayerCloaked) { - BotShootTarget(pBot, WEAPON_GORGE_SPIT, CurrentEnemy); - } - else - { - pBot->DesiredCombatWeapon = WEAPON_GORGE_HEALINGSPRAY; - - if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_GORGE_HEALINGSPRAY) + if (CurrentHealthPercent > 0.5f && AttackResult == ATTACK_SUCCESS) { - pBot->Button |= IN_ATTACK; + BotShootTarget(pBot, WEAPON_GORGE_SPIT, CurrentEnemy); + } + else + { + pBot->DesiredCombatWeapon = WEAPON_GORGE_HEALINGSPRAY; + + if (GetPlayerCurrentWeapon(pBot->Player) == WEAPON_GORGE_HEALINGSPRAY) + { + pBot->Button |= IN_ATTACK; + } } } @@ -7664,6 +7725,21 @@ bool GorgeCombatThink(AvHAIPlayer* pBot) return true; } + // If we're invisible and healing, do nothing. Don't give our position away + if (bPlayerCloaked) + { + if (TrackedEnemyRef->bHasLOS) + { + BotLookAt(pBot, TrackedEnemyRef->LastDetectedLocation); + } + else + { + BotLookAt(pBot, TrackedEnemyRef->LastLOSPosition); + } + + return true; + } + if (bOutOfEnemyLOS) { if (bInHealingRange) @@ -8385,6 +8461,66 @@ bool OnosCombatThink(AvHAIPlayer* pBot) return true; } +void DEBUG_PrintTaskInfo(AvHAIPlayer* pBot) +{ + char buf[511]; + char interbuf[164]; + + sprintf(buf, "Task info for %s:\n\n", STRING(pBot->Edict->v.netname)); + + sprintf(interbuf, "Role: %s\n\n", UTIL_BotRoleToChar(pBot->BotRole)); + strcat(buf, interbuf); + + if (IsPlayerMarine(pBot->Edict)) + { + const char* CommanderTask = UTIL_TaskTypeToChar(pBot->CommanderTask.TaskType); + + sprintf(interbuf, "Commander-Issued: %s\n", CommanderTask); + strcat(buf, interbuf); + } + + const char* PrimaryTask = UTIL_TaskTypeToChar(pBot->PrimaryBotTask.TaskType); + + sprintf(interbuf, "Primary: %s\n", PrimaryTask); + strcat(buf, interbuf); + + const char* SecondaryTask = UTIL_TaskTypeToChar(pBot->SecondaryBotTask.TaskType); + + sprintf(interbuf, "Secondary: %s\n", SecondaryTask); + strcat(buf, interbuf); + + const char* WantAndNeedTask = UTIL_TaskTypeToChar(pBot->WantsAndNeedsTask.TaskType); + + sprintf(interbuf, "Want/Need: %s\n\n", WantAndNeedTask); + strcat(buf, interbuf); + + if (pBot->CurrentTask) + { + const char* CurrentTask = UTIL_TaskTypeToChar(pBot->CurrentTask->TaskType); + + sprintf(interbuf, "Current: %s\n\n", CurrentTask); + strcat(buf, interbuf); + } + + if (pBot->CurrentTask && pBot->CurrentTask->TaskType != TASK_NONE) + { + sprintf(interbuf, "Red = Target, Yellow = Location\n"); + strcat(buf, interbuf); + + if (!FNullEnt(pBot->CurrentTask->TaskTarget)) + { + UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskTarget->v.origin, 255, 0, 0); + } + + if (!vIsZero(pBot->CurrentTask->TaskLocation)) + { + UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, pBot->CurrentTask->TaskLocation, 255, 255, 0); + } + } + + UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf); +} + void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot) { char buf[511]; @@ -8396,18 +8532,18 @@ void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot) if (TrackedEnemy < 0) { - sprintf(interbuf, "Biggest threat: NONE\n\n"); + sprintf(interbuf, "Main Threat: NONE\n\n"); } else { - sprintf(interbuf, "Biggest threat: %s\n\n", STRING(pBot->TrackedEnemies[TrackedEnemy].PlayerEdict->v.netname)); + sprintf(interbuf, "Main Threat: %s\n\n", STRING(pBot->TrackedEnemies[TrackedEnemy].PlayerEdict->v.netname)); } strcat(buf, interbuf); if (TrackedEnemy < 0) { - UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf); + UTIL_DrawHUDText(INDEXENT(1), 0, 0.6f, 0.1f, 255, 255, 255, buf); return; } @@ -8434,24 +8570,40 @@ void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot) strcat(buf, interbuf); - sprintf(interbuf, "Threat: %.2f\n", TrackedInfo->EnemyThreatLevel); + sprintf(interbuf, "Threat: %.2f\n\n", TrackedInfo->EnemyThreatLevel); strcat(buf, interbuf); - UTIL_DrawHUDText(INDEXENT(1), 0, 0.1f, 0.1f, 255, 255, 255, buf); + AvHAICombatStrategy CurrentStrategy = GetBotCombatStrategyForTarget(pBot, TrackedInfo); + + switch (CurrentStrategy) + { + case COMBAT_STRATEGY_AMBUSH: + sprintf(interbuf, "Strategy: Ambush\n"); + break; + case COMBAT_STRATEGY_ATTACK: + sprintf(interbuf, "Strategy: Attack\n"); + break; + case COMBAT_STRATEGY_IGNORE: + sprintf(interbuf, "Strategy: Ignore\n"); + break; + case COMBAT_STRATEGY_RETREAT: + sprintf(interbuf, "Strategy: Retreat\n"); + break; + case COMBAT_STRATEGY_SKIRMISH: + sprintf(interbuf, "Strategy: Skirmish\n"); + break; + default: + sprintf(interbuf, "Strategy: None\n"); + break; + } + + strcat(buf, interbuf); + + UTIL_DrawHUDText(INDEXENT(1), 0, 0.6f, 0.1f, 255, 255, 255, buf); if (!vIsZero(TrackedInfo->LastDetectedLocation)) { UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, TrackedInfo->LastDetectedLocation, 255, 0, 0); } - if (!vIsZero(TrackedInfo->LastLOSPosition)) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, TrackedInfo->LastLOSPosition - Vector(0.0f, 0.0f, 5.0f), 255, 255, 0); - } - - if (!vIsZero(TrackedInfo->LastVisibleLocation)) - { - UTIL_DrawLine(INDEXENT(1), pBot->Edict->v.origin, TrackedInfo->LastVisibleLocation + Vector(0.0f, 0.0f, 5.0f), 0, 0, 255); - } - } \ No newline at end of file diff --git a/main/source/mod/AvHAIPlayer.h b/main/source/mod/AvHAIPlayer.h index 23441020..5b788fe0 100644 --- a/main/source/mod/AvHAIPlayer.h +++ b/main/source/mod/AvHAIPlayer.h @@ -184,6 +184,7 @@ bool OnosCombatThink(AvHAIPlayer* pBot); bool BombardierCombatThink(AvHAIPlayer* pBot); bool RegularMarineCombatThink(AvHAIPlayer* pBot); +void DEBUG_PrintTaskInfo(AvHAIPlayer* pBot); void DEBUG_PrintCombatInfo(AvHAIPlayer* pBot); #endif \ No newline at end of file diff --git a/main/source/mod/AvHAITactical.cpp b/main/source/mod/AvHAITactical.cpp index 7e35c356..e35dd870 100644 --- a/main/source/mod/AvHAITactical.cpp +++ b/main/source/mod/AvHAITactical.cpp @@ -89,6 +89,7 @@ std::vector AITAC_FindAllDeployables(const Vector& Loca if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE) { @@ -122,6 +123,7 @@ std::vector AITAC_FindAllDeployables(const Vector& Loca if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE) { @@ -173,6 +175,7 @@ std::vector AITAC_FindAllDeployablesByRef(const Vector if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE) { @@ -206,6 +209,7 @@ std::vector AITAC_FindAllDeployablesByRef(const Vector if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } if (Filter->ReachabilityFlags != AI_REACHABILITY_NONE) { @@ -253,6 +257,7 @@ bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSe if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -283,6 +288,7 @@ bool AITAC_DeployableExistsAtLocation(const Vector& Location, const DeployableSe if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -331,6 +337,7 @@ AvHAIBuildableStructure AITAC_FindClosestDeployableToLocation(const Vector& Loca if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -360,6 +367,7 @@ AvHAIBuildableStructure AITAC_FindClosestDeployableToLocation(const Vector& Loca if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -405,7 +413,8 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocationByRef(const Vector if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } - if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -435,6 +444,7 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocationByRef(const Vector if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -481,6 +491,7 @@ AvHAIBuildableStructure AITAC_FindFurthestDeployableFromLocation(const Vector& L if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -510,6 +521,7 @@ AvHAIBuildableStructure AITAC_FindFurthestDeployableFromLocation(const Vector& L if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -556,6 +568,7 @@ AvHAIBuildableStructure* AITAC_FindFurthestDeployableFromLocationByRef(const Vec if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -585,6 +598,7 @@ AvHAIBuildableStructure* AITAC_FindFurthestDeployableFromLocationByRef(const Vec if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } if (!(it.second.StructureType & Filter->DeployableTypes)) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -787,6 +801,7 @@ AvHAIBuildableStructure AITAC_GetNearestDeployableDirectlyReachable(AvHAIPlayer* if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -820,6 +835,7 @@ AvHAIBuildableStructure AITAC_GetNearestDeployableDirectlyReachable(AvHAIPlayer* if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -870,6 +886,7 @@ AvHAIBuildableStructure* AITAC_GetNearestDeployableDirectlyReachableByRef(AvHAIP if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -903,6 +920,7 @@ AvHAIBuildableStructure* AITAC_GetNearestDeployableDirectlyReachableByRef(AvHAIP if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -952,6 +970,7 @@ int AITAC_GetNumDeployablesNearLocation(const Vector& Location, const Deployable if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); @@ -982,6 +1001,7 @@ int AITAC_GetNumDeployablesNearLocation(const Vector& Location, const Deployable if (it.second.StructureStatusFlags & Filter->ExcludeStatusFlags) { continue; } if ((it.second.StructureStatusFlags & Filter->IncludeStatusFlags) != Filter->IncludeStatusFlags) { continue; } + if (it.second.Purpose != Filter->PurposeFlags && (it.second.Purpose & Filter->PurposeFlags) != it.second.Purpose) { continue; } unsigned int StructureReachabilityFlags = (it.second.TeamAReachabilityFlags | it.second.TeamBReachabilityFlags); diff --git a/main/source/mod/AvHAITask.cpp b/main/source/mod/AvHAITask.cpp index f372630d..1f08eb41 100644 --- a/main/source/mod/AvHAITask.cpp +++ b/main/source/mod/AvHAITask.cpp @@ -864,8 +864,9 @@ bool AITASK_IsReinforceStructureTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTas if (!FNullEnt(Task->TaskSecondaryTarget) && !UTIL_StructureIsFullyBuilt(Task->TaskSecondaryTarget)) { return true; } AvHTeamNumber BotTeam = pBot->Player->GetTeam(); + AvHTeamNumber EnemyTeam = AIMGR_GetEnemyTeam(BotTeam); - if (Task->TaskTarget->v.team != BotTeam) { return false; } + if (Task->TaskTarget->v.team == EnemyTeam) { return false; } // The reinforce structure task is true if we have an undecided hive available that we could build a new chamber with bool bActiveHiveWithoutTechExists = AITAC_TeamHiveWithTechExists(pBot->Player->GetTeam(), MESSAGE_NULL);