Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
339 changes: 262 additions & 77 deletions bot.cpp

Large diffs are not rendered by default.

21 changes: 21 additions & 0 deletions bot.h
Original file line number Diff line number Diff line change
Expand Up @@ -234,6 +234,21 @@ typedef struct {
// maximum number of jobs storable in the bot's job buffer
constexpr int JOB_BUFFER_MAX = 5;

// Medic and Engineer support starts nearby and may finish a short, bounded
// chase. Urgent Medic care can override ordinary distant combat, but close
// combat and recent damage remain immediate threats and interrupt treatment.
constexpr float SUPPORT_DISCOVERY_RANGE = 400.0f;
constexpr float SUPPORT_MAX_CHASE_RANGE = 600.0f;
constexpr float SUPPORT_REQUEST_DURATION = 5.0f;
constexpr float SUPPORT_COMMIT_DURATION = 1.25f;
constexpr float MEDIC_IMMEDIATE_THREAT_RANGE = 250.0f;
constexpr float MEDIC_DAMAGE_INTERRUPT_DURATION = 1.0f;
constexpr int MEDIC_HEAL_PRIORITY_NORMAL = 540;
constexpr int MEDIC_HEAL_PRIORITY_URGENT = 710;
constexpr int ENGINEER_REPAIR_PRIORITY_NORMAL = 525;
constexpr int ENGINEER_REPAIR_PRIORITY_URGENT = 650;
constexpr int ENGINEER_CRITICAL_ARMOR_PERCENT = 25;

// this structure is shared by all the job types in the bot's job buffer
typedef struct {
float f_bufferedTime; // how long ago this job was put into the buffer
Expand Down Expand Up @@ -349,6 +364,11 @@ typedef struct {
int routeFailureTally; // used to spot successive failures to get to a waypoint goal

float f_find_item_time; // when to next check environment for interesting objects
float f_flag_toss_time; // next recipient scan or retry time
int flag_toss_target_index; // 1-based client index; 0 when no handoff is pending
float f_support_request_time; // expiration time for an urgent saveme request
int support_requester_index; // 1-based patient index; 0 when no request is active
float f_support_commit_time; // short Medic commitment/hysteresis window

float f_pause_time; // remembers when to unpause the bot
float f_duck_time; // remembers when the bot should stop crouching
Expand Down Expand Up @@ -654,6 +674,7 @@ void script(const char* sz);
int PlayerArmorPercent(const edict_t* pEdict);

int PlayerHealthPercent(const edict_t* pEdict);
bool MedicHasImmediateCombatThreat(const bot_t* pBot);

void UTIL_SavePent(edict_t* pent);

Expand Down
76 changes: 51 additions & 25 deletions bot_client.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,37 @@ bot_weapon_t weapon_defs[MAX_WEAPONS]; // array of weapon definitions

int g_state;

// Engine messages are external input to these parsers. Keep every
// weapon/ammo value inside the fixed GoldSrc arrays before it is used.
static bool ValidBotIndex(const int index) {
return index >= 0 && index < MAX_BOTS;
}

static bool ValidWeaponIndex(const int index) {
return index >= 0 && index < MAX_WEAPONS;
}

static bool ValidAmmoIndex(const int index) {
return index >= 0 && index < MAX_AMMO_SLOTS;
}

static void UpdateCurrentWeaponAmmo(const int bot_index) {
if (!ValidBotIndex(bot_index))
return;

const int weapon = bots[bot_index].current_weapon.iId;
if (!ValidWeaponIndex(weapon)) {
bots[bot_index].current_weapon.iAmmo1 = 0;
bots[bot_index].current_weapon.iAmmo2 = 0;
return;
}

const int ammo1 = weapon_defs[weapon].iAmmo1;
const int ammo2 = weapon_defs[weapon].iAmmo2;
bots[bot_index].current_weapon.iAmmo1 = ValidAmmoIndex(ammo1) ? bots[bot_index].m_rgAmmo[ammo1] : 0;
bots[bot_index].current_weapon.iAmmo2 = ValidAmmoIndex(ammo2) ? bots[bot_index].m_rgAmmo[ammo2] : 0;
}

// int MatchScores[4]; // doesn't update reliably on all maps

// This message is sent when the TFC VGUI menu is displayed.
Expand Down Expand Up @@ -152,7 +183,8 @@ void BotClient_Valve_WeaponList(void* p, const int bot_index) {

if (state == 0) {
state++;
std::strcpy(bot_weapon.szClassname, static_cast<char*>(p));
std::strncpy(bot_weapon.szClassname, static_cast<char*>(p), sizeof(bot_weapon.szClassname) - 1);
bot_weapon.szClassname[sizeof(bot_weapon.szClassname) - 1] = '\0';
}
else if (state == 1) {
state++;
Expand Down Expand Up @@ -187,8 +219,9 @@ void BotClient_Valve_WeaponList(void* p, const int bot_index) {

bot_weapon.iFlags = *static_cast<int*>(p); // flags for weapon (WTF???)

// store away this weapon with it's ammo information...
weapon_defs[bot_weapon.iId] = bot_weapon;
// Store only IDs that fit the fixed weapon definition table.
if (ValidWeaponIndex(bot_weapon.iId))
weapon_defs[bot_weapon.iId] = bot_weapon;
}
}

Expand Down Expand Up @@ -236,16 +269,14 @@ void BotClient_Valve_CurrentWeapon(void* p, const int bot_index) {

iClip = *static_cast<int*>(p); // ammo currently in the clip for this weapon

if (iId <= 31) {
bots[bot_index].bot_weapons |= 1 << iId; // set this weapon bit
if (ValidBotIndex(bot_index) && ValidWeaponIndex(iId)) {
bots[bot_index].bot_weapons |= static_cast<int>(1u << iId); // set this weapon bit

if (iState == 1) {
bots[bot_index].current_weapon.iId = iId;
bots[bot_index].current_weapon.iClip = iClip;

// update the ammo counts for this weapon...
bots[bot_index].current_weapon.iAmmo1 = bots[bot_index].m_rgAmmo[weapon_defs[iId].iAmmo1];
bots[bot_index].current_weapon.iAmmo2 = bots[bot_index].m_rgAmmo[weapon_defs[iId].iAmmo2];
UpdateCurrentWeaponAmmo(bot_index);
}
}
}
Expand Down Expand Up @@ -289,13 +320,10 @@ void BotClient_Valve_AmmoX(void* p, const int bot_index) {

ammount = *static_cast<int*>(p); // the amount of ammo currently available

bots[bot_index].m_rgAmmo[index] = ammount; // store it away

const int ammo_index = bots[bot_index].current_weapon.iId;

// update the ammo counts for this weapon...
bots[bot_index].current_weapon.iAmmo1 = bots[bot_index].m_rgAmmo[weapon_defs[ammo_index].iAmmo1];
bots[bot_index].current_weapon.iAmmo2 = bots[bot_index].m_rgAmmo[weapon_defs[ammo_index].iAmmo2];
if (ValidBotIndex(bot_index) && ValidAmmoIndex(index)) {
bots[bot_index].m_rgAmmo[index] = ammount; // store it away
UpdateCurrentWeaponAmmo(bot_index);
}
}
}

Expand Down Expand Up @@ -340,13 +368,10 @@ void BotClient_Valve_AmmoPickup(void* p, const int bot_index) {

ammount = *static_cast<int*>(p);

bots[bot_index].m_rgAmmo[index] = ammount;

const int ammo_index = bots[bot_index].current_weapon.iId;

// update the ammo counts for this weapon...
bots[bot_index].current_weapon.iAmmo1 = bots[bot_index].m_rgAmmo[weapon_defs[ammo_index].iAmmo1];
bots[bot_index].current_weapon.iAmmo2 = bots[bot_index].m_rgAmmo[weapon_defs[ammo_index].iAmmo2];
if (ValidBotIndex(bot_index) && ValidAmmoIndex(index)) {
bots[bot_index].m_rgAmmo[index] = ammount;
UpdateCurrentWeaponAmmo(bot_index);
}
}
}

Expand Down Expand Up @@ -377,8 +402,9 @@ void BotClient_FLF_AmmoPickup(void* p, int bot_index)
void BotClient_Valve_WeaponPickup(void* p, const int bot_index) {
const int index = *static_cast<int*>(p);

// set this weapon bit to indicate that we are carrying this weapon
bots[bot_index].bot_weapons |= 1 << index;
// Set this weapon bit only when both fixed-array indices are valid.
if (ValidBotIndex(bot_index) && ValidWeaponIndex(index))
bots[bot_index].bot_weapons |= static_cast<int>(1u << index);
}

void BotClient_TFC_WeaponPickup(void* p, const int bot_index) {
Expand Down Expand Up @@ -1108,4 +1134,4 @@ void BotClient_TFC_Scores(void* p, int bot_index)
else if (state == 2)
state = 0;
}
#endif
#endif
Loading