#pragma once #ifndef AVH_AI_CONSTANTS_H #define AVH_AI_CONSTANTS_H #include "DetourStatus.h" #include "DetourNavMeshQuery.h" #include "AvHHive.h" #include "AvHEntities.h" #include "AvHAIMath.h" static const float commander_action_cooldown = 1.0f; static const float min_request_spam_time = 10.0f; constexpr auto MAX_AI_PATH_SIZE = 512; // Maximum number of points allowed in a path (this should be enough for any sized map) static const int MAX_NAV_MESHES = 8; // Max number of nav meshes allowed. Currently 3 are used (one for building placement, one for the onos, and a regular one for everyone else) // NS weapon types. Each number refers to the GoldSrc weapon index typedef enum { WEAPON_INVALID = 0, WEAPON_LERK_SPIKE = 4, // I think this is an early NS weapon, replaced by primal scream // Marine Weapons WEAPON_MARINE_KNIFE = 13, WEAPON_MARINE_PISTOL = 14, WEAPON_MARINE_MG = 15, WEAPON_MARINE_SHOTGUN = 16, WEAPON_MARINE_HMG = 17, WEAPON_MARINE_WELDER = 18, WEAPON_MARINE_MINES = 19, WEAPON_MARINE_GL = 20, WEAPON_MARINE_GRENADE = 28, // Alien Abilities WEAPON_SKULK_BITE = 5, WEAPON_SKULK_PARASITE = 10, WEAPON_SKULK_LEAP = 21, WEAPON_SKULK_XENOCIDE = 12, WEAPON_GORGE_SPIT = 2, WEAPON_GORGE_HEALINGSPRAY = 27, WEAPON_GORGE_BILEBOMB = 25, WEAPON_GORGE_WEB = 8, WEAPON_LERK_BITE = 6, WEAPON_LERK_SPORES = 3, WEAPON_LERK_UMBRA = 23, WEAPON_LERK_PRIMALSCREAM = 24, WEAPON_FADE_SWIPE = 7, WEAPON_FADE_BLINK = 11, WEAPON_FADE_METABOLIZE = 9, WEAPON_FADE_ACIDROCKET = 26, WEAPON_ONOS_GORE = 1, WEAPON_ONOS_DEVOUR = 30, WEAPON_ONOS_STOMP = 29, WEAPON_ONOS_CHARGE = 22, WEAPON_MAX = 31 } AvHAIWeapon; // Hives can either be unbuilt ("ghost" hive), in progress or fully built (active) typedef enum { HIVE_STATUS_UNBUILT = 0, HIVE_STATUS_BUILDING = 1, HIVE_STATUS_BUILT = 2 } HiveStatusType; // All tech statuses that can be assigned to a hive typedef enum { HIVE_TECH_NONE = 0, // Hive doesn't have any tech assigned to it yet (no chambers built for it) HIVE_TECH_DEFENCE = 1, HIVE_TECH_SENSORY = 2, HIVE_TECH_MOVEMENT = 3 } HiveTechStatus; typedef enum _AI_REACHABILITY_STATUS { AI_REACHABILITY_NONE = 0, AI_REACHABILITY_MARINE = 1u << 0, AI_REACHABILITY_SKULK = 1u << 1, 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; typedef enum { STRUCTURE_STATUS_NONE = 0, // No filters, all buildings will be returned STRUCTURE_STATUS_COMPLETED = 1, // Structure is fully built STRUCTURE_STATUS_ELECTRIFIED = 1 << 1, STRUCTURE_STATUS_RECYCLING = 1 << 2, STRUCTURE_STATUS_PARASITED = 1 << 3, STRUCTURE_STATUS_UNDERATTACK = 1 << 4, STRUCTURE_STATUS_RESEARCHING = 1 << 5, STRUCTURE_STATUS_DAMAGED = 1 << 6, STRUCTURE_STATUS_DISABLED = 1 << 7, // For marine turrets when there's no TF STRUCTURE_STATUS_ALL = -1 } AvHAIStructureStatus; typedef enum { STRUCTURE_NONE = 0, STRUCTURE_MARINE_RESTOWER = 1u, STRUCTURE_MARINE_INFANTRYPORTAL = 1u << 1, STRUCTURE_MARINE_TURRETFACTORY = 1u << 2, STRUCTURE_MARINE_ADVTURRETFACTORY = 1u << 3, STRUCTURE_MARINE_ARMOURY = 1u << 4, STRUCTURE_MARINE_ADVARMOURY = 1u << 5, STRUCTURE_MARINE_ARMSLAB = 1u << 6, STRUCTURE_MARINE_PROTOTYPELAB = 1u << 7, STRUCTURE_MARINE_OBSERVATORY = 1u << 8, STRUCTURE_MARINE_PHASEGATE = 1u << 9, STRUCTURE_MARINE_TURRET = 1u << 10, STRUCTURE_MARINE_SIEGETURRET = 1u << 11, STRUCTURE_MARINE_COMMCHAIR = 1u << 12, STRUCTURE_MARINE_DEPLOYEDMINE = 1u << 13, STRUCTURE_ALIEN_HIVE = 1u << 14, STRUCTURE_ALIEN_RESTOWER = 1u << 15, STRUCTURE_ALIEN_DEFENCECHAMBER = 1u << 16, STRUCTURE_ALIEN_SENSORYCHAMBER = 1u << 17, STRUCTURE_ALIEN_MOVEMENTCHAMBER = 1u << 18, STRUCTURE_ALIEN_OFFENCECHAMBER = 1u << 19, SEARCH_ALL_MARINE_STRUCTURES = 0xFFF, SEARCH_ALL_ALIEN_STRUCTURES = (STRUCTURE_ALIEN_HIVE | STRUCTURE_ALIEN_RESTOWER | STRUCTURE_ALIEN_DEFENCECHAMBER | STRUCTURE_ALIEN_SENSORYCHAMBER | STRUCTURE_ALIEN_MOVEMENTCHAMBER | STRUCTURE_ALIEN_OFFENCECHAMBER), SEARCH_ANY_RES_TOWER = (STRUCTURE_MARINE_RESTOWER | STRUCTURE_ALIEN_RESTOWER), SEARCH_ALL_STRUCTURES = ((unsigned int)-1 & ~(STRUCTURE_MARINE_DEPLOYEDMINE)) } AvHAIDeployableStructureType; typedef enum { DEPLOYABLE_ITEM_NONE = 0, DEPLOYABLE_ITEM_RESUPPLY = 1u, // For combat mode DEPLOYABLE_ITEM_HEAVYARMOUR = 1u << 1, DEPLOYABLE_ITEM_JETPACK = 1u << 2, DEPLOYABLE_ITEM_CATALYSTS = 1u << 3, DEPLOYABLE_ITEM_SCAN = 1u << 4, DEPLOYABLE_ITEM_HEALTHPACK = 1u << 5, DEPLOYABLE_ITEM_AMMO = 1u << 6, DEPLOYABLE_ITEM_MINES = 1u << 7, DEPLOYABLE_ITEM_WELDER = 1u << 8, DEPLOYABLE_ITEM_SHOTGUN = 1u << 9, DEPLOYABLE_ITEM_HMG = 1u << 10, DEPLOYABLE_ITEM_GRENADELAUNCHER = 1u << 11, DEPLOYABLE_ITEM_WEAPONS = 0xF80, DEPLOYABLE_ITEM_EQUIPMENT = 0x6, DEPLOYABLE_ITEM_ALL = -1 } AvHAIDeployableItemType; // Type of goal the commander wants to achieve typedef enum _STRUCTUREPURPOSE { STRUCTURE_PURPOSE_NONE = 0, STRUCTURE_PURPOSE_GENERAL = 1u, STRUCTURE_PURPOSE_SIEGE = 1u << 1, STRUCTURE_PURPOSE_FORTIFY = 1u << 2, STRUCTURE_PURPOSE_BASE = 1u << 3, STRUCTURE_PURPOSE_ANY = -1 } StructurePurpose; typedef enum _AVHAICOMMANDERMODE { COMMANDERMODE_DISABLED, // AI Commander not allowed COMMANDERMODE_IFNOHUMAN, // AI Commander only allowed if no humans are on the marine team COMMANDERMODE_ENABLED // AI Commander allowed if no human takes charge (following grace period) } AvHAICommanderMode; // Bot's role on the team. For marines, this only governs what they do when left to their own devices. // Marine bots will always listen to orders from the commander regardless of role. typedef enum _AVHAIBOTROLE { BOT_ROLE_NONE, // No defined role // General Roles BOT_ROLE_FIND_RESOURCES, // Will hunt for uncapped resource nodes and cap them. Will attack enemy resource towers BOT_ROLE_SWEEPER, // Defensive role to protect infrastructure and build at base. Will patrol to keep outposts secure BOT_ROLE_ASSAULT, // Will go to attack the enemy base. In combat mode, used for Fade-focus aliens // Marine-only Roles BOT_ROLE_COMMAND, // Will attempt to take command BOT_ROLE_BOMBARDIER, // Bot is armed with a GL and wants to wreck your shit. In combat mode, used for Onos-focus aliens // Alien-only roles BOT_ROLE_BUILDER, // Will focus on building chambers and hives. Stays gorge most of the time BOT_ROLE_HARASS // Focuses on taking down enemy resource nodes and hunting the enemy } AvHAIBotRole; typedef enum _AVHAICOMBATSTRATEGY { COMBAT_STRATEGY_IGNORE = 0, // Don't engage this enemy COMBAT_STRATEGY_AMBUSH, // Set up an ambush for this enemy COMBAT_STRATEGY_RETREAT, // Retreat and find health COMBAT_STRATEGY_SKIRMISH, // Maintain distance, whittle down their health from range and generally be a pain the arse COMBAT_STRATEGY_ATTACK // Attack the enemy } AvHAICombatStrategy; typedef enum _AVHAINAVMESHSTATUS { NAVMESH_STATUS_PENDING = 0, // Waiting to try loading the navmesh NAVMESH_STATUS_FAILED, // Failed to load the navmesh NAVMESH_STATUS_SUCCESS // Successfully loaded the navmesh } AvHAINavMeshStatus; typedef struct _OFF_MESH_CONN { unsigned int ConnectionRefs[2]; unsigned int ConnectionFlags = 0; unsigned int DefaultConnectionFlags = 0; Vector FromLocation = g_vecZero; Vector ToLocation = g_vecZero; edict_t* TargetObject = nullptr; bool bBiDirectional = false; } 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 { AvHFuncResource* ResourceEntity = nullptr; // The func_resource edict reference edict_t* ResourceEdict = nullptr; Vector Location = g_vecZero; // origin of the func_resource edict (not the tower itself) bool bIsOccupied = false; // True if there is any resource tower on it AvHTeamNumber OwningTeam = TEAM_IND; // The team that has currently capped this node (TEAM_IND if none) edict_t* ActiveTowerEntity = nullptr; // Reference to the resource tower edict (if capped) bool bIsBaseNode = false; // Is this a node in the marine base or active alien hive? edict_t* ParentHive = nullptr; 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? bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated float NextReachabilityRefreshTime = 0.0f; } AvHAIResourceNode; // Data structure to hold information about each hive in the map typedef struct _HIVE_DEFINITION_T { AvHHive* HiveEntity = nullptr; // Hive entity reference edict_t* HiveEdict = nullptr; // Hive edict reference Vector Location = g_vecZero; // Origin of the hive Vector FloorLocation = g_vecZero; // Some hives are suspended in the air, this is the floor location directly beneath it HiveStatusType Status = HIVE_STATUS_UNBUILT; // Can be unbuilt, in progress, or fully built AvHMessageID TechStatus = MESSAGE_NULL; // What tech (if any) is assigned to this hive right now bool bIsUnderAttack = false; // Is the hive currently under attack? Becomes false if not taken damage for more than 10 seconds float HealthPercent = 0.0f; // If the hive is built and active, what its health currently is AvHAIResourceNode* HiveResNodeRef = nullptr; // Which resource node (indexes into ResourceNodes array) belongs to this hive? unsigned int ObstacleRefs[MAX_NAV_MESHES]; // When in progress or built, will place an obstacle so bots don't try to walk through it float NextFloorLocationCheck = 0.0f; // When should the closest navigable point to the hive be calculated? Used to delay the check after a hive is built 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] = {'\0'}; } AvHAIHiveDefinition; // A nav profile combines a nav mesh reference (indexed into NavMeshes) and filters to determine how a bot should find paths typedef struct _NAV_PROFILE { int NavMeshIndex = -1; dtQueryFilter Filters; bool bFlyingProfile = false; AvHAIReachabilityStatus ReachabilityFlag = AI_REACHABILITY_NONE; } nav_profile; typedef struct _LOAD_NAV_HINT { unsigned int id = 0; unsigned int hintType = 0; float position[3] = { 0.0f, 0.0f, 0.0f }; } LoadNavHint; typedef struct _NAV_HINT { unsigned int hintType = 0; Vector Position = g_vecZero; edict_t* OccupyingBuilding = nullptr; } NavHint; typedef struct _DEPLOYABLE_SEARCH_FILTER { unsigned int DeployableTypes = SEARCH_ALL_STRUCTURES; unsigned int IncludeStatusFlags = STRUCTURE_STATUS_NONE; unsigned int ExcludeStatusFlags = STRUCTURE_STATUS_NONE; unsigned int ReachabilityFlags = AI_REACHABILITY_NONE; float MinSearchRadius = 0.0f; float MaxSearchRadius = 0.0f; 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 typedef struct _BOT_MSG { char msg[64]; // Message to send float SendTime = 0.0f; // When the bot should send this message bool bIsPending = false; // Represents a valid pending message bool bIsTeamSay = false; // Is this a team-only message? } bot_msg; typedef struct _AVH_AI_GUARD_INFO { Vector GuardLocation = g_vecZero; // What position are we guarding? Vector GuardStandPosition = g_vecZero; // Where the bot should stand to guard position (moves around a bit) std::vector GuardPoints; // All potential areas to watch that an enemy could approach from int NumGuardPoints = 0; // How many watch areas there are for the current location Vector GuardLookLocation = g_vecZero; // Which area are we currently watching? float GuardStartLookTime = 0.0f; // When did we start watching the current area? float ThisGuardLookTime = 0.0f; // How long should we watch this area for? float ThisGuardStandTime = 0.0f; // How long should we watch this area for? float GuardStartStandTime = 0.0f; // How long should we watch this area for? } AvHAIGuardInfo; // Data structure to hold information on any kind of buildable structure (hive, resource tower, chamber, marine building etc) typedef struct _AVH_AI_BUILDABLE_STRUCTURE { AvHBaseBuildable* EntityRef = nullptr; int EntIndex = -1; edict_t* edict = nullptr; // Reference to structure edict Vector Location = g_vecZero; // origin of the structure edict float healthPercent = 0.0f; // Current health of the building float lastDamagedTime = 0.0f; // When it was last damaged by something. Used by bots to determine if still needs defending AvHAIDeployableStructureType StructureType = STRUCTURE_NONE; // Type of structure it is (e.g. hive, comm chair, infantry portal, defence chamber etc.) unsigned int StructureStatusFlags = STRUCTURE_STATUS_NONE; 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 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 StructurePurpose Purpose = STRUCTURE_PURPOSE_NONE; bool bReachabilityMarkedDirty = false; // If true, reachability flags will be recalculated for this structure bool bPlacedByHuman = true; // This structure was placed by a human: AI commander will not recycle these unless it absolutely makes sense to bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; } bool IsCompleted() { return (StructureStatusFlags & STRUCTURE_STATUS_COMPLETED); } bool IsIdle() { return !(StructureStatusFlags & STRUCTURE_STATUS_RESEARCHING); } } AvHAIBuildableStructure; // Any kind of pickup that has been dropped either by the commander or by a player typedef struct _DROPPED_MARINE_ITEM { edict_t* edict = nullptr; // Reference to the item edict Vector Location = g_vecZero; // Origin of the entity AvHAIDeployableItemType ItemType = DEPLOYABLE_ITEM_NONE; // Is it a weapon, health pack, ammo pack etc? unsigned int TeamAReachabilityFlags = AI_REACHABILITY_NONE; unsigned int TeamBReachabilityFlags = AI_REACHABILITY_NONE; bool bReachabilityMarkedDirty = false; // Reachability needs to be recalculated int LastSeen = 0; // Which refresh cycle was this last seen on? Used to determine if the item has been removed from play bool IsValid() { return !FNullEnt(edict) && !edict->free && !(edict->v.flags & EF_NODRAW) && edict->v.deadflag == DEAD_NO; } } AvHAIDroppedItem; // How far a bot can be from a useable object when trying to interact with it. Used also for melee attacks. We make it slightly less than actual to avoid edge cases static const float max_ai_use_reach = 55.0f; // Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down) static const float min_ai_use_interval = 0.5f; // Minimum time a bot can wait between attempts to use something in seconds (when not holding the use key down) static const float max_ai_jump_height = 62.0f; // Affects the bot's pathfinding choices enum BotMoveStyle { MOVESTYLE_NORMAL, // Most direct route to target MOVESTYLE_AMBUSH, // Prefer wall climbing and vents MOVESTYLE_HIDE // Prefer crouched areas like vents }; // The list of potential task types for the bot_task structure typedef enum { TASK_NONE, TASK_GET_HEALTH, TASK_GET_AMMO, TASK_GET_WEAPON, TASK_GET_EQUIPMENT, TASK_BUILD, TASK_ATTACK, TASK_MOVE, TASK_CAP_RESNODE, TASK_DEFEND, TASK_GUARD, TASK_HEAL, TASK_WELD, TASK_RESUPPLY, TASK_EVOLVE, TASK_COMMAND, TASK_USE, TASK_TOUCH, TASK_REINFORCE_STRUCTURE, TASK_SECURE_HIVE, TASK_PLACE_MINE, TASK_ASSAULT_MARINE_BASE } BotTaskType; // typedef enum { ATTACK_SUCCESS, ATTACK_BLOCKED, ATTACK_OUTOFRANGE, ATTACK_INVALIDTARGET, ATTACK_NOWEAPON } BotAttackResult; typedef enum { BUILD_ATTEMPT_NONE = 0, BUILD_ATTEMPT_PENDING, BUILD_ATTEMPT_SUCCESS, BUILD_ATTEMPT_FAILED } BotBuildAttemptStatus; typedef enum { MOVE_TASK_NONE = 0, MOVE_TASK_MOVE, MOVE_TASK_USE, MOVE_TASK_BREAK, MOVE_TASK_TOUCH, MOVE_TASK_PICKUP, MOVE_TASK_WELD } BotMovementTaskType; // Door type. Not currently used, future feature so bots know how to open a door enum DoorActivationType { DOOR_NONE, // No type, cannot be activated (permanently open/shut) DOOR_USE, // Door activated by using it directly DOOR_TRIGGER,// Door activated by touching a trigger_once or trigger_multiple DOOR_BUTTON, // Door activated by pressing a button DOOR_WELD, // Door activated by welding something DOOR_SHOOT, // Door activated by being shot DOOR_BREAK // Door activated by breaking something }; // Door type. Not currently used, future feature so bots know how to open a door enum NavDoorType { DOORTYPE_DOOR, // No type, cannot be activated (permanently open/shut) DOORTYPE_PLAT, // Door activated by using it directly DOORTYPE_TRAIN // Door activated by touching a trigger_once or trigger_multiple }; // The type of base a marine outpost could be. Used to help the AI establish and expand outposts across the map 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_GUARDPOST // A cut-down version of an outpost with just sentry turrets and an observatory }; typedef struct _AI_MARINE_BASE { AvHTeamNumber BaseTeam = TEAM_IND; 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 SiegeTarget = ZERO_VECTOR; // For siege bases, this is where the siege base wants to blast stuff vector 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 typedef struct _BOT_PATH_NODE { Vector FromLocation = g_vecZero; // Location to move from Vector Location = g_vecZero; // Location to move to float requiredZ = 0.0f; // If climbing a up ladder or wall, how high should they aim to get before dismounting. unsigned int flag = 0; // Is this a ladder movement, wall climb, walk etc unsigned char area = 0; // Is this a crouch area, normal walking area etc unsigned int poly = 0; // The nav mesh poly this point resides on } bot_path_node; // Represents a bot's current understanding of an enemy player's status typedef struct _ENEMY_STATUS { AvHPlayer* PlayerRef = nullptr; // Reference to the enemy AvHPlayer edict_t* PlayerEdict = nullptr; // Reference to the enemy player edict Vector LastDetectedLocation = g_vecZero; // Where the bot last detected the enemy, either through sight, motion tracking or sound Vector LastVisibleLocation = g_vecZero; // Last point the bot had visible confirmation of the enemy Vector LastKnownVelocity = g_vecZero; Vector VisiblePointOnPlayer = g_vecZero; float AwarenessOfPlayer = 0.0f; // How aware of this enemy the bot is 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; // 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; Vector LastLOSPosition = g_vecZero; Vector LastCoverPosition = g_vecZero; } enemy_status; // Tracks what orders have been given to which players typedef struct _BOT_SKILL { float marine_bot_reaction_time = 0.2f; // How quickly the bot will react to seeing an enemy float marine_bot_aim_skill = 0.5f; // How quickly the bot can lock on to an enemy float marine_bot_motion_tracking_skill = 0.5f; // How well the bot can follow an enemy target's motion float marine_bot_view_speed = 1.0f; // How fast a bot can spin its view to aim in a given direction float alien_bot_reaction_time = 0.2f; // How quickly the bot will react to seeing an enemy float alien_bot_aim_skill = 0.5f; // How quickly the bot can lock on to an enemy float alien_bot_motion_tracking_skill = 0.5f; // How well the bot can follow an enemy target's motion float alien_bot_view_speed = 0.5f; // How fast a bot can spin its view to aim in a given direction } bot_skill; typedef struct _AVH_AI_BUILD_ATTEMPT { AvHAIDeployableStructureType AttemptedStructureType = STRUCTURE_NONE; Vector AttemptedLocation = g_vecZero; int NumAttempts = 0; BotBuildAttemptStatus BuildStatus = BUILD_ATTEMPT_NONE; float BuildAttemptTime = 0.0f; AvHAIBuildableStructure* LinkedStructure = nullptr; } AvHAIBuildAttempt; // A bot task is a goal the bot wants to perform, such as attacking a structure, placing a structure etc. NOT USED BY COMMANDER typedef struct _AVH_AI_PLAYER_TASK { BotTaskType TaskType = TASK_NONE; // Task Type (e.g. build, attack, defend, heal etc) Vector TaskLocation = g_vecZero; // Task location, if task needs one (e.g. where to place structure for TASK_BUILD) edict_t* TaskTarget = nullptr; // Reference to a target, if task needs one (e.g. TASK_ATTACK) edict_t* TaskSecondaryTarget = nullptr; // Secondary target, if task needs one (e.g. TASK_REINFORCE) AvHAIDeployableStructureType StructureType = STRUCTURE_NONE; // For Gorges, what structure to build (if TASK_BUILD) float TaskStartedTime = 0.0f; // When the bot started this task. Helps time-out if the bot gets stuck trying to complete it bool bIssuedByCommander = false; // Was this task issued by the commander? Top priority if so bool bTargetIsPlayer = false; // Is the TaskTarget a player? bool bTaskIsUrgent = false; // Determines whether this task is prioritised over others if bot has multiple bool bIsWaitingForBuildLink = false; // If true, Gorge has sent the build impulse and is waiting to see if the building materialised float LastBuildAttemptTime = 0.0f; // When did the Gorge last try to place a structure? int BuildAttempts = 0; // How many attempts the Gorge has tried to place it, so it doesn't keep trying forever AvHMessageID Evolution = MESSAGE_NULL; // Used by TASK_EVOLVE to determine what to evolve into float TaskLength = 0.0f; // If a task has gone on longer than this time, it will be considered completed AvHAIBuildAttempt ActiveBuildInfo; // If gorge, the current status of any recent attempt to place a structure } AvHAIPlayerTask; typedef struct _DOOR_TRIGGER { CBaseEntity* Entity = nullptr; CBaseToggle* ToggleEnt = nullptr; edict_t* Edict = nullptr; DoorActivationType TriggerType = DOOR_NONE; bool bIsActivated = false; CBaseEntity* TriggerChangeTargetRef = nullptr; float ActivationDelay = 0.0f; float LastActivatedTime = 0.0f; TOGGLE_STATE LastToggleState = TS_AT_BOTTOM; float LastNextThink = 0.0f; float NextActivationTime = 0.0f; } DoorTrigger; typedef struct _AVH_AI_PLAYER_MOVE_TASK { BotMovementTaskType TaskType = MOVE_TASK_NONE; Vector TaskLocation = g_vecZero; edict_t* TaskTarget = nullptr; DoorTrigger* TriggerToActivate = nullptr; bool bPathGenerated = false; } AvHAIPlayerMoveTask; typedef struct _AVH_AI_STUCK_TRACKER { Vector LastBotPosition = g_vecZero; Vector MoveDestination = g_vecZero; float TotalStuckTime = 0.0f; // Total time the bot has spent stuck bool bPathFollowFailed = false; } AvHAIPlayerStuckTracker; // Contains the bot's current navigation info, such as current path typedef struct _NAV_STATUS { vector CurrentPath; // Bot's path nodes unsigned int CurrentPathPoint = 0; Vector TargetDestination = g_vecZero; // Desired destination Vector ActualMoveDestination = g_vecZero; // Actual destination on nav mesh Vector PathDestination = g_vecZero; // Where the path is currently headed to Vector LastNavMeshCheckPosition = g_vecZero; Vector LastNavMeshPosition = g_vecZero; // Tracks the last place the bot was on the nav mesh. Useful if accidentally straying off it Vector LastOpenLocation = g_vecZero; // Tracks the last place the bot had enough room to move around people. Useful if in a vent and need to back up somewhere to let another player past. int CurrentMoveType = MOVETYPE_NONE; // Tracks the edict's current movement type unsigned int CurrentPoly = 0; // Which nav mesh poly the bot is currently on float LastStuckCheckTime = 0.0f; // Last time the bot checked if it had successfully moved float TotalStuckTime = 0.0f; // Total time the bot has spent stuck float LastDistanceFromDestination = 0.0f; // How far from its destination was it last stuck check Vector StuckCheckMoveLocation = g_vecZero; // Where is the bot trying to go that we're checking if they're stuck? Vector UnstuckMoveLocation = g_vecZero; // If the bot is unable to find a path, blindly move here to try and fix the problem float LandedTime = 0.0f; // When the bot last landed after a fall/jump. float AirStartedTime = 0.0f; // When the bot left the ground if in the air float LeapAttemptedTime = 0.0f; // When the bot last attempted to leap/blink. Avoid spam that sends it flying around too fast bool bIsJumping = false; // Is the bot in the air from a jump? Will duck so it can duck-jump bool IsOnGround = true; // Is the bot currently on the ground, or on a ladder? bool bHasAttemptedJump = false; // Last frame, the bot tried a jump. If the bot is still on the ground, it probably tried to jump in a vent or something float LastFlapTime = 0.0f; // When the bot last flapped its wings (if Lerk). Prevents per-frame spam draining adrenaline bool bShouldWalk = false; // Should the bot walk at this point? BotMoveStyle PreviousMoveStyle = MOVESTYLE_NORMAL; // Previous desired move style (e.g. normal, ambush, hide). Will trigger new path calculations if this changes BotMoveStyle MoveStyle = MOVESTYLE_NORMAL; // Current desired move style (e.g. normal, ambush, hide). Will trigger new path calculations if this changes float LastPathCalcTime = 0.0f; // When the bot last calculated a path, to limit how frequently it can recalculate float NextForceRecalc = 0.0f; // If set, then the bot will force-recalc its current path bool bZig; // Is the bot zigging (moving RIGHT), or zagging (moving LEFT)? float NextZigTime; // Controls how frequently they zig or zag nav_profile NavProfile; bool bNavProfileChanged = false; AvHAIPlayerStuckTracker StuckInfo; unsigned int SpecialMovementFlags = 0; // Any special movement flags required for this path (e.g. needs a welder, needs a jetpack etc.) AvHAIPlayerMoveTask MovementTask; } nav_status; typedef enum { ORDERPURPOSE_NONE, ORDERPURPOSE_SECURE_HIVE, ORDERPURPOSE_SIEGE_HIVE, ORDERPURPOSE_SECURE_RESNODE, ORDERPURPOSE_BUILD_MAINBASE, ORDERPURPOSE_BUILD_SIEGE, ORDERPURPOSE_BUILD_OUTPOST, ORDERPURPOSE_BUILD_GUARDPOST } AvHAIOrderPurpose; typedef struct _AI_COMMANDER_ORDER { edict_t* Assignee = nullptr; AvHAIOrderPurpose OrderPurpose = ORDERPURPOSE_NONE; edict_t* OrderTarget = nullptr; Vector OrderLocation = g_vecZero; float LastReminderTime = 0.0f; float LastPlayerDistance = 0.0f; } ai_commander_order; typedef struct _AI_COMMANDER_REQUEST { bool bNewRequest = false; // Is this a new request just come in? edict_t* Requestor = nullptr; // Who sent the request? AvHMessageID RequestType = MESSAGE_NULL; // What did they request? bool bAcknowledged = false; // If we can't satisfy the request right now, have we at least acknowledged it? bool bResponded = false; // Have we already responded to this request? float RequestTime = 0.0f; // When the request came in int ResponseAttempts = 0; // How many times have we tried to respond to this request? Vector RequestLocation = g_vecZero; // Where was the request raised? Ideal drop location for stuff } ai_commander_request; typedef struct AVH_AI_PLAYER { AvHPlayer* Player = nullptr; edict_t* Edict = nullptr; AvHTeamNumber Team = TEAM_IND; float ForwardMove = 0.0f; float SideMove = 0.0f; float UpMove = 0.0f; int Button = 0.0f; int Impulse = 0.0f; byte AdjustedMsec = 0; bool bIsPendingKill = false; bool bIsInactive = false; float LastUseTime = 0.0f; float f_previous_command_time = 0.0f; Vector desiredMovementDir = g_vecZero; Vector CurrentLadderNormal = g_vecZero; Vector CurrentEyePosition = g_vecZero; Vector CurrentFloorPosition = g_vecZero; Vector LastPosition = g_vecZero; Vector CollisionHullBottomLocation = g_vecZero; Vector CollisionHullTopLocation = g_vecZero; float TimeSinceLastMovement = 0.0f; AvHAIWeapon DesiredMoveWeapon = WEAPON_INVALID; AvHAIWeapon DesiredCombatWeapon = WEAPON_INVALID; frustum_plane_t viewFrustum[6]; // Bot's view frustum. Essentially, their "screen" for determining visibility of stuff enemy_status TrackedEnemies[32]; int CurrentEnemy = -1; AvHAICombatStrategy CurrentCombatStrategy = COMBAT_STRATEGY_ATTACK; edict_t* CurrentEnemyRef = nullptr; vector DangerTurrets; AvHAIPlayerTask PrimaryBotTask; AvHAIPlayerTask SecondaryBotTask; AvHAIPlayerTask WantsAndNeedsTask; AvHAIPlayerTask CommanderTask; // Task assigned by the commander AvHAIPlayerTask* CurrentTask = &PrimaryBotTask; // Bot's current task they're performing float BotNextTaskEvaluationTime = 0.0f; bot_skill BotSkillSettings; char PathStatus[128]; // Debug used to help figure out what's going on with a bot's path finding char MoveStatus[128]; // Debug used to help figure out what's going on with a bot's steering nav_status BotNavInfo; // Bot's movement information, their current path, where in the path they are etc. vector ActiveRequests; vector ActiveOrders; float next_commander_action_time = 0.0f; bot_msg ChatMessages[5]; // Bot can have up to 5 chat messages pending float LastCombatTime = 0.0f; AvHAIGuardInfo GuardInfo; float LastRequestTime = 0.0f; // When bot last used a voice line to request something. Prevents spam float LastTeleportTime = 0.0f; // Last time the bot teleported somewhere Vector DesiredLookDirection = g_vecZero; // What view angle is the bot currently turning towards Vector InterpolatedLookDirection = g_vecZero; // Used to smoothly interpolate the bot's view rather than snap instantly like an aimbot edict_t* LookTarget = nullptr; // Used to work out what view angle is needed to look at the desired entity Vector LookTargetLocation = g_vecZero; // This is the bot's current desired look target. Could be an enemy (see LookTarget), or point of interest Vector MoveLookLocation = g_vecZero; // If the bot has to look somewhere specific for movement (e.g. up for a ladder or wall-climb), this will override LookTargetLocation so the bot doesn't get distracted and mess the move up bool bSnapView = false; // Use for rapid, precise snapping of the bot's view to the target. Useful if the bot requires more precise view angles for movement or other reasons float LastTargetTrackUpdate = 0.0f; // Add a delay to how frequently a bot can track a target's movements float ViewInterpolationSpeed = 0.0f; // How fast should the bot turn its view? Depends on distance to turn float ViewInterpStartedTime = 0.0f; // Used for interpolation float ViewUpdateRate = 0.2f; // How frequently the bot can react to new sightings of enemies etc. float LastViewUpdateTime = 0.0f; // Used to throttle view updates based on ViewUpdateRate Vector ViewForwardVector = g_vecZero; // Bot's current forward unit vector Vector LastSafeLocation = g_vecZero; AvHAIBotRole BotRole = BOT_ROLE_NONE; int ExperiencePointsAvailable = 0; // How much experience the bot has to spend AvHMessageID NextCombatModeUpgrade = MESSAGE_NULL; float ThinkDelta = 0.0f; // How long since this bot last ran AIPlayerThink float LastThinkTime = 0.0f; // When the bot last ran AIPlayerThink float ServerUpdateDelta = 0.0f; // How long since we last called RunPlayerMove float LastServerUpdateTime = 0.0f; // When we last called RunPlayerMove float HearingThreshold = 0.0f; // How loud does a sound need to be before the bot detects it? This is set when hearing a sound so that louder sounds drown out quieter ones, and decrements quickly int DebugValue = 0; // Used for debugging the bot Vector RelocationSpot = ZERO_VECTOR; // If the bot is commanding and wants to relocate, then this is where they plan to go vector Bases; } AvHAIPlayer; typedef struct _AVH_AI_SQUAD { AvHTeamNumber SquadTeam = TEAM_IND; // Which team this squad is for vector SquadMembers; // Which bots are assigned to this Vector SquadGatherLocation = g_vecZero; // Where should the squad gather before attempting the objective? edict_t* SquadTarget = nullptr; // The target of the objective Vector ObjectiveLocation = g_vecZero; BotTaskType SquadObjective = TASK_NONE; // What to do with the objective bool bExecuteObjective = false; // Are we at the gather or execute phase? bool IsValid() { return (SquadMembers.size() > 0 && !FNullEnt(SquadTarget)); } } AvHAISquad; #endif