Improved bot understanding of door triggers and weldables

This commit is contained in:
RGreenlees 2023-10-11 16:20:15 +01:00 committed by pierow
parent efcd2cb03a
commit f8951d672b
19 changed files with 894 additions and 344 deletions

View file

@ -32,6 +32,7 @@ static const unsigned char DT_TILECACHE_CLIMBABLE_AREA = 4;
static const unsigned char DT_TILECACHE_LADDER_AREA = 5;
static const unsigned char DT_TILECACHE_MSTRUCTURE_AREA = 12;
static const unsigned char DT_TILECACHE_ASTRUCTURE_AREA = 13;
static const unsigned char DT_TILECACHE_WELD_AREA = 15;
static const unsigned char DT_TILECACHE_WALKABLE_AREA = 63;
static const unsigned short DT_TILECACHE_NULL_IDX = 0xffff;

View file

@ -35,22 +35,6 @@
#define SF_GLOBAL_SET 1 // Set global state to initial state on spawn
class CEnvGlobal : public CPointEntity
{
public:
void Spawn( void );
void KeyValue( KeyValueData *pkvd );
void Use( CBaseEntity *pActivator, CBaseEntity *pCaller, USE_TYPE useType, float value );
virtual int Save( CSave &save );
virtual int Restore( CRestore &restore );
static TYPEDESCRIPTION m_SaveData[];
string_t m_globalstate;
int m_triggermode;
int m_initialstate;
};
TYPEDESCRIPTION CEnvGlobal::m_SaveData[] =
{

View file

@ -458,6 +458,22 @@ public:
string_t m_globalstate;
};
class CEnvGlobal : public CPointEntity
{
public:
void Spawn(void);
void KeyValue(KeyValueData* pkvd);
void Use(CBaseEntity* pActivator, CBaseEntity* pCaller, USE_TYPE useType, float value);
virtual int Save(CSave& save);
virtual int Restore(CRestore& restore);
static TYPEDESCRIPTION m_SaveData[];
string_t m_globalstate;
int m_triggermode;
int m_initialstate;
};
//
// generic Delay entity.

File diff suppressed because it is too large Load diff

View file

@ -39,6 +39,8 @@ constexpr auto LERK_FLYING_NAV_PROFILE = 9;
constexpr auto GORGE_BUILD_NAV_PROFILE = 10;
constexpr auto MARINE_WELD_NAV_PROFILE = 11;
constexpr auto MIN_PATH_RECALC_TIME = 0.33f; // How frequently can a bot recalculate its path? Default to max 3 times per second
constexpr auto MAX_BOT_STUCK_TIME = 30.0f; // How long a bot can be stuck, unable to move, before giving up and suiciding
@ -49,52 +51,50 @@ constexpr auto MAX_BOT_STUCK_TIME = 30.0f; // How long a bot can be stuck, unabl
// Possible area types. Water, Road, Door and Grass are not used (left-over from Detour library)
enum SamplePolyAreas
{
SAMPLE_POLYAREA_GROUND = 0,
SAMPLE_POLYAREA_CROUCH = 1,
SAMPLE_POLYAREA_WATER = 2,
SAMPLE_POLYAREA_BLOCKED = 3,
SAMPLE_POLYAREA_WALLCLIMB = 4,
SAMPLE_POLYAREA_LADDER = 5,
SAMPLE_POLYAREA_DOOR = 6,
SAMPLE_POLYAREA_JUMP = 7,
SAMPLE_POLYAREA_HIGHJUMP = 8,
SAMPLE_POLYAREA_FALL = 9,
SAMPLE_POLYAREA_HIGHFALL = 10,
SAMPLE_POLYAREA_PHASEGATE = 11,
SAMPLE_POLYAREA_MSTRUCTURE = 12,
SAMPLE_POLYAREA_ASTRUCTURE = 13,
SAMPLE_POLYAREA_FLY = 14
SAMPLE_POLYAREA_GROUND = 0, // Regular ground movement
SAMPLE_POLYAREA_CROUCH = 1, // Requires crouched movement
SAMPLE_POLYAREA_WATER = 2, // Swimming (NOT USED)
SAMPLE_POLYAREA_BLOCKED = 3, // Requires a jump to get over
SAMPLE_POLYAREA_WALLCLIMB = 4, // Requires the ability to wall climb (i.e. skulks/fades/lerks only)
SAMPLE_POLYAREA_LADDER = 5, // Requires climbing a ladder (ignored by skulks)
SAMPLE_POLYAREA_DOOR = 6, // Requires moving through a door (NOT USED)
SAMPLE_POLYAREA_JUMP = 7, // Requires a jump to get through
SAMPLE_POLYAREA_HIGHJUMP = 8, // Requires jumping from a great height
SAMPLE_POLYAREA_FALL = 9, // Requires dropping down from a higher elevation
SAMPLE_POLYAREA_HIGHFALL = 10, // Requires a large drop from a high height
SAMPLE_POLYAREA_PHASEGATE = 11, // Requires accessing a phase gate (i.e. marines only)
SAMPLE_POLYAREA_MSTRUCTURE = 12, // Requires bypassing a marine structure
SAMPLE_POLYAREA_ASTRUCTURE = 13, // Requires bypassing an alien structure
SAMPLE_POLYAREA_FLY = 14, // Requires the ability to fly (currently lerks only)
};
// Possible movement types. Swim and door are not used
enum SamplePolyFlags
{
SAMPLE_POLYFLAGS_WALK = 1 << 0, // Simple walk to traverse
SAMPLE_POLYFLAGS_CROUCH = 1 << 1, // Required crouching to traverse
SAMPLE_POLYFLAGS_SWIM = 1 << 2, // Requires swimming to traverse (not used)
SAMPLE_POLYFLAGS_BLOCKED = 1 << 3, // Blocked by an obstruction, but can be jumped over
SAMPLE_POLYFLAGS_WALLCLIMB = 1 << 4, // Requires climbing a wall to traverse
SAMPLE_POLYFLAGS_LADDER = 1 << 5, // Requires climbing a ladder to traverse
SAMPLE_POLYFLAGS_DOOR = 1 << 6, // Requires opening a door to traverse (not used)
SAMPLE_POLYFLAGS_JUMP = 1 << 7, // Requires a jump to traverse
SAMPLE_POLYFLAGS_HIGHJUMP = 1 << 8, // Requires a jump from a high height to traverse
SAMPLE_POLYFLAGS_FALL = 1 << 9, // Requires dropping down from a safe height to traverse
SAMPLE_POLYFLAGS_HIGHFALL = 1 << 10, // Requires dropping from a high height to traverse
SAMPLE_POLYFLAGS_DISABLED = 1 << 11, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_NOONOS = 1 << 12, // This movement is not allowed by onos
SAMPLE_POLYFLAGS_PHASEGATE = 1 << 13, // Requires using a phase gate to traverse
SAMPLE_POLYFLAGS_MSTRUCTURE = 1 << 14, // Marine Structure in the way, must be destroyed if alien, or impassable if marine
SAMPLE_POLYFLAGS_ASTRUCTURE = 1 << 15, // Structure in the way, must be destroyed if marine, or impassable if alien
SAMPLE_POLYFLAGS_FLY = 1 << 16, // Structure in the way, must be destroyed if marine, or impassable if alien
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
SAMPLE_POLYFLAGS_WALK = 1 << 0, // Simple walk to traverse
SAMPLE_POLYFLAGS_BLOCKED = 1 << 1, // Blocked by an obstruction, but can be jumped over
SAMPLE_POLYFLAGS_WALLCLIMB = 1 << 2, // Requires climbing a wall to traverse
SAMPLE_POLYFLAGS_LADDER = 1 << 3, // Requires climbing a ladder to traverse
SAMPLE_POLYFLAGS_DOOR = 1 << 4, // Requires opening a door to traverse (not used)
SAMPLE_POLYFLAGS_JUMP = 1 << 5, // Requires a jump to traverse
SAMPLE_POLYFLAGS_HIGHJUMP = 1 << 6, // Requires a jump from a high height to traverse
SAMPLE_POLYFLAGS_FALL = 1 << 7, // Requires dropping down from a safe height to traverse
SAMPLE_POLYFLAGS_HIGHFALL = 1 << 8, // Requires dropping from a high height to traverse
SAMPLE_POLYFLAGS_DISABLED = 1 << 9, // Disabled, not usable by anyone
SAMPLE_POLYFLAGS_NOONOS = 1 << 10, // This movement is not allowed by onos
SAMPLE_POLYFLAGS_PHASEGATE = 1 << 11, // Requires using a phase gate to traverse
SAMPLE_POLYFLAGS_MSTRUCTURE = 1 << 12, // Marine Structure in the way, must be destroyed if alien, or impassable if marine
SAMPLE_POLYFLAGS_ASTRUCTURE = 1 << 13, // Structure in the way, must be destroyed if marine, or impassable if alien
SAMPLE_POLYFLAGS_WELD = 1 << 14, // Requires a welder to get through here
SAMPLE_POLYFLAGS_ALL = 0xffff // All abilities.
};
// Door type. Not currently used, future feature so bots know how to open a door
enum DoorActivationType
{
DOOR_NONE, // No type
DOOR_USE, // Door activated by using it
DOOR_TRIGGER,// Door activated by trigger_once or trigger_multiple
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
@ -103,23 +103,22 @@ enum DoorActivationType
typedef struct _DOOR_TRIGGER
{
CBaseEntity* Entity = nullptr;
CBaseToggle* ToggleEnt = nullptr;
edict_t* Edict = nullptr;
DoorActivationType TriggerType = DOOR_NONE;
bool bIsActivated = false;
} DoorTrigger;
// Door reference. Not used, but is a future feature to allow bots to track if a door is open or not, and how to open it etc.
typedef struct _NAV_DOOR
{
CBaseEntity* DoorEntity = nullptr;
CBaseToggle* DoorEntity = nullptr;
edict_t* DoorEdict = nullptr; // Reference to the func_door
unsigned int ObstacleRefs[32][8]; // Dynamic obstacle ref. Used to add/remove the obstacle as the door is opened/closed
int NumObstacles = 0;
DoorTrigger TriggerEnts[8]; // Reference to the trigger edicts (e.g. func_trigger, func_button etc.)
int NumTriggers = 0; // How many triggers can activate the door (bot will pick best one)
vector<DoorTrigger> TriggerEnts; // Reference to the trigger edicts (e.g. func_trigger, func_button etc.)
DoorActivationType ActivationType = DOOR_NONE; // How the door should be opened
Vector PositionOne = g_vecZero; // Door's starting position
Vector PositionTwo = g_vecZero; // Door's open/close position (depending on if it starts open or not)
Vector CurrentPosition = g_vecZero; // Current world position
bool bStartOpen = false; // Does the door start open? (PositionOne = open position, not close position)
TOGGLE_STATE CurrentState = TS_AT_BOTTOM;
float OpenDelay = 0.0f; // How long the door takes to start opening after activation
} nav_door;
@ -463,6 +462,8 @@ void ClearBotPath(AvHAIPlayer* pBot);
// Clears just the bot's current stuck movement attempt (see PerformUnstuckMove())
void ClearBotStuckMovement(AvHAIPlayer* pBot);
void UTIL_ClearDoorData();
// Based on the direction the bot wants to move and it's current facing angle, sets the forward and side move, and the directional buttons to make the bot actually move
void BotMovementInputs(AvHAIPlayer* pBot);
@ -474,14 +475,15 @@ void OnBotEndLadder(AvHAIPlayer* pBot);
// Tracks all doors and their current status
void UTIL_PopulateDoors();
// Mark the door with the matching target name as weldable. Weld-activated doors leave permanent markers on the nav mesh to block movement since they can only be triggered once
void UTIL_MarkDoorWeldable(const char* DoorTargetName);
void UTIL_UpdateWeldableDoors();
void UTIL_UpdateWeldableObstacles();
void UTIL_UpdateDoors(bool bInitial = false);
void UTIL_UpdateDoorTriggers(nav_door* Door);
void UTIL_PopulateTriggersForEntity(edict_t* Entity, vector<DoorTrigger>& TriggerList);
void UTIL_PopulateWeldableObstacles();
void UTIL_ApplyTempObstaclesToDoor(nav_door* DoorRef, const int Area);
nav_door* UTIL_GetNavDoorByEdict(const edict_t* DoorEdict);
Vector UTIL_AdjustPointAwayFromNavWall(const Vector Location, const float MaxDistanceFromWall);

View file

@ -1451,6 +1451,18 @@ void StartNewBotFrame(AvHAIPlayer* pBot)
}
void DroneThink(AvHAIPlayer* pBot)
{
AITASK_BotUpdateAndClearTasks(pBot);
pBot->CurrentTask = &pBot->PrimaryBotTask;
if (pBot->PrimaryBotTask.TaskType != TASK_NONE)
{
BotProgressTask(pBot, &pBot->PrimaryBotTask);
}
}
void TestNavThink(AvHAIPlayer* pBot)
{
AITASK_BotUpdateAndClearTasks(pBot);
@ -1487,4 +1499,10 @@ void TestNavThink(AvHAIPlayer* pBot)
AITASK_ClearBotTask(pBot, &pBot->PrimaryBotTask);
}
}
}
void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot)
{
char* WeaponName = UTIL_WeaponTypeToClassname(NewWeaponSlot);
pBot->Player->SwitchWeapon(WeaponName);
}

View file

@ -53,5 +53,8 @@ void UpdateBotChat(AvHAIPlayer* pBot);
void StartNewBotFrame(AvHAIPlayer* pBot);
void TestNavThink(AvHAIPlayer* pBot);
void DroneThink(AvHAIPlayer* pBot);
void BotSwitchToWeapon(AvHAIPlayer* pBot, AvHAIWeapon NewWeaponSlot);
#endif

View file

@ -4,6 +4,7 @@
#include "AvHAITactical.h"
#include "AvHAINavigation.h"
#include "AvHAIConfig.h"
#include "AvHAIWeaponHelper.h"
#include "../AvHGamerules.h"
#include "../dlls/client.h"
#include <time.h>
@ -489,8 +490,8 @@ void AIMGR_UpdateAIPlayers()
float FrameDelta = CurrTime - PrevTime;
float ThinkDelta = CurrTime - LastThinkTime;
for (int bot_index = 0; bot_index < MAX_PLAYERS; bot_index++)
for (int bot_index = 0; bot_index < gpGlobals->maxClients; bot_index++)
{
if (!ActiveAIPlayers[bot_index].Player) { continue; } // Slot isn't filled
@ -506,7 +507,14 @@ void AIMGR_UpdateAIPlayers()
UpdateBotChat(bot);
TestNavThink(bot);
DroneThink(bot);
AvHAIWeapon DesiredWeapon = (bot->DesiredMoveWeapon != WEAPON_NONE) ? bot->DesiredMoveWeapon : bot->DesiredCombatWeapon;
if (DesiredWeapon != WEAPON_NONE && GetBotCurrentWeapon(bot) != DesiredWeapon)
{
BotSwitchToWeapon(bot, DesiredWeapon);
}
BotUpdateDesiredViewRotation(bot);
@ -706,5 +714,6 @@ AvHAIPlayer* AIMGR_GetAIPlayerAtIndex(const int Index)
void AIMGR_UpdateAIMapData()
{
UTIL_UpdateTileCache();
AITAC_UpdateMapAIData();
}

View file

@ -164,6 +164,35 @@ AvHAIBuildableStructure* AITAC_FindClosestDeployableToLocation(const Vector& Loc
return Result;
}
AvHAIDroppedItem* AITAC_FindClosestItemToLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance)
{
AvHAIDroppedItem* Result = NULL;
float CurrMinDist = 0.0f;
float MinDistSq = sqrf(MinRadius);
float MaxDistSq = sqrf(MaxRadius);
bool bUseMinDist = MinDistSq > 0.1f;
bool bUseMaxDist = MaxDistSq > 0.1f;
for (auto& it : MarineDroppedItemMap)
{
if (!it.second.bIsReachableMarine) { continue; }
if (it.second.ItemType != ItemType) { continue; }
float DistSq = (bConsiderPhaseDistance) ? sqrf(AITAC_GetPhaseDistanceBetweenPoints(it.second.Location, Location)) : vDist2DSq(it.second.Location, Location);
if ((!bUseMinDist || DistSq >= MinDistSq) && (!bUseMaxDist || DistSq <= MaxDistSq) && (!Result || DistSq < CurrMinDist))
{
Result = &it.second;
CurrMinDist = DistSq;
}
}
return Result;
}
AvHAIBuildableStructure* AITAC_GetDeployableRefFromEdict(const edict_t* Structure)
{
if (FNullEnt(Structure)) { return nullptr; }
@ -551,6 +580,8 @@ void AITAC_UpdateMapAIData()
last_item_refresh_time = gpGlobals->time;
}
UTIL_UpdateDoors(false);
AITAC_RefreshHiveData();
}

View file

@ -50,6 +50,8 @@ Vector AITAC_GetTeamStartingLocation(AvHTeamNumber Team);
AvHAIResourceNode* AITAC_GetRandomResourceNode();
AvHAIDroppedItem* AITAC_FindClosestItemToLocation(const Vector& Location, const AvHAIDeployableItemType ItemType, float MinRadius, float MaxRadius, bool bConsiderPhaseDistance);
Vector AITAC_GetFloorLocationForHive(const AvHAIHiveDefinition* Hive);
int AITAC_GetNumHives();

View file

@ -12,6 +12,7 @@
#include "../AvHSharedUtil.h"
#include "../AvHAlienWeaponConstants.h"
#include "../AvHGamerules.h"
#include "../AvHWeldable.h"
void AITASK_ClearAllBotTasks(AvHAIPlayer* pBot)
{
@ -365,7 +366,22 @@ bool AITASK_IsWeldTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
if (!Task) { return false; }
if (FNullEnt(Task->TaskTarget)) { return false; }
if (Task->TaskTarget == pBot->Edict) { return false; }
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER)) { return false; }
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
if (FNullEnt(Task->TaskSecondaryTarget))
{
AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_WELDER, 0.0f, 0.0f, true);
if (NearestWelder)
{
Task->TaskSecondaryTarget = NearestWelder->edict;
}
else
{
return false;
}
}
}
if (IsEdictPlayer(Task->TaskTarget))
{
@ -374,10 +390,22 @@ bool AITASK_IsWeldTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
else
{
if (!UTIL_IsBuildableStructureStillReachable(pBot, Task->TaskTarget)) { return false; }
if (IsEdictStructure(Task->TaskTarget))
{
if (!UTIL_IsBuildableStructureStillReachable(pBot, Task->TaskTarget)) { return false; }
return (Task->TaskTarget->v.health < Task->TaskTarget->v.max_health);
return (Task->TaskTarget->v.health < Task->TaskTarget->v.max_health);
}
AvHWeldable* WeldableRef = dynamic_cast<AvHWeldable*>(CBaseEntity::Instance(Task->TaskTarget));
if (WeldableRef)
{
return !WeldableRef->GetIsWelded();
}
}
return false;
}
bool AITASK_IsAmmoPickupTaskStillValid(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -2125,7 +2153,7 @@ void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
break;
case TASK_WELD:
MarineProgressWeldTask(pBot, Task);
BotProgressWeldTask(pBot, Task);
break;
case TASK_DEFEND:
BotProgressDefendTask(pBot, Task);
@ -2156,10 +2184,18 @@ void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
}
void MarineProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
{
//float DistFromWeldLocation = vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin);
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
if (!FNullEnt(Task->TaskSecondaryTarget))
{
MoveTo(pBot, Task->TaskSecondaryTarget->v.origin, MOVESTYLE_NORMAL);
}
return;
}
if (IsPlayerInUseRange(pBot->Edict, Task->TaskTarget))
{
@ -2177,42 +2213,17 @@ void MarineProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
}
else
{
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
if (IsEdictPlayer(Task->TaskTarget) || IsEdictStructure(Task->TaskTarget))
{
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
}
else
{
MoveTo(pBot, Task->TaskLocation, MOVESTYLE_NORMAL);
}
}
return;
if (vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(2.0f)))
{
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
return;
}
if (!UTIL_PlayerHasLOSToEntity(pBot->Edict, Task->TaskTarget, 9999.0f, false))
{
BotLookAt(pBot, UTIL_GetCentreOfEntity(Task->TaskTarget));
Vector WeldLocation = pBot->BotNavInfo.TargetDestination;
if (vIsZero(WeldLocation) || vDist2DSq(pBot->Edict->v.origin, Task->TaskTarget->v.origin) > sqrf(UTIL_MetresToGoldSrcUnits(1.5f)))
{
int MoveProfile = UTIL_GetMoveProfileForBot(pBot, MOVESTYLE_NORMAL);
WeldLocation = UTIL_GetRandomPointOnNavmeshInDonut(MoveProfile, Task->TaskTarget->v.origin, UTIL_MetresToGoldSrcUnits(1.0f), UTIL_MetresToGoldSrcUnits(1.5f));
if (vIsZero(WeldLocation))
{
WeldLocation = Task->TaskTarget->v.origin;
}
}
MoveTo(pBot, WeldLocation, MOVESTYLE_NORMAL);
return;
}
else
{
MoveTo(pBot, Task->TaskTarget->v.origin, MOVESTYLE_NORMAL);
}
}
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task)
@ -2615,6 +2626,61 @@ char* AITASK_TaskTypeToChar(const BotTaskType TaskType)
}
}
void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent)
{
if (FNullEnt(Target) || (Target->v.deadflag != DEAD_NO))
{
AITASK_ClearBotTask(pBot, Task);
return;
}
if (Task->TaskType == TASK_WELD && Task->TaskTarget == Target)
{
Task->bTaskIsUrgent = bIsUrgent;
return;
}
if (!PlayerHasWeapon(pBot->Player, WEAPON_MARINE_WELDER))
{
AvHAIDroppedItem* NearestWelder = AITAC_FindClosestItemToLocation(pBot->Edict->v.origin, DEPLOYABLE_ITEM_WELDER, 0.0f, 0.0f, true);
if (!NearestWelder)
{
AITASK_ClearBotTask(pBot, Task);
return;
}
else
{
Task->TaskSecondaryTarget = NearestWelder->edict;
}
}
Task->TaskTarget = Target;
Task->TaskType = TASK_WELD;
Task->bTaskIsUrgent = bIsUrgent;
if (IsEdictPlayer(Target) || IsEdictStructure(Target)) { return; }
Vector TargetLocation = UTIL_GetButtonFloorLocation(pBot->Edict->v.origin, Task->TaskTarget);
if (vIsZero(TargetLocation))
{
TargetLocation = Task->TaskTarget->v.origin;
}
int MoveProfile = UTIL_GetMoveProfileForBot(pBot, MOVESTYLE_NORMAL);
Vector TaskLocation = FindClosestNavigablePointToDestination(MoveProfile, pBot->Edict->v.origin, TargetLocation, UTIL_MetresToGoldSrcUnits(5.0f));
if (vIsZero(TaskLocation))
{
AITASK_ClearBotTask(pBot, Task);
return;
}
Task->TaskLocation = TaskLocation;
}
void AITASK_SetAttackTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent)
{
// Don't set the task if the target is invalid, dead or on the same team as the bot (can't picture a situation where you want them to teamkill...)

View file

@ -65,6 +65,7 @@ void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task,
void AITASK_SetReinforceStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const AvHAIDeployableStructureType FirstStructureType, bool bIsUrgent);
void AITASK_SetSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const Vector WaitLocation, bool bIsUrgent);
void AITASK_SetMineStructureTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, bool bIsUrgent);
void AITASK_SetWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task, edict_t* Target, const bool bIsUrgent);
void BotProgressTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
@ -83,7 +84,7 @@ void BotProgressEvolveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressBuildTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressCapResNodeTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void BotProgressWeldTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);
void MarineProgressSecureHiveTask(AvHAIPlayer* pBot, AvHAIPlayerTask* Task);

View file

@ -10,6 +10,7 @@
#include "../AvHGamerules.h"
#include "../AvHAlienWeaponConstants.h"
#include "../AvHAlienWeapons.h"
#include "../AvHMarineEquipmentConstants.h"
#include "../AvHServerUtil.h"
int BotGetCurrentWeaponClipAmmo(const AvHAIPlayer* pBot)
@ -985,4 +986,78 @@ float UTIL_GetProjectileVelocityForWeapon(const AvHAIWeapon Weapon)
default:
return 0.0f; // Hitscan.
}
}
char* UTIL_WeaponTypeToClassname(const AvHAIWeapon WeaponType)
{
switch (WeaponType)
{
case WEAPON_MARINE_MG:
return kwsMachineGun;
case WEAPON_MARINE_PISTOL:
return kwsPistol;
case WEAPON_MARINE_KNIFE:
return kwsKnife;
case WEAPON_MARINE_SHOTGUN:
return kwsShotGun;
case WEAPON_MARINE_HMG:
return kwsHeavyMachineGun;
case WEAPON_MARINE_WELDER:
return kwsWelder;
case WEAPON_MARINE_MINES:
return kwsMine;
case WEAPON_MARINE_GRENADE:
return kwsGrenade;
case WEAPON_MARINE_GL:
return kwsGrenadeGun;
case WEAPON_SKULK_BITE:
return kwsBiteGun;
case WEAPON_SKULK_PARASITE:
return kwsParasiteGun;
case WEAPON_SKULK_LEAP:
return kwsLeap;
case WEAPON_SKULK_XENOCIDE:
return kwsDivineWind;
case WEAPON_GORGE_SPIT:
return kwsSpitGun;
case WEAPON_GORGE_HEALINGSPRAY:
return kwsHealingSpray;
case WEAPON_GORGE_BILEBOMB:
return kwsBileBombGun;
case WEAPON_GORGE_WEB:
return kwsWebSpinner;
case WEAPON_LERK_BITE:
return kwsBite2Gun;
case WEAPON_LERK_SPORES:
return kwsSporeGun;
case WEAPON_LERK_UMBRA:
return kwsUmbraGun;
case WEAPON_LERK_PRIMALSCREAM:
return kwsPrimalScream;
case WEAPON_FADE_SWIPE:
return kwsSwipe;
case WEAPON_FADE_BLINK:
return kwsBlinkGun;
case WEAPON_FADE_METABOLIZE:
return kwsMetabolize;
case WEAPON_FADE_ACIDROCKET:
return kwsAcidRocketGun;
case WEAPON_ONOS_GORE:
return kwsClaws;
case WEAPON_ONOS_DEVOUR:
return kwsDevour;
case WEAPON_ONOS_STOMP:
return kwsStomp;
case WEAPON_ONOS_CHARGE:
return kwsCharge;
default:
return "";
}
return "";
}

View file

@ -63,4 +63,6 @@ BotAttackResult PerformAttackLOSCheck(AvHAIPlayer* pBot, const AvHAIWeapon Weapo
float UTIL_GetProjectileVelocityForWeapon(const AvHAIWeapon Weapon);
bool IsAreaAffectedBySpores(const Vector Location);
char* UTIL_WeaponTypeToClassname(const AvHAIWeapon WeaponType);
#endif

View file

@ -271,8 +271,15 @@ BOOL AvHAlienWeapon::IsUseable(void)
float theLatency = 0.0f;
#ifdef AVH_CLIENT
// : 991 -- added latency-based prediction for the ammount of energy available to the alien
//net_status_s current_status;
//gEngfuncs.pNetAPI->Status(&current_status);
net_status_s current_status;
gEngfuncs.pNetAPI->Status(&current_status);
char TestStruct[256]; // Allocate lots of memory to anticipate Svengine's larger size
gEngfuncs.pNetAPI->Status((net_status_s*)&TestStruct);
memcpy(&current_status, TestStruct, sizeof(net_status_s)); // Now copy back the original struct size. This only works if Svengine added to the struct rather than changed it completely
theLatency = max(0.0f, current_status.latency);
int theNumLevels = AvHGetAlienUpgradeLevel(this->m_pPlayer->pev->iuser4, MASK_UPGRADE_5);

View file

@ -106,7 +106,12 @@
#include "AvHNetworkMessages.h"
#include "AvHNexusServer.h"
#include "AIPlayers/AvHAIPlayerUtil.h"
#include "AIPlayers/AvHAIHelper.h"
#include "AIPlayers/AvHAIMath.h"
#include "AIPlayers/AvHAINavigation.h"
#include "AIPlayers/AvHAIPlayerManager.h"
#include "AIPlayers/AvHAITask.h"
extern AvHParticleTemplateListServer gParticleTemplateList;
extern CVoiceGameMgr g_VoiceGameMgr;
@ -1408,6 +1413,41 @@ BOOL AvHGamerules::ClientCommand( CBasePlayer *pPlayer, const char *pcmd )
theSuccess = true;
}
}
else if (FStrEq(pcmd, "tracedoor"))
{
Vector TraceStart = GetPlayerEyePosition(theAvHPlayer->edict()); // origin + pev->view_ofs
Vector LookDir = UTIL_GetForwardVector(theAvHPlayer->edict()->v.v_angle); // Converts view angles to normalized unit vector
Vector TraceEnd = TraceStart + (LookDir * 1000.0f);
edict_t* TracedEntity = UTIL_TraceEntity(theAvHPlayer->edict(), TraceStart, TraceEnd);
if (!FNullEnt(TracedEntity))
{
const nav_door* Door = UTIL_GetNavDoorByEdict(TracedEntity);
if (Door)
{
bool bThing = true;
}
}
theSuccess = true;
}
else if (FStrEq(pcmd, "cometome"))
{
for (int i = 0; i < AIMGR_GetNumAIPlayers(); i++)
{
AvHAIPlayer* thisBot = AIMGR_GetAIPlayerAtIndex(i);
if (thisBot)
{
AITASK_SetMoveTask(thisBot, &thisBot->PrimaryBotTask, theAvHPlayer->pev->origin, true);
}
}
theSuccess = true;
}
else if( FStrEq( pcmd, kcRemoveUpgrade) )
{
// Allow even with cheats off right now, put this back in for first beta

View file

@ -3608,12 +3608,12 @@ void AvHGamerules::Think(void)
if (avh_botsenabled.value > 0)
{
AIMGR_UpdateAIPlayers();
if (this->GetGameStarted())
{
AIMGR_UpdateAIMapData();
}
AIMGR_UpdateAIPlayers();
}
if(!this->GetGameStarted())

View file

@ -101,6 +101,11 @@ bool AvHWeldable::GetWeldOpens() const
return this->mWeldOpens;
}
string AvHWeldable::GetTargetOnFinish() const
{
return this->mTargetOnFinish;
}
void AvHWeldable::KeyValue( KeyValueData* pkvd )
{
// "Health to destroy once welded (-1 infinite)" : "-1"

View file

@ -43,6 +43,8 @@ public:
bool GetWeldOpens() const;
string GetTargetOnFinish() const;
virtual void KeyValue( KeyValueData* pkvd );
virtual void Killed( entvars_t *pevAttacker, int iGib );