t7x/data/scripts/mp/bots/_bot.gsc_raw

1118 lines
20 KiB
Plaintext
Raw Normal View History

2023-03-13 04:39:59 -04:00
#using scripts\codescripts\struct;
2023-04-20 20:51:05 -04:00
2023-03-13 04:39:59 -04:00
#using scripts\shared\array_shared;
#using scripts\shared\callbacks_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\math_shared;
#using scripts\shared\rank_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons_shared;
2023-04-20 20:51:05 -04:00
#using scripts\shared\weapons\_weapons;
2023-03-13 04:39:59 -04:00
#using scripts\shared\bots\_bot;
#using scripts\shared\bots\_bot_combat;
#using scripts\shared\bots\bot_buttons;
2023-04-20 20:51:05 -04:00
#using scripts\shared\bots\bot_traversals;
2023-03-13 04:39:59 -04:00
#using scripts\mp\bots\_bot_ball;
#using scripts\mp\bots\_bot_clean;
#using scripts\mp\bots\_bot_combat;
#using scripts\mp\bots\_bot_conf;
#using scripts\mp\bots\_bot_ctf;
#using scripts\mp\bots\_bot_dem;
#using scripts\mp\bots\_bot_dom;
#using scripts\mp\bots\_bot_escort;
#using scripts\mp\bots\_bot_hq;
#using scripts\mp\bots\_bot_koth;
#using scripts\mp\bots\_bot_loadout;
#using scripts\mp\bots\_bot_sd;
2023-04-20 20:48:03 -04:00
2023-04-20 20:51:05 -04:00
#using scripts\mp\killstreaks\_ai_tank;
#using scripts\mp\killstreaks\_airsupport;
#using scripts\mp\killstreaks\_combat_robot;
#using scripts\mp\killstreaks\_counteruav;
#using scripts\mp\killstreaks\_dart;
#using scripts\mp\killstreaks\_dogs;
#using scripts\mp\killstreaks\_drone_strike;
#using scripts\mp\killstreaks\_emp;
#using scripts\mp\killstreaks\_flak_drone;
#using scripts\mp\killstreaks\_helicopter;
#using scripts\mp\killstreaks\_helicopter_gunner;
#using scripts\mp\killstreaks\_killstreak_bundles;
#using scripts\mp\killstreaks\_killstreak_detect;
#using scripts\mp\killstreaks\_killstreak_hacking;
2023-03-13 04:39:59 -04:00
#using scripts\mp\killstreaks\_killstreakrules;
#using scripts\mp\killstreaks\_killstreaks;
2023-04-20 20:51:05 -04:00
#using scripts\mp\killstreaks\_microwave_turret;
#using scripts\mp\killstreaks\_planemortar;
#using scripts\mp\killstreaks\_qrdrone;
#using scripts\mp\killstreaks\_raps;
#using scripts\mp\killstreaks\_rcbomb;
#using scripts\mp\killstreaks\_remote_weapons;
#using scripts\mp\killstreaks\_remotemissile;
2023-04-20 20:48:03 -04:00
#using scripts\mp\killstreaks\_satellite;
2023-04-20 20:51:05 -04:00
#using scripts\mp\killstreaks\_sentinel;
#using scripts\mp\killstreaks\_supplydrop;
#using scripts\mp\killstreaks\_turret;
#using scripts\mp\killstreaks\_uav;
2023-03-13 04:39:59 -04:00
#using scripts\mp\teams\_teams;
#using scripts\mp\_util;
#insert scripts\shared\shared.gsh;
#insert scripts\mp\bots\_bot.gsh;
#define MAX_LOCAL_PLAYERS 10
#define MAX_ONLINE_PLAYERS 18
#define MAX_ONLINE_PLAYERS_PER_TEAM 6
2023-04-20 20:48:03 -04:00
#define RESPAWN_DELAY 0.1
#define RESPAWN_INTERVAL 0.1
2023-03-13 04:39:59 -04:00
#namespace bot;
2023-05-13 11:35:53 -04:00
#precache("eventstring", "mpl_killstreak_cruisemissile");
#precache("eventstring", "mpl_killstreak_raps");
2023-05-13 11:35:53 -04:00
REGISTER_SYSTEM("bot_mp", &__init__, undefined)
2023-04-20 20:48:03 -04:00
2023-03-13 04:39:59 -04:00
function __init__()
{
2023-05-13 11:35:53 -04:00
callback::on_start_gametype(&init);
2023-03-13 04:39:59 -04:00
level.getBotSettings = &get_bot_settings;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
level.onBotConnect = &on_bot_connect;
level.onBotSpawned = &on_bot_spawned;
level.onBotKilled = &on_bot_killed;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
level.botIdle = &bot_idle;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
level.botThreatLost = &bot_combat::chase_threat;
level.botPreCombat = &bot_combat::mp_pre_combat;
level.botCombat = &bot_combat::combat_think;
level.botPostCombat = &bot_combat::mp_post_combat;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
level.botIgnoreThreat = &bot_combat::bot_ignore_threat;
2023-05-13 11:35:53 -04:00
2023-05-13 11:30:34 -04:00
level.enemyEmpActive = &emp::enemyEmpActive;
2023-05-13 11:35:53 -04:00
2023-04-20 20:48:03 -04:00
/#
level.botDevguiCmd = &bot_devgui_cmd;
level thread system_devgui_gadget_think();
#/
2023-05-13 11:35:53 -04:00
setDvar("bot_enableWallrun", 1);
2023-03-13 04:39:59 -04:00
}
function init()
2023-05-13 11:35:53 -04:00
{
level endon("game_ended");
2023-03-13 04:39:59 -04:00
level.botSoak = is_bot_soak();
2023-05-13 11:35:53 -04:00
if (!init_bot_gametype())
2023-03-14 08:47:06 -04:00
{
return;
}
2023-04-20 20:48:03 -04:00
2023-03-13 04:39:59 -04:00
wait_for_host();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
level thread populate_bots();
}
// Init Utils
//========================================
function is_bot_soak()
{
2023-05-13 11:35:53 -04:00
return getDvarInt("sv_botsoak", 0);
2023-03-13 04:39:59 -04:00
}
function wait_for_host()
{
2023-05-13 11:35:53 -04:00
level endon("game_ended");
2023-03-13 04:39:59 -04:00
host = util::getHostPlayerForBots();
2023-05-13 11:35:53 -04:00
while (!isdefined(host))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
wait(0.25);
2023-03-13 04:39:59 -04:00
host = util::getHostPlayerForBots();
}
}
function get_host_team()
{
host = util::getHostPlayerForBots();
2023-05-13 11:35:53 -04:00
if (!isdefined(host) || host.team == "spectator")
2023-03-13 04:39:59 -04:00
{
return "allies";
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
return host.team;
}
function is_bot_comp_stomp()
{
return false;
}
// Bot Events
//========================================
function on_bot_connect()
{
2023-05-13 11:35:53 -04:00
self endon("disconnect");
level endon("game_ended");
if (IS_TRUE(level.disableClassSelection))
2023-03-13 04:39:59 -04:00
{
self set_rank();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// Doesn't work if we don't do it in this order
self bot_loadout::pick_hero_gadget();
self bot_loadout::pick_killstreaks();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
return;
}
2023-05-13 11:35:53 -04:00
if (!IS_TRUE(self.pers["bot_loadout"]))
2023-03-13 04:39:59 -04:00
{
self set_rank();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// Doesn't work if we don't do it in this order
self bot_loadout::build_classes();
self bot_loadout::pick_hero_gadget();
self bot_loadout::pick_killstreaks();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
self.pers["bot_loadout"] = true;
}
self bot_loadout::pick_classes();
self choose_class();
}
function on_bot_spawned()
{
self.bot.goalTag = undefined;
2023-04-20 20:48:03 -04:00
/#
weapon = undefined;
2023-05-13 11:35:53 -04:00
if (getDvarInt("scr_botsHasPlayerWeapon") != 0)
2023-04-20 20:48:03 -04:00
{
player = util::getHostPlayer();
2023-05-13 11:30:34 -04:00
weapon = player getCurrentWeapon();
2023-04-20 20:48:03 -04:00
}
2023-05-13 11:35:53 -04:00
if (getDvarString("devgui_bot_weapon", "") != "")
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
weapon = getWeapon(getDvarString("devgui_bot_weapon"));
2023-04-20 20:48:03 -04:00
}
2023-05-13 11:35:53 -04:00
if (isdefined(weapon) && level.weaponNone != weapon)
2023-04-20 20:48:03 -04:00
{
self weapons::detach_all_weapons();
2023-05-13 11:30:34 -04:00
self takeAllWeapons();
2023-05-13 11:35:53 -04:00
self giveWeapon(weapon);
self switchToWeapon(weapon);
self setSpawnWeapon(weapon);
2023-04-20 20:48:03 -04:00
2023-05-13 11:35:53 -04:00
self teams::set_player_model(self.team, weapon);
2023-04-20 20:48:03 -04:00
}
#/
2023-03-13 04:39:59 -04:00
}
function on_bot_killed()
{
self endon("disconnect");
2023-05-13 11:35:53 -04:00
level endon("game_ended");
self endon("spawned");
self waittill("death_delay_finished");
2023-03-13 04:39:59 -04:00
wait RESPAWN_DELAY;
2023-05-13 11:35:53 -04:00
if (self choose_class() && level.playerForceRespawn)
2023-03-13 04:39:59 -04:00
{
return;
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
self thread respawn();
}
function respawn()
{
2023-05-13 11:35:53 -04:00
self endon("spawned");
self endon("disconnect");
level endon("game_ended");
2023-03-13 04:39:59 -04:00
2023-05-13 11:35:53 -04:00
while (1)
2023-03-13 04:39:59 -04:00
{
self bot::tap_use_button();
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
wait RESPAWN_INTERVAL;
}
}
function bot_idle()
2023-05-13 11:35:53 -04:00
{
if (self do_supplydrop())
2023-03-13 04:39:59 -04:00
{
return;
}
2023-04-20 20:48:03 -04:00
2023-03-13 04:39:59 -04:00
// TODO: Look for an enemy radar blip
// TODO: Get points on navmesh and feed into the spawn system to see if an enemy is likely to spawn there
self bot::navmesh_wander();
self bot::sprint_to_goal();
}
// Crate maxs: 23.1482
#define CRATE_GOAL_RADIUS 39
#define CRATE_USE_RADIUS 62 // Wild guess on usable radius
2023-05-13 11:35:53 -04:00
function do_supplydrop(maxRange = 1400) // A little under minimap width
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
crates = getEntArray("care_package", "script_noteworthy");
2023-03-13 04:39:59 -04:00
maxRangeSq = maxRange * maxRange;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
useRadiusSq = CRATE_USE_RADIUS * CRATE_USE_RADIUS;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
closestCrate = undefined;
closestCrateDistSq = undefined;
2023-05-13 11:35:53 -04:00
foreach(crate in crates)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (!crate isOnGround())
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
crateDistSq = distance2DSquared(self.origin, crate.origin);
if (crateDistSq > maxRangeSq)
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
inUse = isdefined(crate.useEnt) && IS_TRUE(crate.useEnt.inUse);
if (crateDistSq <= useRadiusSq)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (inUse && !self useButtonPressed())
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
self bot::press_use_button();
return true;
}
2023-05-13 11:35:53 -04:00
if (!self has_minimap() && !self botSightTracePassed(crate))
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (!isdefined(closestCrate) || crateDistSq < closestCrateDistSq)
2023-03-13 04:39:59 -04:00
{
closestCrate = crate;
closestCrateDistSq = crateDistSq;
}
}
2023-05-13 11:35:53 -04:00
if (isdefined(closestCrate))
{
randomAngle = (0, randomInt(360), 0);
randomVec = AnglesToForward(randomAngle);
2023-03-13 04:39:59 -04:00
point = closestCrate.origin + randomVec * CRATE_GOAL_RADIUS;
2023-05-13 11:35:53 -04:00
if (self botSetGoal(point))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
self thread watch_crate(closestCrate);
2023-03-13 04:39:59 -04:00
return true;
}
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
return false;
}
2023-05-13 11:35:53 -04:00
function watch_crate(crate)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
self endon("death");
self endon("bot_goal_reached");
level endon("game_ended");
while (isdefined(crate) && !self bot_combat::has_threat())
2023-03-13 04:39:59 -04:00
{
wait level.botSettings.thinkInterval;
}
2023-05-13 11:35:53 -04:00
self botSetGoal(self.origin);
2023-03-13 04:39:59 -04:00
}
// Bot Team Population
//========================================
function populate_bots()
{
2023-05-13 11:35:53 -04:00
level endon("game_ended");
if (level.teambased)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
maxAllies = getDvarInt("bot_maxAllies", 0);
maxAxis = getDvarInt("bot_maxAxis", 0);
level thread monitor_bot_team_population(maxAllies, maxAxis);
2023-03-13 04:39:59 -04:00
}
else
{
2023-05-13 11:35:53 -04:00
maxFree = getDvarInt("bot_maxFree", 0);
level thread monitor_bot_population(maxFree);
2023-03-13 04:39:59 -04:00
}
}
2023-05-13 11:35:53 -04:00
function monitor_bot_team_population(maxAllies, maxAxis)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
level endon("game_ended");
if (!maxAllies && !maxAxis)
2023-03-13 04:39:59 -04:00
{
return;
}
2023-05-13 11:35:53 -04:00
fill_balanced_teams(maxAllies, maxAxis);
while (1)
2023-03-13 04:39:59 -04:00
{
wait 3;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// TODO: Get a player count that includes 'CON_CONNECTING' players
2023-05-13 11:35:53 -04:00
allies = getPlayers("allies");
axis = getPlayers("axis");
if (allies.size > maxAllies &&
remove_best_bot(allies))
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (axis.size > maxAxis &&
remove_best_bot(axis))
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (allies.size < maxAllies || axis.size < maxAxis)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
add_balanced_bot(allies, maxAllies, axis, maxAxis);
2023-03-13 04:39:59 -04:00
}
}
}
2023-05-13 11:35:53 -04:00
function fill_balanced_teams(maxAllies, maxAxis)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
allies = getPlayers("allies");
axis = getPlayers("axis");
while ((allies.size < maxAllies || axis.size < maxAxis) &&
add_balanced_bot(allies, maxAllies, axis, maxAxis))
{
2023-03-13 04:39:59 -04:00
WAIT_SERVER_FRAME;
2023-05-13 11:35:53 -04:00
allies = getPlayers("allies");
axis = getPlayers("axis");
2023-03-13 04:39:59 -04:00
}
}
2023-05-13 11:35:53 -04:00
function add_balanced_bot(allies, maxAllies, axis, maxAxis)
{
2023-03-13 04:39:59 -04:00
bot = undefined;
2023-05-13 11:35:53 -04:00
if (allies.size < maxAllies &&
(allies.size <= axis.size || axis.size >= maxAxis))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
bot = add_bot("allies");
}
else if (axis.size < maxAxis)
{
bot = add_bot("axis");
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
return isdefined(bot);
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
function monitor_bot_population(maxFree)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
level endon("game_ended");
if (!maxFree)
2023-03-13 04:39:59 -04:00
{
return;
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// Initial Fill
2023-05-13 11:35:53 -04:00
players = getPlayers();
while (players.size < maxFree)
2023-03-13 04:39:59 -04:00
{
add_bot();
WAIT_SERVER_FRAME;
2023-05-13 11:35:53 -04:00
players = getPlayers();
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
while (1)
2023-03-13 04:39:59 -04:00
{
wait 3;
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// TODO: Get a player count that includes 'CON_CONNECTING' players
2023-05-13 11:35:53 -04:00
players = getPlayers();
if (players.size < maxFree)
2023-03-13 04:39:59 -04:00
{
add_bot();
}
2023-05-13 11:35:53 -04:00
else if (players.size > maxFree)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
remove_best_bot(players);
2023-03-13 04:39:59 -04:00
}
}
}
2023-05-13 11:35:53 -04:00
function remove_best_bot(players)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
bots = filter_bots(players);
if (!bots.size)
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
// Prefer non-combat bots
bestBots = [];
2023-05-13 11:35:53 -04:00
foreach(bot in bots)
2023-03-13 04:39:59 -04:00
{
// Don't kick bots in the process of connecting
2023-05-13 11:35:53 -04:00
if (bot.sessionstate == "spectator")
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (bot.sessionstate == "dead" || !bot bot_combat::has_threat())
2023-03-13 04:39:59 -04:00
{
bestBots[bestBots.size] = bot;
}
}
2023-05-13 11:35:53 -04:00
if (bestBots.size)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
remove_bot(bestBots[randomInt(bestBots.size)]);
2023-03-13 04:39:59 -04:00
}
else
{
2023-05-13 11:35:53 -04:00
remove_bot(bots[randomInt(bots.size)]);
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
return true;
}
// Bot Loadouts
//========================================
function choose_class()
{
2023-05-13 11:35:53 -04:00
if (IS_TRUE(level.disableClassSelection))
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
currClass = self bot_loadout::get_current_class();
2023-05-13 11:35:53 -04:00
if (!isdefined(currClass) || randomInt(100) < VAL(level.botSettings.changeClassWeight, 0))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
classIndex = randomInt(self.loadoutClasses.size);
2023-03-13 04:39:59 -04:00
className = self.loadoutClasses[classIndex].name;
}
2023-05-13 11:35:53 -04:00
if (!isdefined(className) || className == = currClass)
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
self notify("menuresponse", MENU_CHANGE_CLASS, className);
2023-03-13 04:39:59 -04:00
return true;
}
// Killstreaks
//========================================
function use_killstreak()
{
2023-05-13 11:35:53 -04:00
if (!level.loadoutKillstreaksEnabled ||
self emp::enemyEmpActive())
2023-03-13 04:39:59 -04:00
{
return;
}
2023-05-13 11:35:53 -04:00
2023-05-13 11:30:34 -04:00
weapons = self getWeaponsList();
inventoryWeapon = self getInventoryWeapon();
2023-05-13 11:35:53 -04:00
foreach(weapon in weapons)
{
killstreak = killstreaks::get_killstreak_for_weapon(weapon);
if (!isdefined(killstreak))
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (weapon != inventoryWeapon && !self getWeaponAmmoClip(weapon))
2023-03-13 04:39:59 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
if (self killstreakrules::isKillstreakAllowed(killstreak, self.team))
2023-03-13 04:39:59 -04:00
{
useWeapon = weapon;
break;
}
}
2023-05-13 11:35:53 -04:00
if (!isdefined(useWeapon))
2023-03-13 04:39:59 -04:00
{
return;
}
2023-05-13 11:35:53 -04:00
killstreak_ref = killstreaks::get_menu_name(killstreak);
switch (killstreak_ref)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
case "killstreak_uav":
case "killstreak_counteruav":
case "killstreak_satellite":
case "killstreak_helicopter_player_gunner":
case "killstreak_raps":
case "killstreak_sentinel":
{
self switchToWeapon(useWeapon);
break;
}
case "killstreak_ai_tank_drop":
{
self use_supply_drop(weapon);
break;
}
case "killstreak_remote_missile":
{
self switchToWeapon(weapon);
self waittill("weapon_change_complete");
wait 1.5;
self bot::press_attack_button();
return;
}
2023-03-13 04:39:59 -04:00
}
}
2023-05-13 11:35:53 -04:00
function get_closest_enemy(origin, on_radar)
{
2023-05-13 11:35:53 -04:00
enemies = self get_enemies(on_radar);
enemies = arraysort(enemies, origin);
2023-05-13 11:35:53 -04:00
if (enemies.size)
return enemies[0];
2023-05-13 11:35:53 -04:00
return undefined;
}
2023-05-13 11:35:53 -04:00
function use_supply_drop(weapon)
{
2023-05-13 11:35:53 -04:00
if (weapon == "inventory_supplydrop_mp" || weapon == "supplydrop_mp")
{
if (gettime() - self.spawntime > 5000)
return;
}
2023-05-13 11:35:53 -04:00
yaw = (0, self.angles[1], 0);
dir = anglestoforward(yaw);
dir = vectornormalize(dir);
drop_point = self.origin + vectorscale(dir, 384);
end = drop_point + vectorscale((0, 0, 1), 2048.0);
2023-05-13 11:35:53 -04:00
if (!sighttracepassed(drop_point, end, 0, undefined))
return;
2023-05-13 11:35:53 -04:00
if (!sighttracepassed(self.origin, end, 0, undefined))
return;
2023-05-13 11:35:53 -04:00
end = drop_point - vectorscale((0, 0, 1), 32.0);
2023-05-13 11:35:53 -04:00
if (bullettracepassed(drop_point, end, 0, undefined))
return;
2023-05-13 11:35:53 -04:00
self addgoal(self.origin, 24, 4, "killstreak");
2023-05-13 11:35:53 -04:00
if (weapon == "missile_drone_mp" || weapon == "inventory_missile_drone_mp")
self lookat(drop_point + vectorscale((0, 0, 1), 384.0));
else
self lookat(drop_point);
2023-05-13 11:35:53 -04:00
wait 0.5;
2023-05-13 11:35:53 -04:00
if (self getCurrentWeapon() != weapon)
{
self thread weapon_switch_failsafe();
self switchToWeapon(weapon);
2023-05-13 11:35:53 -04:00
self waittill("weapon_change_complete");
}
2023-05-13 11:35:53 -04:00
use_item(weapon);
self switchToWeapon(self.lastnonkillstreakweapon);
self clearlookat();
self cancelgoal("killstreak");
}
2023-05-13 11:35:53 -04:00
function use_item(weapon)
{
2023-05-13 11:35:53 -04:00
self bot::press_attack_button();
wait 0.5;
for (i = 0; i < 10; i++)
{
if (self getCurrentWeapon() == weapon || self getCurrentWeapon() == "none")
self bot::press_attack_button();
else
return;
wait 0.5;
}
}
2023-05-13 11:35:53 -04:00
function killstreak_location(num, weapon)
{
2023-05-13 11:35:53 -04:00
enemies = get_enemies();
2023-05-13 11:35:53 -04:00
if (!enemies.size)
return;
2023-05-13 11:35:53 -04:00
if (!self switchToWeapon(weapon))
return;
2023-05-13 11:35:53 -04:00
self waittill("weapon_change");
2023-05-13 11:35:53 -04:00
self util::freeze_player_controls(true);
wait_time = 1;
2023-05-13 11:35:53 -04:00
while (!isdefined(self.selectinglocation) || self.selectinglocation == 0)
{
wait 0.05;
wait_time -= 0.05;
2023-05-13 11:35:53 -04:00
if (wait_time <= 0)
{
self util::freeze_player_controls(false);
self switchToWeapon(self.lastnonkillstreakweapon);
return;
}
}
2023-05-13 11:35:53 -04:00
wait 2;
2023-05-13 11:35:53 -04:00
for (i = 0; i < num; i++)
{
enemies = get_enemies();
2023-05-13 11:35:53 -04:00
if (enemies.size)
{
enemy = randomInt(enemies);
self notify("confirm_location", enemy.origin, 0);
}
2023-05-13 11:35:53 -04:00
wait 0.25;
}
2023-05-13 11:35:53 -04:00
self util::freeze_player_controls(false);
}
function weapon_switch_failsafe()
{
2023-05-13 11:35:53 -04:00
self endon("death");
self endon("disconnect");
self endon("weapon_change_complete");
wait 10;
self notify("weapon_change_complete");
}
2023-03-13 04:39:59 -04:00
function has_radar()
{
2023-05-13 11:35:53 -04:00
if (level.teambased)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
return (uav::HasUAV(self.team) || satellite::HasSatellite(self.team));
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
return (uav::HasUAV(self.entnum) || satellite::HasSatellite(self.entnum));
2023-03-13 04:39:59 -04:00
}
function has_minimap()
{
2023-05-13 11:35:53 -04:00
if (self IsEmpJammed())
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
if (IS_TRUE(level.hardcoreMode))
2023-03-13 04:39:59 -04:00
{
return self has_radar();
}
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
return true;
}
2023-05-13 11:35:53 -04:00
function get_enemies(on_radar)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (!isdefined(on_radar))
2023-03-13 04:39:59 -04:00
{
on_radar = false;
}
enemies = self GetEnemies();
2023-04-20 20:48:03 -04:00
/#
2023-05-13 11:35:53 -04:00
for (i = 0; i < enemies.size; i++)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
if (isplayer(enemies[i]) && enemies[i] isInMoveMode("ufo", "noclip"))
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
arrayRemoveIndex(enemies, i);
2023-04-20 20:48:03 -04:00
i--;
}
}
#/
2023-05-13 11:35:53 -04:00
if (on_radar && !self has_radar())
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
for (i = 0; i < enemies.size; i++)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (!isdefined(enemies[i].lastFireTime))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
arrayRemoveIndex(enemies, i);
2023-03-13 04:39:59 -04:00
i--;
}
2023-05-13 11:35:53 -04:00
else if (GetTime() - enemies[i].lastFireTime > 2000)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
arrayRemoveIndex(enemies, i);
2023-03-13 04:39:59 -04:00
i--;
}
}
}
return enemies;
}
function set_rank()
{
2023-05-13 11:30:34 -04:00
players = getPlayers();
2023-03-13 04:39:59 -04:00
ranks = [];
bot_ranks = [];
human_ranks = [];
2023-05-13 11:35:53 -04:00
for (i = 0; i < players.size; i++)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (players[i] == self)
2023-03-13 04:39:59 -04:00
continue;
2023-05-13 11:35:53 -04:00
if (isdefined(players[i].pers["rank"]))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
if (players[i] util::is_bot())
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
bot_ranks[bot_ranks.size] = players[i].pers["rank"];
2023-03-13 04:39:59 -04:00
}
else
{
2023-05-13 11:35:53 -04:00
human_ranks[human_ranks.size] = players[i].pers["rank"];
2023-03-13 04:39:59 -04:00
}
}
}
2023-05-13 11:35:53 -04:00
if (!human_ranks.size)
human_ranks[human_ranks.size] = 10;
2023-03-13 04:39:59 -04:00
2023-05-13 11:35:53 -04:00
human_avg = math::array_average(human_ranks);
2023-03-13 04:39:59 -04:00
2023-05-13 11:35:53 -04:00
while (bot_ranks.size + human_ranks.size < 5)
2023-03-13 04:39:59 -04:00
{
// add some random ranks for better random number distribution
2023-05-13 11:35:53 -04:00
r = human_avg + randomIntRange(-5, 5);
rank = math::clamp(r, 0, level.maxRank);
human_ranks[human_ranks.size] = rank;
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
ranks = arrayCombine(human_ranks, bot_ranks, true, false);
avg = math::array_average(ranks);
s = math::array_std_deviation(ranks, avg);
rank = Int(math::random_normal_distribution(avg, s, 0, level.maxRank));
2023-03-13 04:39:59 -04:00
2023-05-13 11:35:53 -04:00
while (!isdefined(self.pers["codpoints"]))
2023-03-13 04:39:59 -04:00
{
wait 0.1;
}
2023-05-13 11:35:53 -04:00
self.pers["rank"] = rank;
self.pers["rankxp"] = rank::getRankInfoMinXP(rank);
self setRank(rank);
2023-03-13 04:39:59 -04:00
self rank::syncXPStat();
}
function init_bot_gametype()
{
2023-05-13 11:35:53 -04:00
switch (level.gameType)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
case "ball":
bot_ball::init();
return true;
case "conf":
bot_conf::init();
return true;
case "ctf":
bot_ctf::init();
return true;
case "dem":
bot_dem::init();
return true;
case "dm":
return true;
case "dom":
bot_dom::init();
return true;
case "escort":
bot_escort::init();
return true;
2023-04-20 22:28:15 -04:00
// case "infect":
// return true;
2023-05-13 11:35:53 -04:00
case "gun":
return true;
case "koth":
bot_koth::init();
return true;
case "sd":
bot_sd::init();
return true;
case "clean":
bot_clean::init();
return true;
case "tdm":
return true;
case "sas":
return true;
case "prop":
return true;
case "sniperonly":
return true;
2023-03-13 04:39:59 -04:00
}
return false;
}
function get_bot_settings()
{
2023-05-13 11:35:53 -04:00
switch (getDvarInt("bot_difficulty", 1))
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
case 0:
bundleName = "bot_mp_easy";
break;
case 1:
bundleName = "bot_mp_normal";
break;
case 2:
bundleName = "bot_mp_hard";
break;
case 3:
default:
bundleName = "bot_mp_veteran";
break;
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
return struct::get_script_bundle("botsettings", bundleName);
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
function friend_goal_in_radius(goal_name, origin, radius)
2023-03-13 04:39:59 -04:00
{
return 0;
}
2023-05-13 11:35:53 -04:00
function friend_in_radius(goal_name, origin, radius)
2023-03-13 04:39:59 -04:00
{
return false;
}
function get_friends()
{
2023-05-13 11:35:53 -04:00
return[];
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
function bot_vehicle_weapon_ammo(weaponName)
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
function navmesh_points_visible(origin, point)
2023-03-13 04:39:59 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
function dive_to_prone(exit_stance)
2023-03-13 04:39:59 -04:00
{
2023-05-13 11:35:53 -04:00
2023-03-13 04:39:59 -04:00
}
2023-05-13 11:35:53 -04:00
/ #
2023-04-20 20:48:03 -04:00
// Devgui
//========================================
2023-05-13 11:35:53 -04:00
function bot_devgui_cmd(cmd)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
cmdTokens = strtok(cmd, " ");
if (cmdTokens.size == 0)
2023-04-20 20:48:03 -04:00
{
return false;
}
2023-05-13 11:35:53 -04:00
2023-04-20 20:48:03 -04:00
host = util::getHostPlayerForBots();
team = get_host_team();
2023-05-13 11:35:53 -04:00
switch (cmdTokens[0])
2023-04-20 20:48:03 -04:00
{
case "spawn_enemy":
2023-05-13 11:35:53 -04:00
team = util::getotherteam(team);
case "spawn_friendly":
2023-04-20 20:48:03 -04:00
count = 1;
2023-05-13 11:35:53 -04:00
if (cmdTokens.size > 1)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
count = int(cmdTokens[1]);
2023-04-20 20:48:03 -04:00
}
2023-05-13 11:35:53 -04:00
for (i = 0; i < count; i++)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
add_bot(team);
2023-04-20 20:48:03 -04:00
}
return true;
case "remove_enemy":
team = util::getotherteam(team);
case "remove_friendly":
2023-05-13 11:35:53 -04:00
remove_bots(undefined, team);
2023-04-20 20:48:03 -04:00
return true;
case "fixed_spawn_enemy":
2023-05-13 11:35:53 -04:00
team = util::getotherteam(team);
case "fixed_spawn_friendly":
bot = add_bot_at_eye_trace(team);
if (isdefined(bot))
2023-04-20 20:48:03 -04:00
{
bot thread fixed_spawn_override();
}
return true;
case "player_weapon":
2023-05-13 11:30:34 -04:00
players = getPlayers();
2023-05-13 11:35:53 -04:00
foreach(player in players)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
if (!player util::is_bot())
2023-04-20 20:48:03 -04:00
{
continue;
}
2023-05-13 11:30:34 -04:00
weapon = host getCurrentWeapon();
2023-04-20 20:48:03 -04:00
player weapons::detach_all_weapons();
2023-05-13 11:30:34 -04:00
player takeAllWeapons();
2023-05-13 11:35:53 -04:00
player giveWeapon(weapon);
player switchToWeapon(weapon);
player setSpawnWeapon(weapon);
2023-04-20 20:48:03 -04:00
2023-05-13 11:35:53 -04:00
player teams::set_player_model(player.team, weapon);
2023-04-20 20:48:03 -04:00
}
return true;
}
return false;
}
function system_devgui_gadget_think()
{
2023-05-13 11:35:53 -04:00
setDvar("devgui_bot_gadget", "");
2023-04-20 20:48:03 -04:00
2023-05-13 11:35:53 -04:00
for (;; )
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
wait(1);
gadget = getDvarString("devgui_bot_gadget");
2023-04-20 20:48:03 -04:00
2023-05-13 11:38:13 -04:00
if (gadget.size == 0)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
bot_turn_on_gadget(getWeapon(gadget));
setDvar("devgui_bot_gadget", "");
}
2023-04-20 20:48:03 -04:00
}
}
2023-05-13 11:35:53 -04:00
function bot_turn_on_gadget(gadget)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:30:34 -04:00
players = getPlayers();
2023-05-13 11:35:53 -04:00
foreach(player in players)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
if (!player util::is_bot())
2023-04-20 20:48:03 -04:00
{
continue;
}
2023-05-13 11:35:53 -04:00
2023-04-20 20:48:03 -04:00
host = util::getHostPlayer();
2023-05-13 11:30:34 -04:00
weapon = host getCurrentWeapon();
2023-05-13 11:35:53 -04:00
if (!isdefined(weapon) || weapon == level.weaponNone || weapon == level.weaponNull)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
weapon = getWeapon("smg_standard");
2023-04-20 20:48:03 -04:00
}
player weapons::detach_all_weapons();
2023-05-13 11:30:34 -04:00
player takeAllWeapons();
2023-05-13 11:35:53 -04:00
player giveWeapon(weapon);
player switchToWeapon(weapon);
player setSpawnWeapon(weapon);
2023-04-20 20:48:03 -04:00
2023-05-13 11:35:53 -04:00
player teams::set_player_model(player.team, weapon);
2023-04-20 20:48:03 -04:00
2023-05-13 11:35:53 -04:00
player giveWeapon(gadget);
slot = player gadgetGetSlot(gadget);
player gadgetPowerSet(slot, 100.0);
player botPressButtonForGadget(gadget);
}
}
2023-04-20 20:48:03 -04:00
function fixed_spawn_override()
{
2023-05-13 11:35:53 -04:00
self endon("disconnect");
2023-04-20 20:48:03 -04:00
spawnOrigin = self.origin;
spawnAngles = self.angles;
2023-05-13 11:35:53 -04:00
while (1)
2023-04-20 20:48:03 -04:00
{
2023-05-13 11:35:53 -04:00
self waittill("spawned_player");
self setOrigin(spawnOrigin);
self setPlayerAngles(spawnAngles);
2023-04-20 20:48:03 -04:00
}
}
#/