init
This commit is contained in:
921 files changed
+337975
No files matched your search
@@ -0,0 +1,954 @@
|
||||
#using scripts\codescripts\struct;
|
||||
#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;
|
||||
#using scripts\shared\weapons\_weapons;
|
||||
|
||||
Line truncated
|
||||
|
||||
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
#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;
|
||||
|
||||
#using scripts\mp\killstreaks\_killstreakrules;
|
||||
#using scripts\mp\killstreaks\_killstreaks;
|
||||
#using scripts\mp\killstreaks\_uav;
|
||||
#using scripts\mp\killstreaks\_satellite;
|
||||
#using scripts\mp\killstreaks\_emp;
|
||||
#using scripts\mp\teams\_teams;
|
||||
#using scripts\mp\_util;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot;
|
||||
|
||||
function autoexec __init__sytem__() { system::register("bot_mp",&__init__,undefined,undefined); }
|
||||
|
||||
function __init__()
|
||||
{
|
||||
callback::on_start_gametype( &init );
|
||||
|
||||
level.getBotSettings = &get_bot_settings;
|
||||
|
||||
level.onBotConnect = &on_bot_connect;
|
||||
level.onBotSpawned = &on_bot_spawned;
|
||||
level.onBotKilled = &on_bot_killed;
|
||||
|
||||
level.botIdle = &bot_idle;
|
||||
|
||||
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;
|
||||
|
||||
level.botIgnoreThreat = &bot_combat::bot_ignore_threat;
|
||||
|
||||
level.enemyEmpActive = &emp::EnemyEmpActive;
|
||||
|
||||
/#
|
||||
level.botDevguiCmd = &bot_devgui_cmd;
|
||||
level thread system_devgui_gadget_think();
|
||||
#/
|
||||
}
|
||||
|
||||
function init()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
level.botSoak = is_bot_soak();
|
||||
|
||||
if ( ( level.rankedMatch && !level.botSoak ) || !init_bot_gametype() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wait_for_host();
|
||||
|
||||
level thread populate_bots();
|
||||
}
|
||||
|
||||
// Init Utils
|
||||
//========================================
|
||||
|
||||
function is_bot_soak()
|
||||
{
|
||||
/#
|
||||
return GetDvarInt( "sv_botsoak", 0 );
|
||||
#/
|
||||
return IsDedicated() && GetDvarInt( "sv_botsoak", 0 );
|
||||
}
|
||||
|
||||
function wait_for_host()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( level.botSoak )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
host = util::getHostPlayerForBots();
|
||||
|
||||
while ( !isdefined( host ) )
|
||||
{
|
||||
wait( 0.25 );
|
||||
host = util::getHostPlayerForBots();
|
||||
}
|
||||
}
|
||||
|
||||
function get_host_team()
|
||||
{
|
||||
host = util::getHostPlayerForBots();
|
||||
|
||||
if ( !isdefined( host ) || host.team == "spectator" )
|
||||
{
|
||||
return "allies";
|
||||
}
|
||||
|
||||
return host.team;
|
||||
}
|
||||
|
||||
function is_bot_comp_stomp()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
// Bot Events
|
||||
//========================================
|
||||
|
||||
function on_bot_connect()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( ( isdefined( level.disableClassSelection ) && level.disableClassSelection ) )
|
||||
{
|
||||
self set_rank();
|
||||
|
||||
// Doesn't work if we don't do it in this order
|
||||
self bot_loadout::pick_hero_gadget();
|
||||
self bot_loadout::pick_killstreaks();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if ( !( isdefined( self.pers["bot_loadout"] ) && self.pers["bot_loadout"] ) )
|
||||
{
|
||||
self set_rank();
|
||||
|
||||
// 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();
|
||||
|
||||
self.pers["bot_loadout"] = true;
|
||||
}
|
||||
|
||||
self bot_loadout::pick_classes();
|
||||
self choose_class();
|
||||
}
|
||||
|
||||
function on_bot_spawned()
|
||||
{
|
||||
self.bot.goalTag = undefined;
|
||||
/#
|
||||
weapon = undefined;
|
||||
|
||||
if ( GetDvarInt( "scr_botsHasPlayerWeapon" ) != 0 )
|
||||
{
|
||||
player = util::getHostPlayer();
|
||||
weapon = player GetCurrentWeapon();
|
||||
}
|
||||
|
||||
if ( GetDvarString( "devgui_bot_weapon", "" ) != "" )
|
||||
{
|
||||
weapon = GetWeapon( GetDvarString( "devgui_bot_weapon" ) );
|
||||
}
|
||||
|
||||
if ( isdefined( weapon ) && level.weaponNone != weapon )
|
||||
{
|
||||
self weapons::detach_all_weapons();
|
||||
self TakeAllWeapons();
|
||||
self GiveWeapon( weapon );
|
||||
self SwitchToWeapon( weapon );
|
||||
self SetSpawnWeapon( weapon );
|
||||
|
||||
self teams::set_player_model( self.team, weapon );
|
||||
}
|
||||
#/
|
||||
}
|
||||
|
||||
function on_bot_killed()
|
||||
{
|
||||
self endon("disconnect");
|
||||
level endon( "game_ended" );
|
||||
self endon( "spawned" );
|
||||
self waittill ( "death_delay_finished" );
|
||||
|
||||
wait 0.1;
|
||||
|
||||
if ( self choose_class() && level.playerForceRespawn )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self thread respawn();
|
||||
}
|
||||
|
||||
function respawn()
|
||||
{
|
||||
self endon( "spawned" );
|
||||
self endon( "disconnect" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
self bot::tap_use_button();
|
||||
|
||||
wait 0.1;
|
||||
}
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if ( self do_supplydrop() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// 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
|
||||
|
||||
// Wild guess on usable radius
|
||||
|
||||
function do_supplydrop( maxRange = 1400 ) // A little under minimap width
|
||||
{
|
||||
crates = GetEntArray( "care_package", "script_noteworthy" );
|
||||
|
||||
maxRangeSq = maxRange * maxRange;
|
||||
|
||||
useRadiusSq = 62 * 62;
|
||||
|
||||
closestCrate = undefined;
|
||||
closestCrateDistSq = undefined;
|
||||
|
||||
foreach( crate in crates )
|
||||
{
|
||||
if ( !crate IsOnGround() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
crateDistSq = Distance2DSquared( self.origin, crate.origin );
|
||||
|
||||
if ( crateDistSq > maxRangeSq )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
inUse = isdefined( crate.useEnt ) && ( isdefined( crate.useEnt.inUse ) && crate.useEnt.inUse );
|
||||
|
||||
if ( crateDistSq <= useRadiusSq )
|
||||
{
|
||||
if ( inUse && !self useButtonPressed() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
self bot::press_use_button();
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( !self has_minimap() && !self BotSightTracePassed( crate ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !isdefined( closestCrate ) || crateDistSq < closestCrateDistSq )
|
||||
{
|
||||
closestCrate = crate;
|
||||
closestCrateDistSq = crateDistSq;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isdefined( closestCrate ) )
|
||||
{
|
||||
randomAngle = ( 0, RandomInt( 360 ), 0 );
|
||||
randomVec = AnglesToForward( randomAngle );
|
||||
|
||||
point = closestCrate.origin + randomVec * 39;
|
||||
|
||||
if ( self BotSetGoal( point ) )
|
||||
{
|
||||
self thread watch_crate( closestCrate );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function watch_crate( crate )
|
||||
{
|
||||
self endon( "death" );
|
||||
self endon( "bot_goal_reached" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( isdefined( crate ) && !self bot_combat::has_threat() )
|
||||
{
|
||||
wait level.botSettings.thinkInterval;
|
||||
}
|
||||
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
|
||||
// Bot Team Population
|
||||
//========================================
|
||||
|
||||
function populate_bots()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( level.teambased )
|
||||
{
|
||||
maxAllies = GetDvarInt( "bot_maxAllies", 0 );
|
||||
maxAxis = GetDvarInt( "bot_maxAxis", 0 );
|
||||
|
||||
level thread monitor_bot_team_population( maxAllies, maxAxis );
|
||||
}
|
||||
else
|
||||
{
|
||||
maxFree = GetDvarInt( "bot_maxFree", 0 );
|
||||
|
||||
level thread monitor_bot_population( maxFree );
|
||||
}
|
||||
}
|
||||
|
||||
function monitor_bot_team_population( maxAllies, maxAxis )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( !maxAllies && !maxAxis )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
fill_balanced_teams( maxAllies, maxAxis );
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
wait 3;
|
||||
|
||||
// TODO: Get a player count that includes 'CON_CONNECTING' players
|
||||
allies = GetPlayers( "allies" );
|
||||
axis = GetPlayers( "axis" );
|
||||
|
||||
if ( allies.size > maxAllies &&
|
||||
remove_best_bot( allies ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( axis.size > maxAxis &&
|
||||
remove_best_bot( axis ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( allies.size < maxAllies || axis.size < maxAxis )
|
||||
{
|
||||
add_balanced_bot( allies, maxAllies, axis, maxAxis );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function fill_balanced_teams( maxAllies, maxAxis )
|
||||
{
|
||||
allies = GetPlayers( "allies" );
|
||||
axis = GetPlayers( "axis" );
|
||||
|
||||
while ( ( allies.size < maxAllies || axis.size < maxAxis ) &&
|
||||
add_balanced_bot( allies, maxAllies, axis, maxAxis ) )
|
||||
{
|
||||
{wait(.05);};
|
||||
|
||||
allies = GetPlayers( "allies" );
|
||||
axis = GetPlayers( "axis" );
|
||||
}
|
||||
}
|
||||
|
||||
function add_balanced_bot( allies, maxAllies, axis, maxAxis )
|
||||
{
|
||||
bot = undefined;
|
||||
|
||||
if ( allies.size < maxAllies &&
|
||||
( allies.size <= axis.size || axis.size >= maxAxis ) )
|
||||
{
|
||||
bot = add_bot( "allies" );
|
||||
}
|
||||
else if ( axis.size < maxAxis )
|
||||
{
|
||||
bot = add_bot( "axis" );
|
||||
}
|
||||
|
||||
return isdefined( bot );
|
||||
}
|
||||
|
||||
function monitor_bot_population( maxFree )
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
if ( !maxFree )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// Initial Fill
|
||||
players = GetPlayers( );
|
||||
while ( players.size < maxFree )
|
||||
{
|
||||
add_bot();
|
||||
{wait(.05);};
|
||||
players = GetPlayers( );
|
||||
}
|
||||
|
||||
while ( 1 )
|
||||
{
|
||||
wait 3;
|
||||
|
||||
// TODO: Get a player count that includes 'CON_CONNECTING' players
|
||||
players = GetPlayers( );
|
||||
|
||||
if ( players.size < maxFree )
|
||||
{
|
||||
add_bot();
|
||||
}
|
||||
else if ( players.size > maxFree )
|
||||
{
|
||||
remove_best_bot( players );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function remove_best_bot( players )
|
||||
{
|
||||
bots = filter_bots( players );
|
||||
|
||||
if ( !bots.size )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// Prefer non-combat bots
|
||||
bestBots = [];
|
||||
|
||||
foreach( bot in bots )
|
||||
{
|
||||
// Don't kick bots in the process of connecting
|
||||
if ( bot.sessionstate == "spectator" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( bot.sessionstate == "dead" || !bot bot_combat::has_threat() )
|
||||
{
|
||||
bestBots[bestBots.size] = bot;
|
||||
}
|
||||
}
|
||||
|
||||
if ( bestBots.size )
|
||||
{
|
||||
remove_bot( bestBots[RandomInt( bestBots.size )] );
|
||||
}
|
||||
else
|
||||
{
|
||||
remove_bot( bots[RandomInt( bots.size )] );
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Bot Loadouts
|
||||
//========================================
|
||||
|
||||
function choose_class()
|
||||
{
|
||||
if ( ( isdefined( level.disableClassSelection ) && level.disableClassSelection ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
currClass = self bot_loadout::get_current_class();
|
||||
|
||||
if ( !isdefined( currClass ) || RandomInt( 100 ) < (isdefined(level.botSettings.changeClassWeight)?level.botSettings.changeClassWeight:0) )
|
||||
{
|
||||
classIndex = RandomInt( self.loadoutClasses.size );
|
||||
className = self.loadoutClasses[classIndex].name;
|
||||
}
|
||||
|
||||
if ( !isdefined(className) || className === currClass )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self notify( "menuresponse", "ChooseClass_InGame", className );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Killstreaks
|
||||
//========================================
|
||||
|
||||
function use_killstreak()
|
||||
{
|
||||
if ( !level.loadoutKillstreaksEnabled ||
|
||||
self emp::EnemyEMPActive() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
weapons = self GetWeaponsList();
|
||||
inventoryWeapon = self GetInventoryWeapon();
|
||||
|
||||
foreach( weapon in weapons )
|
||||
{
|
||||
killstreak = killstreaks::get_killstreak_for_weapon( weapon );
|
||||
|
||||
if ( !isdefined( killstreak ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( weapon != inventoryWeapon && !self GetWeaponAmmoClip( weapon ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
|
||||
{
|
||||
useWeapon = weapon;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if ( !isdefined( useWeapon ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
killstreak_ref = killstreaks::get_menu_name( killstreak );
|
||||
|
||||
switch( killstreak_ref )
|
||||
{
|
||||
case "killstreak_uav":
|
||||
case "killstreak_counteruav":
|
||||
case "killstreak_satellite":
|
||||
case "killstreak_helicopter_player_gunner":
|
||||
case "killstreak_raps":
|
||||
case "killstreak_sentinel":
|
||||
self SwitchToWeapon( useWeapon );
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function has_radar()
|
||||
{
|
||||
if ( level.teambased )
|
||||
{
|
||||
return ( uav::HasUAV( self.team ) || satellite::HasSatellite( self.team ) );
|
||||
}
|
||||
|
||||
return ( uav::HasUAV( self.entnum ) || satellite::HasSatellite( self.entnum ) );
|
||||
}
|
||||
|
||||
function has_minimap()
|
||||
{
|
||||
if ( self IsEmpJammed() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( ( isdefined( level.hardcoreMode ) && level.hardcoreMode ) )
|
||||
{
|
||||
return self has_radar();
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_enemies( on_radar )
|
||||
{
|
||||
if ( !isdefined( on_radar ) )
|
||||
{
|
||||
on_radar = false;
|
||||
}
|
||||
|
||||
enemies = self GetEnemies();
|
||||
|
||||
/#
|
||||
for ( i = 0; i < enemies.size; i++ )
|
||||
{
|
||||
if ( isplayer( enemies[i] ) && enemies[i] IsInMoveMode( "ufo", "noclip" ) )
|
||||
{
|
||||
ArrayRemoveIndex( enemies, i );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
#/
|
||||
|
||||
if ( on_radar && !self has_radar() )
|
||||
{
|
||||
for ( i = 0; i < enemies.size; i++ )
|
||||
{
|
||||
if ( !isdefined( enemies[i].lastFireTime ) )
|
||||
{
|
||||
ArrayRemoveIndex( enemies, i );
|
||||
i--;
|
||||
}
|
||||
else if ( GetTime() - enemies[i].lastFireTime > 2000 )
|
||||
{
|
||||
ArrayRemoveIndex( enemies, i );
|
||||
i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return enemies;
|
||||
}
|
||||
|
||||
function set_rank()
|
||||
{
|
||||
players = GetPlayers();
|
||||
|
||||
ranks = [];
|
||||
bot_ranks = [];
|
||||
human_ranks = [];
|
||||
|
||||
for ( i = 0; i < players.size; i++ )
|
||||
{
|
||||
if ( players[i] == self )
|
||||
continue;
|
||||
|
||||
if ( isdefined( players[i].pers[ "rank" ] ) )
|
||||
{
|
||||
if ( players[i] util::is_bot() )
|
||||
{
|
||||
bot_ranks[ bot_ranks.size ] = players[i].pers[ "rank" ];
|
||||
}
|
||||
else
|
||||
{
|
||||
human_ranks[ human_ranks.size ] = players[i].pers[ "rank" ];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if( !human_ranks.size )
|
||||
human_ranks[ human_ranks.size ] = 10;
|
||||
|
||||
human_avg = math::array_average( human_ranks );
|
||||
|
||||
while ( bot_ranks.size + human_ranks.size < 5 )
|
||||
{
|
||||
// add some random ranks for better random number distribution
|
||||
r = human_avg + RandomIntRange( -5, 5 );
|
||||
rank = math::clamp( r, 0, level.maxRank );
|
||||
human_ranks[ human_ranks.size ] = rank;
|
||||
}
|
||||
|
||||
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 ) );
|
||||
|
||||
while ( !isdefined( self.pers["codpoints"] ) )
|
||||
{
|
||||
wait 0.1;
|
||||
}
|
||||
|
||||
self.pers[ "rank" ] = rank;
|
||||
self.pers[ "rankxp" ] = rank::getRankInfoMinXP( rank );
|
||||
|
||||
self setRank( rank );
|
||||
self rank::syncXPStat();
|
||||
}
|
||||
|
||||
function init_bot_gametype()
|
||||
{
|
||||
switch( level.gameType )
|
||||
{
|
||||
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;
|
||||
case "infect":
|
||||
return true;
|
||||
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;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_bot_settings()
|
||||
{
|
||||
switch ( GetDvarInt( "bot_difficulty", 1 ) )
|
||||
{
|
||||
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;
|
||||
}
|
||||
|
||||
return struct::get_script_bundle( "botsettings", bundleName );
|
||||
}
|
||||
|
||||
function friend_goal_in_radius( goal_name, origin, radius )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
function friend_in_radius( goal_name, origin, radius )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function get_friends()
|
||||
{
|
||||
return [];
|
||||
}
|
||||
|
||||
function get_closest_enemy( origin, someFlag )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function bot_vehicle_weapon_ammo( weaponName )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function navmesh_points_visible( origin, point )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function dive_to_prone( exit_stance )
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
/#
|
||||
|
||||
// Devgui
|
||||
//========================================
|
||||
|
||||
function bot_devgui_cmd( cmd )
|
||||
{
|
||||
cmdTokens = strtok( cmd," ");
|
||||
|
||||
if ( cmdTokens.size == 0 )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
host = util::getHostPlayerForBots();
|
||||
team = get_host_team();
|
||||
|
||||
switch( cmdTokens[0] )
|
||||
{
|
||||
case "spawn_enemy":
|
||||
team = util::getotherteam(team);
|
||||
case "spawn_friendly":
|
||||
count = 1;
|
||||
if ( cmdTokens.size > 1 )
|
||||
{
|
||||
count = int( cmdTokens[1] );
|
||||
}
|
||||
for( i = 0; i < count; i++ )
|
||||
{
|
||||
add_bot( team );
|
||||
}
|
||||
return true;
|
||||
case "remove_enemy":
|
||||
team = util::getotherteam(team);
|
||||
case "remove_friendly":
|
||||
remove_bots( undefined, team );
|
||||
return true;
|
||||
case "fixed_spawn_enemy":
|
||||
team = util::getotherteam(team);
|
||||
case "fixed_spawn_friendly":
|
||||
bot = add_bot_at_eye_trace( team );
|
||||
if ( isdefined( bot ) )
|
||||
{
|
||||
bot thread fixed_spawn_override();
|
||||
}
|
||||
return true;
|
||||
|
||||
case "player_weapon":
|
||||
players = GetPlayers();
|
||||
foreach( player in players )
|
||||
{
|
||||
if ( !player util::is_bot() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
weapon = host GetCurrentWeapon();
|
||||
|
||||
player weapons::detach_all_weapons();
|
||||
player TakeAllWeapons();
|
||||
player GiveWeapon( weapon );
|
||||
player SwitchToWeapon( weapon );
|
||||
player SetSpawnWeapon( weapon );
|
||||
|
||||
player teams::set_player_model( player.team, weapon );
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function system_devgui_gadget_think()
|
||||
{
|
||||
SetDvar( "devgui_bot_gadget", "" );
|
||||
|
||||
for ( ;; )
|
||||
{
|
||||
wait( 1 );
|
||||
|
||||
gadget = GetDvarString( "devgui_bot_gadget" );
|
||||
|
||||
if ( gadget != "" )
|
||||
{
|
||||
bot_turn_on_gadget( GetWeapon(gadget) );
|
||||
SetDvar( "devgui_bot_gadget", "" );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bot_turn_on_gadget( gadget )
|
||||
{
|
||||
players = GetPlayers();
|
||||
|
||||
foreach( player in players )
|
||||
{
|
||||
if ( !player util::is_bot() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
host = util::getHostPlayer();
|
||||
weapon = host GetCurrentWeapon();
|
||||
|
||||
if ( !isdefined( weapon ) || weapon == level.weaponNone || weapon == level.weaponNull )
|
||||
{
|
||||
weapon = GetWeapon( "smg_standard" );
|
||||
}
|
||||
|
||||
player weapons::detach_all_weapons();
|
||||
player TakeAllWeapons();
|
||||
player GiveWeapon( weapon );
|
||||
player SwitchToWeapon( weapon );
|
||||
player SetSpawnWeapon( weapon );
|
||||
|
||||
player teams::set_player_model( player.team, weapon );
|
||||
|
||||
player GiveWeapon( gadget );
|
||||
slot = player GadgetGetSlot( gadget );
|
||||
player GadgetPowerSet( slot, 100.0 );
|
||||
player BotPressButtonForGadget( gadget );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
function fixed_spawn_override()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
spawnOrigin = self.origin;
|
||||
spawnAngles = self.angles;
|
||||
|
||||
while( 1 )
|
||||
{
|
||||
self waittill( "spawned_player" );
|
||||
|
||||
self SetOrigin( spawnOrigin );
|
||||
self SetPlayerAngles( spawnAngles );
|
||||
}
|
||||
}
|
||||
|
||||
#/
|
||||
@@ -0,0 +1,57 @@
|
||||
// matches the enum in bot.h
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 65 degrees
|
||||
// 85 degrees
|
||||
// 100 degrees
|
||||
// 160 degrees
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\callbacks_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\system_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
|
||||
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
|
||||
|
||||
#using scripts\mp\teams\_teams;
|
||||
#using scripts\mp\_util;
|
||||
|
||||
#namespace bot_ball;
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botIdle = &bot_idle;
|
||||
level.botCombat = &bot_combat;
|
||||
level.botPreCombat = &bot_pre_combat;
|
||||
}
|
||||
|
||||
function release_control_on_landing()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while ( self IsOnGround() )
|
||||
{
|
||||
{wait(.05);};
|
||||
}
|
||||
|
||||
while ( !self IsOnGround() )
|
||||
{
|
||||
{wait(.05);};
|
||||
}
|
||||
|
||||
self BotReleaseManualControl();
|
||||
}
|
||||
|
||||
function bot_pre_combat()
|
||||
{
|
||||
if ( isdefined( self.carryObject ) )
|
||||
{
|
||||
if ( self IsOnGround() && self BotGoalSet() )
|
||||
{
|
||||
goal = level.ball_goals[util::getotherteam( self.team )];
|
||||
|
||||
radius = 300;
|
||||
radiusSq = radius * radius;
|
||||
|
||||
if ( Distance2DSquared( self.origin, goal.trigger.origin ) <= radiusSq )
|
||||
{
|
||||
if ( self BotSightTrace( goal.trigger ) )
|
||||
{
|
||||
self BotTakeManualControl();
|
||||
self thread bot::jump_to( goal.trigger.origin );
|
||||
self thread release_control_on_landing();
|
||||
|
||||
// TODO: Try throwing the ball
|
||||
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !self IsMeleeing() )
|
||||
{
|
||||
self bot::use_killstreak();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::mp_pre_combat();
|
||||
}
|
||||
|
||||
function bot_combat()
|
||||
{
|
||||
if ( isdefined( self.carryObject ) )
|
||||
{
|
||||
if ( self bot_combat::has_threat() )
|
||||
{
|
||||
self bot_combat::clear_threat();
|
||||
}
|
||||
|
||||
meleeThreat = bot_combat::get_greatest_threat( level.botSettings.meleeRange );
|
||||
|
||||
if ( isdefined( meleeThreat ) )
|
||||
{
|
||||
angles = self GetPlayerAngles();
|
||||
fwd = AnglesToForward( angles );
|
||||
|
||||
threatDir = meleeThreat.origin - self.origin ;
|
||||
threatDir = VectorNormalize( threatDir );
|
||||
|
||||
dot = VectorDot( fwd, threatDir );
|
||||
|
||||
if ( dot > level.botSettings.meleeDot )
|
||||
{
|
||||
self bot::tap_melee_button();
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::combat_think();
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if ( isdefined( self.carryObject ) )
|
||||
{
|
||||
if ( !self BotGoalSet() )
|
||||
{
|
||||
// The goal trigger is too far off the navmesh
|
||||
goal = level.ball_goals[util::getotherteam( self.team )];
|
||||
goalPoint = goal.origin - ( 0, 0, 125 );
|
||||
self bot::approach_point( goalPoint );
|
||||
self bot::sprint_to_goal();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
triggers = [];
|
||||
balls = array::randomize( level.balls );
|
||||
|
||||
foreach( ball in balls )
|
||||
{
|
||||
if ( !isdefined( ball.carrier ) && !ball.in_goal )
|
||||
{
|
||||
triggers[triggers.size] = ball.trigger;
|
||||
}
|
||||
else if ( isdefined( ball.carrier ) && ball.carrier.team != self.team )
|
||||
{
|
||||
// Don't go straight for the carrier since the objective doesn't update perfectly
|
||||
self bot::approach_point( ball.carrier.origin, 250, 1000, 128 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Go pick up the closest ball
|
||||
if ( triggers.size > 0 )
|
||||
{
|
||||
triggers = ArraySort( triggers, self.origin );
|
||||
|
||||
self BotSetGoal( triggers[0].origin );
|
||||
self bot::sprint_to_goal();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
@@ -0,0 +1,187 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\callbacks_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\system_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
|
||||
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
|
||||
|
||||
#using scripts\mp\teams\_teams;
|
||||
#using scripts\mp\_util;
|
||||
|
||||
#namespace bot_clean;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botPostCombat = &bot_post_combat;
|
||||
level.botIdle = &bot_idle;
|
||||
|
||||
level.botUpdateThreatGoal = &update_threat_goal;
|
||||
}
|
||||
|
||||
function bot_post_combat()
|
||||
{
|
||||
// Forget about the current target or hub when entering combat
|
||||
//if ( self bot_combat::has_threat() )
|
||||
//{
|
||||
// self.targetTaco = undefined;
|
||||
// self.targetHub = undefined;
|
||||
//}
|
||||
|
||||
// Ditch the hub if it's inactive or out of tacos
|
||||
if ( isdefined( self.targetHub ) )
|
||||
{
|
||||
if ( self.carriedTacos == 0 || self.targetHub.interactTeam == "none" )
|
||||
{
|
||||
self.targetHub = undefined;
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
}
|
||||
|
||||
// Ditch the taco if it's inactive or recycled
|
||||
if ( isdefined( self.targetTaco ) )
|
||||
{
|
||||
if ( self.targetTaco.interactTeam == "none" || self.targetTaco.DropTime != self.targetTacoDropTime )
|
||||
{
|
||||
self.targetTaco = undefined;
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
}
|
||||
|
||||
// Check for nearby tacos
|
||||
if ( !self bot_combat::has_threat() )
|
||||
{
|
||||
look_for_taco( 1024 );
|
||||
}
|
||||
|
||||
self bot_combat::mp_post_combat();
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
// Go to/stay in the deposit point
|
||||
if ( isdefined( self.targetHub ) )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( self.targetHub.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
|
||||
// Go look for a deposit point
|
||||
if ( RandomInt( 10 ) < self.carriedTacos )
|
||||
{
|
||||
foreach( hub in level.cleanDepositHubs )
|
||||
{
|
||||
if ( hub.interactTeam == "any" )
|
||||
{
|
||||
self.targetHub = hub;
|
||||
self.targetTaco = undefined;
|
||||
self bot::path_to_point_in_trigger( self.targetHub.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( look_for_taco( 1024 ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
|
||||
function look_for_taco( radius )
|
||||
{
|
||||
bestTaco = get_best_taco( radius );
|
||||
|
||||
if ( !isdefined( bestTaco ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
self.targetTaco = bestTaco;
|
||||
self.targetTacoDropTime = bestTaco.dropTime;
|
||||
|
||||
self bot::path_to_point_in_trigger( bestTaco.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function get_best_taco( radius )
|
||||
{
|
||||
radiusSq = radius * radius;
|
||||
|
||||
// Look for the closest taco in the nearby radius, or the closest one on the map
|
||||
bestTaco = undefined;
|
||||
bestTacoDistSq = undefined;
|
||||
|
||||
foreach ( taco in level.tacos )
|
||||
{
|
||||
if ( taco.interactTeam == "none" || !IsPointOnNavMesh( taco.origin , self ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
tacoDistSq = Distance2DSquared( self.origin, taco.origin );
|
||||
|
||||
if ( taco.attacker != self && tacoDistSq > radiusSq )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !isdefined( bestTaco ) || tacoDistSq < bestTacoDistSq )
|
||||
{
|
||||
bestTaco = taco;
|
||||
bestTacoDistSq = tacoDistSq;
|
||||
}
|
||||
}
|
||||
|
||||
return bestTaco;
|
||||
}
|
||||
|
||||
function update_threat_goal()
|
||||
{
|
||||
if ( isdefined( self.targetHub ) )
|
||||
{
|
||||
if ( !self BotGoalSet() )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( self.targetHub.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
radiusSq = 256 * 256;
|
||||
|
||||
if ( isdefined( self.targetTaco ) )
|
||||
{
|
||||
tacoDistSq = Distance2DSquared( self.origin, self.targetTaco.origin );
|
||||
if ( tacoDistSq > radiusSq )
|
||||
{
|
||||
self.targetTaco = undefined;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isdefined( self.targetTaco ) || self look_for_taco( 1024 ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::update_threat_goal();
|
||||
}
|
||||
@@ -0,0 +1,242 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
#using scripts\shared\weapons_shared;
|
||||
#using scripts\shared\weapons\_weapon_utils;
|
||||
#using scripts\shared\weapons\_weapons;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\mp\_util;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\shared\killstreaks_shared;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// 128 * 128
|
||||
|
||||
#namespace bot_combat;
|
||||
|
||||
function bot_ignore_threat( entity )
|
||||
{
|
||||
if ( threat_requires_launcher( entity ) && !self bot::has_launcher() )
|
||||
{
|
||||
return true;
|
||||
/* TODO:
|
||||
eyePos = self GetEye();
|
||||
|
||||
pitchMax = GetDvarFloat( "player_view_pitch_up" );
|
||||
pitchMin = -GetDvarFloat( "player_view_pitch_down" );
|
||||
|
||||
// Entity is in FOV but outside pitch range
|
||||
threatAngles = VectorToAngles( entity.origin - eyePos );
|
||||
|
||||
// TODO: Is threatAngles[0] in 0 to 360 or -180 to 180?
|
||||
if ( threatAngles[0] > pitchMax || threatAngles[0] < pitchMin )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function mp_pre_combat()
|
||||
{
|
||||
self bot_combat::bot_pre_combat();
|
||||
|
||||
if ( self IsReloading() ||
|
||||
self IsSwitchingWeapons() ||
|
||||
self IsThrowingGrenade() ||
|
||||
self IsMeleeing() ||
|
||||
self IsRemoteControlling() ||
|
||||
self IsInVehicle() ||
|
||||
self IsWeaponViewOnlyLinked() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self has_threat() )
|
||||
{
|
||||
self threat_switch_weapon();
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self switch_weapon() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self reload_weapon() )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::use_killstreak();
|
||||
}
|
||||
|
||||
|
||||
function mp_post_combat()
|
||||
{
|
||||
// Dogtag handling
|
||||
if( !IsDefined( level.dogtags ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( isdefined( self.bot.goalTag ) )
|
||||
{
|
||||
if ( !self.bot.goalTag gameobjects::can_interact_with( self ) )
|
||||
{
|
||||
// Cancel the tag
|
||||
self.bot.goalTag = undefined;
|
||||
|
||||
if ( !self bot_combat::has_threat() && self BotGoalSet() )
|
||||
{
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
}
|
||||
else if ( !self.bot.goalTagOnGround &&
|
||||
!self bot_combat::has_threat() &&
|
||||
self IsOnGround() &&
|
||||
Distance2DSquared( self.origin , self.bot.goalTag.origin ) < 16384 &&
|
||||
self BotSightTrace( self.bot.goalTag ) )
|
||||
{
|
||||
self thread bot::jump_to( self.bot.goalTag.origin );
|
||||
}
|
||||
}
|
||||
else if ( !self BotGoalSet() )
|
||||
{
|
||||
closestTag = self get_closest_tag();
|
||||
|
||||
if ( isdefined( closestTag ) )
|
||||
{
|
||||
// Trigger radius from _dogtags.gsc
|
||||
self set_goal_tag( closestTag );
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
function threat_requires_launcher( enemy )
|
||||
{
|
||||
if ( !isdefined( enemy ) || IsPlayer( enemy ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
killstreakType = undefined;
|
||||
|
||||
if ( isdefined( enemy.killstreakType ) )
|
||||
{
|
||||
killstreakType = enemy.killstreakType;
|
||||
}
|
||||
else if ( isdefined( enemy.parentStruct ) && isdefined( enemy.parentStruct.killstreakType ) )
|
||||
{
|
||||
killstreakType = enemy.parentStruct.killstreakType;
|
||||
}
|
||||
|
||||
if ( !isdefined( killstreakType ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
switch( killstreakType )
|
||||
{
|
||||
case "uav":
|
||||
case "counteruav":
|
||||
case "satellite":
|
||||
case "helicopter_gunner":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function combat_throw_proximity( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_throw_smoke( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_throw_lethal( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_throw_tactical( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_toss_frag( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_toss_flash( origin )
|
||||
{
|
||||
}
|
||||
|
||||
function combat_tactical_insertion( origin )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
function nearest_node( origin )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function dot_product( origin )
|
||||
{
|
||||
return bot::fwd_dot( origin );
|
||||
}
|
||||
|
||||
// Dogtags
|
||||
//========================================
|
||||
|
||||
function get_closest_tag()
|
||||
{
|
||||
closestTag = undefined;
|
||||
closestTagDistSq = undefined;
|
||||
|
||||
foreach( tag in level.dogtags )
|
||||
{
|
||||
if ( !tag gameobjects::can_interact_with( self ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
distSq = DistanceSquared( self.origin, tag.origin );
|
||||
|
||||
if ( !isdefined( closestTag ) || distSq < closestTagDistSq )
|
||||
{
|
||||
closestTag = tag;
|
||||
closestTagDistSq = distSq;
|
||||
}
|
||||
}
|
||||
|
||||
return closestTag;
|
||||
}
|
||||
|
||||
function set_goal_tag( tag )
|
||||
{
|
||||
self.bot.goalTag = tag;
|
||||
|
||||
traceStart = tag.origin;
|
||||
traceEnd = tag.origin + (0,0,-64);
|
||||
trace = BulletTrace( traceStart, traceEnd, false, undefined );
|
||||
|
||||
self.bot.goalTagOnGround = ( trace["fraction"] < 1 );
|
||||
|
||||
self bot::path_to_trigger( tag.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
#using scripts\codescripts\struct;
|
||||
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_conf;
|
||||
|
||||
function init()
|
||||
{
|
||||
// Handled by the base tag seeking behavior now
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,105 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\mp\gametypes\ctf;
|
||||
|
||||
#using scripts\mp\_util;
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_ctf;
|
||||
|
||||
function init()
|
||||
{
|
||||
level.onBotConnect = &on_bot_connect;
|
||||
level.botIdle = &bot_idle;
|
||||
}
|
||||
|
||||
function on_bot_connect()
|
||||
{
|
||||
foreach( flag in level.flags )
|
||||
{
|
||||
if ( flag gameobjects::get_owner_team() == self.team )
|
||||
{
|
||||
self.bot.flag = flag;
|
||||
}
|
||||
else
|
||||
{
|
||||
self.bot.enemyFlag = flag;
|
||||
}
|
||||
}
|
||||
|
||||
self bot::on_bot_connect();
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
carrier = self.bot.enemyFlag gameobjects::get_carrier();
|
||||
|
||||
// Carrying the enemy flag
|
||||
if ( isdefined( carrier ) && carrier == self )
|
||||
{
|
||||
// Wander around the home flag area until it comes back
|
||||
if ( self.bot.flag gameobjects::is_object_away_from_home() )
|
||||
{
|
||||
self bot::approach_point( self.bot.flag.flagBase.trigger.origin, 0, 1024 );
|
||||
}
|
||||
// Capture the enemy flag
|
||||
else
|
||||
{
|
||||
self bot::approach_goal_trigger( self.bot.flag.flagBase.trigger );
|
||||
}
|
||||
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
// Defend home base
|
||||
else if ( Distance2DSquared( self.origin, self.bot.flag.flagBase.trigger.origin ) < ( 1024 * 1024 ) &&
|
||||
RandomInt( 100 ) < 80 )
|
||||
{
|
||||
self bot::approach_point( self.bot.flag.flagBase.trigger.origin, 0, 1024 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
// Return the friendly flag
|
||||
else if ( self.bot.flag gameobjects::is_object_away_from_home() )
|
||||
{
|
||||
enemyCarrier = self.bot.flag gameobjects::get_carrier();
|
||||
|
||||
if ( isdefined( enemyCarrier ) )
|
||||
{
|
||||
// Don't go straight for the carrier since the objective doesn't update perfectly
|
||||
self bot::approach_point( enemyCarrier.origin, 250, 1000, 128 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
self BotSetGoal( self.bot.flag.trigger.origin );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Take the enemy flag
|
||||
else if ( !isdefined( carrier ) )
|
||||
{
|
||||
self bot::approach_goal_trigger( self.bot.enemyFlag.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
@@ -0,0 +1,148 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
#namespace bot_dem;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botIdle = &bot_idle;
|
||||
level.botCombat = &combat_think;
|
||||
level.botThreatLost = &bot_threat_lost;
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
approachRadiusSq = 750 * 750;
|
||||
|
||||
// Check for Plant / Defuse
|
||||
foreach( zone in level.bombZones )
|
||||
{
|
||||
if ( ( isdefined( zone.bombExploded ) && zone.bombExploded ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self IsTouching( zone.trigger ) )
|
||||
{
|
||||
if ( self can_plant( zone ) || self can_defuse( zone ) )
|
||||
{
|
||||
self bot::press_use_button();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( DistanceSquared( self.origin, zone.trigger.origin ) < approachRadiusSq )
|
||||
{
|
||||
// Defuse / Plant nearby zone
|
||||
if ( self can_plant( zone ) || self can_defuse( zone ) )
|
||||
{
|
||||
self bot::path_to_trigger( zone.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle zones
|
||||
zones = array::randomize( level.bombZones );
|
||||
|
||||
// Check for zones to defuse
|
||||
foreach( zone in zones )
|
||||
{
|
||||
if ( ( isdefined( zone.bombExploded ) && zone.bombExploded ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self can_defuse( zone ) && RandomInt( 100 ) < 70 )
|
||||
{
|
||||
self bot::approach_goal_trigger( zone.trigger, 750 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Go to a random zone to plant / guard
|
||||
foreach( zone in zones )
|
||||
{
|
||||
if ( ( isdefined( zone.bombExploded ) && zone.bombExploded ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Defend the nearby point
|
||||
if ( DistanceSquared( self.origin, zone.trigger.origin ) < approachRadiusSq &&
|
||||
RandomInt( 100 ) < 70 )
|
||||
{
|
||||
triggerRadius = self bot::get_trigger_radius( zone.trigger );
|
||||
self bot::approach_point( zone.trigger.origin, triggerRadius, 750 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
|
||||
function can_plant( zone )
|
||||
{
|
||||
return !( isdefined( zone.bombPlanted ) && zone.bombPlanted ) && self.team != zone gameobjects::get_owner_team();
|
||||
}
|
||||
|
||||
function can_defuse( zone )
|
||||
{
|
||||
return ( isdefined( zone.bombPlanted ) && zone.bombPlanted ) && self.team == zone gameobjects::get_owner_team();
|
||||
}
|
||||
|
||||
|
||||
function combat_think()
|
||||
{
|
||||
if ( ( isdefined( self.isPlanting ) && self.isPlanting ) ||
|
||||
( isdefined( self.isDefusing ) && self.isDefusing ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::combat_think();
|
||||
}
|
||||
|
||||
function bot_threat_lost()
|
||||
{
|
||||
approachRadiusSq = 750 * 750;
|
||||
|
||||
// Don't chase enemies if near a plantable/defusable zone
|
||||
foreach( zone in level.bombZones )
|
||||
{
|
||||
if ( ( isdefined( zone.bombExploded ) && zone.bombExploded ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( DistanceSquared( self.origin, zone.trigger.origin ) < approachRadiusSq )
|
||||
{
|
||||
// Defuse / Plant nearby zone
|
||||
if ( self can_plant( zone ) || self can_defuse( zone ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self bot_combat::chase_threat();
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
Line truncated
|
||||
|
||||
#using scripts\mp\gametypes\dom;
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_dom;
|
||||
|
||||
// size of radius trigger in BO2
|
||||
|
||||
|
||||
// Default Radius * 2.5
|
||||
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botUpdate = &bot_update;
|
||||
level.botPreCombat = &bot_pre_combat;
|
||||
level.botUpdateThreatGoal = &bot_update_threat_goal;
|
||||
level.botIdle = &bot_idle;
|
||||
}
|
||||
|
||||
function bot_update()
|
||||
{
|
||||
self.bot.capturingFlag = self get_capturing_flag();
|
||||
|
||||
self.bot.goalFlag = undefined;
|
||||
|
||||
if ( !self BotGoalReached() )
|
||||
{
|
||||
foreach( flag in level.domFlags )
|
||||
{
|
||||
if ( self bot::goal_in_trigger( flag.trigger ) )
|
||||
{
|
||||
self.bot.goalFlag = flag;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self bot::bot_update();
|
||||
}
|
||||
|
||||
function bot_pre_combat()
|
||||
{
|
||||
// Stop heading to goals that have been captured
|
||||
if ( !self bot_combat::has_threat() &&
|
||||
isdefined( self.bot.goalFlag ) &&
|
||||
self.bot.goalflag gameobjects::get_owner_team() == self.team )
|
||||
{
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
|
||||
self bot_combat::mp_pre_combat();
|
||||
}
|
||||
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if ( isdefined( self.bot.capturingFlag ) )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( self.bot.capturingFlag.trigger );
|
||||
return;
|
||||
}
|
||||
|
||||
bestFlag = get_best_flag();
|
||||
|
||||
if ( isdefined( bestFlag ) )
|
||||
{
|
||||
self bot::approach_goal_trigger( bestFlag.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
|
||||
// Combat
|
||||
//========================================
|
||||
|
||||
// Stay inside the goal when attacking
|
||||
function bot_update_threat_goal()
|
||||
{
|
||||
if ( isdefined( self.bot.capturingFlag ) )
|
||||
{
|
||||
if ( self BotGoalReached() )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( self.bot.capturingFlag.trigger );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::update_threat_goal();
|
||||
}
|
||||
|
||||
// Dom Flags
|
||||
//========================================
|
||||
|
||||
function get_capturing_flag()
|
||||
{
|
||||
foreach( flag in level.domFlags )
|
||||
{
|
||||
if ( self.team != flag gameobjects::get_owner_team() && self IsTouching( flag.trigger ) )
|
||||
{
|
||||
return flag;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function get_best_flag()
|
||||
{
|
||||
bestFlag = undefined;
|
||||
bestFlagDistSq = undefined;
|
||||
|
||||
// Closest flag under attack by the enemy, or not owned by their team
|
||||
foreach( flag in level.domFlags )
|
||||
{
|
||||
ownerTeam = flag gameobjects::get_owner_team();
|
||||
contested = flag gameobjects::get_num_touching_except_team( ownerTeam );
|
||||
distSq = Distance2DSquared( self.origin, flag.origin );
|
||||
|
||||
if ( ownerTeam == self.team && !contested )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !isdefined( bestFlag ) ||
|
||||
distSq < bestFlagDistSq )
|
||||
{
|
||||
bestFlag = flag;
|
||||
bestFlagDistSq = distSq;
|
||||
}
|
||||
}
|
||||
|
||||
return bestFlag;
|
||||
}
|
||||
@@ -0,0 +1,76 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\callbacks_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\system_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
|
||||
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_traversals;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
|
||||
|
||||
#using scripts\mp\teams\_teams;
|
||||
#using scripts\mp\_util;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_escort;
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botIdle = &bot_idle;
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if ( self.team == game["attackers"] )
|
||||
{
|
||||
self attackers_idle();
|
||||
}
|
||||
else
|
||||
{
|
||||
self defenders_idle();
|
||||
}
|
||||
}
|
||||
|
||||
function attackers_idle()
|
||||
{
|
||||
if ( isdefined( level.moveObject ) && ( level.robot.active || level.rebootPlayers > 0 ) )
|
||||
{
|
||||
if ( !level.robot.moving || math::cointoss() )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( level.moveObject.trigger );
|
||||
}
|
||||
else
|
||||
{
|
||||
self bot::approach_point( level.moveObject.trigger.origin, 160, 400 );
|
||||
}
|
||||
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
|
||||
function defenders_idle()
|
||||
{
|
||||
if ( isdefined( level.moveObject ) && level.robot.active )
|
||||
{
|
||||
self bot::approach_point( level.moveObject.trigger.origin, 160, 400 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
@@ -0,0 +1,203 @@
|
||||
Line truncated
|
||||
|
||||
|
||||
|
||||
#namespace bot_hack;
|
||||
|
||||
function hack_tank_get_goal_origin( tank )
|
||||
{
|
||||
nodes = GetNodesInRadiusSorted( tank.origin, 256, 0, 64, "Path" );
|
||||
|
||||
foreach( node in nodes )
|
||||
{
|
||||
dir = VectorNormalize( node.origin - tank.origin );
|
||||
dir = VectorScale( dir, 32 );
|
||||
|
||||
goal = tank.origin + dir;
|
||||
|
||||
if ( self FindPath( self.origin, goal, false ) )
|
||||
{
|
||||
return goal;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function hack_has_goal( tank )
|
||||
{
|
||||
goal = self GetGoal( "hack" );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
if ( DistanceSquared( goal, tank.origin ) < 128 * 128 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hack_at_goal()
|
||||
{
|
||||
if ( self AtGoal( "hack" ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
goal = self GetGoal( "hack" );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
tanks = GetEntArray( "talon", "targetname" );
|
||||
tanks = ArraySort( tanks, self.origin );
|
||||
|
||||
foreach( tank in tanks )
|
||||
{
|
||||
if ( DistanceSquared( goal, tank.origin ) < 128 * 128 )
|
||||
{
|
||||
if ( isdefined( tank.trigger ) && self IsTouching( tank.trigger ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hack_goal_pregame( tanks )
|
||||
{
|
||||
foreach( tank in tanks )
|
||||
{
|
||||
if ( isdefined( tank.owner ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isdefined( tank.team ) && tank.team == self.team )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
goal = self hack_tank_get_goal_origin( tank );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
if ( self AddGoal( goal, 24, 2, "hack" ) )
|
||||
{
|
||||
self.goal_flag = tank;
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hack_think()
|
||||
{
|
||||
if ( hack_at_goal() )
|
||||
{
|
||||
self SetStance( "crouch" );
|
||||
wait( 0.25 );
|
||||
|
||||
self AddGoal( self.origin, 24, 4, "hack" );
|
||||
self PressUseButton( level.drone_hack_time + 1 );
|
||||
wait( level.drone_hack_time + 1 );
|
||||
|
||||
self SetStance( "stand" );
|
||||
self CancelGoal( "hack" );
|
||||
}
|
||||
|
||||
tanks = GetEntArray( "talon", "targetname" );
|
||||
tanks = ArraySort( tanks, self.origin );
|
||||
|
||||
if ( !( isdefined( level.drones_spawned ) && level.drones_spawned ) )
|
||||
{
|
||||
self hack_goal_pregame( tanks );
|
||||
}
|
||||
else
|
||||
{
|
||||
foreach( tank in tanks )
|
||||
{
|
||||
if ( isdefined( tank.owner ) && tank.owner == self )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( !isdefined( tank.owner ) )
|
||||
{
|
||||
if ( self hack_has_goal( tank ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
goal = self hack_tank_get_goal_origin( tank );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
self AddGoal( goal, 24, 2, "hack" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( tank.isStunned && DistanceSquared( self.origin, tank.origin ) < 512 * 512 )
|
||||
{
|
||||
goal = self hack_tank_get_goal_origin( tank );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
self AddGoal( goal, 24, 3, "hack" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
if ( !bot::bot_vehicle_weapon_ammo( "emp_grenade" ) )
|
||||
{
|
||||
ammo = GetEntArray( "weapon_scavenger_item_hack", "classname" );
|
||||
ammo = ArraySort( ammo, self.origin );
|
||||
|
||||
foreach( bag in ammo )
|
||||
{
|
||||
if ( FindPath( self.origin, bag.origin, false ) )
|
||||
{
|
||||
self AddGoal( bag.origin, BOT_DEFAULT_GOAL_RADIUS, PRIORITY_NORMAL, "hack" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
*/
|
||||
|
||||
foreach( tank in tanks )
|
||||
{
|
||||
if ( isdefined( tank.owner ) && tank.owner == self )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( tank.isStunned )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self ThrowGrenade( GetWeapon( "emp_grenade" ), tank.origin ) )
|
||||
{
|
||||
self waittill( "grenade_fire" );
|
||||
|
||||
goal = self hack_tank_get_goal_origin( tank );
|
||||
|
||||
if ( isdefined( goal ) )
|
||||
{
|
||||
self AddGoal( goal, 24, 3, "hack" );
|
||||
wait( 0.5 );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,435 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_hq;
|
||||
|
||||
function hq_think()
|
||||
{
|
||||
time = GetTime();
|
||||
|
||||
if ( time < self.bot.update_objective )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
self.bot.update_objective = time + RandomIntRange( 500, 1500 );
|
||||
|
||||
if ( should_patrol_hq() )
|
||||
{
|
||||
self patrol_hq();
|
||||
}
|
||||
else if ( !has_hq_goal() )
|
||||
{
|
||||
self move_to_hq();
|
||||
}
|
||||
|
||||
if ( self is_capturing_hq() )
|
||||
{
|
||||
self capture_hq();
|
||||
}
|
||||
|
||||
hq_tactical_insertion();
|
||||
hq_grenade();
|
||||
|
||||
if ( !is_capturing_hq() && !self AtGoal( "hq_patrol" ) )
|
||||
{
|
||||
mine = GetNearestNode( self.origin );
|
||||
point = hq_nearest_point();
|
||||
|
||||
if ( IsDefined( mine ) && bot::navmesh_points_visible( mine.origin, point ) )
|
||||
{
|
||||
self LookAt( level.radio.baseorigin + ( 0, 0, 30 ) );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function has_hq_goal()
|
||||
{
|
||||
origin = self GetGoal( "hq_radio" );
|
||||
|
||||
if ( isdefined( origin ) )
|
||||
{
|
||||
foreach( point in level.radio.points )
|
||||
{
|
||||
if ( DistanceSquared( origin, point ) < 64 * 64 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_capturing_hq()
|
||||
{
|
||||
return ( self AtGoal( "hq_radio" ) );
|
||||
}
|
||||
|
||||
function should_patrol_hq()
|
||||
{
|
||||
if ( level.radio.gameobject.ownerTeam == "neutral" )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( level.radio.gameobject.ownerTeam != self.team )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if ( hq_is_contested() )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function patrol_hq()
|
||||
{
|
||||
self CancelGoal( "hq_radio" );
|
||||
|
||||
if ( self AtGoal( "hq_patrol" ) )
|
||||
{
|
||||
node = GetNearestNode( self.origin );
|
||||
|
||||
if ( isdefined( node ) && node.type == "Path" )
|
||||
{
|
||||
self SetStance( "crouch" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self SetStance( "stand" );
|
||||
}
|
||||
|
||||
if ( GetTime() > self.bot.update_lookat )
|
||||
{
|
||||
origin = self get_look_at();
|
||||
|
||||
z = 20;
|
||||
|
||||
if ( DistanceSquared( origin, self.origin ) > 512 * 512 )
|
||||
{
|
||||
z = RandomIntRange( 16, 60 );
|
||||
}
|
||||
|
||||
self LookAt( origin + ( 0, 0, z ) );
|
||||
|
||||
if ( DistanceSquared( origin, self.origin ) > 256 * 256 )
|
||||
{
|
||||
dir = VectorNormalize( self.origin - origin );
|
||||
dir = VectorScale( dir, 256 );
|
||||
|
||||
origin = origin + dir;
|
||||
}
|
||||
|
||||
self bot_combat::combat_throw_proximity( origin );
|
||||
|
||||
self.bot.update_lookat = GetTime() + RandomIntRange( 1500, 3000 );
|
||||
}
|
||||
|
||||
goal = self GetGoal( "hq_patrol" );
|
||||
nearest = hq_nearest_point();
|
||||
|
||||
mine = GetNearestNode( goal );
|
||||
|
||||
if ( isdefined( mine ) && !bot::navmesh_points_visible( mine.origin, nearest ) )
|
||||
{
|
||||
self ClearLookAt();
|
||||
self CancelGoal( "hq_patrol" );
|
||||
}
|
||||
|
||||
if ( GetTime() > self.bot.update_objective_patrol )
|
||||
{
|
||||
self ClearLookAt();
|
||||
self CancelGoal( "hq_patrol" );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
nearest = hq_nearest_point();
|
||||
|
||||
if ( self HasGoal( "hq_patrol" ) )
|
||||
{
|
||||
goal = self GetGoal( "hq_patrol" );
|
||||
|
||||
if ( DistanceSquared( self.origin, goal ) < 256 * 256 )
|
||||
{
|
||||
origin = self get_look_at();
|
||||
self LookAt( origin );
|
||||
}
|
||||
|
||||
if ( DistanceSquared( self.origin, goal ) < 128 * 128 )
|
||||
{
|
||||
self.bot.update_objective_patrol = GetTime() + RandomIntRange( 3000, 6000 );
|
||||
}
|
||||
|
||||
mine = GetNearestNode( goal );
|
||||
|
||||
if ( isdefined( mine ) && !bot::navmesh_points_visible( mine.origin, nearest ) )
|
||||
{
|
||||
self ClearLookAt();
|
||||
self CancelGoal( "hq_patrol" );
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
points = util::PositionQuery_PointArray( nearest, 0, 512, 70, 64 );
|
||||
points = NavPointSightFilter( points, nearest );
|
||||
assert( points.size );
|
||||
|
||||
i = RandomInt( points.size );
|
||||
|
||||
for ( ; i < points.size; i++ )
|
||||
{
|
||||
if ( self bot::friend_goal_in_radius( "hq_radio", points[i], 128 ) == 0 )
|
||||
{
|
||||
if ( self bot::friend_goal_in_radius( "hq_patrol", points[i], 256 ) == 0 )
|
||||
{
|
||||
self AddGoal( points[i], 24, 3, "hq_patrol" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function move_to_hq()
|
||||
{
|
||||
self ClearLookAt();
|
||||
self CancelGoal( "hq_radio" );
|
||||
self CancelGoal( "hq_patrol" );
|
||||
|
||||
if ( self GetStance() == "prone" )
|
||||
{
|
||||
self SetStance( "crouch" );
|
||||
wait( 0.25 );
|
||||
}
|
||||
|
||||
if ( self GetStance() == "crouch" )
|
||||
{
|
||||
self SetStance( "stand" );
|
||||
wait( 0.25 );
|
||||
}
|
||||
|
||||
points = array::randomize( level.radio.points );
|
||||
|
||||
foreach( point in points )
|
||||
{
|
||||
if ( self bot::friend_goal_in_radius( "hq_radio", point, 64 ) == 0 )
|
||||
{
|
||||
self AddGoal( point, 24, 3, "hq_radio" );
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self AddGoal( array::random( points ), 24, 3, "hq_radio" );
|
||||
}
|
||||
|
||||
function get_look_at()
|
||||
{
|
||||
enemy = self bot::get_closest_enemy( self.origin, true );
|
||||
|
||||
if ( isdefined( enemy ) )
|
||||
{
|
||||
node = GetVisibleNode( self.origin, enemy.origin );
|
||||
|
||||
if ( isdefined( node ) && DistanceSquared( self.origin, node.origin ) > 128 * 128 )
|
||||
{
|
||||
return node.origin;
|
||||
}
|
||||
}
|
||||
|
||||
enemies = self bot::get_enemies( false );
|
||||
|
||||
if ( enemies.size )
|
||||
{
|
||||
enemy = array::random( enemies );
|
||||
}
|
||||
|
||||
if ( isdefined( enemy ) )
|
||||
{
|
||||
node = GetVisibleNode( self.origin, enemy.origin );
|
||||
|
||||
if ( isdefined( node ) && DistanceSquared( self.origin, node.origin ) > 128 * 128 )
|
||||
{
|
||||
return node.origin;
|
||||
}
|
||||
}
|
||||
|
||||
spawn = array::random( level.spawnpoints );
|
||||
node = GetVisibleNode( self.origin, spawn.origin );
|
||||
|
||||
if ( isdefined( node ) && DistanceSquared( self.origin, node.origin ) > 128 * 128 )
|
||||
{
|
||||
return node.origin;
|
||||
}
|
||||
|
||||
return level.radio.baseorigin;
|
||||
}
|
||||
|
||||
function capture_hq()
|
||||
{
|
||||
self AddGoal( self.origin, 24, 3, "hq_radio" );
|
||||
self SetStance( "crouch" );
|
||||
|
||||
if ( GetTime() > self.bot.update_lookat )
|
||||
{
|
||||
origin = self get_look_at();
|
||||
|
||||
z = 20;
|
||||
|
||||
if ( DistanceSquared( origin, self.origin ) > 512 * 512 )
|
||||
{
|
||||
z = RandomIntRange( 16, 60 );
|
||||
}
|
||||
|
||||
self LookAt( origin + ( 0, 0, z ) );
|
||||
|
||||
if ( DistanceSquared( origin, self.origin ) > 256 * 256 )
|
||||
{
|
||||
dir = VectorNormalize( self.origin - origin );
|
||||
dir = VectorScale( dir, 256 );
|
||||
|
||||
origin = origin + dir;
|
||||
}
|
||||
|
||||
self bot_combat::combat_throw_proximity( origin );
|
||||
|
||||
self.bot.update_lookat = GetTime() + RandomIntRange( 1500, 3000 );
|
||||
}
|
||||
}
|
||||
|
||||
function any_other_team_touching( skip_team )
|
||||
{
|
||||
foreach( team in level.teams )
|
||||
{
|
||||
if ( team == skip_team )
|
||||
continue;
|
||||
|
||||
if ( level.radio.gameobject.numtouching[ team ] )
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function is_hq_contested( skip_team )
|
||||
{
|
||||
if ( any_other_team_touching( skip_team ) )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
enemy = self bot::get_closest_enemy( level.radio.baseorigin, true );
|
||||
|
||||
if ( isdefined( enemy ) && DistanceSquared( enemy.origin, level.radio.baseorigin ) < 512 * 512 )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function hq_grenade()
|
||||
{
|
||||
enemies = bot::get_enemies();
|
||||
|
||||
if ( !enemies.size )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if ( self AtGoal( "hq_patrol" ) || self AtGoal( "hq_radio" ) )
|
||||
{
|
||||
if ( self GetWeaponAmmoStock( GetWeapon( "proximity_grenade" ) ) > 0 )
|
||||
{
|
||||
origin = get_look_at();
|
||||
|
||||
if ( self bot_combat::combat_throw_proximity( origin ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if ( !is_hq_contested( self.team ) )
|
||||
{
|
||||
self bot_combat::combat_throw_smoke( level.radio.baseorigin );
|
||||
return;
|
||||
}
|
||||
|
||||
enemy = self bot::get_closest_enemy( level.radio.baseorigin, false );
|
||||
|
||||
if ( isdefined( enemy ) )
|
||||
{
|
||||
origin = enemy.origin;
|
||||
}
|
||||
else
|
||||
{
|
||||
origin = level.radio.baseorigin;
|
||||
}
|
||||
|
||||
dir = VectorNormalize( self.origin - origin );
|
||||
dir = ( 0, dir[1], 0 );
|
||||
|
||||
origin = origin + VectorScale( dir, 128 );
|
||||
|
||||
if ( !self bot_combat::combat_throw_lethal( origin ) )
|
||||
{
|
||||
self bot_combat::combat_throw_tactical( origin );
|
||||
}
|
||||
}
|
||||
|
||||
function hq_tactical_insertion()
|
||||
{
|
||||
if ( !self HasWeapon( GetWeapon( "tactical_insertion" ) ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
dist = self GetLookAheadDist();
|
||||
dir = self GetLookaheadDir();
|
||||
|
||||
if ( !isdefined( dist ) || !isdefined( dir ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
point = hq_nearest_point();
|
||||
mine = GetNearestNode( self.origin );
|
||||
|
||||
if ( isdefined( mine ) && !bot::navmesh_points_visible( mine.origin, point ) )
|
||||
{
|
||||
origin = self.origin + VectorScale( dir, dist );
|
||||
|
||||
next = GetNearestNode( origin );
|
||||
|
||||
if ( isdefined( next ) && bot::navmesh_points_visible( next.origin, point ) )
|
||||
{
|
||||
bot_combat::combat_tactical_insertion( self.origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function hq_nearest_point()
|
||||
{
|
||||
return array::random( level.radio.points );
|
||||
}
|
||||
|
||||
function hq_is_contested()
|
||||
{
|
||||
enemy = self bot::get_closest_enemy( level.radio.baseorigin, false );
|
||||
return ( IsDefined( enemy ) && DistanceSquared( enemy.origin, level.radio.baseorigin ) < level.radio.node_radius * level.radio.node_radius );
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
#namespace bot_koth;
|
||||
|
||||
function init()
|
||||
{
|
||||
level.onBotSpawned = &on_bot_spawned;
|
||||
level.botUpdateThreatGoal = &bot_update_threat_goal;
|
||||
level.botIdle = &bot_idle;
|
||||
}
|
||||
|
||||
function on_bot_spawned()
|
||||
{
|
||||
self thread wait_zone_moved();
|
||||
|
||||
self bot::on_bot_spawned();
|
||||
}
|
||||
|
||||
function wait_zone_moved()
|
||||
{
|
||||
self endon( "death" );
|
||||
level endon( "game_ended" );
|
||||
|
||||
while(1)
|
||||
{
|
||||
level waittill( "zone_moved" );
|
||||
|
||||
if ( !self bot_combat::has_threat() && self BotGoalSet() )
|
||||
{
|
||||
self BotSetGoal( self.origin );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function bot_update_threat_goal()
|
||||
{
|
||||
if ( isdefined( level.zone ) && self IsTouching( level.zone.gameobject.trigger ) )
|
||||
{
|
||||
if ( self BotGoalReached() )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( level.zone.gameobject.trigger );
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self bot_combat::update_threat_goal();
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if ( isdefined( level.zone ) )
|
||||
{
|
||||
if ( self IsTouching( level.zone.gameobject.trigger ) )
|
||||
{
|
||||
self bot::path_to_point_in_trigger( level.zone.gameobject.trigger );
|
||||
}
|
||||
else
|
||||
{
|
||||
self bot::approach_goal_trigger( level.zone.gameobject.trigger );
|
||||
self bot::sprint_to_goal();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
@@ -0,0 +1,876 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\rank_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
|
||||
#using scripts\mp\killstreaks\_killstreaks;
|
||||
|
||||
#using scripts\mp\bots\_bot;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
#namespace bot_loadout;
|
||||
|
||||
// Item Whitelist
|
||||
//========================================
|
||||
|
||||
function in_whitelist( itemName )
|
||||
{
|
||||
if ( !isdefined( itemName ) )
|
||||
return false;
|
||||
|
||||
switch( itemName )
|
||||
{
|
||||
// Secondaries
|
||||
case "WEAPON_KNIFE_LOADOUT":
|
||||
|
||||
case "WEAPON_PISTOL_STANDARD":
|
||||
case "WEAPON_PISTOL_BURST":
|
||||
case "WEAPON_PISTOL_FULLAUTO":
|
||||
|
||||
case "WEAPON_LAUNCHER_STANDARD":
|
||||
case "WEAPON_LAUNCHER_LOCKONLY":
|
||||
|
||||
case "WEAPON_SMG_STANDARD":
|
||||
case "WEAPON_SMG_BURST":
|
||||
case "WEAPON_SMG_FASTFIRE":
|
||||
case "WEAPON_SMG_LONGRANGE":
|
||||
case "WEAPON_SMG_VERSATILE":
|
||||
case "WEAPON_SMG_CAPACITY":
|
||||
|
||||
// Primaries
|
||||
case "WEAPON_AR_STANDARD":
|
||||
case "WEAPON_AR_ACCURATE":
|
||||
case "WEAPON_AR_CQB":
|
||||
case "WEAPON_AR_DAMAGE":
|
||||
case "WEAPON_AR_FASTBURST":
|
||||
case "WEAPON_AR_LONGBURST":
|
||||
case "WEAPON_AR_MARKSMAN":
|
||||
|
||||
case "WEAPON_LMG_CQB":
|
||||
case "WEAPON_LMG_HEAVY":
|
||||
case "WEAPON_LMG_LIGHT":
|
||||
case "WEAPON_LMG_SLOWFIRE":
|
||||
|
||||
case "WEAPON_SNIPER_FASTBOLT":
|
||||
case "WEAPON_SNIPER_FASTSEMI":
|
||||
case "WEAPON_SNIPER_POWERBOLT":
|
||||
case "WEAPON_SNIPER_CHARGESHOT":
|
||||
|
||||
case "WEAPON_SHOTGUN_FULLAUTO":
|
||||
case "WEAPON_SHOTGUN_PRECISION":
|
||||
case "WEAPON_SHOTGUN_PUMP":
|
||||
case "WEAPON_SHOTGUN_SEMIAUTO":
|
||||
|
||||
// Lethals
|
||||
case "WEAPON_FRAGGRENADE":
|
||||
case "WEAPON_HATCHET":
|
||||
case "WEAPON_STICKY_GRENADE":
|
||||
//case "WEAPON_SATCHEL_CHARGE":
|
||||
case "WEAPON_BOUNCINGBETTY":
|
||||
case "WEAPON_INCENDIARY_GRENADE":
|
||||
|
||||
// Tacticals
|
||||
case "WEAPON_WILLY_PETE":
|
||||
case "WEAPON_STUN_GRENADE":
|
||||
case "WEAPON_EMPGRENADE":
|
||||
case "WEAPON_FLASHBANG":
|
||||
case "WEAPON_PROXIMITY_GRENADE":
|
||||
//case "WEAPON_PDA_HACK":
|
||||
//case "WEAPON_TROPHY_SYSTEM":
|
||||
|
||||
// Killstreaks
|
||||
//case "KILLSTREAK_RCBOMB":
|
||||
case "KILLSTREAK_RECON":
|
||||
case "KILLSTREAK_COUNTER_UAV":
|
||||
//case "KILLSTREAK_SUPPLY_DROP":
|
||||
//case "KILLSTREAK_MICROWAVE_TURRET":
|
||||
//case "KILLSTREAK_REMOTE_MISSILE":
|
||||
//case "KILLSTREAK_PLANEMORTAR":
|
||||
//case "KILLSTREAK_AUTO_TURRET":
|
||||
//case "KILLSTREAK_AI_TANK_DROP":
|
||||
//case "KILLSTREAK_HELICOPTER_COMLINK":
|
||||
case "KILLSTREAK_SATELLITE":
|
||||
//case "KILLSTREAK_EMP":
|
||||
//case "KILLSTREAK_HELICOPTER_GUNNER":
|
||||
case "KILLSTREAK_RAPS":
|
||||
//case "KILLSTREAK_DRONE_STRIKE":
|
||||
//case "KILLSTREAK_DART":
|
||||
case "KILLSTREAK_SENTINEL":
|
||||
|
||||
// TU Something Weapons
|
||||
//case "WEAPON_MELEE_KNUCKLES":
|
||||
//case "WEAPON_MELEE_BUTTERFLY":
|
||||
//case "WEAPON_MELEE_WRENCH":
|
||||
|
||||
// TU 6 Weapons
|
||||
//case "WEAPON_PISTOL_SHOTGUN":
|
||||
//case "WEAPON_AR_GARAND":
|
||||
//case "WEAPON_SPECIAL_CROSSBOW":
|
||||
//case "WEAPON_MELEE_CROWBAR":
|
||||
//case "WEAPON_MELEE_SWORD":
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// Classes
|
||||
//========================================
|
||||
|
||||
function build_classes()
|
||||
{
|
||||
primaryWeapons = self get_available_items( undefined, "primary" );
|
||||
secondaryWeapons = self get_available_items( undefined, "secondary" );
|
||||
lethals = self get_available_items( undefined, "primarygadget" );
|
||||
tacticals = self get_available_items( undefined, "secondarygadget" );
|
||||
if ( ( isdefined( level.perksEnabled ) && level.perksEnabled ) )
|
||||
{
|
||||
specialties1 = self get_available_items( undefined, "specialty1" );
|
||||
specialties2 = self get_available_items( undefined, "specialty2" );
|
||||
specialties3 = self get_available_items( undefined, "specialty3" );
|
||||
}
|
||||
|
||||
foreach( className, classValue in level.classMap )
|
||||
{
|
||||
if ( !isSubstr( className, "custom" ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
classIndex = int( className[className.size-1] );
|
||||
|
||||
pickedItems = [];
|
||||
|
||||
pick_item( pickedItems, primaryWeapons );
|
||||
|
||||
if ( RandomInt( 100 ) < 95 ) // 5% chance to be a boxer for Scronce
|
||||
{
|
||||
pick_item( pickedItems, secondaryWeapons );
|
||||
}
|
||||
|
||||
// Shuffle these selections around a bit so the classes don't all look the same when the allocation is low
|
||||
otherItems = Array ( lethals, tacticals, specialties1, specialties2, specialties3 );
|
||||
otherItems = array::randomize( otherItems );
|
||||
|
||||
for ( i = 0; i < otherItems.size; i ++ )
|
||||
{
|
||||
pick_item( pickedItems, otherItems[i] );
|
||||
}
|
||||
|
||||
// Add items up to the max allocation
|
||||
for ( i = 0; i < pickedItems.size && i < level.maxAllocation; i++ )
|
||||
{
|
||||
self BotClassAddItem( classIndex, pickedItems[i] );
|
||||
}
|
||||
|
||||
// TODO: Pick primary/secondary attachments, extra perks, extra lethal, extra tactical, overkill
|
||||
/*
|
||||
primaryWeapon = self GetLoadoutWeapon( classIndex, "primary" );
|
||||
|
||||
if ( primaryWeapon != level.weaponNone && primaryWeapon.supportedAttachments.size )
|
||||
{
|
||||
attachment = array::random( primaryWeapon.supportedAttachments );
|
||||
self BotClassAddAttachment( classIndex, primaryWeapon, attachment, "primary" );
|
||||
}
|
||||
|
||||
secondaryWeapon = self GetLoadoutWeapon( classIndex, "secondary" );
|
||||
|
||||
if ( secondaryWeapon != level.weaponNone && secondaryWeapon.supportedAttachments.size )
|
||||
{
|
||||
attachment = array::random( secondaryWeapon.supportedAttachments );
|
||||
self BotClassAddAttachment( classIndex, secondaryWeapon, attachment, "secondary" );
|
||||
}
|
||||
*/
|
||||
}
|
||||
}
|
||||
|
||||
function pick_item( &pickedItems, items )
|
||||
{
|
||||
if ( !isdefined( items ) || items.size <= 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
pickedItems[pickedItems.size] = array::random( items );
|
||||
}
|
||||
|
||||
function pick_classes()
|
||||
{
|
||||
self.loadoutClasses = [];
|
||||
self.launcherClassCount = 0;
|
||||
|
||||
foreach( className, classValue in level.classMap )
|
||||
{
|
||||
if ( isSubstr( className, "custom" ) )
|
||||
{
|
||||
if ( level.disableCAC )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
classIndex = int( className[className.size-1] );
|
||||
}
|
||||
else
|
||||
{
|
||||
// Things bots could use better in the default classes:
|
||||
// C4, Trophy System, Lock on only launcher
|
||||
classIndex = level.classToClassNum[ classValue ];
|
||||
}
|
||||
|
||||
primary = self GetLoadoutWeapon( classIndex, "primary" );
|
||||
secondary = self GetLoadoutWeapon( classIndex, "secondary" );
|
||||
|
||||
botClass = SpawnStruct();
|
||||
botClass.name = className;
|
||||
botClass.index = classIndex;
|
||||
botClass.value = classValue;
|
||||
botClass.primary = primary;
|
||||
botClass.secondary = secondary;
|
||||
|
||||
if ( botClass.secondary.isRocketLauncher )
|
||||
{
|
||||
self.launcherClassCount++;
|
||||
}
|
||||
|
||||
self.loadoutClasses[ self.loadoutClasses.size ] = botClass;
|
||||
}
|
||||
}
|
||||
|
||||
function get_current_class()
|
||||
{
|
||||
currValue = self.pers["class"];
|
||||
if ( !isdefined( currValue ) )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
foreach( botClass in self.loadoutClasses )
|
||||
{
|
||||
if ( botClass.value == currValue )
|
||||
{
|
||||
return botClass;
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Specialists
|
||||
//========================================
|
||||
|
||||
function pick_hero_gadget()
|
||||
{
|
||||
if ( RandomInt( 2 ) < 1 || !self pick_hero_ability() )
|
||||
{
|
||||
self pick_hero_weapon();
|
||||
}
|
||||
}
|
||||
|
||||
function pick_hero_weapon()
|
||||
{
|
||||
heroWeaponRef = self GetHeroWeaponName();
|
||||
|
||||
if ( IsItemRestricted( heroWeaponRef ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
heroWeaponName = self get_item_name( heroWeaponRef );
|
||||
self BotClassAddItem( 0, heroWeaponName );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
function pick_hero_ability()
|
||||
{
|
||||
heroAbilityRef = self GetHeroAbilityName();
|
||||
|
||||
if ( IsItemRestricted( heroAbilityRef ) )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
heroAbilityName = self get_item_name( heroAbilityRef );
|
||||
self BotClassAddItem( 0, heroAbilityName );
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Killstreaks
|
||||
//========================================
|
||||
|
||||
function pick_killstreaks()
|
||||
{
|
||||
killstreaks = array::randomize( self get_available_items( "killstreak" ) );
|
||||
|
||||
for( i = 0; i < 3 && i < killstreaks.size; i++ )
|
||||
{
|
||||
self BotClassAddItem( 0, killstreaks[i] );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// Get Items
|
||||
//========================================
|
||||
|
||||
function get_available_items( filterGroup, filterSlot )
|
||||
{
|
||||
// Get unlocked and unrestricted items
|
||||
items = [];
|
||||
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
row = tableLookupRowNum( level.statsTableID, 0, i );
|
||||
|
||||
if ( row < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
name = tableLookupColumnForRow( level.statsTableID, row, 3 );
|
||||
|
||||
if ( name == "" || !in_whitelist( name ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allocation = Int( tableLookupColumnForRow( level.statsTableID, row, 12 ) );
|
||||
|
||||
if ( allocation < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
ref = tableLookupColumnForRow( level.statsTableId, row, 4 );
|
||||
|
||||
if ( IsItemRestricted( ref ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
number = Int( tableLookupColumnForRow( level.statsTableID, row, 0 ) );
|
||||
|
||||
if ( !SessionModeIsPrivate() && self IsItemLocked( number ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( isdefined( filterGroup ) )
|
||||
{
|
||||
group = tableLookupColumnForRow( level.statsTableID, row, 2 );
|
||||
|
||||
if ( group != filterGroup )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if ( isdefined( filterSlot ) )
|
||||
{
|
||||
slot = tableLookupColumnForRow( level.statsTableID, row, 13 );
|
||||
|
||||
if ( slot != filterSlot )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
items[items.size] = name;
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function get_item_name( itemReference )
|
||||
{
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
row = tableLookupRowNum( level.statsTableID, 0, i );
|
||||
|
||||
if ( row < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
reference = tableLookupColumnForRow( level.statsTableID, row, 4 );
|
||||
|
||||
if ( reference != itemReference )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
name = tableLookupColumnForRow( level.statsTableID, row, 3 );
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
return undefined;
|
||||
}
|
||||
|
||||
// Not in use
|
||||
|
||||
function init()
|
||||
{
|
||||
level endon( "game_ended" );
|
||||
|
||||
level.bot_banned_killstreaks = Array ( "KILLSTREAK_RCBOMB",
|
||||
"KILLSTREAK_QRDRONE",
|
||||
"KILLSTREAK_REMOTE_MISSILE",
|
||||
"KILLSTREAK_REMOTE_MORTAR",
|
||||
"KILLSTREAK_HELICOPTER_GUNNER" );
|
||||
for ( ;; )
|
||||
{
|
||||
level waittill( "connected", player );
|
||||
|
||||
if ( !player IsTestClient() )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
player thread on_bot_connect();
|
||||
}
|
||||
}
|
||||
|
||||
function on_bot_connect()
|
||||
{
|
||||
self endon( "disconnect" );
|
||||
|
||||
if ( isdefined( self.pers[ "bot_loadout" ] ) )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
wait( 0.10 );
|
||||
|
||||
if ( self GetEntityNumber() % 2 == 0 )
|
||||
{
|
||||
{wait(.05);};
|
||||
}
|
||||
|
||||
self bot::set_rank();
|
||||
|
||||
self BotSetRandomCharacterCustomization();
|
||||
|
||||
if ( level.onlineGame && !SessionModeIsPrivate() )
|
||||
{
|
||||
self BotSetDefaultClass( 5, "class_assault" );
|
||||
self BotSetDefaultClass( 6, "class_smg" );
|
||||
self BotSetDefaultClass( 7, "class_lmg" );
|
||||
self BotSetDefaultClass( 8, "class_cqb" );
|
||||
self BotSetDefaultClass( 9, "class_sniper" );
|
||||
}
|
||||
else
|
||||
{
|
||||
self BotSetDefaultClass( 5, "class_assault" );
|
||||
self BotSetDefaultClass( 6, "class_smg" );
|
||||
self BotSetDefaultClass( 7, "class_lmg" );
|
||||
self BotSetDefaultClass( 8, "class_cqb" );
|
||||
self BotSetDefaultClass( 9, "class_sniper" );
|
||||
}
|
||||
|
||||
max_allocation = 10;
|
||||
|
||||
if ( !SessionModeIsPrivate() )
|
||||
{
|
||||
for ( i = 1; i <= 3; i++ )
|
||||
{
|
||||
if ( self IsItemLocked( rank::GetItemIndex( "feature_allocation_slot_" + i ) ) )
|
||||
{
|
||||
max_allocation--;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
self construct_loadout( max_allocation );
|
||||
self.pers[ "bot_loadout" ] = true;
|
||||
}
|
||||
|
||||
function construct_loadout( allocation_max )
|
||||
{
|
||||
if ( !SessionModeIsPrivate() && self IsItemLocked( rank::GetItemIndex( "feature_cac" ) ) )
|
||||
{
|
||||
// cac still locked
|
||||
return;
|
||||
}
|
||||
|
||||
pixbeginevent( "bot_construct_loadout" );
|
||||
|
||||
item_list = build_item_list();
|
||||
|
||||
// item_list["primary"] = [];
|
||||
// item_list["primary"][0] = "WEAPON_RIOTSHIELD";
|
||||
|
||||
construct_class( 0, item_list, allocation_max );
|
||||
construct_class( 1, item_list, allocation_max );
|
||||
construct_class( 2, item_list, allocation_max );
|
||||
construct_class( 3, item_list, allocation_max );
|
||||
construct_class( 4, item_list, allocation_max );
|
||||
|
||||
killstreaks = item_list["killstreak1"];
|
||||
|
||||
if ( isdefined( item_list["killstreak2"] ) )
|
||||
{
|
||||
killstreaks = ArrayCombine( killstreaks, item_list["killstreak2"], true, false );
|
||||
}
|
||||
|
||||
if ( isdefined( item_list["killstreak3"] ) )
|
||||
{
|
||||
killstreaks = ArrayCombine( killstreaks, item_list["killstreak3"], true, false );
|
||||
}
|
||||
|
||||
if ( isdefined( killstreaks ) && killstreaks.size )
|
||||
{
|
||||
choose_weapon( 0, killstreaks );
|
||||
choose_weapon( 0, killstreaks );
|
||||
choose_weapon( 0, killstreaks );
|
||||
}
|
||||
|
||||
self.claimed_items = undefined;
|
||||
pixendevent();
|
||||
}
|
||||
|
||||
function construct_class( constructclass, items, allocation_max )
|
||||
{
|
||||
allocation = 0;
|
||||
|
||||
claimed_count = build_claimed_list( items );
|
||||
self.claimed_items = [];
|
||||
|
||||
// primary
|
||||
weapon = choose_weapon( constructclass, items["primary"] );
|
||||
claimed_count["primary"]++;
|
||||
allocation++;
|
||||
|
||||
// secondary
|
||||
weapon = choose_weapon( constructclass, items["secondary"] );
|
||||
choose_weapon_option( constructclass, "camo", 1 );
|
||||
}
|
||||
|
||||
function make_choice( chance, claimed, max_claim )
|
||||
{
|
||||
return ( claimed < max_claim && RandomInt( 100 ) < chance );
|
||||
}
|
||||
|
||||
function chose_action( action1, chance1, action2, chance2, action3, chance3, action4, chance4 )
|
||||
{
|
||||
chance1 = Int( chance1 / 10 );
|
||||
chance2 = Int( chance2 / 10 );
|
||||
chance3 = Int( chance3 / 10 );
|
||||
chance4 = Int( chance4 / 10 );
|
||||
|
||||
actions = [];
|
||||
|
||||
for( i = 0; i < chance1; i++ )
|
||||
{
|
||||
actions[ actions.size ] = action1;
|
||||
}
|
||||
|
||||
for( i = 0; i < chance2; i++ )
|
||||
{
|
||||
actions[ actions.size ] = action2;
|
||||
}
|
||||
|
||||
for( i = 0; i < chance3; i++ )
|
||||
{
|
||||
actions[ actions.size ] = action3;
|
||||
}
|
||||
|
||||
for( i = 0; i < chance4; i++ )
|
||||
{
|
||||
actions[ actions.size ] = action4;
|
||||
}
|
||||
|
||||
return array::random( actions );
|
||||
}
|
||||
|
||||
function item_is_claimed( item )
|
||||
{
|
||||
foreach( claim in self.claimed_items )
|
||||
{
|
||||
if ( claim == item )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function choose_weapon( weaponclass, items )
|
||||
{
|
||||
if ( !isdefined( items ) || !items.size )
|
||||
{
|
||||
return undefined;
|
||||
}
|
||||
|
||||
start = RandomInt( items.size );
|
||||
|
||||
for( i = 0; i < items.size; i++ )
|
||||
{
|
||||
weapon = items[ start ];
|
||||
|
||||
if ( !item_is_claimed( weapon ) )
|
||||
{
|
||||
break;
|
||||
}
|
||||
|
||||
start = ( start + 1 ) % items.size;
|
||||
}
|
||||
|
||||
self.claimed_items[ self.claimed_items.size ] = weapon;
|
||||
|
||||
self BotClassAddItem( weaponclass, weapon );
|
||||
return weapon;
|
||||
}
|
||||
|
||||
function build_weapon_options_list( optionType )
|
||||
{
|
||||
level.botWeaponOptionsId[optionType] = [];
|
||||
level.botWeaponOptionsProb[optionType] = [];
|
||||
|
||||
csv_filename = "gamedata/weapons/common/attachmentTable.csv";
|
||||
prob = 0;
|
||||
for ( row = 0 ; row < 255 ; row++ )
|
||||
{
|
||||
if ( tableLookupColumnForRow( csv_filename, row, 1 ) == optionType )
|
||||
{
|
||||
index = level.botWeaponOptionsId[optionType].size;
|
||||
level.botWeaponOptionsId[optionType][index] = Int( tableLookupColumnForRow( csv_filename, row, 0 ) );
|
||||
prob += Int( tableLookupColumnForRow( csv_filename, row, 15 ) );
|
||||
level.botWeaponOptionsProb[optionType][index] = prob;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function choose_weapon_option( weaponclass, optionType, primary )
|
||||
{
|
||||
if ( !isdefined( level.botWeaponOptionsId ) )
|
||||
{
|
||||
level.botWeaponOptionsId = [];
|
||||
level.botWeaponOptionsProb = [];
|
||||
|
||||
build_weapon_options_list( "camo" );
|
||||
build_weapon_options_list( "reticle" );
|
||||
}
|
||||
|
||||
// weapon options cannot be set in local matches
|
||||
if ( !level.onlineGame && !level.systemLink )
|
||||
return;
|
||||
|
||||
// Increase the range of the probability to reduce the chances of picking the option when the bot's level is less than BOT_RANK_ALL_OPTIONS_AVAILABLE
|
||||
// (in system link all options are available)
|
||||
numOptions = level.botWeaponOptionsProb[optionType].size;
|
||||
maxProb = level.botWeaponOptionsProb[optionType][numOptions-1];
|
||||
if ( !level.systemLink && self.pers[ "rank" ] < 20 )
|
||||
maxProb += 4 * maxProb * ( ( 20 - self.pers[ "rank" ] ) / 20 );
|
||||
|
||||
rnd = RandomInt( Int( maxProb ) );
|
||||
for (i=0 ; i<numOptions ; i++)
|
||||
{
|
||||
if ( level.botWeaponOptionsProb[optionType][i] > rnd )
|
||||
{
|
||||
self BotClassSetWeaponOption( weaponclass, primary, optionType, level.botWeaponOptionsId[optionType][i] );
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function choose_primary_attachments( weaponclass, weapon, allocation, allocation_max )
|
||||
{
|
||||
attachments = weapon.supportedAttachments;
|
||||
remaining = allocation_max - allocation;
|
||||
|
||||
if ( !attachments.size || !remaining )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
attachment_action = chose_action( "3_attachments", 25, "2_attachments", 35, "1_attachments", 35, "none", 5 );
|
||||
|
||||
if ( remaining >= 4 && attachment_action == "3_attachments" )
|
||||
{
|
||||
a1 = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a1, "primaryattachment1" );
|
||||
count = 1;
|
||||
|
||||
attachments = GetWeaponAttachments( weapon, a1 );
|
||||
|
||||
if ( attachments.size )
|
||||
{
|
||||
a2 = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a2, "primaryattachment2" );
|
||||
count++;
|
||||
|
||||
attachments = GetWeaponAttachments( weapon, a1, a2 );
|
||||
|
||||
if ( attachments.size )
|
||||
{
|
||||
a3 = array::random( attachments );
|
||||
self BotClassAddItem( weaponclass, "BONUSCARD_PRIMARY_GUNFIGHTER" );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a3, "primaryattachment3" );
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
else if ( remaining >= 2 && attachment_action == "2_attachments" )
|
||||
{
|
||||
a1 = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a1, "primaryattachment1" );
|
||||
|
||||
attachments = GetWeaponAttachments( weapon, a1 );
|
||||
|
||||
if ( attachments.size )
|
||||
{
|
||||
a2 = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a2, "primaryattachment2" );
|
||||
return 2;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if ( remaining >= 1 && attachment_action == "1_attachments" )
|
||||
{
|
||||
a = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a, "primaryattachment1" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function choose_secondary_attachments( weaponclass, weapon, allocation, allocation_max )
|
||||
{
|
||||
attachments = weapon.supportedAttachments ;
|
||||
remaining = allocation_max - allocation;
|
||||
|
||||
if ( !attachments.size || !remaining )
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
attachment_action = chose_action( "2_attachments", 10, "1_attachments", 40, "none", 50, "none", 0 );
|
||||
|
||||
if ( remaining >= 3 && attachment_action == "2_attachments" )
|
||||
{
|
||||
a1 = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a1, "secondaryattachment1" );
|
||||
|
||||
attachments = GetWeaponAttachments( weapon, a1 );
|
||||
|
||||
if ( attachments.size )
|
||||
{
|
||||
a2 = array::random( attachments );
|
||||
self BotClassAddItem( weaponclass, "BONUSCARD_SECONDARY_GUNFIGHTER" );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a2, "secondaryattachment2" );
|
||||
return 3;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
else if ( remaining >= 1 && attachment_action == "1_attachments" )
|
||||
{
|
||||
a = array::random( attachments );
|
||||
self BotClassAddAttachment( weaponclass, weapon, a, "secondaryattachment1" );
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
function build_item_list()
|
||||
{
|
||||
items = [];
|
||||
|
||||
for( i = 0; i < 256; i++ )
|
||||
{
|
||||
row = tableLookupRowNum( level.statsTableID, 0, i );
|
||||
|
||||
if ( row > -1 )
|
||||
{
|
||||
slot = tableLookupColumnForRow( level.statsTableID, row, 13 );
|
||||
|
||||
if ( slot == "" )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
number = Int( tableLookupColumnForRow( level.statsTableID, row, 0 ) );
|
||||
|
||||
if ( !SessionModeIsPrivate() && self IsItemLocked( number ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
allocation = Int( tableLookupColumnForRow( level.statsTableID, row, 12 ) );
|
||||
|
||||
if ( allocation < 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
name = tableLookupColumnForRow( level.statsTableID, row, 3 );
|
||||
/*
|
||||
if ( item_is_banned( slot, name ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
*/
|
||||
if ( !isdefined( items[slot] ) )
|
||||
{
|
||||
items[slot] = [];
|
||||
}
|
||||
|
||||
items[ slot ][ items[slot].size ] = name;
|
||||
}
|
||||
}
|
||||
|
||||
return items;
|
||||
}
|
||||
|
||||
function item_is_banned( slot, item )
|
||||
{
|
||||
if ( item == "WEAPON_KNIFE_BALLISTIC" )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( GetDvarInt("tu6_enableDLCWeapons") == 0 && item == "WEAPON_PEACEKEEPER" )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if ( slot != "killstreak1" && slot != "killstreak2" && slot != "killstreak3" )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
foreach( banned in level.bot_banned_killstreaks )
|
||||
{
|
||||
if ( item == banned )
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
function build_claimed_list( items )
|
||||
{
|
||||
claimed = [];
|
||||
keys = GetArrayKeys( items );
|
||||
|
||||
foreach( key in keys )
|
||||
{
|
||||
claimed[ key ] = 0;
|
||||
}
|
||||
|
||||
return claimed;
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
#using scripts\shared\array_shared;
|
||||
#using scripts\shared\gameobjects_shared;
|
||||
#using scripts\shared\math_shared;
|
||||
#using scripts\shared\util_shared;
|
||||
|
||||
Line truncated
|
||||
|
||||
#using scripts\mp\gametypes\_globallogic_utils;
|
||||
|
||||
#using scripts\shared\bots\_bot;
|
||||
#using scripts\shared\bots\_bot_combat;
|
||||
#using scripts\shared\bots\bot_buttons;
|
||||
#using scripts\mp\bots\_bot;
|
||||
#using scripts\mp\bots\_bot_combat;
|
||||
|
||||
|
||||
|
||||
#namespace bot_sd;
|
||||
|
||||
|
||||
|
||||
|
||||
function init()
|
||||
{
|
||||
level.botIdle = &bot_idle;
|
||||
}
|
||||
|
||||
function bot_idle()
|
||||
{
|
||||
if( !level.bombPlanted && !level.multibomb && self.team == game["attackers"] )
|
||||
{
|
||||
carrier = level.sdBomb gameobjects::get_carrier();
|
||||
|
||||
if( !isdefined( carrier ) )
|
||||
{
|
||||
self BotSetGoal( level.sdBomb.trigger.origin );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
approachRadiusSq = 750 * 750;
|
||||
|
||||
// Check for Plant / Defuse
|
||||
foreach( zone in level.bombZones )
|
||||
{
|
||||
if ( ( isdefined( level.bombPlanted ) && level.bombPlanted ) && !( isdefined( zone.isPlanted ) && zone.isPlanted ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
zoneTrigger = self get_zone_trigger( zone );
|
||||
|
||||
if ( self IsTouching( zoneTrigger ) )
|
||||
{
|
||||
if ( self can_plant( zone ) || self can_defuse( zone ) )
|
||||
{
|
||||
self bot::press_use_button();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if ( DistanceSquared( self.origin, zone.trigger.origin ) < approachRadiusSq )
|
||||
{
|
||||
// Defuse / Plant nearby zone
|
||||
if ( self can_plant( zone ) || self can_defuse( zone ) )
|
||||
{
|
||||
self bot::path_to_trigger( zoneTrigger );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Shuffle zones
|
||||
zones = array::randomize( level.bombZones );
|
||||
|
||||
// Check for zones to defuse
|
||||
foreach( zone in zones )
|
||||
{
|
||||
if ( ( isdefined( level.bombPlanted ) && level.bombPlanted ) && !( isdefined( zone.isPlanted ) && zone.isPlanted ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
if ( self can_defuse( zone ) )
|
||||
{
|
||||
self bot::approach_goal_trigger( zoneTrigger, 750 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// Go to a random zone to plant / guard
|
||||
foreach( zone in zones )
|
||||
{
|
||||
if ( ( isdefined( level.bombPlanted ) && level.bombPlanted ) && !( isdefined( zone.isPlanted ) && zone.isPlanted ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// Defend the nearby point
|
||||
if ( DistanceSquared( self.origin, zone.trigger.origin ) < approachRadiusSq &&
|
||||
RandomInt( 100 ) < 70 )
|
||||
{
|
||||
triggerRadius = self bot::get_trigger_radius( zone.trigger );
|
||||
self bot::approach_point( zone.trigger.origin, triggerRadius, 750 );
|
||||
self bot::sprint_to_goal();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
self bot::bot_idle();
|
||||
}
|
||||
|
||||
function get_zone_trigger( zone )
|
||||
{
|
||||
if ( self.team == zone gameobjects::get_owner_team() )
|
||||
{
|
||||
return zone.bombDefuseTrig;
|
||||
}
|
||||
|
||||
return zone.trigger;
|
||||
}
|
||||
|
||||
function can_plant( zone )
|
||||
{
|
||||
if ( level.multibomb )
|
||||
{
|
||||
return !( isdefined( zone.isPlanted ) && zone.isPlanted ) && self.team != zone gameobjects::get_owner_team();
|
||||
}
|
||||
|
||||
carrier = level.sdBomb gameobjects::get_carrier();
|
||||
|
||||
return isdefined( carrier ) && self == carrier;
|
||||
}
|
||||
|
||||
function can_defuse( zone )
|
||||
{
|
||||
return ( isdefined( zone.isPlanted ) && zone.isPlanted ) && self.team == zone gameobjects::get_owner_team();
|
||||
}
|
||||
Reference in new issue
Block a user