// // HPB bot - botman's High Ping Bastard bot // // (http://planethalflife.com/botman/) // // bot_combat.cpp // #include "dlls/extdll.h" #include "dlls/util.h" #include "dlls/cbase.h" #include "bot.h" #include "bot_func.h" #include "bot_weapons.h" #include "mod/AvHBasePlayerWeaponConstants.h" #include "mod/AvHMarineWeaponConstants.h" #include "mod/AvHAlienWeaponConstants.h" #include "mod/AvHPlayer.h" extern int mod_id; extern bot_weapon_t weapon_defs[MAX_WEAPONS]; extern bool b_observer_mode; extern int team_allies[4]; extern edict_t *pent_info_ctfdetect; extern float is_team_play; extern bool checked_teamplay; FILE *fp; typedef struct { int iId; // the weapon ID value char weapon_name[64]; // name of the weapon when selecting it int skill_level; // bot skill must be less than or equal to this value float primary_min_distance; // 0 = no minimum float primary_max_distance; // 9999 = no maximum float secondary_min_distance; // 0 = no minimum float secondary_max_distance; // 9999 = no maximum int use_percent; // times out of 100 to use this weapon when available bool can_use_underwater; // can use this weapon underwater int primary_fire_percent; // times out of 100 to use primary fire int min_primary_ammo; // minimum ammout of primary ammo needed to fire int min_secondary_ammo; // minimum ammout of seconday ammo needed to fire bool primary_fire_hold; // hold down primary fire button to use? bool secondary_fire_hold; // hold down secondary fire button to use? bool primary_fire_charge; // charge weapon using primary fire? bool secondary_fire_charge; // charge weapon using secondary fire? float primary_charge_delay; // time to charge weapon float secondary_charge_delay; // time to charge weapon } bot_weapon_select_t; typedef struct { int iId; float primary_base_delay; float primary_min_delay[5]; float primary_max_delay[5]; float secondary_base_delay; float secondary_min_delay[5]; float secondary_max_delay[5]; } bot_fire_delay_t; // weapons are stored in priority order, most desired weapon should be at // the start of the array and least desired should be at the end bot_weapon_select_t valve_weapon_select[] = { {VALVE_WEAPON_CROWBAR, "weapon_crowbar", 2, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_HANDGRENADE, "weapon_handgrenade", 5, 250.0, 750.0, 0.0, 0.0, 30, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_SNARK, "weapon_snark", 5, 150.0, 500.0, 0.0, 0.0, 50, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_EGON, "weapon_egon", 5, 0.0, 9999.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_GAUSS, "weapon_gauss", 5, 0.0, 9999.0, 0.0, 9999.0, 100, FALSE, 80, 1, 10, FALSE, FALSE, FALSE, TRUE, 0.0, 0.8}, {VALVE_WEAPON_SHOTGUN, "weapon_shotgun", 5, 30.0, 150.0, 30.0, 150.0, 100, FALSE, 70, 1, 2, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_PYTHON, "weapon_357", 5, 30.0, 700.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_HORNETGUN, "weapon_hornetgun", 5, 30.0, 1000.0, 30.0, 1000.0, 100, TRUE, 50, 1, 4, FALSE, TRUE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_MP5, "weapon_9mmAR", 5, 0.0, 250.0, 300.0, 600.0, 100, FALSE, 90, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_CROSSBOW, "weapon_crossbow", 5, 100.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_RPG, "weapon_rpg", 5, 300.0, 9999.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {VALVE_WEAPON_GLOCK, "weapon_9mmhandgun", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 70, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, /* terminator */ {0, "", 0, 0.0, 0.0, 0.0, 0.0, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0} }; bot_weapon_select_t tfc_weapon_select[] = { {TF_WEAPON_AXE, "tf_weapon_axe", 5, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_KNIFE, "tf_weapon_knife", 5, 0.0, 40.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_SPANNER, "tf_weapon_knife", 5, 0.0, 40.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_MEDIKIT, "tf_weapon_medikit", 5, 0.0, 40.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_SNIPERRIFLE, "tf_weapon_sniperrifle", 5, 300.0, 2500.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, TRUE, FALSE, 1.0, 0.0}, {TF_WEAPON_FLAMETHROWER, "tf_weapon_flamethrower", 5, 100.0, 500.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_AC, "tf_weapon_ac", 5, 50.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_GL, "tf_weapon_gl", 5, 300.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_RPG, "tf_weapon_rpg", 5, 300.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_IC, "tf_weapon_ic", 5, 300.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_TRANQ, "tf_weapon_tranq", 5, 40.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_RAILGUN, "tf_weapon_railgun", 5, 40.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_SUPERNAILGUN, "tf_weapon_superng", 5, 40.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_SUPERSHOTGUN, "tf_weapon_supershotgun", 5, 40.0, 500.0, 0.0, 0.0, 100, TRUE, 100, 2, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_AUTORIFLE, "tf_weapon_autorifle", 5, 0.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_SHOTGUN, "tf_weapon_shotgun", 5, 40.0, 400.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {TF_WEAPON_NAILGUN, "tf_weapon_ng", 5, 40.0, 600.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, /* terminator */ {0, "", 0, 0.0, 0.0, 0.0, 0.0, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0} }; bot_weapon_select_t cs_weapon_select[] = { {CS_WEAPON_KNIFE, "weapon_knife", 5, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {CS_WEAPON_USP, "weapon_usp", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {CS_WEAPON_GLOCK18, "weapon_glock18", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, /* terminator */ {0, "", 0, 0.0, 0.0, 0.0, 0.0, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0} }; bot_weapon_select_t gearbox_weapon_select[] = { {GEARBOX_WEAPON_PIPEWRENCH, "weapon_pipewrench", 3, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_KNIFE, "weapon_knife", 4, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_CROWBAR, "weapon_crowbar", 2, 0.0, 50.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_DISPLACER, "weapon_displacer", 5, 100.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_SPORELAUNCHER, "weapon_sporelauncher", 5, 500.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_SHOCKRIFLE, "weapon_shockrifle", 5, 50.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_SNIPERRIFLE, "weapon_sniperrifle", 5, 50.0, 1500.0, 0.0, 0.0, 100, FALSE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_HANDGRENADE, "weapon_handgrenade", 5, 250.0, 750.0, 0.0, 0.0, 30, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_SNARK, "weapon_snark", 5, 150.0, 500.0, 0.0, 0.0, 50, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_EGON, "weapon_egon", 5, 0.0, 9999.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_GAUSS, "weapon_gauss", 5, 0.0, 9999.0, 0.0, 9999.0, 100, FALSE, 80, 1, 10, FALSE, FALSE, FALSE, TRUE, 0.0, 0.8}, {GEARBOX_WEAPON_M249, "weapon_m249", 5, 0.0, 400.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_SHOTGUN, "weapon_shotgun", 5, 30.0, 150.0, 30.0, 150.0, 100, FALSE, 70, 1, 2, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_EAGLE, "weapon_eagle", 5, 0.0, 1200.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_PYTHON, "weapon_357", 5, 30.0, 700.0, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_HORNETGUN, "weapon_hornetgun", 5, 30.0, 1000.0, 30.0, 1000.0, 100, TRUE, 50, 1, 4, FALSE, TRUE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_MP5, "weapon_9mmAR", 5, 0.0, 250.0, 300.0, 600.0, 100, FALSE, 90, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_CROSSBOW, "weapon_crossbow", 5, 100.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_RPG, "weapon_rpg", 5, 300.0, 9999.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {GEARBOX_WEAPON_GLOCK, "weapon_9mmhandgun", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 70, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, /* terminator */ {0, "", 0, 0.0, 0.0, 0.0, 0.0, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0} }; bot_weapon_select_t frontline_weapon_select[] = { {FLF_WEAPON_HEGRENADE, "weapon_hegrenade", 3, 200.0, 1000.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_FLASHBANG, "weapon_flashbang", 3, 100.0, 800.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, // {FLF_WEAPON_KNIFE, "weapon_knife", 3, 0.0, 60.0, 0.0, 0.0, // 100, TRUE, 100, 0, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_HK21, "weapon_hk21", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_UMP45, "weapon_ump45", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_FAMAS, "weapon_famas", 5, 0.0, 500.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_MSG90, "weapon_msg90", 5, 0.0, 2500.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_MP5A2, "weapon_mp5a2", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_AK5, "weapon_ak5", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_MP5SD, "weapon_mp5sd", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_M4, "weapon_m4", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_SPAS12, "weapon_spas12", 5, 0.0, 900.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_MAC10, "weapon_mac10", 5, 0.0, 500.0, 0.0, 0.0, 100, TRUE, 100, 1, 0, TRUE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_BERETTA, "weapon_beretta", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, {FLF_WEAPON_MK23, "weapon_mk23", 5, 0.0, 1200.0, 0.0, 1200.0, 100, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0}, /* terminator */ {0, "", 0, 0.0, 0.0, 0.0, 0.0, 0, TRUE, 0, 1, 1, FALSE, FALSE, FALSE, FALSE, 0.0, 0.0} }; //int iId; // the weapon ID value //char weapon_name[64]; // name of the weapon when selecting it //int skill_level; // bot skill must be less than or equal to this value //float primary_min_distance; // 0 = no minimum //float primary_max_distance; // 9999 = no maximum //float secondary_min_distance; // 0 = no minimum //float secondary_max_distance; // 9999 = no maximum //int use_percent; // times out of 100 to use this weapon when available //bool can_use_underwater; // can use this weapon underwater //int primary_fire_percent; // times out of 100 to use primary fire //int min_primary_ammo; // minimum ammout of primary ammo needed to fire //int min_secondary_ammo; // minimum ammout of seconday ammo needed to fire //bool primary_fire_hold; // hold down primary fire button to use? //bool secondary_fire_hold; // hold down secondary fire button to use? //bool primary_fire_charge; // charge weapon using primary fire? //bool secondary_fire_charge; // charge weapon using secondary fire? //float primary_charge_delay; // time to charge weapon //float secondary_charge_delay; // time to charge weapon bot_weapon_select_t ns_weapon_select[] = { {AVH_WEAPON_GRENADE_GUN, kwsGrenadeGun, 3, 120, kGGRange, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_SONIC, kwsShotGun, 3, 0.0, kSGRange, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_HMG, kwsHeavyMachineGun, 3, 0.0, kHMGRange, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_MG, kwsMachineGun, 3, 0.0, kMGRange, 0.0, 0.0, 100, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_PISTOL, kwsPistol, 3, 0.0, kHGRange, 0.0, 0.0, 75, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_KNIFE, kwsKnife, 3, 0.0, kKNRange, 0.0, 0.0, 50, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, // Base alien abilities {AVH_WEAPON_PRIMALSCREAM, kwsPrimalScream, 3, 0.0, 300, 0.0, 0.0, 20, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_SWIPE, kwsSwipe, 3, 0.0, kSwipeRange, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_SPORES, kwsSporeGun, 3, 0.0, kSporeRange, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_SPIT, kwsSpitGun, 3, 0.0, kSpitGRange, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_CLAWS, kwsClaws, 3, 0.0, kClawsRange, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_BITE, kwsBiteGun, 3, 0.0, 60, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_ABILITY_LEAP, kwsLeap, 3, 0.0, 600, 0.0, 0.0, 50, FALSE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, {AVH_WEAPON_SPIKE, kwsSpikeGun, 3, 0.0, kSpikeRange, 0.0, 0.0, 75, TRUE, 100, 1, 0, FALSE, FALSE, FALSE, FALSE, 0.0f, 0.0f}, }; // weapon firing delay based on skill (min and max delay for each weapon) // THESE MUST MATCH THE SAME ORDER AS THE WEAPON SELECT ARRAY!!! bot_fire_delay_t valve_fire_delay[] = { {VALVE_WEAPON_CROWBAR, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_HANDGRENADE, 0.1, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_SNARK, 0.1, {0.0, 0.1, 0.2, 0.4, 0.6}, {0.1, 0.2, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_EGON, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_GAUSS, 0.2, {0.0, 0.2, 0.3, 0.5, 1.0}, {0.1, 0.3, 0.5, 0.8, 1.2}, 1.0, {0.2, 0.3, 0.5, 0.8, 1.2}, {0.5, 0.7, 1.0, 1.5, 2.0}}, {VALVE_WEAPON_SHOTGUN, 0.75, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}, 1.5, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}}, {VALVE_WEAPON_PYTHON, 0.75, {0.0, 0.2, 0.4, 1.0, 1.5}, {0.25, 0.5, 0.8, 1.3, 2.2}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_HORNETGUN, 0.25, {0.0, 0.25, 0.4, 0.6, 1.0}, {0.1, 0.4, 0.7, 1.0, 1.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_MP5, 0.1, {0.0, 0.1, 0.25, 0.4, 0.5}, {0.1, 0.3, 0.45, 0.65, 0.8}, 1.0, {0.0, 0.4, 0.7, 1.0, 1.4}, {0.3, 0.7, 1.0, 1.6, 2.0}}, {VALVE_WEAPON_CROSSBOW, 0.75, {0.0, 0.2, 0.5, 0.8, 1.0}, {0.25, 0.4, 0.7, 1.0, 1.3}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_RPG, 1.5, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {VALVE_WEAPON_GLOCK, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.2, {0.0, 0.0, 0.1, 0.1, 0.2}, {0.1, 0.1, 0.2, 0.2, 0.4}}, /* terminator */ {0, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; bot_fire_delay_t tfc_fire_delay[] = { {TF_WEAPON_AXE, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_KNIFE, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_SPANNER, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_MEDIKIT, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_SNIPERRIFLE, 1.0, {0.0, 0.4, 0.7, 1.0, 1.4}, {0.3, 0.7, 1.0, 1.6, 2.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_FLAMETHROWER, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_AC, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_GL, 0.6, {0.0, 0.2, 0.5, 0.8, 1.0}, {0.25, 0.4, 0.7, 1.0, 1.3}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_RPG, 0.5, {0.0, 0.1, 0.3, 0.6, 1.0}, {0.1, 0.2, 0.7, 1.0, 2.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_IC, 2.0, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_TRANQ, 1.5, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_RAILGUN, 0.4, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_SUPERNAILGUN, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_SUPERSHOTGUN, 0.6, {0.0, 0.2, 0.5, 0.8, 1.0}, {0.25, 0.4, 0.7, 1.0, 1.3}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_AUTORIFLE, 0.1, {0.0, 0.1, 0.2, 0.4, 0.6}, {0.1, 0.2, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_SHOTGUN, 0.5, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {TF_WEAPON_NAILGUN, 0.1, {0.0, 0.1, 0.2, 0.4, 0.6}, {0.1, 0.2, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, /* terminator */ {0, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; bot_fire_delay_t cs_fire_delay[] = { {CS_WEAPON_KNIFE, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {CS_WEAPON_USP, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.2, {0.0, 0.0, 0.1, 0.1, 0.2}, {0.1, 0.1, 0.2, 0.2, 0.4}}, {CS_WEAPON_GLOCK18, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.2, {0.0, 0.0, 0.1, 0.1, 0.2}, {0.1, 0.1, 0.2, 0.2, 0.4}}, /* terminator */ {0, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; bot_fire_delay_t gearbox_fire_delay[] = { {GEARBOX_WEAPON_PIPEWRENCH, 0.5, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_KNIFE, 0.4, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_CROWBAR, 0.3, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_DISPLACER, 5.0, {0.0, 0.5, 0.8, 1.6, 2.5}, {0.3, 0.8, 1.4, 2.2, 3.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_SPORELAUNCHER, 0.5, {0.0, 0.2, 0.3, 0.4, 0.6}, {0.1, 0.3, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_SHOCKRIFLE, 0.1, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_SNIPERRIFLE, 1.5, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_HANDGRENADE, 0.1, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_SNARK, 0.1, {0.0, 0.1, 0.2, 0.4, 0.6}, {0.1, 0.2, 0.5, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_EGON, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_GAUSS, 0.2, {0.0, 0.2, 0.3, 0.5, 1.0}, {0.1, 0.3, 0.5, 0.8, 1.2}, 1.0, {0.2, 0.3, 0.5, 0.8, 1.2}, {0.5, 0.7, 1.0, 1.5, 2.0}}, {GEARBOX_WEAPON_M249, 0.1, {0.0, 0.1, 0.25, 0.4, 0.5}, {0.1, 0.3, 0.45, 0.65, 0.8}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_SHOTGUN, 0.75, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}, 1.5, {0.0, 0.2, 0.4, 0.6, 0.8}, {0.25, 0.5, 0.8, 1.2, 2.0}}, {GEARBOX_WEAPON_EAGLE, 0.25, {0.0, 0.1, 0.2, 0.3, 0.5}, {0.1, 0.25, 0.4, 0.7, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_PYTHON, 0.75, {0.0, 0.2, 0.4, 1.0, 1.5}, {0.25, 0.5, 0.8, 1.3, 2.2}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_HORNETGUN, 0.25, {0.0, 0.25, 0.4, 0.6, 1.0}, {0.1, 0.4, 0.7, 1.0, 1.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_MP5, 0.1, {0.0, 0.1, 0.25, 0.4, 0.5}, {0.1, 0.3, 0.45, 0.65, 0.8}, 1.0, {0.0, 0.4, 0.7, 1.0, 1.4}, {0.3, 0.7, 1.0, 1.6, 2.0}}, {GEARBOX_WEAPON_CROSSBOW, 0.75, {0.0, 0.2, 0.5, 0.8, 1.0}, {0.25, 0.4, 0.7, 1.0, 1.3}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_RPG, 1.5, {1.0, 2.0, 3.0, 4.0, 5.0}, {3.0, 4.0, 5.0, 6.0, 7.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {GEARBOX_WEAPON_GLOCK, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.2, {0.0, 0.0, 0.1, 0.1, 0.2}, {0.1, 0.1, 0.2, 0.2, 0.4}}, /* terminator */ {0, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; bot_fire_delay_t frontline_fire_delay[] = { {FLF_WEAPON_HEGRENADE, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_FLASHBANG, 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, // {FLF_WEAPON_KNIFE, // 0.3, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, // 0.2, {0.0, 0.0, 0.1, 0.1, 0.2}, {0.1, 0.1, 0.2, 0.2, 0.4}}, {FLF_WEAPON_HK21, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_UMP45, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_FAMAS, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_MSG90, 1.2, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_MP5A2, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_AK5, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_MP5SD, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_M4, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_SPAS12, 0.9, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_MAC10, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_BERETTA, 0.4, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {FLF_WEAPON_MK23, 0.4, {0.0, 0.1, 0.2, 0.3, 0.4}, {0.1, 0.2, 0.3, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, /* terminator */ {0, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; bot_fire_delay_t ns_fire_delay[] = { {AVH_WEAPON_GRENADE_GUN, 0.3f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.2, 0.3, 0.4, 0.5, 1.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_SONIC, 0.0f, {0.0, 0.0, 0.0, 0.1, 0.3}, {0.0, 0.0, 0.0, 0.2, 0.6}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_HMG, 0.0f, {0.0, 0.0, 0.0, 0.1, 0.3}, {0.0, 0.0, 0.0, 0.2, 0.6}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_MG, 0.0f, {0.0, 0.0, 0.0, 0.1, 0.3}, {0.0, 0.0, 0.0, 0.2, 0.6}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_PISTOL, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.1, 0.15, 0.2, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_KNIFE, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_PRIMALSCREAM, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_SWIPE, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_SPORES, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_SPIT, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_CLAWS, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.4, 0.5}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_BITE, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_ABILITY_LEAP, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}}, {AVH_WEAPON_SPIKE, 0.0f, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}, 0.0, {0.0, 0.0, 0.0, 0.0, 0.0}, {0.0, 0.0, 0.0, 0.0, 0.0}} }; void BotCheckTeamplay(void) { // is this TFC or Counter-Strike or OpFor teamplay or FrontLineForce? if ((mod_id == TFC_DLL) || (mod_id == CSTRIKE_DLL) || ((mod_id == GEARBOX_DLL) && (pent_info_ctfdetect != NULL)) || (mod_id == FRONTLINE_DLL) || (mod_id == AVH_DLL) ) is_team_play = 1.0; else is_team_play = CVAR_GET_FLOAT("mp_teamplay"); // teamplay enabled? checked_teamplay = TRUE; } edict_t *BotFindEnemy( bot_t *pBot ) { Vector vecEnd; static bool flag=TRUE; edict_t *pent = NULL; edict_t *pNewEnemy; float nearestdistance; int i; edict_t *pEdict = pBot->pEdict; if(pBot->mBotPlayMode != PLAYMODE_PLAYING) { pBot->pBotEnemy = NULL; } if (pBot->pBotEnemy != NULL) // does the bot already have an enemy? { vecEnd = pBot->pBotEnemy->v.origin + pBot->pBotEnemy->v.view_ofs; // if the enemy is dead? if (!IsAlive(pBot->pBotEnemy)) // is the enemy dead?, assume bot killed it { // the enemy is dead, jump for joy about 10% of the time if (RANDOM_LONG(1, 100) <= 10) pEdict->v.button |= IN_JUMP; // Taunt sometimes after a kill if(RANDOM_LONG(1, 5) == 1) { pBot->mTimeOfNextTaunt = gpGlobals->time + RANDOM_FLOAT(.3f, 4.0f); } // don't have an enemy anymore so null out the pointer... pBot->pBotEnemy = NULL; } else if (FInViewCone( &vecEnd, pEdict ) && FVisible( vecEnd, pEdict )) { if ((mod_id == TFC_DLL) && (pEdict->v.playerclass == TFC_CLASS_MEDIC)) { if (pBot->pBotEnemy->v.health >= pBot->pBotEnemy->v.max_health) { pBot->pBotEnemy = NULL; // player is healed, null out pointer } } else { // if enemy is still visible and in field of view, keep it // face the enemy Vector v_enemy = pBot->pBotEnemy->v.origin - pEdict->v.origin; Vector bot_angles = UTIL_VecToAngles( v_enemy ); pEdict->v.ideal_yaw = bot_angles.y; BotFixIdealYaw(pEdict); // keep track of when we last saw an enemy pBot->f_bot_see_enemy_time = gpGlobals->time; return (pBot->pBotEnemy); } } } pent = NULL; pNewEnemy = NULL; nearestdistance = 1000; if (mod_id == TFC_DLL) { if (pEdict->v.playerclass == TFC_CLASS_MEDIC) { // search the world for players... for (i = 1; i <= gpGlobals->maxClients; i++) { edict_t *pPlayer = INDEXENT(i); // skip invalid players and skip self (i.e. this bot) if ((pPlayer) && (!pPlayer->free) && (pPlayer != pEdict)) { // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pPlayer)) continue; if ((b_observer_mode) && !(pPlayer->v.flags & FL_FAKECLIENT)) continue; int player_team = UTIL_GetTeam(pPlayer); int bot_team = UTIL_GetTeam(pEdict); // don't target your enemies... if ((bot_team != player_team) && !(team_allies[bot_team] & (1<v.health / pPlayer->v.max_health) > 0.50) continue; // health greater than 50% so ignore vecEnd = pPlayer->v.origin + pPlayer->v.view_ofs; // see if bot can see the player... if (FInViewCone( &vecEnd, pEdict ) && FVisible( vecEnd, pEdict )) { float distance = (pPlayer->v.origin - pEdict->v.origin).Length(); if (distance < nearestdistance) { nearestdistance = distance; pNewEnemy = pPlayer; pBot->pBotUser = NULL; // don't follow user when enemy found } } } } } if (pNewEnemy == NULL) { while ((pent = UTIL_FindEntityByClassname( pent, "building_sentrygun" )) != NULL) { int sentry_team = -1; int bot_team = UTIL_GetTeam(pEdict); if (pent->v.colormap == 0xA096) sentry_team = 0; // blue team's sentry else if (pent->v.colormap == 0x04FA) sentry_team = 1; // red team's sentry else if (pent->v.colormap == 0x372D) sentry_team = 2; // yellow team's sentry else if (pent->v.colormap == 0x6E64) sentry_team = 3; // green team's sentry // don't target your own team's sentry guns... if (bot_team == sentry_team) continue; // don't target your allie's sentry guns either... if (team_allies[bot_team] & (1<v.origin + pent->v.view_ofs; // is this sentry gun visible? if (FInViewCone( &vecEnd, pEdict ) && FVisible( vecEnd, pEdict )) { float distance = (pent->v.origin - pEdict->v.origin).Length(); // is this the closest sentry gun? if (distance < nearestdistance) { nearestdistance = distance; pNewEnemy = pent; pBot->pBotUser = NULL; // don't follow user when enemy found } } } } } if (pNewEnemy == NULL) { nearestdistance = 2500; // search the world for players...(and enemies) //for (i = 1; i <= gpGlobals->maxClients; i++) for (i = 1; i <= gpGlobals->maxEntities; i++) { edict_t *pEntity = INDEXENT(i); // skip invalid players and skip self (i.e. this bot) if ((pEntity) && (!pEntity->free) && (pEntity != pEdict) && (FBitSet(pEntity->v.flags, FL_MONSTER) || FBitSet(pEntity->v.flags, FL_CLIENT))) { // Skip webs? const char* theClassName = STRING(pEntity->v.classname); // Skip cloaked players // if(pEntity->v.iuser4 & MASK_ALIEN_CLOAKED) // continue; // skip this player if not alive (i.e. dead or dying) if (!IsAlive(pEntity)) continue; if ((b_observer_mode) && !(pEntity->v.flags & FL_FAKECLIENT)) continue; if (!checked_teamplay) // check for team play... BotCheckTeamplay(); // is team play enabled? if (is_team_play > 0.0) { int player_team = UTIL_GetTeam(pEntity); int bot_team = UTIL_GetTeam(pEdict); // don't target your teammates... if (bot_team == player_team) continue; if (mod_id == TFC_DLL) { // don't target your allies either... if (team_allies[bot_team] & (1<v.origin + pEntity->v.view_ofs; float theDistance = (pEdict->v.origin - pEntity->v.origin).Length(); // see if bot can see the player... if ((FInViewCone( &vecEnd, pEdict ) && FVisible( vecEnd, pEdict )) || (theDistance < 32)) { float distance = (pEntity->v.origin - pEdict->v.origin).Length(); if (distance < nearestdistance) { nearestdistance = distance; pNewEnemy = pEntity; pBot->pBotUser = NULL; // don't follow user when enemy found } } } } } if (pNewEnemy) { // face the enemy Vector v_enemy = pNewEnemy->v.origin - pEdict->v.origin; Vector bot_angles = UTIL_VecToAngles( v_enemy ); pEdict->v.ideal_yaw = bot_angles.y; BotFixIdealYaw(pEdict); // keep track of when we last saw an enemy pBot->f_bot_see_enemy_time = gpGlobals->time; } // has the bot NOT seen an ememy for at least 5 seconds (time to reload)? if ((pBot->f_bot_see_enemy_time > 0) && ((pBot->f_bot_see_enemy_time + 5.0) <= gpGlobals->time)) { pBot->f_bot_see_enemy_time = -1; // so we won't keep reloading if ((mod_id == VALVE_DLL) || (mod_id == GEARBOX_DLL) || (mod_id == AVH_DLL)) { pEdict->v.button |= IN_RELOAD; // press reload button } } return (pNewEnemy); } Vector BotBodyTarget( edict_t *pBotEnemy, bot_t *pBot ) { Vector target; float f_distance; float f_scale; int d_x = 0; int d_y = 0; int d_z = 0; edict_t *pEdict = pBot->pEdict; f_distance = (pBotEnemy->v.origin - pEdict->v.origin).Length(); if (f_distance > 1000) f_scale = 1.0; else if (f_distance > 100) f_scale = f_distance / 1000.0; else f_scale = 0.1; switch (pBot->bot_skill) { case 0: // VERY GOOD, same as from CBasePlayer::BodyTarget (in player.h) target = pBotEnemy->v.origin + pBotEnemy->v.view_ofs * RANDOM_FLOAT( 0.5, 1.1 ); d_x = 0; // no offset d_y = 0; d_z = 0; break; case 1: // GOOD, offset a little for x, y, and z target = pBotEnemy->v.origin + pBotEnemy->v.view_ofs; // aim for the head (if you can find it) d_x = RANDOM_FLOAT(-5, 5) * f_scale; d_y = RANDOM_FLOAT(-5, 5) * f_scale; d_z = RANDOM_FLOAT(-10, 10) * f_scale; break; case 2: // FAIR, offset somewhat for x, y, and z target = pBotEnemy->v.origin; // aim for the body d_x = RANDOM_FLOAT(-10, 10) * f_scale; d_y = RANDOM_FLOAT(-10, 10) * f_scale; d_z = RANDOM_FLOAT(-18, 18) * f_scale; break; case 3: // POOR, offset for x, y, and z target = pBotEnemy->v.origin; // aim for the body d_x = RANDOM_FLOAT(-20, 20) * f_scale; d_y = RANDOM_FLOAT(-20, 20) * f_scale; d_z = RANDOM_FLOAT(-32, 32) * f_scale; break; case 4: // BAD, offset lots for x, y, and z target = pBotEnemy->v.origin; // aim for the body d_x = RANDOM_FLOAT(-35, 35) * f_scale; d_y = RANDOM_FLOAT(-35, 35) * f_scale; d_z = RANDOM_FLOAT(-50, 50) * f_scale; break; } target = target + Vector(d_x, d_y, d_z); return target; } // specifing a weapon_choice allows you to choose the weapon the bot will // use (assuming enough ammo exists for that weapon) // BotFireWeapon will return TRUE if weapon was fired, FALSE otherwise bool BotFireWeapon( Vector v_enemy, bot_t *pBot, int weapon_choice) { bot_weapon_select_t *pSelect = NULL; bot_fire_delay_t *pDelay = NULL; int select_index; int iId; bool use_primary; bool use_secondary; int use_percent; int primary_percent; edict_t *pEdict = pBot->pEdict; float distance = v_enemy.Length(); // how far away is the enemy? if (mod_id == VALVE_DLL) { pSelect = &valve_weapon_select[0]; pDelay = &valve_fire_delay[0]; } else if (mod_id == TFC_DLL) { pSelect = &tfc_weapon_select[0]; pDelay = &tfc_fire_delay[0]; } else if (mod_id == CSTRIKE_DLL) { pSelect = &cs_weapon_select[0]; pDelay = &cs_fire_delay[0]; } else if (mod_id == GEARBOX_DLL) { pSelect = &gearbox_weapon_select[0]; pDelay = &gearbox_fire_delay[0]; } else if (mod_id == FRONTLINE_DLL) { pSelect = &frontline_weapon_select[0]; pDelay = &frontline_fire_delay[0]; } else if (mod_id == AVH_DLL) { pSelect = &ns_weapon_select[0]; pDelay = &ns_fire_delay[0]; } if (pSelect) { // are we charging the primary fire? if (pBot->f_primary_charging > 0) { iId = pBot->charging_weapon_id; if (mod_id == TFC_DLL) { if (iId == TF_WEAPON_SNIPERRIFLE) { pBot->f_move_speed = 0; // don't move while using sniper rifle } } // is it time to fire the charged weapon? if (pBot->f_primary_charging <= gpGlobals->time) { // we DON'T set pEdict->v.button here to release the // fire button which will fire the charged weapon pBot->f_primary_charging = -1; // -1 means not charging // find the correct fire delay for this weapon select_index = 0; while ((pSelect[select_index].iId) && (pSelect[select_index].iId != iId)) select_index++; // set next time to shoot int skill = pBot->bot_skill; float base_delay, min_delay, max_delay; base_delay = pDelay[select_index].primary_base_delay; min_delay = pDelay[select_index].primary_min_delay[skill]; max_delay = pDelay[select_index].primary_max_delay[skill]; pBot->f_shoot_time = gpGlobals->time + base_delay + RANDOM_FLOAT(min_delay, max_delay); return TRUE; } else { pEdict->v.button |= IN_ATTACK; // charge the weapon pBot->f_shoot_time = gpGlobals->time; // keep charging return TRUE; } } // are we charging the secondary fire? if (pBot->f_secondary_charging > 0) { iId = pBot->charging_weapon_id; // is it time to fire the charged weapon? if (pBot->f_secondary_charging <= gpGlobals->time) { // we DON'T set pEdict->v.button here to release the // fire button which will fire the charged weapon pBot->f_secondary_charging = -1; // -1 means not charging // find the correct fire delay for this weapon select_index = 0; while ((pSelect[select_index].iId) && (pSelect[select_index].iId != iId)) select_index++; // set next time to shoot int skill = pBot->bot_skill; float base_delay, min_delay, max_delay; base_delay = pDelay[select_index].secondary_base_delay; min_delay = pDelay[select_index].secondary_min_delay[skill]; max_delay = pDelay[select_index].secondary_max_delay[skill]; pBot->f_shoot_time = gpGlobals->time + base_delay + RANDOM_FLOAT(min_delay, max_delay); return TRUE; } else { pEdict->v.button |= IN_ATTACK2; // charge the weapon pBot->f_shoot_time = gpGlobals->time; // keep charging return TRUE; } } select_index = 0; // loop through all the weapons until terminator is found... while (pSelect[select_index].iId) { // was a weapon choice specified? (and if so do they NOT match?) if ((weapon_choice != 0) && (weapon_choice != pSelect[select_index].iId)) { select_index++; // skip to next weapon continue; } // is the bot NOT carrying this weapon? if (!(pBot->bot_weapons & (1<bot_skill+1) > pSelect[select_index].skill_level) { select_index++; // skip to next weapon continue; } // is the bot underwater and does this weapon NOT work under water? if ((pEdict->v.waterlevel == 3) && !(pSelect[select_index].can_use_underwater)) { select_index++; // skip to next weapon continue; } use_percent = RANDOM_LONG(1, 100); // is use percent greater than weapon use percent? if (use_percent > pSelect[select_index].use_percent) { select_index++; // skip to next weapon continue; } iId = pSelect[select_index].iId; use_primary = FALSE; use_secondary = FALSE; primary_percent = RANDOM_LONG(1, 100); // is primary percent less than weapon primary percent AND // no ammo required for this weapon OR // enough ammo available to fire AND // the bot is far enough away to use primary fire AND // the bot is close enough to the enemy to use primary fire if ((primary_percent <= pSelect[select_index].primary_fire_percent) && ((weapon_defs[iId].iAmmo1 == -1) || (pBot->m_rgAmmo[weapon_defs[iId].iAmmo1] >= pSelect[select_index].min_primary_ammo)) && (distance >= pSelect[select_index].primary_min_distance) && (distance <= pSelect[select_index].primary_max_distance)) { use_primary = TRUE; } // otherwise see if there is enough secondary ammo AND // the bot is far enough away to use secondary fire AND // the bot is close enough to the enemy to use secondary fire else if (((weapon_defs[iId].iAmmo2 == -1) || (pBot->m_rgAmmo[weapon_defs[iId].iAmmo2] >= pSelect[select_index].min_secondary_ammo)) && (distance >= pSelect[select_index].secondary_min_distance) && (distance <= pSelect[select_index].secondary_max_distance)) { use_secondary = TRUE; } // see if there wasn't enough ammo to fire the weapon... if ((use_primary == FALSE) && (use_secondary == FALSE)) { select_index++; // skip to next weapon continue; } // select this weapon if it isn't already selected if (pBot->current_weapon.iId != iId) UTIL_SelectItem(pEdict, pSelect[select_index].weapon_name); if (pDelay[select_index].iId != iId) { char msg[80]; sprintf(msg, "fire_delay mismatch for weapon id=%d\n",iId); ALERT(at_console, msg); return FALSE; } if (mod_id == TFC_DLL) { if (iId == TF_WEAPON_SNIPERRIFLE) { pBot->f_move_speed = 0; // don't move while using sniper rifle if (pEdict->v.velocity.Length() > 50) { return FALSE; // don't press attack key until velocity is < 50 } } if (pEdict->v.playerclass == TFC_CLASS_MEDIC) { int player_team = UTIL_GetTeam(pBot->pBotEnemy); int bot_team = UTIL_GetTeam(pEdict); // only heal your teammates or allies... if (((bot_team == player_team) || (team_allies[bot_team] & (1<v.button |= IN_ATTACK; // use primary attack if (pSelect[select_index].primary_fire_charge) { pBot->charging_weapon_id = iId; // release primary fire after the appropriate delay... pBot->f_primary_charging = gpGlobals->time + pSelect[select_index].primary_charge_delay; pBot->f_shoot_time = gpGlobals->time; // keep charging } else { // set next time to shoot if (pSelect[select_index].primary_fire_hold) pBot->f_shoot_time = gpGlobals->time; // don't let button up else { int skill = pBot->bot_skill; float base_delay, min_delay, max_delay; base_delay = pDelay[select_index].primary_base_delay; min_delay = pDelay[select_index].primary_min_delay[skill]; max_delay = pDelay[select_index].primary_max_delay[skill]; pBot->f_shoot_time = gpGlobals->time + base_delay + RANDOM_FLOAT(min_delay, max_delay); } } } else // MUST be use_secondary... { pEdict->v.button |= IN_ATTACK2; // use secondary attack if (pSelect[select_index].secondary_fire_charge) { pBot->charging_weapon_id = iId; // release secondary fire after the appropriate delay... pBot->f_secondary_charging = gpGlobals->time + pSelect[select_index].secondary_charge_delay; pBot->f_shoot_time = gpGlobals->time; // keep charging } else { // set next time to shoot if (pSelect[select_index].secondary_fire_hold) pBot->f_shoot_time = gpGlobals->time; // don't let button up else { int skill = pBot->bot_skill; float base_delay, min_delay, max_delay; base_delay = pDelay[select_index].secondary_base_delay; min_delay = pDelay[select_index].secondary_min_delay[skill]; max_delay = pDelay[select_index].secondary_max_delay[skill]; pBot->f_shoot_time = gpGlobals->time + base_delay + RANDOM_FLOAT(min_delay, max_delay); } } } return TRUE; // weapon was fired } } // didn't have any available weapons or ammo, return FALSE return FALSE; } void BotShootAtEnemy( bot_t *pBot ) { float f_distance; edict_t *pEdict = pBot->pEdict; // aim for the head and/or body Vector v_enemy = BotBodyTarget( pBot->pBotEnemy, pBot ) - GetGunPosition(pEdict); pEdict->v.v_angle = UTIL_VecToAngles( v_enemy ); if (pEdict->v.v_angle.y > 180) pEdict->v.v_angle.y -=360; // Paulo-La-Frite - START bot aiming bug fix if (pEdict->v.v_angle.x > 180) pEdict->v.v_angle.x -=360; // set the body angles to point the gun correctly pEdict->v.angles.x = pEdict->v.v_angle.x / 3; pEdict->v.angles.y = pEdict->v.v_angle.y; pEdict->v.angles.z = 0; // adjust the view angle pitch to aim correctly (MUST be after body v.angles stuff) pEdict->v.v_angle.x = -pEdict->v.v_angle.x; // Paulo-La-Frite - END float x = pEdict->v.v_angle.y; if (x > 180) x -= 360; if (abs(pEdict->v.ideal_yaw - x) > 2.0) fp = NULL; pEdict->v.ideal_yaw = pEdict->v.v_angle.y; BotFixIdealYaw(pEdict); v_enemy.z = 0; // ignore z component (up & down) f_distance = v_enemy.Length(); // how far away is the enemy scum? if (f_distance > 200) // run if distance to enemy is far pBot->f_move_speed = pBot->f_max_speed; else if (f_distance > 20) // walk if distance is closer pBot->f_move_speed = pBot->f_max_speed / 2; else // don't move if close enough pBot->f_move_speed = 0.0; // is it time to shoot yet? if (pBot->f_shoot_time <= gpGlobals->time) { // select the best weapon to use at this distance and fire... BotFireWeapon(v_enemy, pBot, 0); } } bool BotShootTripmine( bot_t *pBot ) { edict_t *pEdict = pBot->pEdict; if (pBot->b_shoot_tripmine != TRUE) return FALSE; // aim at the tripmine and fire the glock... Vector v_enemy = pBot->v_tripmine - GetGunPosition( pEdict ); pEdict->v.v_angle = UTIL_VecToAngles( v_enemy ); if (pEdict->v.v_angle.y > 180) pEdict->v.v_angle.y -=360; // Paulo-La-Frite - START bot aiming bug fix if (pEdict->v.v_angle.x > 180) pEdict->v.v_angle.x -=360; // set the body angles to point the gun correctly pEdict->v.angles.x = pEdict->v.v_angle.x / 3; pEdict->v.angles.y = pEdict->v.v_angle.y; pEdict->v.angles.z = 0; // adjust the view angle pitch to aim correctly (MUST be after body v.angles stuff) pEdict->v.v_angle.x = -pEdict->v.v_angle.x; // Paulo-La-Frite - END pEdict->v.ideal_yaw = pEdict->v.v_angle.y; BotFixIdealYaw(pEdict); return (BotFireWeapon( v_enemy, pBot, VALVE_WEAPON_GLOCK )); }