diff --git a/patch_mp/maps/mp/bots/_bot.gsc b/patch_mp/maps/mp/bots/_bot.gsc new file mode 100644 index 0000000..b005f2d --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot.gsc @@ -0,0 +1,2684 @@ +#include maps/mp/gametypes/_dev; +#include maps/mp/killstreaks/_killstreakrules; +#include maps/mp/killstreaks/_killstreaks; +#include maps/mp/bots/_bot_conf; +#include maps/mp/bots/_bot_hq; +#include maps/mp/bots/_bot_koth; +#include maps/mp/bots/_bot_dom; +#include maps/mp/bots/_bot_dem; +#include maps/mp/bots/_bot_ctf; +#include maps/mp/bots/_bot; +#include maps/mp/killstreaks/_radar; +#include maps/mp/teams/_teams; +#include maps/mp/gametypes/_weapons; +#include maps/mp/gametypes/_rank; +#include maps/mp/bots/_bot_combat; +#include maps/mp/_utility; +#include common_scripts/utility; + +init() +{ +/# + level thread bot_system_devgui_think(); +#/ + level thread maps/mp/bots/_bot_loadout::init(); + if ( !bot_gametype_allowed() ) + { + return; + } + if ( level.rankedmatch && !is_bot_ranked_match() ) + { + return; + } + bot_friends = getDvarInt( "bot_friends" ); + bot_enemies = getDvarInt( "bot_enemies" ); + if ( bot_friends <= 0 && bot_enemies <= 0 ) + { + return; + } + bot_wait_for_host(); + bot_set_difficulty(); + if ( is_bot_comp_stomp() ) + { + team = bot_choose_comp_stomp_team(); + level thread bot_comp_stomp_think( team ); + } + else if ( is_bot_ranked_match() ) + { + level thread bot_ranked_think(); + } + else + { + level thread bot_local_think(); + } +} + +spawn_bot( team ) +{ + bot = addtestclient(); + if ( isDefined( bot ) ) + { + bot.pers[ "isBot" ] = 1; + if ( team != "autoassign" ) + { + bot.pers[ "team" ] = team; + } + bot thread bot_spawn_think( team ); + return 1; + } + return 0; +} + +getenemyteamwithlowestplayercount( player_team ) +{ + counts = []; + _a83 = level.teams; + _k83 = getFirstArrayKey( _a83 ); + while ( isDefined( _k83 ) ) + { + team = _a83[ _k83 ]; + counts[ team ] = 0; + _k83 = getNextArrayKey( _a83, _k83 ); + } + _a88 = level.players; + _k88 = getFirstArrayKey( _a88 ); + while ( isDefined( _k88 ) ) + { + player = _a88[ _k88 ]; + if ( !isDefined( player.team ) ) + { + } + else if ( !isDefined( counts[ player.team ] ) ) + { + } + else + { + counts[ player.team ]++; + } + _k88 = getNextArrayKey( _a88, _k88 ); + } + count = 999999; + enemy_team = player_team; + _a102 = level.teams; + _k102 = getFirstArrayKey( _a102 ); + while ( isDefined( _k102 ) ) + { + team = _a102[ _k102 ]; + if ( team == player_team ) + { + } + else if ( team == "spectator" ) + { + } + else + { + if ( counts[ team ] < count ) + { + enemy_team = team; + count = counts[ team ]; + } + } + _k102 = getNextArrayKey( _a102, _k102 ); + } + return enemy_team; +} + +getenemyteamwithgreatestbotcount( player_team ) +{ + counts = []; + _a124 = level.teams; + _k124 = getFirstArrayKey( _a124 ); + while ( isDefined( _k124 ) ) + { + team = _a124[ _k124 ]; + counts[ team ] = 0; + _k124 = getNextArrayKey( _a124, _k124 ); + } + _a129 = level.players; + _k129 = getFirstArrayKey( _a129 ); + while ( isDefined( _k129 ) ) + { + player = _a129[ _k129 ]; + if ( !isDefined( player.team ) ) + { + } + else if ( !isDefined( counts[ player.team ] ) ) + { + } + else if ( !player is_bot() ) + { + } + else + { + counts[ player.team ]++; + } + _k129 = getNextArrayKey( _a129, _k129 ); + } + count = -1; + enemy_team = undefined; + _a146 = level.teams; + _k146 = getFirstArrayKey( _a146 ); + while ( isDefined( _k146 ) ) + { + team = _a146[ _k146 ]; + if ( team == player_team ) + { + } + else if ( team == "spectator" ) + { + } + else + { + if ( counts[ team ] > count ) + { + enemy_team = team; + count = counts[ team ]; + } + } + _k146 = getNextArrayKey( _a146, _k146 ); + } + return enemy_team; +} + +bot_wait_for_host() +{ + host = gethostplayerforbots(); + while ( !isDefined( host ) ) + { + wait 0,25; + host = gethostplayerforbots(); + } + if ( level.prematchperiod > 0 && level.inprematchperiod == 1 ) + { + wait 1; + } +} + +bot_count_humans( team ) +{ + players = get_players(); + count = 0; + _a185 = players; + _k185 = getFirstArrayKey( _a185 ); + while ( isDefined( _k185 ) ) + { + player = _a185[ _k185 ]; + if ( player is_bot() ) + { + } + else if ( isDefined( team ) ) + { + if ( getassignedteam( player ) == team ) + { + count++; + } + } + else + { + count++; + } + _k185 = getNextArrayKey( _a185, _k185 ); + } + return count; +} + +bot_count_bots( team ) +{ + players = get_players(); + count = 0; + _a213 = players; + _k213 = getFirstArrayKey( _a213 ); + while ( isDefined( _k213 ) ) + { + player = _a213[ _k213 ]; + if ( !player is_bot() ) + { + } + else if ( isDefined( team ) ) + { + if ( isDefined( player.team ) && player.team == team ) + { + count++; + } + } + else + { + count++; + } + _k213 = getNextArrayKey( _a213, _k213 ); + } + return count; +} + +bot_count_enemy_bots( friend_team ) +{ + if ( !level.teambased ) + { + return bot_count_bots(); + } + enemies = 0; + _a245 = level.teams; + _k245 = getFirstArrayKey( _a245 ); + while ( isDefined( _k245 ) ) + { + team = _a245[ _k245 ]; + if ( team == friend_team ) + { + } + else + { + enemies += bot_count_bots( team ); + } + _k245 = getNextArrayKey( _a245, _k245 ); + } + return enemies; +} + +bot_choose_comp_stomp_team() +{ + host = gethostplayerforbots(); +/# + assert( isDefined( host ) ); +#/ + teamkeys = getarraykeys( level.teams ); +/# + assert( teamkeys.size == 2 ); +#/ + enemy_team = host.pers[ "team" ]; +/# + if ( isDefined( enemy_team ) ) + { + assert( enemy_team != "spectator" ); + } +#/ + return getotherteam( enemy_team ); +} + +bot_comp_stomp_think( team ) +{ + for ( ;; ) + { + humans = bot_count_humans(); + bots = bot_count_bots(); + if ( humans == bots ) + { + break; + } + else + { + if ( bots < humans ) + { + spawn_bot( team ); + } + if ( bots > humans ) + { + bot_comp_stomp_remove( team ); + } + wait 1; + } + } + wait 3; +} +} + +bot_comp_stomp_remove( team ) +{ + players = get_players(); + bots = []; + remove = undefined; + _a310 = players; + _k310 = getFirstArrayKey( _a310 ); + while ( isDefined( _k310 ) ) + { + player = _a310[ _k310 ]; + if ( !isDefined( player.team ) ) + { + } + else if ( player is_bot() ) + { + if ( level.teambased ) + { + if ( player.team == team ) + { + bots[ bots.size ] = player; + } + break; + } + else + { + bots[ bots.size ] = player; + } + } + _k310 = getNextArrayKey( _a310, _k310 ); + } + if ( !bots.size ) + { + return; + } + _a339 = bots; + _k339 = getFirstArrayKey( _a339 ); + while ( isDefined( _k339 ) ) + { + bot = _a339[ _k339 ]; + if ( !bot maps/mp/bots/_bot_combat::bot_has_enemy() ) + { + remove = bot; + break; + } + else + { + _k339 = getNextArrayKey( _a339, _k339 ); + } + } + if ( !isDefined( remove ) ) + { + remove = random( bots ); + } + remove botleavegame(); +} + +bot_ranked_remove() +{ + if ( !level.teambased ) + { + bot_comp_stomp_remove(); + return; + } + high = -1; + highest_team = undefined; + _a367 = level.teams; + _k367 = getFirstArrayKey( _a367 ); + while ( isDefined( _k367 ) ) + { + team = _a367[ _k367 ]; + count = countplayers( team ); + if ( count > high ) + { + high = count; + highest_team = team; + } + _k367 = getNextArrayKey( _a367, _k367 ); + } + bot_comp_stomp_remove( highest_team ); +} + +bot_ranked_count( team ) +{ + count = countplayers( team ); + if ( count < 6 ) + { + spawn_bot( team ); + return 1; + } + else + { + if ( count > 6 ) + { + bot_comp_stomp_remove( team ); + return 1; + } + } + return 0; +} + +bot_ranked_think() +{ + level endon( "game_ended" ); + wait 5; + for ( ;; ) + { + wait 1; + teams = []; + teams[ 0 ] = "axis"; + teams[ 1 ] = "allies"; + if ( cointoss() ) + { + teams[ 0 ] = "allies"; + teams[ 1 ] = "axis"; + } + if ( !bot_ranked_count( teams[ 0 ] ) && !bot_ranked_count( teams[ 1 ] ) ) + { + break; + } + else + { + } + } + level waittill_any( "connected", "disconnect" ); + wait 5; + while ( isDefined( level.hostmigrationtimer ) ) + { + wait 1; + } +} +} + +bot_local_friends( expected_friends, max, host_team ) +{ + if ( level.teambased ) + { + players = get_players(); + friends = bot_count_bots( host_team ); + if ( friends < expected_friends && players.size < max ) + { + spawn_bot( host_team ); + return 1; + } + if ( friends > expected_friends ) + { + bot_comp_stomp_remove( host_team ); + return 1; + } + } + return 0; +} + +bot_local_enemies( expected_enemies, max, host_team ) +{ + enemies = bot_count_enemy_bots( host_team ); + players = get_players(); + if ( enemies < expected_enemies && players.size < max ) + { + team = getenemyteamwithlowestplayercount( host_team ); + spawn_bot( team ); + return 1; + } + if ( enemies > expected_enemies ) + { + team = getenemyteamwithgreatestbotcount( host_team ); + if ( isDefined( team ) ) + { + bot_comp_stomp_remove( team ); + } + return 1; + } + return 0; +} + +bot_local_think() +{ + wait 5; + host = gethostplayerforbots(); +/# + assert( isDefined( host ) ); +#/ + host_team = host.team; + if ( !isDefined( host_team ) || host_team == "spectator" ) + { + host_team = "allies"; + } + bot_expected_friends = getDvarInt( "bot_friends" ); + bot_expected_enemies = getDvarInt( "bot_enemies" ); + if ( islocalgame() ) + { + } + else + { + } + max_players = 18; + for ( ;; ) + { + if ( bot_local_friends( bot_expected_friends, max_players, host_team ) ) + { + wait 0,5; + continue; + } + else if ( bot_local_enemies( bot_expected_enemies, max_players, host_team ) ) + { + wait 0,5; + continue; + } + else + { + } + } + wait 3; +} +} + +is_bot_ranked_match() +{ + bot_enemies = getDvarInt( "bot_enemies" ); + isdedicatedbotsoak = getDvarInt( #"E76315E0" ); + if ( level.rankedmatch && bot_enemies ) + { + return isdedicatedbotsoak == 0; + } +} + +is_bot_comp_stomp() +{ + if ( is_bot_ranked_match() ) + { + return !getDvarInt( "party_autoteams" ); + } +} + +bot_spawn_think( team ) +{ + self endon( "disconnect" ); + while ( !isDefined( self.pers[ "bot_loadout" ] ) ) + { + wait 0,1; + } + while ( !isDefined( self.team ) ) + { + wait 0,05; + } + if ( level.teambased ) + { + self notify( "menuresponse" ); + wait 0,5; + } + self notify( "joined_team" ); + bot_classes = bot_build_classes(); + self notify( "menuresponse" ); +} + +bot_build_classes() +{ + bot_classes = []; + if ( !self isitemlocked( maps/mp/gametypes/_rank::getitemindex( "feature_cac" ) ) ) + { + bot_classes[ bot_classes.size ] = "custom0"; + bot_classes[ bot_classes.size ] = "custom1"; + bot_classes[ bot_classes.size ] = "custom2"; + bot_classes[ bot_classes.size ] = "custom3"; + bot_classes[ bot_classes.size ] = "custom4"; + if ( randomint( 100 ) < 10 ) + { + bot_classes[ bot_classes.size ] = "class_smg"; + bot_classes[ bot_classes.size ] = "class_cqb"; + bot_classes[ bot_classes.size ] = "class_assault"; + bot_classes[ bot_classes.size ] = "class_lmg"; + bot_classes[ bot_classes.size ] = "class_sniper"; + } + } + else + { + bot_classes[ bot_classes.size ] = "class_smg"; + bot_classes[ bot_classes.size ] = "class_cqb"; + bot_classes[ bot_classes.size ] = "class_assault"; + bot_classes[ bot_classes.size ] = "class_lmg"; + bot_classes[ bot_classes.size ] = "class_sniper"; + } + return bot_classes; +} + +bot_choose_class() +{ + bot_classes = bot_build_classes(); + while ( !self maps/mp/bots/_bot_combat::threat_requires_rocket( self.bot.attacker ) && self maps/mp/bots/_bot_combat::threat_is_qrdrone( self.bot.attacker ) && !maps/mp/bots/_bot_combat::threat_is_warthog( self.bot.attacker ) ) + { + if ( randomint( 100 ) < 75 ) + { + bot_classes[ bot_classes.size ] = "class_smg"; + bot_classes[ bot_classes.size ] = "class_cqb"; + bot_classes[ bot_classes.size ] = "class_assault"; + bot_classes[ bot_classes.size ] = "class_lmg"; + bot_classes[ bot_classes.size ] = "class_sniper"; + } + i = 0; + while ( i < bot_classes.size ) + { + sidearm = self getloadoutweapon( i, "secondary" ); + if ( sidearm == "fhj18_mp" ) + { + self notify( "menuresponse" ); + return; + i++; + continue; + } + else + { + if ( sidearm == "smaw_mp" ) + { + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + } + } + i++; + } + } + while ( maps/mp/bots/_bot_combat::threat_requires_rocket( self.bot.attacker ) || maps/mp/bots/_bot_combat::threat_is_warthog( self.bot.attacker ) ) + { + i = 0; + while ( i < bot_classes.size ) + { + perks = self getloadoutperks( i ); + _a642 = perks; + _k642 = getFirstArrayKey( _a642 ); + while ( isDefined( _k642 ) ) + { + perk = _a642[ _k642 ]; + if ( perk == "specialty_nottargetedbyairsupport" ) + { + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + bot_classes[ bot_classes.size ] = bot_classes[ i ]; + } + _k642 = getNextArrayKey( _a642, _k642 ); + } + i++; + } + } + self notify( "menuresponse" ); +} + +bot_spawn() +{ + self endon( "disconnect" ); +/# + weapon = undefined; + if ( getDvarInt( "scr_botsHasPlayerWeapon" ) != 0 ) + { + player = gethostplayer(); + weapon = player getcurrentweapon(); + } + if ( getDvar( "devgui_bot_weapon" ) != "" ) + { + weapon = getDvar( "devgui_bot_weapon" ); + } + if ( isDefined( weapon ) ) + { + self maps/mp/gametypes/_weapons::detach_all_weapons(); + self takeallweapons(); + self giveweapon( weapon ); + self switchtoweapon( weapon ); + self setspawnweapon( weapon ); + self maps/mp/teams/_teams::set_player_model( self.team, weapon ); +#/ + } + self bot_spawn_init(); + if ( isDefined( self.bot_first_spawn ) ) + { + self bot_choose_class(); + } + self.bot_first_spawn = 1; + self thread bot_main(); +/# + self thread bot_devgui_think(); +#/ +} + +bot_spawn_init() +{ + time = getTime(); + if ( !isDefined( self.bot ) ) + { + self.bot = spawnstruct(); + self.bot.threat = spawnstruct(); + } + self.bot.glass_origin = undefined; + self.bot.ignore_entity = []; + self.bot.previous_origin = self.origin; + self.bot.time_ads = 0; + self.bot.update_c4 = time + randomintrange( 1000, 3000 ); + self.bot.update_crate = time + randomintrange( 1000, 3000 ); + self.bot.update_crouch = time + randomintrange( 1000, 3000 ); + self.bot.update_failsafe = time + randomintrange( 1000, 3000 ); + self.bot.update_idle_lookat = time + randomintrange( 1000, 3000 ); + self.bot.update_killstreak = time + randomintrange( 1000, 3000 ); + self.bot.update_lookat = time + randomintrange( 1000, 3000 ); + self.bot.update_objective = time + randomintrange( 1000, 3000 ); + self.bot.update_objective_patrol = time + randomintrange( 1000, 3000 ); + self.bot.update_patrol = time + randomintrange( 1000, 3000 ); + self.bot.update_toss = time + randomintrange( 1000, 3000 ); + self.bot.update_launcher = time + randomintrange( 1000, 3000 ); + self.bot.update_weapon = time + randomintrange( 1000, 3000 ); + difficulty = bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + self.bot.think_interval = 0,5; + self.bot.fov = 0,4226; + break; + case "normal": + self.bot.think_interval = 0,25; + self.bot.fov = 0,0872; + break; + case "hard": + self.bot.think_interval = 0,2; + self.bot.fov = -0,1736; + break; + case "fu": + self.bot.think_interval = 0,1; + self.bot.fov = -0,9396; + break; + default: + self.bot.think_interval = 0,25; + self.bot.fov = 0,0872; + break; + } + self.bot.threat.entity = undefined; + self.bot.threat.position = ( 0, 0, 0 ); + self.bot.threat.time_first_sight = 0; + self.bot.threat.time_recent_sight = 0; + self.bot.threat.time_aim_interval = 0; + self.bot.threat.time_aim_correct = 0; + self.bot.threat.update_riotshield = 0; +} + +bot_wakeup_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon( "game_ended" ); + for ( ;; ) + { + wait self.bot.think_interval; + self notify( "wakeup" ); + } +} + +bot_damage_think() +{ + self notify( "bot_damage_think" ); + self endon( "bot_damage_think" ); + self endon( "disconnect" ); + level endon( "game_ended" ); + for ( ;; ) + { + self waittill( "damage", damage, attacker, direction, point, mod, unused1, unused2, unused3, weapon, flags, inflictor ); + if ( attacker.classname == "worldspawn" ) + { + continue; + } + else if ( isDefined( weapon ) ) + { + if ( weapon == "proximity_grenade_mp" || weapon == "proximity_grenade_aoe_mp" ) + { + continue; + } + else if ( weapon == "claymore_mp" ) + { + continue; + } + else if ( weapon == "satchel_charge_mp" ) + { + continue; + } + else if ( weapon == "bouncingbetty_mp" ) + { + continue; + } + } + else + { + if ( isDefined( inflictor ) ) + { + switch( inflictor.classname ) + { + case "auto_turret": + case "script_vehicle": + attacker = inflictor; + break; + break; + } + } + if ( isDefined( attacker.viewlockedentity ) ) + { + attacker = attacker.viewlockedentity; + } + if ( maps/mp/bots/_bot_combat::threat_requires_rocket( attacker ) || maps/mp/bots/_bot_combat::threat_is_warthog( attacker ) ) + { + level thread bot_killstreak_dangerous_think( self.origin, self.team, attacker ); + } + self.bot.attacker = attacker; + self notify( "wakeup" ); + } + } +} + +bot_killcam_think() +{ + self notify( "bot_killcam_think" ); + self endon( "bot_killcam_think" ); + self endon( "disconnect" ); + level endon( "game_ended" ); + wait_time = 0,5; + if ( level.playerrespawndelay ) + { + wait_time = level.playerrespawndelay + 1,5; + } + if ( !level.killcam ) + { + self waittill( "death" ); + } + else + { + self waittill( "begin_killcam" ); + } + wait wait_time; + for ( ;; ) + { + self pressusebutton( 0,1 ); + wait 0,5; + } +} + +bot_glass_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon( "game_ended" ); + for ( ;; ) + { + self waittill( "glass", origin ); + self.bot.glass_origin = origin; + self notify( "wakeup" ); + } +} + +bot_main() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon( "game_ended" ); + if ( level.inprematchperiod ) + { + level waittill( "prematch_over" ); + self.bot.update_failsafe = getTime() + randomintrange( 1000, 3000 ); + } + self thread bot_wakeup_think(); + self thread bot_damage_think(); + self thread bot_killcam_think(); + self thread bot_glass_think(); + for ( ;; ) + { + self waittill( "wakeup", damage, attacker, direction ); + if ( self isremotecontrolling() ) + { + continue; + } + else + { + self maps/mp/bots/_bot_combat::bot_combat_think( damage, attacker, direction ); + self bot_update_glass(); + self bot_update_patrol(); + self bot_update_lookat(); + self bot_update_killstreak(); + self bot_update_wander(); + self bot_update_c4(); + self bot_update_launcher(); + self bot_update_weapon(); + if ( cointoss() ) + { + self bot_update_toss_flash(); + self bot_update_toss_frag(); + } + else + { + self bot_update_toss_frag(); + self bot_update_toss_flash(); + } + self [[ level.bot_gametype ]](); + } + } +} + +bot_failsafe_node_valid( nearest, node ) +{ + if ( isDefined( node.script_noteworthy ) ) + { + return 0; + } + if ( ( node.origin[ 2 ] - self.origin[ 2 ] ) > 18 ) + { + return 0; + } + if ( nearest == node ) + { + return 0; + } + if ( !nodesvisible( nearest, node ) ) + { + return 0; + } + if ( self bot_friend_in_radius( node.origin, 32 ) ) + { + return 0; + } + if ( isDefined( level.spawn_all ) && level.spawn_all.size > 0 ) + { + spawns = arraysort( level.spawn_all, node.origin ); + } + else + { + if ( isDefined( level.spawnpoints ) && level.spawnpoints.size > 0 ) + { + spawns = arraysort( level.spawnpoints, node.origin ); + } + else + { + if ( isDefined( level.spawn_start ) && level.spawn_start.size > 0 ) + { + spawns = arraycombine( level.spawn_start[ "allies" ], level.spawn_start[ "axis" ], 1, 0 ); + spawns = arraysort( spawns, node.origin ); + } + else + { + return 0; + } + } + } + goal = bot_nearest_node( spawns[ 0 ].origin ); + if ( isDefined( goal ) && findpath( node.origin, goal.origin, undefined, 0, 1 ) ) + { + return 1; + } + return 0; +} + +bot_get_mantle_start() +{ + dist = self getlookaheaddist(); + dir = self getlookaheaddir(); + if ( dist > 0 && isDefined( dir ) ) + { + forward = anglesToForward( self.angles ); + if ( vectordot( dir, forward ) < 0 ) + { + dir = vectorScale( dir, dist ); + origin = self.origin + dir; + nodes = getnodesinradius( origin, 16, 0, 16, "Begin" ); + if ( nodes.size && nodes[ 0 ].spawnflags & 8388608 ) + { + return nodes[ 0 ]; + } + } + } + return undefined; +} + +bot_is_traversing() +{ + if ( !self isonground() ) + { + if ( !self ismantling() ) + { + return !self isonladder(); + } + } + return 0; +} + +bot_update_failsafe() +{ + time = getTime(); + if ( ( time - self.spawntime ) < 7500 ) + { + return; + } + if ( bot_is_traversing() ) + { + wait 0,25; + node = bot_get_mantle_start(); + if ( isDefined( node ) ) + { + end = getnode( node.target, "targetname" ); + self clearlookat(); + self botsetfailsafenode( end ); + self wait_endon( 1, "goal" ); + self botsetfailsafenode(); + return; + } + } + if ( time < self.bot.update_failsafe ) + { + return; + } + if ( !self ismantling() || self isonladder() && !self isonground() ) + { + wait randomfloatrange( 0,1, 0,25 ); + return; + } + if ( !self atgoal() && distance2dsquared( self.bot.previous_origin, self.origin ) < 256 ) + { + nodes = getnodesinradius( self.origin, 512, 0 ); + nodes = array_randomize( nodes ); + nearest = bot_nearest_node( self.origin ); + failsafe = 0; + while ( isDefined( nearest ) ) + { + _a1092 = nodes; + _k1092 = getFirstArrayKey( _a1092 ); + while ( isDefined( _k1092 ) ) + { + node = _a1092[ _k1092 ]; + if ( !bot_failsafe_node_valid( nearest, node ) ) + { + } + else + { + self botsetfailsafenode( node ); + wait 0,5; + self.bot.update_idle_lookat = 0; + self bot_update_lookat(); + self cancelgoal( "enemy_patrol" ); + self wait_endon( 4, "goal" ); + self botsetfailsafenode(); + self bot_update_lookat(); + failsafe = 1; + break; + } + _k1092 = getNextArrayKey( _a1092, _k1092 ); + } + } + if ( !failsafe && nodes.size ) + { + node = random( nodes ); + self botsetfailsafenode( node ); + wait 0,5; + self.bot.update_idle_lookat = 0; + self bot_update_lookat(); + self cancelgoal( "enemy_patrol" ); + self wait_endon( 4, "goal" ); + self botsetfailsafenode(); + self bot_update_lookat(); + } + } + self.bot.update_failsafe = getTime() + 3500; + self.bot.previous_origin = self.origin; +} + +bot_update_crouch() +{ + time = getTime(); + if ( time < self.bot.update_crouch ) + { + return; + } + if ( self atgoal() ) + { + return; + } + if ( !self ismantling() || self isonladder() && !self isonground() ) + { + return; + } + dist = self getlookaheaddist(); + if ( dist > 0 ) + { + dir = self getlookaheaddir(); +/# + assert( isDefined( dir ) ); +#/ + dir = vectorScale( dir, dist ); + start = self.origin + vectorScale( ( 0, 0, 0 ), 70 ); + end = start + dir; + if ( dist >= 256 ) + { + self.bot.update_crouch = time + 1500; + } + if ( self getstance() == "stand" ) + { + trace = worldtrace( start, end ); + if ( trace[ "fraction" ] < 1 ) + { + self setstance( "crouch" ); + self.bot.update_crouch = time + 2500; + } + return; + } + else + { + if ( self getstance() == "crouch" ) + { + trace = worldtrace( start, end ); + if ( trace[ "fraction" ] >= 1 ) + { + self setstance( "stand" ); + } + } + } + } +} + +bot_update_glass() +{ + if ( isDefined( self.bot.glass_origin ) ) + { + forward = anglesToForward( self.angles ); + dir = vectornormalize( self.bot.glass_origin - self.origin ); + dot = vectordot( forward, dir ); + if ( dot > 0 ) + { + self lookat( self.bot.glass_origin ); + wait_time = 0,5 * ( 1 - dot ); + wait_time = clamp( wait_time, 0,05, 0,5 ); + wait wait_time; + self pressmelee(); + wait 0,25; + self clearlookat(); + self.bot.glass_origin = undefined; + } + } +} + +bot_has_radar() +{ + if ( level.teambased ) + { + if ( !maps/mp/killstreaks/_radar::teamhasspyplane( self.team ) ) + { + return maps/mp/killstreaks/_radar::teamhassatellite( self.team ); + } + } + if ( isDefined( self.hasspyplane ) && !self.hasspyplane ) + { + if ( isDefined( self.hassatellite ) ) + { + return self.hassatellite; + } + } +} + +bot_get_enemies( on_radar ) +{ + if ( !isDefined( on_radar ) ) + { + on_radar = 0; + } + enemies = self getenemies( 1 ); +/# + i = 0; + while ( i < enemies.size ) + { + if ( enemies[ i ] isinmovemode( "ufo", "noclip" ) ) + { + arrayremoveindex( enemies, i ); + i--; + + } + i++; +#/ + } + while ( on_radar && !self bot_has_radar() ) + { + i = 0; + while ( i < enemies.size ) + { + if ( !isDefined( enemies[ i ].lastfiretime ) ) + { + arrayremoveindex( enemies, i ); + i--; + + i++; + continue; + } + else + { + if ( ( getTime() - enemies[ i ].lastfiretime ) > 2000 ) + { + arrayremoveindex( enemies, i ); + i--; + + } + } + i++; + } + } + return enemies; +} + +bot_get_friends() +{ + friends = self getfriendlies( 1 ); +/# + i = 0; + while ( i < friends.size ) + { + if ( friends[ i ] isinmovemode( "ufo", "noclip" ) ) + { + arrayremoveindex( friends, i ); + i--; + + } + i++; +#/ + } + return friends; +} + +bot_friend_goal_in_radius( goal_name, origin, radius ) +{ + count = 0; + friends = bot_get_friends(); + _a1290 = friends; + _k1290 = getFirstArrayKey( _a1290 ); + while ( isDefined( _k1290 ) ) + { + friend = _a1290[ _k1290 ]; + if ( friend is_bot() ) + { + goal = friend getgoal( goal_name ); + if ( isDefined( goal ) && distancesquared( origin, goal ) < ( radius * radius ) ) + { + count++; + } + } + _k1290 = getNextArrayKey( _a1290, _k1290 ); + } + return count; +} + +bot_friend_in_radius( origin, radius ) +{ + friends = bot_get_friends(); + _a1310 = friends; + _k1310 = getFirstArrayKey( _a1310 ); + while ( isDefined( _k1310 ) ) + { + friend = _a1310[ _k1310 ]; + if ( distancesquared( friend.origin, origin ) < ( radius * radius ) ) + { + return 1; + } + _k1310 = getNextArrayKey( _a1310, _k1310 ); + } + return 0; +} + +bot_get_closest_enemy( origin, on_radar ) +{ + enemies = self bot_get_enemies( on_radar ); + enemies = arraysort( enemies, origin ); + if ( enemies.size ) + { + return enemies[ 0 ]; + } + return undefined; +} + +bot_update_wander() +{ + goal = self getgoal( "wander" ); + if ( isDefined( goal ) ) + { + if ( distancesquared( goal, self.origin ) > 65536 ) + { + return; + } + } + if ( isDefined( level.spawn_all ) && level.spawn_all.size > 0 ) + { + spawns = arraysort( level.spawn_all, self.origin ); + } + else + { + if ( isDefined( level.spawnpoints ) && level.spawnpoints.size > 0 ) + { + spawns = arraysort( level.spawnpoints, self.origin ); + } + else + { + if ( isDefined( level.spawn_start ) && level.spawn_start.size > 0 ) + { + spawns = arraycombine( level.spawn_start[ "allies" ], level.spawn_start[ "axis" ], 1, 0 ); + spawns = arraysort( spawns, self.origin ); + } + else + { + return; + } + } + } + far = int( spawns.size / 2 ); + far = randomintrange( far, spawns.size ); + goal = bot_nearest_node( spawns[ far ].origin ); + if ( !isDefined( goal ) ) + { + return; + } + self addgoal( goal, 24, 1, "wander" ); +} + +bot_get_look_at() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + } + enemies = self maps/mp/bots/_bot::bot_get_enemies( 0 ); + if ( enemies.size ) + { + enemy = random( enemies ); + } + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + } + spawn = self getgoal( "wander" ); + if ( isDefined( spawn ) ) + { + node = getvisiblenode( self.origin, spawn ); + } + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + return undefined; +} + +bot_update_lookat() +{ + path = isDefined( self getlookaheaddir() ); + if ( !path && getTime() > self.bot.update_idle_lookat ) + { + origin = bot_get_look_at(); + if ( !isDefined( origin ) ) + { + return; + } + self lookat( origin + vectorScale( ( 0, 0, 0 ), 16 ) ); + self.bot.update_idle_lookat = getTime() + randomintrange( 1500, 3000 ); + } + else + { + if ( path && self.bot.update_idle_lookat > 0 ) + { + self clearlookat(); + self.bot.update_idle_lookat = 0; + } + } +} + +bot_update_patrol() +{ + closest = bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( closest ) && distancesquared( self.origin, closest.origin ) < 262144 ) + { + goal = self getgoal( "enemy_patrol" ); + if ( isDefined( goal ) && distancesquared( goal, closest.origin ) > 16384 ) + { + self cancelgoal( "enemy_patrol" ); + self.bot.update_patrol = 0; + } + } + if ( getTime() < self.bot.update_patrol ) + { + return; + } + self maps/mp/bots/_bot_combat::bot_patrol_near_enemy(); + self.bot.update_patrol = getTime() + randomintrange( 5000, 10000 ); +} + +bot_update_toss_flash() +{ + if ( bot_get_difficulty() == "easy" ) + { + return; + } + time = getTime(); + if ( ( time - self.spawntime ) < 7500 ) + { + return; + } + if ( time < self.bot.update_toss ) + { + return; + } + self.bot.update_toss = time + 1500; + if ( self getweaponammostock( "sensor_grenade_mp" ) <= 0 && self getweaponammostock( "proximity_grenade_mp" ) <= 0 && self getweaponammostock( "trophy_system_mp" ) <= 0 ) + { + return; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + node = undefined; + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + } + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) < 65536 ) + { + self lookat( node.origin ); + wait 0,75; + self pressattackbutton( 2 ); + self.bot.update_toss = time + 20000; + self clearlookat(); + } +} + +bot_update_toss_frag() +{ + if ( bot_get_difficulty() == "easy" ) + { + return; + } + time = getTime(); + if ( ( time - self.spawntime ) < 7500 ) + { + return; + } + if ( time < self.bot.update_toss ) + { + return; + } + self.bot.update_toss = time + 1500; + if ( self getweaponammostock( "bouncingbetty_mp" ) <= 0 && self getweaponammostock( "claymore_mp" ) <= 0 && self getweaponammostock( "satchel_charge_mp" ) <= 0 ) + { + return; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + node = undefined; + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + } + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) < 65536 ) + { + self lookat( node.origin ); + wait 0,75; + self pressattackbutton( 1 ); + self.bot.update_toss = time + 20000; + self clearlookat(); + } +} + +bot_set_rank() +{ + players = get_players(); + ranks = []; + bot_ranks = []; + human_ranks = []; + i = 0; + while ( i < players.size ) + { + if ( players[ i ] == self ) + { + i++; + continue; + } + else if ( isDefined( players[ i ].pers[ "rank" ] ) ) + { + if ( players[ i ] is_bot() ) + { + bot_ranks[ bot_ranks.size ] = players[ i ].pers[ "rank" ]; + i++; + continue; + } + else + { + human_ranks[ human_ranks.size ] = players[ i ].pers[ "rank" ]; + } + } + i++; + } + if ( !human_ranks.size ) + { + human_ranks[ human_ranks.size ] = 10; + } + human_avg = array_average( human_ranks ); + while ( ( bot_ranks.size + human_ranks.size ) < 5 ) + { + r = human_avg + randomintrange( -5, 5 ); + rank = clamp( r, 0, level.maxrank ); + human_ranks[ human_ranks.size ] = rank; + } + ranks = arraycombine( human_ranks, bot_ranks, 1, 0 ); + avg = array_average( ranks ); + s = array_std_deviation( ranks, avg ); + rank = int( random_normal_distribution( avg, s, 0, level.maxrank ) ); + self.pers[ "rank" ] = rank; + self.pers[ "rankxp" ] = maps/mp/gametypes/_rank::getrankinfominxp( rank ); + self setrank( rank ); + self maps/mp/gametypes/_rank::syncxpstat(); +} + +bot_gametype_allowed() +{ + level.bot_gametype = ::gametype_void; + switch( level.gametype ) + { + case "dm": + case "tdm": + return 1; + case "ctf": + level.bot_gametype = ::maps/mp/bots/_bot_ctf::bot_ctf_think; + return 1; + case "dem": + level.bot_gametype = ::maps/mp/bots/_bot_dem::bot_dem_think; + return 1; + case "dom": + level.bot_gametype = ::maps/mp/bots/_bot_dom::bot_dom_think; + return 1; + case "koth": + level.bot_gametype = ::maps/mp/bots/_bot_koth::bot_koth_think; + return 1; + case "hq": + level.bot_gametype = ::maps/mp/bots/_bot_hq::bot_hq_think; + return 1; + case "conf": + level.bot_gametype = ::maps/mp/bots/_bot_conf::bot_conf_think; + return 1; + } + return 0; +} + +bot_get_difficulty() +{ + if ( !isDefined( level.bot_difficulty ) ) + { + level.bot_difficulty = "normal"; + difficulty = getdvarintdefault( "bot_difficulty", 1 ); + if ( difficulty == 0 ) + { + level.bot_difficulty = "easy"; + } + else if ( difficulty == 1 ) + { + level.bot_difficulty = "normal"; + } + else if ( difficulty == 2 ) + { + level.bot_difficulty = "hard"; + } + else + { + if ( difficulty == 3 ) + { + level.bot_difficulty = "fu"; + } + } + } + return level.bot_difficulty; +} + +bot_set_difficulty() +{ + difficulty = bot_get_difficulty(); + if ( difficulty == "fu" ) + { + setdvar( "bot_MinDeathTime", "250" ); + setdvar( "bot_MaxDeathTime", "500" ); + setdvar( "bot_MinFireTime", "100" ); + setdvar( "bot_MaxFireTime", "250" ); + setdvar( "bot_PitchUp", "-5" ); + setdvar( "bot_PitchDown", "10" ); + setdvar( "bot_Fov", "160" ); + setdvar( "bot_MinAdsTime", "3000" ); + setdvar( "bot_MaxAdsTime", "5000" ); + setdvar( "bot_MinCrouchTime", "100" ); + setdvar( "bot_MaxCrouchTime", "400" ); + setdvar( "bot_TargetLeadBias", "2" ); + setdvar( "bot_MinReactionTime", "40" ); + setdvar( "bot_MaxReactionTime", "70" ); + setdvar( "bot_StrafeChance", "1" ); + setdvar( "bot_MinStrafeTime", "3000" ); + setdvar( "bot_MaxStrafeTime", "6000" ); + setdvar( "scr_help_dist", "512" ); + setdvar( "bot_AllowGrenades", "1" ); + setdvar( "bot_MinGrenadeTime", "1500" ); + setdvar( "bot_MaxGrenadeTime", "4000" ); + setdvar( "bot_MeleeDist", "70" ); + setdvar( "bot_YawSpeed", "2" ); + } + else if ( difficulty == "hard" ) + { + setdvar( "bot_MinDeathTime", "250" ); + setdvar( "bot_MaxDeathTime", "500" ); + setdvar( "bot_MinFireTime", "400" ); + setdvar( "bot_MaxFireTime", "600" ); + setdvar( "bot_PitchUp", "-5" ); + setdvar( "bot_PitchDown", "10" ); + setdvar( "bot_Fov", "100" ); + setdvar( "bot_MinAdsTime", "3000" ); + setdvar( "bot_MaxAdsTime", "5000" ); + setdvar( "bot_MinCrouchTime", "100" ); + setdvar( "bot_MaxCrouchTime", "400" ); + setdvar( "bot_TargetLeadBias", "2" ); + setdvar( "bot_MinReactionTime", "400" ); + setdvar( "bot_MaxReactionTime", "700" ); + setdvar( "bot_StrafeChance", "0.9" ); + setdvar( "bot_MinStrafeTime", "3000" ); + setdvar( "bot_MaxStrafeTime", "6000" ); + setdvar( "scr_help_dist", "384" ); + setdvar( "bot_AllowGrenades", "1" ); + setdvar( "bot_MinGrenadeTime", "1500" ); + setdvar( "bot_MaxGrenadeTime", "4000" ); + setdvar( "bot_MeleeDist", "70" ); + setdvar( "bot_YawSpeed", "1.4" ); + } + else if ( difficulty == "easy" ) + { + setdvar( "bot_MinDeathTime", "1000" ); + setdvar( "bot_MaxDeathTime", "2000" ); + setdvar( "bot_MinFireTime", "900" ); + setdvar( "bot_MaxFireTime", "1000" ); + setdvar( "bot_PitchUp", "-20" ); + setdvar( "bot_PitchDown", "40" ); + setdvar( "bot_Fov", "50" ); + setdvar( "bot_MinAdsTime", "3000" ); + setdvar( "bot_MaxAdsTime", "5000" ); + setdvar( "bot_MinCrouchTime", "4000" ); + setdvar( "bot_MaxCrouchTime", "6000" ); + setdvar( "bot_TargetLeadBias", "8" ); + setdvar( "bot_MinReactionTime", "1200" ); + setdvar( "bot_MaxReactionTime", "1600" ); + setdvar( "bot_StrafeChance", "0.1" ); + setdvar( "bot_MinStrafeTime", "3000" ); + setdvar( "bot_MaxStrafeTime", "6000" ); + setdvar( "scr_help_dist", "256" ); + setdvar( "bot_AllowGrenades", "0" ); + setdvar( "bot_MeleeDist", "40" ); + } + else + { + setdvar( "bot_MinDeathTime", "500" ); + setdvar( "bot_MaxDeathTime", "1000" ); + setdvar( "bot_MinFireTime", "600" ); + setdvar( "bot_MaxFireTime", "800" ); + setdvar( "bot_PitchUp", "-10" ); + setdvar( "bot_PitchDown", "20" ); + setdvar( "bot_Fov", "70" ); + setdvar( "bot_MinAdsTime", "3000" ); + setdvar( "bot_MaxAdsTime", "5000" ); + setdvar( "bot_MinCrouchTime", "2000" ); + setdvar( "bot_MaxCrouchTime", "4000" ); + setdvar( "bot_TargetLeadBias", "4" ); + setdvar( "bot_MinReactionTime", "600" ); + setdvar( "bot_MaxReactionTime", "800" ); + setdvar( "bot_StrafeChance", "0.6" ); + setdvar( "bot_MinStrafeTime", "3000" ); + setdvar( "bot_MaxStrafeTime", "6000" ); + setdvar( "scr_help_dist", "256" ); + setdvar( "bot_AllowGrenades", "1" ); + setdvar( "bot_MinGrenadeTime", "1500" ); + setdvar( "bot_MaxGrenadeTime", "4000" ); + setdvar( "bot_MeleeDist", "70" ); + setdvar( "bot_YawSpeed", "1.2" ); + } + if ( level.gametype == "oic" && difficulty == "fu" ) + { + setdvar( "bot_MinReactionTime", "400" ); + setdvar( "bot_MaxReactionTime", "500" ); + setdvar( "bot_MinAdsTime", "1000" ); + setdvar( "bot_MaxAdsTime", "2000" ); + } + if ( level.gametype == "oic" || difficulty == "hard" && difficulty == "fu" ) + { + setdvar( "bot_SprintDistance", "256" ); + } +} + +bot_update_c4() +{ + if ( !isDefined( self.weaponobjectwatcherarray ) ) + { + return; + } + time = getTime(); + if ( time < self.bot.update_c4 ) + { + return; + } + self.bot.update_c4 = time + randomintrange( 1000, 2000 ); + radius = getweaponexplosionradius( "satchel_charge_mp" ); + _a1817 = self.weaponobjectwatcherarray; + _k1817 = getFirstArrayKey( _a1817 ); + while ( isDefined( _k1817 ) ) + { + watcher = _a1817[ _k1817 ]; + if ( watcher.name == "satchel_charge" ) + { + break; + } + else + { + _k1817 = getNextArrayKey( _a1817, _k1817 ); + } + } + while ( watcher.objectarray.size ) + { + _a1827 = watcher.objectarray; + _k1827 = getFirstArrayKey( _a1827 ); + while ( isDefined( _k1827 ) ) + { + weapon = _a1827[ _k1827 ]; + if ( !isDefined( weapon ) ) + { + } + else + { + enemy = bot_get_closest_enemy( weapon.origin, 0 ); + if ( !isDefined( enemy ) ) + { + return; + } + if ( distancesquared( enemy.origin, weapon.origin ) < ( radius * radius ) ) + { + self pressattackbutton( 1 ); + return; + } + } + _k1827 = getNextArrayKey( _a1827, _k1827 ); + } + } +} + +bot_update_launcher() +{ + time = getTime(); + if ( time < self.bot.update_launcher ) + { + return; + } + self.bot.update_launcher = time + randomintrange( 5000, 10000 ); + if ( !self maps/mp/bots/_bot_combat::bot_has_launcher() ) + { + return; + } + enemies = self getthreats( -1 ); + _a1868 = enemies; + _k1868 = getFirstArrayKey( _a1868 ); + while ( isDefined( _k1868 ) ) + { + enemy = _a1868[ _k1868 ]; + if ( !target_istarget( enemy ) ) + { + } + else if ( maps/mp/bots/_bot_combat::threat_is_warthog( enemy ) ) + { + } + else if ( !maps/mp/bots/_bot_combat::threat_requires_rocket( enemy ) ) + { + } + else origin = self getplayercamerapos(); + angles = vectorToAngle( enemy.origin - origin ); + if ( angles[ 0 ] < 290 ) + { + } + else + { + if ( self botsighttracepassed( enemy ) ) + { + self maps/mp/bots/_bot_combat::bot_lookat_entity( enemy ); + return; + } + } + _k1868 = getNextArrayKey( _a1868, _k1868 ); + } +} + +bot_update_weapon() +{ + time = getTime(); + if ( time < self.bot.update_weapon ) + { + return; + } + self.bot.update_weapon = time + randomintrange( 5000, 7500 ); + weapon = self getcurrentweapon(); + ammo = self getweaponammoclip( weapon ) + self getweaponammostock( weapon ); + if ( weapon == "none" ) + { + return; + } + if ( self maps/mp/bots/_bot_combat::bot_can_reload() ) + { + frac = 0,5; + if ( maps/mp/bots/_bot_combat::bot_has_lmg() ) + { + frac = 0,25; + } + frac += randomfloatrange( -0,1, 0,1 ); + if ( maps/mp/bots/_bot_combat::bot_weapon_ammo_frac() < frac ) + { + self pressusebutton( 0,1 ); + return; + } + } + if ( ammo && !self maps/mp/bots/_bot_combat::bot_has_pistol() && !self maps/mp/bots/_bot_combat::bot_using_launcher() ) + { + return; + } + primaries = self getweaponslistprimaries(); + _a1946 = primaries; + _k1946 = getFirstArrayKey( _a1946 ); + while ( isDefined( _k1946 ) ) + { + primary = _a1946[ _k1946 ]; + if ( primary == "knife_held_mp" ) + { + } + else + { + if ( primary != weapon || self getweaponammoclip( primary ) && self getweaponammostock( primary ) ) + { + self switchtoweapon( primary ); + return; + } + } + _k1946 = getNextArrayKey( _a1946, _k1946 ); + } +} + +bot_update_crate() +{ + time = getTime(); + if ( time < self.bot.update_crate ) + { + return; + } + self.bot.update_crate = time + randomintrange( 1000, 3000 ); + self cancelgoal( "care package" ); + radius = getDvarFloat( "player_useRadius" ); + crates = getentarray( "care_package", "script_noteworthy" ); + _a1977 = crates; + _k1977 = getFirstArrayKey( _a1977 ); + while ( isDefined( _k1977 ) ) + { + crate = _a1977[ _k1977 ]; + if ( distancesquared( self.origin, crate.origin ) < ( radius * radius ) ) + { + if ( isDefined( crate.hacker ) ) + { + if ( crate.hacker == self ) + { + break; + } + else if ( crate.hacker.team == self.team ) + { + break; + } + } + else + { + if ( crate.owner == self ) + { + time = ( level.crateownerusetime / 1000 ) + 0,5; + } + else + { + time = ( level.cratenonownerusetime / 1000 ) + 0,5; + } + self setstance( "crouch" ); + self addgoal( self.origin, 24, 4, "care package" ); + self pressusebutton( time ); + wait time; + self setstance( "stand" ); + self cancelgoal( "care package" ); + self.bot.update_crate = getTime() + randomintrange( 1000, 3000 ); + return; + } + } + _k1977 = getNextArrayKey( _a1977, _k1977 ); + } + while ( self getweaponammostock( "pda_hack_mp" ) ) + { + _a2020 = crates; + _k2020 = getFirstArrayKey( _a2020 ); + while ( isDefined( _k2020 ) ) + { + crate = _a2020[ _k2020 ]; + if ( !isDefined( crate.friendlyobjid ) ) + { + } + else if ( isDefined( crate.hacker ) ) + { + if ( crate.hacker == self ) + { + } + else if ( crate.hacker.team == self.team ) + { + } + } + else + { + if ( self botsighttracepassed( crate ) ) + { + self lookat( crate.origin ); + self addgoal( self.origin, 24, 4, "care package" ); + wait 0,75; + start = getTime(); + if ( !isDefined( crate.owner ) ) + { + self cancelgoal( "care package" ); + return; + } + if ( crate.owner == self ) + { + end = level.crateownerusetime + 1000; + } + else + { + end = level.cratenonownerusetime + 1000; + } + while ( getTime() < ( start + end ) ) + { + self pressattackbutton( 2 ); + wait 0,05; + } + self.bot.update_crate = getTime() + randomintrange( 1000, 3000 ); + self cancelgoal( "care package" ); + return; + } + } + _k2020 = getNextArrayKey( _a2020, _k2020 ); + } + } +} + +bot_update_killstreak() +{ + if ( !level.loadoutkillstreaksenabled ) + { + return; + } + time = getTime(); + if ( time < self.bot.update_killstreak ) + { + return; + } + if ( self isweaponviewonlylinked() ) + { + return; + } +/# + if ( !getDvarInt( "scr_botsAllowKillstreaks" ) ) + { + return; +#/ + } + self.bot.update_killstreak = time + randomintrange( 1000, 3000 ); + weapons = self getweaponslist(); + ks_weapon = undefined; + inventoryweapon = self getinventoryweapon(); + _a2109 = weapons; + _k2109 = getFirstArrayKey( _a2109 ); + while ( isDefined( _k2109 ) ) + { + weapon = _a2109[ _k2109 ]; + if ( self getweaponammoclip( weapon ) <= 0 || !isDefined( inventoryweapon ) && weapon != inventoryweapon ) + { + } + else + { + if ( iskillstreakweapon( weapon ) ) + { + killstreak = maps/mp/killstreaks/_killstreaks::getkillstreakforweapon( weapon ); + if ( self maps/mp/killstreaks/_killstreakrules::iskillstreakallowed( killstreak, self.team ) ) + { + ks_weapon = weapon; + break; + } + } + } + else + { + _k2109 = getNextArrayKey( _a2109, _k2109 ); + } + } + if ( !isDefined( ks_weapon ) ) + { + return; + } + killstreak = maps/mp/killstreaks/_killstreaks::getkillstreakforweapon( ks_weapon ); + killstreak_ref = maps/mp/killstreaks/_killstreaks::getkillstreakmenuname( killstreak ); + if ( !isDefined( killstreak_ref ) ) + { + return; + } + switch( killstreak_ref ) + { + case "killstreak_helicopter_comlink": + bot_killstreak_location( 1, weapon ); + break; + case "killstreak_planemortar": + bot_killstreak_location( 3, weapon ); + break; + case "killstreak_ai_tank_drop": + case "killstreak_missile_drone": + case "killstreak_supply_drop": + self bot_use_supply_drop( weapon ); + break; + case "killstreak_auto_turret": + case "killstreak_microwave_turret": + case "killstreak_tow_turret": + self bot_turret_location( weapon ); + break; + case "killstreak_helicopter_player_gunner": + case "killstreak_qrdrone": + case "killstreak_rcbomb": + case "killstreak_remote_mortar": + return; + case "killstreak_remote_missile": + if ( ( time - self.spawntime ) < 6000 ) + { + self switchtoweapon( weapon ); + self waittill( "weapon_change_complete" ); + wait 1,5; + self pressattackbutton(); + } + return; + default: + self switchtoweapon( weapon ); + break; + } + } + } +} + +bot_get_vehicle_entity() +{ + if ( self isremotecontrolling() ) + { + if ( isDefined( self.rcbomb ) ) + { + return self.rcbomb; + } + else + { + if ( isDefined( self.qrdrone ) ) + { + return self.qrdrone; + } + } + } + return undefined; +} + +bot_rccar_think() +{ + self endon( "disconnect" ); + self endon( "rcbomb_done" ); + self endon( "weapon_object_destroyed" ); + level endon( "game_ended" ); + wait 2; + self thread bot_rccar_kill(); + for ( ;; ) + { + wait 0,5; + ent = self bot_get_vehicle_entity(); + if ( !isDefined( ent ) ) + { + return; + } + players = get_players(); + i = 0; + while ( i < players.size ) + { + player = players[ i ]; + if ( player == self ) + { + i++; + continue; + } + else if ( !isalive( player ) ) + { + i++; + continue; + } + else if ( level.teambased && player.team == self.team ) + { + i++; + continue; + } + else + { +/# + if ( player isinmovemode( "ufo", "noclip" ) ) + { + i++; + continue; +#/ + } + else if ( bot_get_difficulty() == "easy" ) + { + if ( distancesquared( ent.origin, player.origin ) < 262144 ) + { + self pressattackbutton(); + } + i++; + continue; + } + else + { + if ( distancesquared( ent.origin, player.origin ) < 40000 ) + { + self pressattackbutton(); + } + } + } + i++; + } + } +} + +bot_rccar_kill() +{ + self endon( "disconnect" ); + self endon( "rcbomb_done" ); + self endon( "weapon_object_destroyed" ); + level endon( "game_ended" ); + og_origin = self.origin; + for ( ;; ) + { + wait 1; + ent = bot_get_vehicle_entity(); + if ( !isDefined( ent ) ) + { + return; + } + if ( distancesquared( og_origin, ent.origin ) < 256 ) + { + wait 2; + if ( !isDefined( ent ) ) + { + return; + } + if ( distancesquared( og_origin, ent.origin ) < 256 ) + { + self pressattackbutton(); + } + } + og_origin = ent.origin; + } +} + +bot_turret_location( weapon ) +{ + enemy = bot_get_closest_enemy( self.origin ); + if ( !isDefined( enemy ) ) + { + return; + } + forward = anglesToForward( self getplayerangles() ); + forward = vectornormalize( forward ); + delta = enemy.origin - self.origin; + delta = vectornormalize( delta ); + dot = vectordot( forward, delta ); + if ( dot < 0,707 ) + { + return; + } + node = getvisiblenode( self.origin, enemy.origin ); + if ( !isDefined( node ) ) + { + return; + } + if ( distancesquared( self.origin, node.origin ) < 262144 ) + { + return; + } + delta = node.origin - self.origin; + delta = vectornormalize( delta ); + dot = vectordot( forward, delta ); + if ( dot < 0,707 ) + { + return; + } + self thread weapon_switch_failsafe(); + self switchtoweapon( weapon ); + self waittill( "weapon_change_complete" ); + self freeze_player_controls( 1 ); + wait 1; + self freeze_player_controls( 0 ); + bot_use_item( weapon ); + self switchtoweapon( self.lastnonkillstreakweapon ); +} + +bot_use_supply_drop( weapon ) +{ + if ( weapon == "inventory_supplydrop_mp" || weapon == "supplydrop_mp" ) + { + if ( ( getTime() - self.spawntime ) > 5000 ) + { + return; + } + } + yaw = ( 0, self.angles[ 1 ], 0 ); + dir = anglesToForward( yaw ); + dir = vectornormalize( dir ); + drop_point = self.origin + vectorScale( dir, 384 ); + end = drop_point + vectorScale( ( 0, 0, 0 ), 2048 ); + if ( !sighttracepassed( drop_point, end, 0, undefined ) ) + { + return; + } + if ( !sighttracepassed( self.origin, end, 0, undefined ) ) + { + return; + } + end = drop_point - vectorScale( ( 0, 0, 0 ), 32 ); + if ( bullettracepassed( drop_point, end, 0, undefined ) ) + { + return; + } + self addgoal( self.origin, 24, 4, "killstreak" ); + if ( weapon == "missile_drone_mp" || weapon == "inventory_missile_drone_mp" ) + { + self lookat( drop_point + vectorScale( ( 0, 0, 0 ), 384 ) ); + } + else + { + self lookat( drop_point ); + } + wait 0,5; + if ( self getcurrentweapon() != weapon ) + { + self thread weapon_switch_failsafe(); + self switchtoweapon( weapon ); + self waittill( "weapon_change_complete" ); + } + bot_use_item( weapon ); + self switchtoweapon( self.lastnonkillstreakweapon ); + self clearlookat(); + self cancelgoal( "killstreak" ); +} + +bot_use_item( weapon ) +{ + self pressattackbutton(); + wait 0,5; + i = 0; + while ( i < 10 ) + { + if ( self getcurrentweapon() == weapon || self getcurrentweapon() == "none" ) + { + self pressattackbutton(); + } + else + { + return; + } + wait 0,5; + i++; + } +} + +bot_killstreak_location( num, weapon ) +{ + enemies = bot_get_enemies(); + if ( !enemies.size ) + { + return; + } + if ( !self switchtoweapon( weapon ) ) + { + return; + } + self waittill( "weapon_change" ); + self freeze_player_controls( 1 ); + wait_time = 1; + while ( !isDefined( self.selectinglocation ) || self.selectinglocation == 0 ) + { + wait 0,05; + wait_time -= 0,05; + if ( wait_time <= 0 ) + { + self freeze_player_controls( 0 ); + self switchtoweapon( self.lastnonkillstreakweapon ); + return; + } + } + wait 2; + i = 0; + while ( i < num ) + { + enemies = bot_get_enemies(); + if ( enemies.size ) + { + enemy = random( enemies ); + self notify( "confirm_location" ); + } + wait 0,25; + i++; + } + self freeze_player_controls( 0 ); +} + +bot_killstreak_dangerous_think( origin, team, attacker ) +{ + if ( !level.teambased ) + { + return; + } + nodes = getnodesinradius( origin + vectorScale( ( 0, 0, 0 ), 384 ), 384, 0 ); + _a2505 = nodes; + _k2505 = getFirstArrayKey( _a2505 ); + while ( isDefined( _k2505 ) ) + { + node = _a2505[ _k2505 ]; + if ( node isdangerous( team ) ) + { + return; + } + _k2505 = getNextArrayKey( _a2505, _k2505 ); + } + _a2513 = nodes; + _k2513 = getFirstArrayKey( _a2513 ); + while ( isDefined( _k2513 ) ) + { + node = _a2513[ _k2513 ]; + node setdangerous( team, 1 ); + _k2513 = getNextArrayKey( _a2513, _k2513 ); + } + attacker wait_endon( 25, "death" ); + _a2520 = nodes; + _k2520 = getFirstArrayKey( _a2520 ); + while ( isDefined( _k2520 ) ) + { + node = _a2520[ _k2520 ]; + node setdangerous( team, 0 ); + _k2520 = getNextArrayKey( _a2520, _k2520 ); + } +} + +weapon_switch_failsafe() +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "weapon_change_complete" ); + wait 10; + self notify( "weapon_change_complete" ); +} + +bot_dive_to_prone( exit_stance ) +{ + self pressdtpbutton(); + event = self waittill_any_timeout( 0,25, "dtp_start" ); + if ( event == "dtp_start" ) + { + self waittill( "dtp_end" ); + self setstance( "prone" ); + wait 0,35; + self setstance( exit_stance ); + } +} + +gametype_void() +{ +} + +bot_debug_star( origin, seconds, color ) +{ +/# + if ( !isDefined( seconds ) ) + { + seconds = 1; + } + if ( !isDefined( color ) ) + { + color = ( 0, 0, 0 ); + } + frames = int( 20 * seconds ); + debugstar( origin, frames, color ); +#/ +} + +bot_debug_circle( origin, radius, seconds, color ) +{ +/# + if ( !isDefined( seconds ) ) + { + seconds = 1; + } + if ( !isDefined( color ) ) + { + color = ( 0, 0, 0 ); + } + frames = int( 20 * seconds ); + circle( origin, radius, color, 0, 1, frames ); +#/ +} + +bot_debug_box( origin, mins, maxs, yaw, seconds, color ) +{ +/# + if ( !isDefined( yaw ) ) + { + yaw = 0; + } + if ( !isDefined( seconds ) ) + { + seconds = 1; + } + if ( !isDefined( color ) ) + { + color = ( 0, 0, 0 ); + } + frames = int( 20 * seconds ); + box( origin, mins, maxs, yaw, color, 1, 0, frames ); +#/ +} + +bot_devgui_think() +{ +/# + self endon( "death" ); + self endon( "disconnect" ); + setdvar( "devgui_bot", "" ); + setdvar( "scr_bot_follow", "0" ); + for ( ;; ) + { + wait 1; + reset = 1; + switch( getDvar( "devgui_bot" ) ) + { + case "crosshair": + if ( getDvarInt( "scr_bot_follow" ) != 0 ) + { + iprintln( "Bot following enabled" ); + self thread bot_crosshair_follow(); + } + else + { + iprintln( "Bot following disabled" ); + self notify( "crosshair_follow_off" ); + setdvar( "bot_AllowMovement", "0" ); + } + break; + case "laststand": + setdvar( "scr_forcelaststand", "1" ); + self setperk( "specialty_pistoldeath" ); + self setperk( "specialty_finalstand" ); + self dodamage( self.health, self.origin ); + break; + case "": + default: + reset = 0; + break; + } + if ( reset ) + { + setdvar( "devgui_bot", "" ); + } +#/ + } +} + +bot_system_devgui_think() +{ +/# + setdvar( "devgui_bot", "" ); + setdvar( "devgui_bot_weapon", "" ); + for ( ;; ) + { + wait 1; + reset = 1; + switch( getDvar( "devgui_bot" ) ) + { + case "spawn_friendly": + player = gethostplayer(); + team = player.team; + devgui_bot_spawn( team ); + break; + case "spawn_enemy": + player = gethostplayer(); + team = getenemyteamwithlowestplayercount( player.team ); + devgui_bot_spawn( team ); + break; + case "loadout": + case "player_weapon": + players = get_players(); + _a2692 = players; + _k2692 = getFirstArrayKey( _a2692 ); + while ( isDefined( _k2692 ) ) + { + player = _a2692[ _k2692 ]; + if ( !player is_bot() ) + { + } + else + { + host = gethostplayer(); + weapon = host getcurrentweapon(); + player maps/mp/gametypes/_weapons::detach_all_weapons(); + player takeallweapons(); + player giveweapon( weapon ); + player switchtoweapon( weapon ); + player setspawnweapon( weapon ); + player maps/mp/teams/_teams::set_player_model( player.team, weapon ); + } + _k2692 = getNextArrayKey( _a2692, _k2692 ); + } + case "routes": + devgui_debug_route(); + break; + case "": + default: + reset = 0; + break; + } + if ( reset ) + { + setdvar( "devgui_bot", "" ); + } +#/ + } + } +} + +bot_crosshair_follow() +{ +/# + self notify( "crosshair_follow_off" ); + self endon( "death" ); + self endon( "disconnect" ); + self endon( "crosshair_follow_off" ); + for ( ;; ) + { + wait 1; + setdvar( "bot_AllowMovement", "1" ); + setdvar( "bot_IgnoreHumans", "1" ); + setdvar( "bot_ForceStand", "1" ); + player = gethostplayerforbots(); + direction = player getplayerangles(); + direction_vec = anglesToForward( direction ); + eye = player geteye(); + scale = 8000; + direction_vec = ( direction_vec[ 0 ] * scale, direction_vec[ 1 ] * scale, direction_vec[ 2 ] * scale ); + trace = bullettrace( eye, eye + direction_vec, 0, undefined ); + origin = trace[ "position" ] + ( 0, 0, 0 ); + if ( distancesquared( self.origin, origin ) > 16384 ) + { + } +#/ + } +} + +bot_debug_patrol( node1, node2 ) +{ +/# + self endon( "death" ); + self endon( "debug_patrol" ); + for ( ;; ) + { + self addgoal( node1, 24, 4, "debug_route" ); + self waittill( "debug_route", result ); + if ( result == "failed" ) + { + self cancelgoal( "debug_route" ); + wait 5; + } + self addgoal( node2, 24, 4, "debug_route" ); + self waittill( "debug_route", result ); + if ( result == "failed" ) + { + self cancelgoal( "debug_route" ); + wait 5; + } +#/ + } +} + +devgui_debug_route() +{ +/# + iprintln( "Choose nodes with 'A' or press 'B' to cancel" ); + nodes = maps/mp/gametypes/_dev::dev_get_node_pair(); + if ( !isDefined( nodes ) ) + { + iprintln( "Route Debug Cancelled" ); + return; + } + iprintln( "Sending bots to chosen nodes" ); + players = get_players(); + _a2804 = players; + _k2804 = getFirstArrayKey( _a2804 ); + while ( isDefined( _k2804 ) ) + { + player = _a2804[ _k2804 ]; + if ( !player is_bot() ) + { + } + else + { + player notify( "debug_patrol" ); + player thread bot_debug_patrol( nodes[ 0 ], nodes[ 1 ] ); + } + _k2804 = getNextArrayKey( _a2804, _k2804 ); +#/ + } +} + +devgui_bot_spawn( team ) +{ +/# + player = gethostplayer(); + direction = player getplayerangles(); + direction_vec = anglesToForward( direction ); + eye = player geteye(); + scale = 8000; + direction_vec = ( direction_vec[ 0 ] * scale, direction_vec[ 1 ] * scale, direction_vec[ 2 ] * scale ); + trace = bullettrace( eye, eye + direction_vec, 0, undefined ); + direction_vec = player.origin - trace[ "position" ]; + direction = vectorToAngle( direction_vec ); + bot = addtestclient(); + if ( !isDefined( bot ) ) + { + println( "Could not add test client" ); + return; + } + bot.pers[ "isBot" ] = 1; + bot thread bot_spawn_think( team ); + yaw = direction[ 1 ]; + bot thread devgui_bot_spawn_think( trace[ "position" ], yaw ); +#/ +} + +devgui_bot_spawn_think( origin, yaw ) +{ +/# + self endon( "disconnect" ); + for ( ;; ) + { + self waittill( "spawned_player" ); + self setorigin( origin ); + angles = ( 0, yaw, 0 ); + self setplayerangles( angles ); +#/ + } +} diff --git a/patch_mp/maps/mp/bots/_bot_combat.gsc b/patch_mp/maps/mp/bots/_bot_combat.gsc new file mode 100644 index 0000000..e87a429 --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_combat.gsc @@ -0,0 +1,1936 @@ +#include maps/mp/gametypes/_weapon_utils; +#include maps/mp/bots/_bot; +#include maps/mp/_utility; +#include common_scripts/utility; + +bot_combat_think( damage, attacker, direction ) +{ + self allowattack( 0 ); + self pressads( 0 ); + for ( ;; ) + { + if ( self atgoal( "enemy_patrol" ) ) + { + self cancelgoal( "enemy_patrol" ); + } + self maps/mp/bots/_bot::bot_update_failsafe(); + self maps/mp/bots/_bot::bot_update_crouch(); + self maps/mp/bots/_bot::bot_update_crate(); + if ( !bot_can_do_combat() ) + { + return; + } + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); +/# + if ( bot_has_enemy() ) + { + if ( getDvarInt( "bot_IgnoreHumans" ) ) + { + if ( isplayer( self.bot.threat.entity ) && !self.bot.threat.entity is_bot() ) + { + self bot_combat_idle(); +#/ + } + } + } + sight = bot_best_enemy(); + bot_select_weapon(); + ent = self.bot.threat.entity; + pos = self.bot.threat.position; + if ( !sight ) + { + if ( threat_is_player() ) + { + if ( distancesquared( self.origin, ent.origin ) < 65536 ) + { + prediction = self predictposition( ent, 4 ); + height = ent getplayerviewheight(); + self lookat( prediction + ( 0, 0, height ) ); + self addgoal( ent.origin, 24, 4, "enemy_patrol" ); + self allowattack( 0 ); + wait 0,05; + continue; + } + else self addgoal( self.origin, 24, 3, "enemy_patrol" ); + if ( difficulty != "easy" && cointoss() ) + { + self bot_combat_throw_lethal( pos ); + self bot_combat_throw_tactical( pos ); + } + bot_combat_dead(); + self addgoal( pos, 24, 4, "enemy_patrol" ); + } + bot_combat_idle( damage, attacker, direction ); + return; + } + else + { + if ( threat_dead() ) + { + bot_combat_dead(); + return; + } + } + bot_update_cover(); + bot_combat_main(); + if ( threat_is_turret() ) + { + bot_turret_set_dangerous( ent ); + bot_combat_throw_smoke( ent.origin ); + } + if ( !threat_is_turret() || threat_is_ai_tank() && threat_is_equipment() ) + { + bot_combat_throw_emp( ent.origin ); + bot_combat_throw_lethal( ent.origin ); + } + else + { + if ( threat_is_qrdrone() ) + { + bot_combat_throw_emp( ent.origin ); + break; + } + else + { + if ( threat_requires_rocket( ent ) ) + { + self cancelgoal( "enemy_patrol" ); + self addgoal( self.origin, 24, 4, "cover" ); + } + } + } + if ( difficulty == "easy" ) + { + wait 0,5; + continue; + } + else if ( difficulty == "normal" ) + { + wait 0,25; + continue; + } + else if ( difficulty == "hard" ) + { + wait 0,1; + continue; + } + else + { + wait 0,05; + } + } +} + +bot_can_do_combat() +{ + if ( self ismantling() || self isonladder() ) + { + return 0; + } + return 1; +} + +threat_dead() +{ + if ( bot_has_enemy() ) + { + ent = self.bot.threat.entity; + if ( threat_is_turret() ) + { + if ( isDefined( ent.dead ) ) + { + return ent.dead; + } + } + else + { + if ( threat_is_qrdrone() ) + { + if ( isDefined( ent.crash_accel ) ) + { + return ent.crash_accel; + } + } + } + return !isalive( ent ); + } + return 1; +} + +bot_can_reload() +{ + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return 0; + } + if ( !self getweaponammostock( weapon ) ) + { + return 0; + } + if ( !self isreloading() || self isswitchingweapons() && self isthrowinggrenade() ) + { + return 0; + } + return 1; +} + +bot_combat_idle( damage, attacker, direction ) +{ + self pressads( 0 ); + self allowattack( 0 ); + self allowsprint( 1 ); + bot_clear_enemy(); + weapon = self getcurrentweapon(); + if ( bot_can_reload() ) + { + frac = 0,5; + if ( bot_has_lmg() ) + { + frac = 0,25; + } + frac += randomfloatrange( -0,1, 0,1 ); + if ( bot_weapon_ammo_frac() < frac ) + { + self pressusebutton( 0,1 ); + } + } + if ( isDefined( damage ) ) + { + bot_patrol_near_enemy( damage, attacker, direction ); + return; + } + self cancelgoal( "cover" ); + self cancelgoal( "flee" ); +} + +bot_combat_dead( damage ) +{ + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + wait 0,75; + break; + case "normal": + wait 0,5; + break; + case "hard": + wait 0,25; + break; + case "fu": + wait 0,1; + break; + } + self allowattack( 0 ); + switch( difficulty ) + { + case "easy": + case "normal": + wait 1; + break; + case "hard": + wait_endon( 0,5, "damage" ); + break; + case "fu": + wait_endon( 0,25, "damage" ); + break; + } + bot_clear_enemy(); +} + +bot_combat_main() +{ + weapon = self getcurrentweapon(); + currentammo = self getweaponammoclip( weapon ) + self getweaponammostock( weapon ); + if ( !currentammo || bot_has_melee_weapon() ) + { + if ( threat_is_player() || threat_is_dog() ) + { + bot_combat_melee( weapon ); + } + return; + } + time = getTime(); + if ( !bot_should_hip_fire() ) + { + ads = self.bot.threat.dot > 0,96; + } + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + if ( ads ) + { + self pressads( 1 ); + } + else + { + self pressads( 0 ); + } + if ( ads && self playerads() < 1 ) + { + ratio = int( floor( bot_get_converge_time() / bot_get_converge_rate() ) ); + step = ratio % 50; + self.bot.threat.time_aim_interval = ratio - step; + self.bot.threat.time_aim_correct = time; + ideal = bot_update_aim( 4 ); + bot_update_lookat( ideal, 0 ); + return; + } + frames = 4; + frames += randomintrange( 0, 3 ); + if ( difficulty != "fu" ) + { + if ( distancesquared( self.bot.threat.entity.origin, self.bot.threat.position ) > 225 ) + { + self.bot.threat.time_aim_correct = time; + if ( time > self.bot.threat.time_first_sight ) + { + self.bot.threat.time_first_sight = time - 100; + } + } + } + if ( time >= self.bot.threat.time_aim_correct ) + { + self.bot.threat.time_aim_correct += self.bot.threat.time_aim_interval; + frac = ( time - self.bot.threat.time_first_sight ) / bot_get_converge_time(); + frac = clamp( frac, 0, 1 ); + if ( !threat_is_player() ) + { + frac = 1; + } + self.bot.threat.aim_target = bot_update_aim( frames ); + self.bot.threat.position = self.bot.threat.entity.origin; + bot_update_lookat( self.bot.threat.aim_target, frac ); + } + if ( difficulty == "hard" || difficulty == "fu" ) + { + if ( bot_on_target( self.bot.threat.entity.origin, 30 ) ) + { + self allowattack( 1 ); + } + else + { + self allowattack( 0 ); + } + } + else + { + if ( bot_on_target( self.bot.threat.aim_target, 45 ) ) + { + self allowattack( 1 ); + } + else + { + self allowattack( 0 ); + } + } + if ( threat_is_equipment() ) + { + if ( bot_on_target( self.bot.threat.entity.origin, 3 ) ) + { + self allowattack( 1 ); + } + else + { + self allowattack( 0 ); + } + } + if ( isDefined( self.stingerlockstarted ) && self.stingerlockstarted ) + { + self allowattack( self.stingerlockfinalized ); + return; + } + if ( threat_is_player() ) + { + if ( self iscarryingturret() && self.bot.threat.dot > 0 ) + { + self pressattackbutton(); + } + if ( self.bot.threat.dot > 0 && distance2dsquared( self.origin, self.bot.threat.entity.origin ) < bot_get_melee_range_sq() ) + { + self addgoal( self.bot.threat.entity.origin, 24, 4, "enemy_patrol" ); + self pressmelee(); + } + } + if ( threat_using_riotshield() ) + { + self bot_riotshield_think( self.bot.threat.entity ); + } + else + { + if ( bot_has_shotgun() ) + { + self bot_shotgun_think(); + } + } +} + +bot_combat_melee( weapon ) +{ + if ( !threat_is_player() && !threat_is_dog() ) + { + threat_ignore( self.bot.threat.entity, 60 ); + self bot_clear_enemy(); + return; + } + self cancelgoal( "cover" ); + self pressads( 0 ); + self allowattack( 0 ); + for ( ;; ) + { + if ( !isalive( self.bot.threat.entity ) ) + { + self bot_clear_enemy(); + self cancelgoal( "enemy_patrol" ); + return; + } + if ( self isthrowinggrenade() || self isswitchingweapons() ) + { + self cancelgoal( "enemy_patrol" ); + return; + } + if ( !bot_has_melee_weapon() && self getweaponammoclip( weapon ) ) + { + self cancelgoal( "enemy_patrol" ); + return; + } + frames = 4; + prediction = self predictposition( self.bot.threat.entity, frames ); + if ( !isplayer( self.bot.threat.entity ) ) + { + height = self.bot.threat.entity getcentroid()[ 2 ] - prediction[ 2 ]; + return prediction + ( 0, 0, height ); + } + else + { + height = self.bot.threat.entity getplayerviewheight(); + } + self lookat( prediction + ( 0, 0, height ) ); + distsq = distance2dsquared( self.origin, prediction ); + dot = bot_dot_product( self.bot.threat.entity.origin ); + if ( dot > 0 && distsq < bot_get_melee_range_sq() ) + { + if ( self.bot.threat.entity getstance() == "prone" ) + { + self setstance( "crouch" ); + } + if ( weapon == "knife_held_mp" ) + { + self pressattackbutton(); + wait 0,1; + break; + } + else + { + self pressmelee(); + wait 0,1; + } + } + goal = self getgoal( "enemy_patrol" ); + if ( !isDefined( goal ) || distancesquared( prediction, goal ) > bot_get_melee_range_sq() ) + { + if ( !findpath( self.origin, prediction, undefined, 0, 1 ) ) + { + threat_ignore( self.bot.threat.entity, 10 ); + self bot_clear_enemy(); + self cancelgoal( "enemy_patrol" ); + return; + } + if ( weapon == "riotshield_mp" ) + { + if ( maps/mp/bots/_bot::bot_get_difficulty() != "easy" ) + { + self setstance( "crouch" ); + self allowsprint( 0 ); + } + } + self addgoal( prediction, 4, 4, "enemy_patrol" ); + } + wait 0,05; + } +} + +bot_get_fov() +{ + weapon = self getcurrentweapon(); + reduction = 1; + if ( weapon != "none" && isweaponscopeoverlay( weapon ) && self playerads() >= 1 ) + { + reduction = 0,25; + } + return self.bot.fov * reduction; +} + +bot_get_converge_time() +{ + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + return 3500; + case "normal": + return 2000; + case "hard": + return 1500; + case "fu": + return 100; + } + return 2000; + } + } + } + } +} + +bot_get_converge_rate() +{ + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + return 2; + case "normal": + return 4; + case "hard": + return 5; + case "fu": + return 7; + } + return 4; + } + } + } + } +} + +bot_get_melee_range_sq() +{ + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + return 1600; + case "normal": + return 4900; + case "hard": + return 4900; + case "fu": + return 4900; + } + return 4900; + } + } + } + } +} + +bot_get_aim_error() +{ + difficulty = maps/mp/bots/_bot::bot_get_difficulty(); + switch( difficulty ) + { + case "easy": + return 30; + case "normal": + return 20; + case "hard": + return 15; + case "fu": + return 2; + } + return 20; + } + } + } + } +} + +bot_update_lookat( origin, frac ) +{ + angles = vectorToAngle( origin - self.origin ); + right = anglesToRight( angles ); + error = bot_get_aim_error() * ( 1 - frac ); + if ( cointoss() ) + { + error *= -1; + } + height = origin[ 2 ] - self.bot.threat.entity.origin[ 2 ]; + height *= 1 - frac; + if ( cointoss() ) + { + height *= -1; + } + end = origin + ( right * error ); + end += ( 0, 0, height ); + red = 1 - frac; + green = frac; + self lookat( end ); +} + +bot_on_target( aim_target, radius ) +{ + angles = self getplayerangles(); + forward = anglesToForward( angles ); + origin = self getplayercamerapos(); + len = distance( aim_target, origin ); + end = origin + ( forward * len ); + if ( distance2dsquared( aim_target, end ) < ( radius * radius ) ) + { + return 1; + } + return 0; +} + +bot_dot_product( origin ) +{ + angles = self getplayerangles(); + forward = anglesToForward( angles ); + delta = origin - self getplayercamerapos(); + delta = vectornormalize( delta ); + dot = vectordot( forward, delta ); + return dot; +} + +bot_has_enemy() +{ + return isDefined( self.bot.threat.entity ); +} + +threat_is_player() +{ + ent = self.bot.threat.entity; + if ( isDefined( ent ) ) + { + return isplayer( ent ); + } +} + +threat_is_dog() +{ + ent = self.bot.threat.entity; + if ( isDefined( ent ) ) + { + return isai( ent ); + } +} + +threat_is_turret() +{ + ent = self.bot.threat.entity; + if ( isDefined( ent ) ) + { + return ent.classname == "auto_turret"; + } +} + +threat_is_ai_tank() +{ + ent = self.bot.threat.entity; + if ( isDefined( ent ) && isDefined( ent.targetname ) ) + { + return ent.targetname == "talon"; + } +} + +threat_is_qrdrone( ent ) +{ + if ( !isDefined( ent ) ) + { + ent = self.bot.threat.entity; + } + if ( isDefined( ent ) && isDefined( ent.helitype ) ) + { + return ent.helitype == "qrdrone"; + } +} + +threat_using_riotshield() +{ + if ( threat_is_player() ) + { + weapon = self.bot.threat.entity getcurrentweapon(); + return weapon == "riotshield_mp"; + } + return 0; +} + +threat_is_equipment() +{ + ent = self.bot.threat.entity; + if ( !isDefined( ent ) ) + { + return 0; + } + if ( threat_is_player() ) + { + return 0; + } + if ( isDefined( ent.model ) && ent.model == "t6_wpn_tac_insert_world" ) + { + return 1; + } + if ( isDefined( ent.name ) ) + { + return isweaponequipment( ent.name ); + } +} + +bot_clear_enemy() +{ + self clearlookat(); + self.bot.threat.entity = undefined; +} + +bot_best_enemy() +{ + fov = bot_get_fov(); + ent = self.bot.threat.entity; + if ( isDefined( ent ) ) + { + if ( isplayer( ent ) || isai( ent ) ) + { + dot = bot_dot_product( ent.origin ); + if ( dot >= fov ) + { + if ( self botsighttracepassed( ent ) ) + { + self.bot.threat.time_recent_sight = getTime(); + self.bot.threat.dot = dot; + return 1; + } + } + } + } + enemies = self getthreats( fov ); + _a791 = enemies; + _k791 = getFirstArrayKey( _a791 ); + while ( isDefined( _k791 ) ) + { + enemy = _a791[ _k791 ]; + if ( threat_should_ignore( enemy ) ) + { + } + else if ( !isplayer( enemy ) && enemy.classname != "grenade" ) + { + if ( level.gametype == "hack" ) + { + if ( enemy.classname == "script_vehicle" ) + { + } + } + else if ( enemy.classname == "auto_turret" ) + { + if ( isDefined( enemy.dead ) || enemy.dead && isDefined( enemy.carried ) && enemy.carried ) + { + } + else + { + if ( isDefined( enemy.turret_active ) && !enemy.turret_active ) + { + break; + } + } + else + { + if ( threat_requires_rocket( enemy ) ) + { + if ( !bot_has_launcher() ) + { + break; + } + else origin = self getplayercamerapos(); + angles = vectorToAngle( enemy.origin - origin ); + if ( angles[ 0 ] < 290 ) + { + threat_ignore( enemy, 3,5 ); + break; + } + } + } + else + { + if ( self botsighttracepassed( enemy ) ) + { + self.bot.threat.entity = enemy; + self.bot.threat.time_first_sight = getTime(); + self.bot.threat.time_recent_sight = getTime(); + self.bot.threat.dot = bot_dot_product( enemy.origin ); + self.bot.threat.position = enemy.origin; + return 1; + } + } + } + } + _k791 = getNextArrayKey( _a791, _k791 ); + } + return 0; +} + +threat_requires_rocket( enemy ) +{ + if ( !isDefined( enemy ) || isplayer( enemy ) ) + { + return 0; + } + if ( isDefined( enemy.helitype ) && enemy.helitype == "qrdrone" ) + { + return 0; + } + if ( isDefined( enemy.targetname ) ) + { + if ( enemy.targetname == "remote_mortar" ) + { + return 1; + } + else + { + if ( enemy.targetname == "uav" || enemy.targetname == "counteruav" ) + { + return 1; + } + } + } + if ( enemy.classname == "script_vehicle" && enemy.vehicleclass == "helicopter" ) + { + return 1; + } + return 0; +} + +threat_is_warthog( enemy ) +{ + if ( !isDefined( enemy ) || isplayer( enemy ) ) + { + return 0; + } + if ( enemy.classname == "script_vehicle" && enemy.vehicleclass == "plane" ) + { + return 1; + } + return 0; +} + +threat_should_ignore( entity ) +{ + ignore_time = self.bot.ignore_entity[ entity getentitynumber() ]; + if ( isDefined( ignore_time ) ) + { + if ( getTime() < ignore_time ) + { + return 1; + } + } + return 0; +} + +threat_ignore( entity, secs ) +{ + self.bot.ignore_entity[ entity getentitynumber() ] = getTime() + ( secs * 1000 ); +} + +bot_update_aim( frames ) +{ + ent = self.bot.threat.entity; + prediction = self predictposition( ent, frames ); + if ( bot_using_launcher() && !threat_requires_rocket( ent ) ) + { + return prediction - ( 0, 0, randomintrange( 0, 10 ) ); + } + if ( !threat_is_player() ) + { + height = ent getcentroid()[ 2 ] - prediction[ 2 ]; + return prediction + ( 0, 0, height ); + } + height = ent getplayerviewheight(); + if ( threat_using_riotshield() ) + { + dot = ent bot_dot_product( self.origin ); + if ( dot > 0,8 && ent getstance() == "stand" ) + { + return prediction + vectorScale( ( 0, 0, 1 ), 5 ); + } + } + torso = prediction + ( 0, 0, height / 1,6 ); + return torso; +} + +bot_update_cover() +{ + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + return; + } + if ( bot_has_melee_weapon() ) + { + self cancelgoal( "cover" ); + self cancelgoal( "flee" ); + return; + } + if ( threat_using_riotshield() ) + { + self cancelgoal( "enemy_patrol" ); + self cancelgoal( "flee" ); + return; + } + enemy = self.bot.threat.entity; + if ( threat_is_turret() && !bot_has_sniper() && !bot_has_melee_weapon() ) + { + goal = enemy turret_get_attack_node(); + if ( isDefined( goal ) ) + { + self cancelgoal( "enemy_patrol" ); + self addgoal( goal, 24, 3, "cover" ); + } + } + if ( !isplayer( enemy ) ) + { + return; + } + dot = enemy bot_dot_product( self.origin ); + if ( dot < 0,8 && !bot_has_shotgun() ) + { + self cancelgoal( "cover" ); + self cancelgoal( "flee" ); + return; + } + ammo_frac = bot_weapon_ammo_frac(); + health_frac = bot_health_frac(); + cover_score = dot - ammo_frac - health_frac; + if ( bot_should_hip_fire() && !bot_has_shotgun() ) + { + cover_score += 1; + } + if ( cover_score > 0,25 ) + { + nodes = getnodesinradiussorted( self.origin, 1024, 256, 512, "Path", 8 ); + nearest = bot_nearest_node( enemy.origin ); + while ( isDefined( nearest ) && !self hasgoal( "flee" ) ) + { + _a1018 = nodes; + _k1018 = getFirstArrayKey( _a1018 ); + while ( isDefined( _k1018 ) ) + { + node = _a1018[ _k1018 ]; + if ( !nodesvisible( nearest, node ) && !nodescanpath( nearest, node ) ) + { + self cancelgoal( "cover" ); + self cancelgoal( "enemy_patrol" ); + self addgoal( node, 24, 4, "flee" ); + return; + } + _k1018 = getNextArrayKey( _a1018, _k1018 ); + } + } + } + else if ( cover_score > -0,25 ) + { + if ( self hasgoal( "cover" ) ) + { + return; + } + nodes = getnodesinradiussorted( self.origin, 512, 0, 256, "Cover" ); + if ( !nodes.size ) + { + nodes = getnodesinradiussorted( self.origin, 256, 0, 256, "Path", 8 ); + } + nearest = bot_nearest_node( enemy.origin ); + while ( isDefined( nearest ) ) + { + _a1048 = nodes; + _k1048 = getFirstArrayKey( _a1048 ); + while ( isDefined( _k1048 ) ) + { + node = _a1048[ _k1048 ]; + if ( !canclaimnode( node, self.team ) ) + { + } + else if ( node.type != "Path" && !within_fov( node.origin, node.angles, enemy.origin, bot_get_fov() ) ) + { + } + else + { + if ( !nodescanpath( nearest, node ) && nodesvisible( nearest, node ) ) + { + if ( node.type == "Cover Left" ) + { + right = anglesToRight( node.angles ); + dir = vectorScale( right, 16 ); + node = node.origin - dir; + } + else + { + if ( node.type == "Cover Right" ) + { + right = anglesToRight( node.angles ); + dir = vectorScale( right, 16 ); + node = node.origin + dir; + } + } + self cancelgoal( "flee" ); + self cancelgoal( "enemy_patrol" ); + self addgoal( node, 8, 4, "cover" ); + return; + } + } + _k1048 = getNextArrayKey( _a1048, _k1048 ); + } + } + } + else if ( bot_has_shotgun() ) + { + self addgoal( enemy.origin, 24, 4, "cover" ); + } +} + +bot_update_attack( enemy, dot_from, dot_to, sight, aim_target ) +{ + self allowattack( 0 ); + self pressads( 0 ); + if ( sight == 0 ) + { + return; + } + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return; + } + radius = 50; + if ( dot_to > 0,9 ) + { + self pressads( 1 ); + } + ads = 1; + if ( bot_should_hip_fire() ) + { + self pressads( 0 ); + ads = 0; + radius = 15; + } + if ( isweaponscopeoverlay( weapon ) && ads ) + { + if ( self playerads() < 1 ) + { + self.bot.time_ads = getTime(); + } + if ( getTime() < ( self.bot.time_ads + 1000 ) ) + { + return; + } + } + if ( !ads || self playerads() >= 1 ) + { + self allowattack( 1 ); + } +} + +bot_weapon_ammo_frac() +{ + if ( self isreloading() || self isswitchingweapons() ) + { + return 0; + } + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return 1; + } + total = weaponclipsize( weapon ); + if ( total <= 0 ) + { + return 1; + } + current = self getweaponammoclip( weapon ); + return current / total; +} + +bot_select_weapon() +{ + if ( !self isthrowinggrenade() || self isswitchingweapons() && self isreloading() ) + { + return; + } + if ( !self isonground() ) + { + return; + } + ent = self.bot.threat.entity; + if ( !isDefined( ent ) ) + { + return; + } + primaries = self getweaponslistprimaries(); + weapon = self getcurrentweapon(); + stock = self getweaponammostock( weapon ); + clip = self getweaponammoclip( weapon ); + if ( weapon == "none" ) + { + return; + } + if ( threat_requires_rocket( ent ) || threat_is_qrdrone() ) + { + if ( !bot_using_launcher() ) + { + _a1202 = primaries; + _k1202 = getFirstArrayKey( _a1202 ); + while ( isDefined( _k1202 ) ) + { + primary = _a1202[ _k1202 ]; + if ( !self getweaponammoclip( primary ) && !self getweaponammostock( primary ) ) + { + } + else + { + if ( primary == "smaw_mp" || primary == "fhj18_mp" ) + { + self switchtoweapon( primary ); + return; + } + } + _k1202 = getNextArrayKey( _a1202, _k1202 ); + } + } + else if ( !clip && !stock && !threat_is_qrdrone() ) + { + threat_ignore( ent, 5 ); + } + return; + } + else + { + if ( weapon == "fhj18_mp" && !target_istarget( ent ) ) + { + _a1225 = primaries; + _k1225 = getFirstArrayKey( _a1225 ); + while ( isDefined( _k1225 ) ) + { + primary = _a1225[ _k1225 ]; + if ( primary != weapon ) + { + self switchtoweapon( primary ); + return; + } + _k1225 = getNextArrayKey( _a1225, _k1225 ); + } + return; + } + } + while ( !clip ) + { + if ( stock ) + { + if ( weaponhasattachment( weapon, "fastreload" ) ) + { + return; + } + } + _a1247 = primaries; + _k1247 = getFirstArrayKey( _a1247 ); + while ( isDefined( _k1247 ) ) + { + primary = _a1247[ _k1247 ]; + if ( primary == weapon || primary == "fhj18_mp" ) + { + } + else + { + if ( self getweaponammoclip( primary ) ) + { + self switchtoweapon( primary ); + return; + } + } + _k1247 = getNextArrayKey( _a1247, _k1247 ); + } + while ( bot_using_launcher() || bot_has_lmg() ) + { + _a1263 = primaries; + _k1263 = getFirstArrayKey( _a1263 ); + while ( isDefined( _k1263 ) ) + { + primary = _a1263[ _k1263 ]; + if ( primary == weapon || primary == "fhj18_mp" ) + { + } + else + { + self switchtoweapon( primary ); + return; + } + _k1263 = getNextArrayKey( _a1263, _k1263 ); + } + } + } +} + +bot_has_shotgun() +{ + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return 0; + } + if ( weaponisdualwield( weapon ) ) + { + return 1; + } + if ( !bot_has_weapon_class( "spread" ) ) + { + return bot_has_weapon_class( "pistol spread" ); + } +} + +bot_has_crossbow() +{ + weapon = self getcurrentweapon(); + return weapon == "crossbow_mp"; +} + +bot_has_launcher() +{ + if ( self getweaponammoclip( "smaw_mp" ) > 0 || self getweaponammostock( "smaw_mp" ) > 0 ) + { + return 1; + } + if ( self getweaponammoclip( "fhj18_mp" ) > 0 || self getweaponammostock( "fhj18_mp" ) > 0 ) + { + return 1; + } + return 0; +} + +bot_has_melee_weapon() +{ + weapon = self getcurrentweapon(); + if ( weapon == "fhj18_mp" ) + { + if ( isDefined( self.bot.threat.entity ) && !target_istarget( self.bot.threat.entity ) ) + { + return 1; + } + } + if ( weapon != "riotshield_mp" ) + { + return weapon == "knife_held_mp"; + } +} + +bot_has_pistol() +{ + if ( !bot_has_weapon_class( "pistol" ) ) + { + return bot_has_weapon_class( "pistol spread" ); + } +} + +bot_has_lmg() +{ + return bot_has_weapon_class( "mg" ); +} + +bot_has_sniper() +{ + return bot_has_weapon_class( "sniper" ); +} + +bot_using_launcher() +{ + weapon = self getcurrentweapon(); + if ( weapon != "smaw_mp" && weapon != "fhj18_mp" ) + { + return weapon == "usrpg_mp"; + } +} + +bot_has_minigun() +{ + weapon = self getcurrentweapon(); + if ( weapon != "minigun_mp" ) + { + return weapon == "inventory_minigun_mp"; + } +} + +bot_has_weapon_class( class ) +{ + if ( self isreloading() ) + { + return 0; + } + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return 0; + } + return weaponclass( weapon ) == class; +} + +bot_health_frac() +{ + return self.health / self.maxhealth; +} + +bot_should_hip_fire() +{ + enemy = self.bot.threat.entity; + weapon = self getcurrentweapon(); + if ( weapon == "none" ) + { + return 0; + } + if ( weaponisdualwield( weapon ) ) + { + return 1; + } + class = weaponclass( weapon ); + if ( isplayer( enemy ) && class == "spread" ) + { + return 1; + } + distsq = distancesquared( self.origin, enemy.origin ); + distcheck = 0; + switch( class ) + { + case "mg": + distcheck = 250; + break; + case "smg": + distcheck = 350; + break; + case "spread": + distcheck = 400; + break; + case "pistol": + distcheck = 200; + break; + case "rocketlauncher": + distcheck = 0; + break; + case "rifle": + default: + distcheck = 300; + break; + } + if ( isweaponscopeoverlay( weapon ) ) + { + distcheck = 500; + } + return distsq < ( distcheck * distcheck ); +} + +bot_patrol_near_enemy( damage, attacker, direction ) +{ + if ( threat_is_warthog( attacker ) ) + { + return; + } + if ( threat_requires_rocket( attacker ) && !self bot_has_launcher() ) + { + return; + } + if ( isDefined( attacker ) ) + { + self bot_lookat_entity( attacker ); + } + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + return; + } + if ( !isDefined( attacker ) ) + { + attacker = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + } + if ( !isDefined( attacker ) ) + { + return; + } + if ( attacker.classname == "auto_turret" ) + { + self bot_turret_set_dangerous( attacker ); + } + node = bot_nearest_node( attacker.origin ); + if ( !isDefined( node ) ) + { + nodes = getnodesinradiussorted( attacker.origin, 1024, 0, 512, "Path", 8 ); + if ( nodes.size ) + { + node = nodes[ 0 ]; + } + } + if ( isDefined( node ) ) + { + if ( isDefined( damage ) ) + { + self addgoal( node, 24, 4, "enemy_patrol" ); + return; + } + else + { + self addgoal( node, 24, 2, "enemy_patrol" ); + } + } +} + +bot_nearest_node( origin ) +{ + node = getnearestnode( origin ); + if ( isDefined( node ) ) + { + return node; + } + nodes = getnodesinradiussorted( origin, 256, 0, 256 ); + if ( nodes.size ) + { + return nodes[ 0 ]; + } + return undefined; +} + +bot_lookat_entity( entity ) +{ + if ( isplayer( entity ) && entity getstance() != "prone" ) + { + if ( distancesquared( self.origin, entity.origin ) < 65536 ) + { + origin = entity getcentroid() + vectorScale( ( 0, 0, 1 ), 10 ); + self lookat( origin ); + return; + } + } + offset = target_getoffset( entity ); + if ( isDefined( offset ) ) + { + self lookat( entity.origin + offset ); + } + else + { + self lookat( entity getcentroid() ); + } +} + +bot_combat_throw_lethal( origin ) +{ + weapons = self getweaponslist(); + radius = 256; + if ( self hasperk( "specialty_flakjacket" ) ) + { + radius *= 0,25; + } + if ( distancesquared( self.origin, origin ) < ( radius * radius ) ) + { + return 0; + } + _a1562 = weapons; + _k1562 = getFirstArrayKey( _a1562 ); + while ( isDefined( _k1562 ) ) + { + weapon = _a1562[ _k1562 ]; + if ( self getweaponammostock( weapon ) <= 0 ) + { + } + else + { + if ( weapon == "frag_grenade_mp" || weapon == "sticky_grenade_mp" ) + { + if ( self throwgrenade( weapon, origin ) ) + { + return 1; + } + } + } + _k1562 = getNextArrayKey( _a1562, _k1562 ); + } + return 0; +} + +bot_combat_throw_tactical( origin ) +{ + weapons = self getweaponslist(); + if ( !self hasperk( "specialty_flashprotection" ) ) + { + if ( distancesquared( self.origin, origin ) < 422500 ) + { + return 0; + } + } + _a1593 = weapons; + _k1593 = getFirstArrayKey( _a1593 ); + while ( isDefined( _k1593 ) ) + { + weapon = _a1593[ _k1593 ]; + if ( self getweaponammostock( weapon ) <= 0 ) + { + } + else + { + if ( weapon == "flash_grenade_mp" || weapon == "concussion_grenade_mp" ) + { + if ( self throwgrenade( weapon, origin ) ) + { + return 1; + } + } + } + _k1593 = getNextArrayKey( _a1593, _k1593 ); + } + return 0; +} + +bot_combat_throw_smoke( origin ) +{ + if ( self getweaponammostock( "willy_pete_mp" ) <= 0 ) + { + return 0; + } + time = getTime(); + _a1621 = level.players; + _k1621 = getFirstArrayKey( _a1621 ); + while ( isDefined( _k1621 ) ) + { + player = _a1621[ _k1621 ]; + if ( !isDefined( player.smokegrenadetime ) ) + { + } + else if ( ( time - player.smokegrenadetime ) > 12000 ) + { + } + else + { + if ( distancesquared( origin, player.smokegrenadeposition ) < 65536 ) + { + return 0; + } + } + _k1621 = getNextArrayKey( _a1621, _k1621 ); + } + return self throwgrenade( "willy_pete_mp", origin ); +} + +bot_combat_throw_emp( origin ) +{ + if ( self getweaponammostock( "emp_mp" ) <= 0 ) + { + return 0; + } + return self throwgrenade( "emp_mp", origin ); +} + +bot_combat_throw_proximity( origin ) +{ + _a1654 = level.missileentities; + _k1654 = getFirstArrayKey( _a1654 ); + while ( isDefined( _k1654 ) ) + { + missile = _a1654[ _k1654 ]; + if ( isDefined( missile ) && distancesquared( missile.origin, origin ) < 65536 ) + { + return 0; + } + _k1654 = getNextArrayKey( _a1654, _k1654 ); + } + return self throwgrenade( "proximity_grenade_mp", origin ); +} + +bot_combat_tactical_insertion( origin ) +{ + _a1667 = level.missileentities; + _k1667 = getFirstArrayKey( _a1667 ); + while ( isDefined( _k1667 ) ) + { + missile = _a1667[ _k1667 ]; + if ( isDefined( missile ) && distancesquared( missile.origin, origin ) < 65536 ) + { + return 0; + } + _k1667 = getNextArrayKey( _a1667, _k1667 ); + } + return self throwgrenade( "tactical_insertion_mp", origin ); +} + +bot_combat_toss_flash( origin ) +{ + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + return 0; + } + if ( self getweaponammostock( "sensor_grenade_mp" ) <= 0 && self getweaponammostock( "proximity_grenade_mp" ) <= 0 && self getweaponammostock( "trophy_system_mp" ) <= 0 ) + { + return 0; + } + _a1690 = level.missileentities; + _k1690 = getFirstArrayKey( _a1690 ); + while ( isDefined( _k1690 ) ) + { + missile = _a1690[ _k1690 ]; + if ( isDefined( missile ) && distancesquared( missile.origin, origin ) < 65536 ) + { + return 0; + } + _k1690 = getNextArrayKey( _a1690, _k1690 ); + } + self pressattackbutton( 2 ); + return 1; +} + +bot_combat_toss_frag( origin ) +{ + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + return 0; + } + if ( self getweaponammostock( "bouncingbetty_mp" ) <= 0 && self getweaponammostock( "claymore_mp" ) <= 0 && self getweaponammostock( "satchel_charge_mp" ) <= 0 ) + { + return 0; + } + _a1714 = level.missileentities; + _k1714 = getFirstArrayKey( _a1714 ); + while ( isDefined( _k1714 ) ) + { + missile = _a1714[ _k1714 ]; + if ( isDefined( missile ) && distancesquared( missile.origin, origin ) < 16384 ) + { + return 0; + } + _k1714 = getNextArrayKey( _a1714, _k1714 ); + } + self pressattackbutton( 1 ); + return 1; +} + +bot_shotgun_think() +{ + if ( self isthrowinggrenade() || self isswitchingweapons() ) + { + return; + } + enemy = self.bot.threat.entity; + weapon = self getcurrentweapon(); + self allowattack( 0 ); + distsq = distancesquared( enemy.origin, self.origin ); + if ( threat_is_turret() ) + { + goal = enemy turret_get_attack_node(); + if ( isDefined( goal ) ) + { + self cancelgoal( "enemy_patrol" ); + self addgoal( goal, 24, 4, "cover" ); + } + if ( weapon != "none" && !weaponisdualwield( weapon ) && distsq < 65536 ) + { + self pressads( 1 ); + } + } + else + { + if ( self getweaponammoclip( weapon ) && distsq < 90000 ) + { + self cancelgoal( "enemy_patrol" ); + self addgoal( self.origin, 24, 4, "cover" ); + } + } + dot = self bot_dot_product( self.bot.threat.aim_target ); + if ( distsq < 250000 && dot > 0,98 ) + { + self allowattack( 1 ); + return; + } + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + return; + } + if ( self threat_is_player() ) + { + dot = enemy bot_dot_product( self.origin ); + if ( dot < 0,9 ) + { + return; + } + } + else + { + return; + } + primaries = self getweaponslistprimaries(); + weapon = self getcurrentweapon(); + _a1793 = primaries; + _k1793 = getFirstArrayKey( _a1793 ); + while ( isDefined( _k1793 ) ) + { + primary = _a1793[ _k1793 ]; + if ( primary == weapon ) + { + } + else if ( !self getweaponammoclip( primary ) ) + { + } + else if ( maps/mp/gametypes/_weapon_utils::isguidedrocketlauncherweapon( primary ) ) + { + } + else class = weaponclass( primary ); + if ( class != "spread" && class != "pistol spread" || class == "melee" && class == "item" ) + { + } + else + { + if ( self switchtoweapon( primary ) ) + { + return; + } + } + _k1793 = getNextArrayKey( _a1793, _k1793 ); + } + if ( self getweaponammostock( "willy_pete_mp" ) > 0 ) + { + self pressattackbutton( 2 ); + return; + } +} + +bot_turret_set_dangerous( turret ) +{ + if ( !level.teambased ) + { + return; + } + if ( isDefined( turret.dead ) || turret.dead && isDefined( turret.carried ) && turret.carried ) + { + return; + } + if ( isDefined( turret.turret_active ) && !turret.turret_active ) + { + return; + } + if ( turret.dangerous_nodes.size ) + { + return; + } + nearest = bot_turret_nearest_node( turret ); + if ( !isDefined( nearest ) ) + { + return; + } + forward = anglesToForward( turret.angles ); + if ( turret.turrettype == "sentry" ) + { + nodes = getvisiblenodes( nearest ); + _a1868 = nodes; + _k1868 = getFirstArrayKey( _a1868 ); + while ( isDefined( _k1868 ) ) + { + node = _a1868[ _k1868 ]; + dir = vectornormalize( node.origin - turret.origin ); + dot = vectordot( forward, dir ); + if ( dot >= 0,5 ) + { + turret turret_mark_node_dangerous( node ); + } + _k1868 = getNextArrayKey( _a1868, _k1868 ); + } + } + else while ( turret.turrettype == "microwave" ) + { + nodes = getnodesinradius( turret.origin, level.microwave_radius, 0 ); + _a1883 = nodes; + _k1883 = getFirstArrayKey( _a1883 ); + while ( isDefined( _k1883 ) ) + { + node = _a1883[ _k1883 ]; + if ( !nodesvisible( nearest, node ) ) + { + } + else + { + dir = vectornormalize( node.origin - turret.origin ); + dot = vectordot( forward, dir ); + if ( dot >= level.microwave_turret_cone_dot ) + { + turret turret_mark_node_dangerous( node ); + } + } + _k1883 = getNextArrayKey( _a1883, _k1883 ); + } + } +} + +bot_turret_nearest_node( turret ) +{ + nodes = getnodesinradiussorted( turret.origin, 256, 0 ); + forward = anglesToForward( turret.angles ); + _a1906 = nodes; + _k1906 = getFirstArrayKey( _a1906 ); + while ( isDefined( _k1906 ) ) + { + node = _a1906[ _k1906 ]; + dir = vectornormalize( node.origin - turret.origin ); + dot = vectordot( forward, dir ); + if ( dot > 0,5 ) + { + return node; + } + _k1906 = getNextArrayKey( _a1906, _k1906 ); + } + if ( nodes.size ) + { + return nodes[ 0 ]; + } + return undefined; +} + +turret_mark_node_dangerous( node ) +{ + _a1927 = level.teams; + _k1927 = getFirstArrayKey( _a1927 ); + while ( isDefined( _k1927 ) ) + { + team = _a1927[ _k1927 ]; + if ( team == self.owner.team ) + { + } + else + { + node setdangerous( team, 1 ); + } + _k1927 = getNextArrayKey( _a1927, _k1927 ); + } + self.dangerous_nodes[ self.dangerous_nodes.size ] = node; +} + +turret_get_attack_node() +{ + nearest = bot_nearest_node( self.origin ); + if ( !isDefined( nearest ) ) + { + return undefined; + } + nodes = getnodesinradiussorted( self.origin, 512, 64 ); + forward = anglesToForward( self.angles ); + _a1952 = nodes; + _k1952 = getFirstArrayKey( _a1952 ); + while ( isDefined( _k1952 ) ) + { + node = _a1952[ _k1952 ]; + if ( !nodesvisible( node, nearest ) ) + { + } + else + { + dir = vectornormalize( node.origin - self.origin ); + dot = vectordot( forward, dir ); + if ( dot < 0,5 ) + { + return node; + } + } + _k1952 = getNextArrayKey( _a1952, _k1952 ); + } + return undefined; +} + +bot_riotshield_think( enemy ) +{ + dot = enemy bot_dot_product( self.origin ); + if ( !bot_has_crossbow() && !bot_using_launcher() && enemy getstance() != "stand" ) + { + if ( dot > 0,8 ) + { + self allowattack( 0 ); + } + } + forward = anglesToForward( enemy.angles ); + origin = enemy.origin + ( forward * randomintrange( 256, 512 ) ); + if ( self bot_combat_throw_lethal( origin ) ) + { + return; + } + if ( self bot_combat_throw_tactical( origin ) ) + { + return; + } + if ( self throwgrenade( "proximity_grenade_mp", origin ) ) + { + return; + } + if ( self atgoal( "cover" ) ) + { + self.bot.threat.update_riotshield = 0; + } + if ( getTime() > self.bot.threat.update_riotshield ) + { + self thread bot_riotshield_dangerous_think( enemy ); + self.bot.threat.update_riotshield = getTime() + randomintrange( 5000, 7500 ); + } +} + +bot_riotshield_dangerous_think( enemy, goal ) +{ + nearest = bot_nearest_node( enemy.origin ); + if ( !isDefined( nearest ) ) + { + threat_ignore( enemy, 10 ); + return; + } + nodes = getnodesinradius( enemy.origin, 768, 0 ); + if ( !nodes.size ) + { + threat_ignore( enemy, 10 ); + return; + } + nodes = array_randomize( nodes ); + forward = anglesToForward( enemy.angles ); + _a2037 = nodes; + _k2037 = getFirstArrayKey( _a2037 ); + while ( isDefined( _k2037 ) ) + { + node = _a2037[ _k2037 ]; + if ( !nodesvisible( node, nearest ) ) + { + } + else + { + dir = vectornormalize( node.origin - enemy.origin ); + dot = vectordot( forward, dir ); + if ( dot < 0 ) + { + if ( distancesquared( self.origin, enemy.origin ) < 262144 ) + { + self addgoal( node, 24, 4, "cover" ); + } + else + { + self addgoal( node, 24, 3, "cover" ); + } + break; + } + } + else + { + _k2037 = getNextArrayKey( _a2037, _k2037 ); + } + } + if ( !level.teambased ) + { + return; + } + nodes = getnodesinradius( enemy.origin, 512, 0 ); + _a2069 = nodes; + _k2069 = getFirstArrayKey( _a2069 ); + while ( isDefined( _k2069 ) ) + { + node = _a2069[ _k2069 ]; + dir = vectornormalize( node.origin - enemy.origin ); + dot = vectordot( forward, dir ); + if ( dot >= 0,5 ) + { + node setdangerous( self.team, 1 ); + } + _k2069 = getNextArrayKey( _a2069, _k2069 ); + } + enemy wait_endon( 5, "death" ); + _a2082 = nodes; + _k2082 = getFirstArrayKey( _a2082 ); + while ( isDefined( _k2082 ) ) + { + node = _a2082[ _k2082 ]; + node setdangerous( self.team, 0 ); + _k2082 = getNextArrayKey( _a2082, _k2082 ); + } +} diff --git a/patch_mp/maps/mp/bots/_bot_conf.gsc b/patch_mp/maps/mp/bots/_bot_conf.gsc new file mode 100644 index 0000000..d1b4e30 --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_conf.gsc @@ -0,0 +1,103 @@ +#include maps/mp/_utility; +#include common_scripts/utility; + +bot_conf_think() +{ + time = getTime(); + if ( time < self.bot.update_objective ) + { + return; + } + self.bot.update_objective = time + randomintrange( 500, 1500 ); + goal = self getgoal( "conf_dogtag" ); + if ( isDefined( goal ) ) + { + if ( !conf_tag_in_radius( goal, 64 ) ) + { + self cancelgoal( "conf_dogtag" ); + } + } + conf_get_tag_in_sight(); +} + +conf_get_tag_in_sight() +{ + angles = self getplayerangles(); + forward = anglesToForward( angles ); + forward = vectornormalize( forward ); + closest = 999999; + _a41 = level.dogtags; + _k41 = getFirstArrayKey( _a41 ); + while ( isDefined( _k41 ) ) + { + tag = _a41[ _k41 ]; + if ( is_true( tag.unreachable ) ) + { + } + else distsq = distancesquared( tag.curorigin, self.origin ); + if ( distsq > closest ) + { + } + else delta = tag.curorigin - self.origin; + delta = vectornormalize( delta ); + dot = vectordot( forward, delta ); + if ( dot < self.bot.fov && distsq > 40000 ) + { + } + else + { + if ( dot > self.bot.fov && distsq > 1440000 ) + { + break; + } + else + { + nearest = getnearestnode( tag.curorigin ); + if ( !isDefined( nearest ) ) + { + tag.unreachable = 1; + break; + } + else if ( ( tag.curorigin[ 2 ] - nearest.origin[ 2 ] ) > 18 ) + { + tag.unreachable = 1; + break; + } + else + { + if ( !isDefined( tag.unreachable ) && !findpath( self.origin, tag.curorigin, tag, 0, 1 ) ) + { + tag.unreachable = 1; + } + else + { + tag.unreachable = 0; + } + closest = distsq; + closetag = tag; + } + } + } + _k41 = getNextArrayKey( _a41, _k41 ); + } + if ( isDefined( closetag ) ) + { + self addgoal( closetag.curorigin, 16, 3, "conf_dogtag" ); + } +} + +conf_tag_in_radius( origin, radius ) +{ + _a106 = level.dogtags; + _k106 = getFirstArrayKey( _a106 ); + while ( isDefined( _k106 ) ) + { + tag = _a106[ _k106 ]; + if ( distancesquared( origin, tag.curorigin ) < ( radius * radius ) ) + { + return 1; + } + _k106 = getNextArrayKey( _a106, _k106 ); + } + return 0; +} diff --git a/patch_mp/maps/mp/bots/_bot_ctf.gsc b/patch_mp/maps/mp/bots/_bot_ctf.gsc new file mode 100644 index 0000000..c4a71ec --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_ctf.gsc @@ -0,0 +1,404 @@ +#include maps/mp/gametypes/_gameobjects; +#include maps/mp/bots/_bot_combat; +#include maps/mp/bots/_bot; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/ctf; + +bot_ctf_think() +{ + time = getTime(); + if ( time < self.bot.update_objective ) + { + return; + } + self.bot.update_objective = time + randomintrange( 500, 1500 ); + if ( maps/mp/bots/_bot::bot_get_difficulty() != "easy" ) + { + flag_mine = ctf_get_flag( self.team ); + if ( flag_mine ishome() && distancesquared( self.origin, flag_mine.curorigin ) < 262144 ) + { + nodes = getnodesinradius( flag_mine.curorigin, 256, 0, 64, "any", 8 ); + node = random( nodes ); + if ( cointoss() ) + { + } + else + { + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( node.origin, flag_mine.curorigin ); + if ( cointoss() ) + { + } + else + { + } + self maps/mp/bots/_bot_combat::bot_combat_toss_frag( node.origin, flag_mine.curorigin ); + if ( cointoss() ) + { + } + else + { + } + self maps/mp/bots/_bot_combat::bot_combat_toss_flash( node.origin, flag_mine.curorigin ); + } + } + if ( bot_should_patrol_flag() ) + { + bot_patrol_flag(); + return; + } + self cancelgoal( "ctf_flag_patrol" ); + if ( !bot_ctf_defend() ) + { + bot_ctf_capture(); + } + flag_mine = ctf_get_flag( self.team ); + flag_enemy = ctf_get_flag( getotherteam( self.team ) ); + home_mine = flag_mine ctf_flag_get_home(); + if ( ctf_has_flag( flag_enemy ) && self issprinting() && distancesquared( self.origin, home_mine ) < 36864 ) + { + if ( bot_dot_product( home_mine ) > 0,9 ) + { + self bot_dive_to_prone( "stand" ); + } + } + else + { + if ( !flag_mine ishome() && !isDefined( flag_mine.carrier ) ) + { + if ( self issprinting() && distancesquared( self.origin, flag_mine.curorigin ) < 36864 ) + { + if ( bot_dot_product( flag_mine.curorigin ) > 0,9 ) + { + self bot_dive_to_prone( "stand" ); + } + } + } + } +} + +bot_should_patrol_flag() +{ + flag_mine = ctf_get_flag( self.team ); + flag_enemy = ctf_get_flag( getotherteam( self.team ) ); + home_mine = flag_mine ctf_flag_get_home(); + if ( self hasgoal( "ctf_flag" ) && !self atgoal( "ctf_flag" ) ) + { + return 0; + } + if ( ctf_has_flag( flag_enemy ) ) + { + if ( !flag_mine ishome() ) + { + return 1; + } + else + { + return 0; + } + } + if ( !flag_mine ishome() ) + { + return 0; + } + if ( distancesquared( self.origin, flag_enemy.curorigin ) < 262144 ) + { + return 0; + } + if ( bot_get_friends().size && self maps/mp/bots/_bot::bot_friend_goal_in_radius( "ctf_flag_patrol", home_mine, 1024 ) == 0 ) + { + return 1; + } + return 0; +} + +ctf_get_flag( team ) +{ + _a115 = level.flags; + _k115 = getFirstArrayKey( _a115 ); + while ( isDefined( _k115 ) ) + { + f = _a115[ _k115 ]; + if ( f maps/mp/gametypes/_gameobjects::getownerteam() == team ) + { + return f; + } + _k115 = getNextArrayKey( _a115, _k115 ); + } + return undefined; +} + +ctf_flag_get_home() +{ + return self.trigger.baseorigin; +} + +ctf_has_flag( flag ) +{ + if ( isDefined( flag.carrier ) ) + { + return flag.carrier == self; + } +} + +bot_ctf_capture() +{ + flag_enemy = ctf_get_flag( getotherteam( self.team ) ); + flag_mine = ctf_get_flag( self.team ); + home_enemy = flag_enemy ctf_flag_get_home(); + home_mine = flag_mine ctf_flag_get_home(); + if ( ctf_has_flag( flag_enemy ) ) + { + self addgoal( home_mine, 16, 4, "ctf_flag" ); + } + else if ( isDefined( flag_enemy.carrier ) ) + { + if ( self atgoal( "ctf_flag" ) ) + { + self cancelgoal( "ctf_flag" ); + } + goal = self getgoal( "ctf_flag" ); + if ( isDefined( goal ) && distancesquared( goal, flag_enemy.carrier.origin ) < 589824 ) + { + return; + } + nodes = getnodesinradius( flag_enemy.carrier.origin, 512, 64, 256, "any", 8 ); + if ( nodes.size ) + { + self addgoal( random( nodes ), 16, 3, "ctf_flag" ); + } + else + { + self addgoal( flag_enemy.carrier.origin, 16, 3, "ctf_flag" ); + } + } + else + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "ctf_flag", flag_enemy.curorigin, 16 ) <= 1 ) + { + self addgoal( flag_enemy.curorigin, 16, 3, "ctf_flag" ); + } + } +} + +bot_ctf_defend() +{ + flag_enemy = ctf_get_flag( getotherteam( self.team ) ); + flag_mine = ctf_get_flag( self.team ); + home_enemy = flag_enemy ctf_flag_get_home(); + home_mine = flag_mine ctf_flag_get_home(); + if ( flag_mine ishome() ) + { + return 0; + } + if ( ctf_has_flag( flag_enemy ) ) + { + return 0; + } + if ( !isDefined( flag_mine.carrier ) ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "ctf_flag", flag_mine.curorigin, 16 ) <= 1 ) + { + return self bot_ctf_add_goal( flag_mine.curorigin, 4, "ctf_flag" ); + } + } + else + { + if ( !flag_enemy ishome() || distance2dsquared( self.origin, home_enemy ) > 250000 ) + { + return self bot_ctf_add_goal( flag_mine.curorigin, 4, "ctf_flag" ); + } + else + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "ctf_flag", home_enemy, 16 ) <= 1 ) + { + self addgoal( home_enemy, 16, 4, "ctf_flag" ); + } + } + } + return 1; +} + +bot_ctf_add_goal( origin, goal_priority, goal_name ) +{ + goal = undefined; + if ( findpath( self.origin, origin, undefined, 0, 1 ) ) + { + goal = origin; + } + else + { + node = bot_ctf_random_visible_node( origin ); + if ( isDefined( node ) ) + { + if ( findpath( self.origin, node.origin, undefined, 0, 1 ) ) + { + goal = node; + self.bot.update_objective += randomintrange( 3000, 5000 ); + } + } + } + if ( isDefined( goal ) ) + { + self addgoal( goal, 16, goal_priority, goal_name ); + return 1; + } + return 0; +} + +bot_get_look_at() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + enemies = self maps/mp/bots/_bot::bot_get_enemies( 0 ); + if ( enemies.size ) + { + enemy = random( enemies ); + } + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + flag_mine = ctf_get_flag( self.team ); + home_mine = flag_mine ctf_flag_get_home(); + return home_mine; +} + +bot_patrol_flag() +{ + self cancelgoal( "ctf_flag" ); + flag_mine = ctf_get_flag( self.team ); + if ( self atgoal( "ctf_flag_patrol" ) ) + { + node = getnearestnode( self.origin ); + if ( !isDefined( node ) ) + { + self clearlookat(); + self cancelgoal( "ctf_flag_patrol" ); + return; + } + if ( node.type == "Path" ) + { + self setstance( "crouch" ); + } + else + { + self setstance( "stand" ); + } + if ( getTime() > self.bot.update_lookat ) + { + origin = self bot_get_look_at(); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + self.bot.update_lookat = getTime() + randomintrange( 1500, 3000 ); + } + goal = self getgoal( "ctf_flag_patrol" ); + nearest = base_nearest_node( flag_mine ); + mine = getnearestnode( goal ); + if ( isDefined( mine ) && !nodesvisible( mine, nearest ) ) + { + self clearlookat(); + self cancelgoal( "ctf_flag_patrol" ); + } + if ( getTime() > self.bot.update_objective_patrol ) + { + self clearlookat(); + self cancelgoal( "ctf_flag_patrol" ); + } + return; + } + nearest = base_nearest_node( flag_mine ); + if ( self hasgoal( "ctf_flag_patrol" ) ) + { + goal = self getgoal( "ctf_flag_patrol" ); + if ( distancesquared( self.origin, goal ) < 65536 ) + { + origin = self bot_get_look_at(); + self lookat( origin ); + } + if ( distancesquared( self.origin, goal ) < 16384 ) + { + self.bot.update_objective_patrol = getTime() + randomintrange( 3000, 6000 ); + } + mine = getnearestnode( goal ); + if ( isDefined( mine ) && !nodesvisible( mine, nearest ) ) + { + self clearlookat(); + self cancelgoal( "ctf_flag_patrol" ); + } + return; + } + if ( getTime() < self.bot.update_objective_patrol ) + { + return; + } + nodes = getvisiblenodes( nearest ); +/# + assert( nodes.size ); +#/ + i = randomint( nodes.size ); + while ( i < nodes.size ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "ctf_flag_patrol", nodes[ i ].origin, 256 ) == 0 ) + { + self addgoal( nodes[ i ], 24, 3, "ctf_flag_patrol" ); + self.bot.update_objective_patrol = getTime() + randomintrange( 3000, 6000 ); + return; + } + i++; + } +} + +base_nearest_node( flag ) +{ + home = flag ctf_flag_get_home(); + nodes = getnodesinradiussorted( home, 256, 0 ); +/# + assert( nodes.size ); +#/ + return nodes[ 0 ]; +} + +bot_ctf_random_visible_node( origin ) +{ + nodes = getnodesinradius( origin, 384, 0, 256 ); + nearest = maps/mp/bots/_bot_combat::bot_nearest_node( origin ); + while ( isDefined( nearest ) && nodes.size ) + { + current = randomintrange( 0, nodes.size ); + i = 0; + while ( i < nodes.size ) + { + current = ( current + 1 ) % nodes.size; + if ( nodesvisible( nodes[ current ], nearest ) ) + { + return nodes[ current ]; + } + i++; + } + } + return undefined; +} diff --git a/patch_mp/maps/mp/bots/_bot_dem.gsc b/patch_mp/maps/mp/bots/_bot_dem.gsc new file mode 100644 index 0000000..6695085 --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_dem.gsc @@ -0,0 +1,381 @@ +#include maps/mp/bots/_bot_combat; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/dem; + +bot_dem_think() +{ + while ( !isDefined( level.bombzones[ 0 ].dem_nodes ) ) + { + _a11 = level.bombzones; + _k11 = getFirstArrayKey( _a11 ); + while ( isDefined( _k11 ) ) + { + zone = _a11[ _k11 ]; + zone.dem_nodes = []; + zone.dem_nodes = getnodesinradius( zone.trigger.origin, 1024, 64, 128, "Path" ); + _k11 = getNextArrayKey( _a11, _k11 ); + } + } + if ( self.team == game[ "attackers" ] ) + { + bot_dem_attack_think(); + } + else + { + bot_dem_defend_think(); + } +} + +bot_dem_attack_think() +{ + zones = dem_get_alive_zones(); + if ( !zones.size ) + { + return; + } + while ( !isDefined( self.goal_flag ) ) + { + zones = array_randomize( zones ); + _a42 = zones; + _k42 = getFirstArrayKey( _a42 ); + while ( isDefined( _k42 ) ) + { + zone = _a42[ _k42 ]; + if ( zones.size == 1 || is_true( zone.bombplanted ) && !is_true( zone.bombexploded ) ) + { + self.goal_flag = zone; + break; + } + else if ( randomint( 100 ) < 50 ) + { + self.goal_flag = zone; + break; + } + else + { + _k42 = getNextArrayKey( _a42, _k42 ); + } + } + } + if ( isDefined( self.goal_flag ) ) + { + if ( is_true( self.goal_flag.bombexploded ) ) + { + self.goal_flag = undefined; + self cancelgoal( "dem_guard" ); + self cancelgoal( "bomb" ); + return; + } + else if ( is_true( self.goal_flag.bombplanted ) ) + { + self bot_dem_guard( self.goal_flag, self.goal_flag.dem_nodes, self.goal_flag.trigger.origin ); + return; + } + else if ( self bot_dem_friend_interacting( self.goal_flag.trigger.origin ) ) + { + self bot_dem_guard( self.goal_flag, self.goal_flag.dem_nodes, self.goal_flag.trigger.origin ); + return; + } + else + { + self bot_dem_attack( self.goal_flag ); + } + } +} + +bot_dem_defend_think() +{ + zones = dem_get_alive_zones(); + if ( !zones.size ) + { + return; + } + while ( !isDefined( self.goal_flag ) ) + { + zones = array_randomize( zones ); + _a95 = zones; + _k95 = getFirstArrayKey( _a95 ); + while ( isDefined( _k95 ) ) + { + zone = _a95[ _k95 ]; + if ( zones.size == 1 || is_true( zone.bombplanted ) && !is_true( zone.bombexploded ) ) + { + self.goal_flag = zone; + break; + } + else if ( randomint( 100 ) < 50 ) + { + self.goal_flag = zone; + break; + } + else + { + _k95 = getNextArrayKey( _a95, _k95 ); + } + } + } + if ( isDefined( self.goal_flag ) ) + { + if ( is_true( self.goal_flag.bombexploded ) ) + { + self.goal_flag = undefined; + self cancelgoal( "dem_guard" ); + self cancelgoal( "bomb" ); + return; + } + else if ( is_true( self.goal_flag.bombplanted ) && !self bot_dem_friend_interacting( self.goal_flag.trigger.origin ) ) + { + self bot_dem_defuse( self.goal_flag ); + return; + } + else + { + self bot_dem_guard( self.goal_flag, self.goal_flag.dem_nodes, self.goal_flag.trigger.origin ); + } + } +} + +bot_dem_attack( zone ) +{ + self cancelgoal( "dem_guard" ); + if ( !self hasgoal( "bomb" ) ) + { + self.bomb_goal = self dem_get_bomb_goal( zone.visuals[ 0 ] ); + if ( isDefined( self.bomb_goal ) ) + { + self addgoal( self.bomb_goal, 48, 2, "bomb" ); + } + return; + } + if ( !self atgoal( "bomb" ) ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_smoke( self.bomb_goal ) ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( self.bomb_goal ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( self.bomb_goal ); + } + } + return; + } + self addgoal( self.bomb_goal, 48, 4, "bomb" ); + self setstance( "prone" ); + self pressusebutton( level.planttime + 1 ); + wait 0,5; + if ( is_true( self.isplanting ) ) + { + wait ( level.planttime + 1 ); + } + self pressusebutton( 0 ); + defenders = self bot_get_enemies(); + _a172 = defenders; + _k172 = getFirstArrayKey( _a172 ); + while ( isDefined( _k172 ) ) + { + defender = _a172[ _k172 ]; + if ( defender is_bot() ) + { + defender.goal_flag = undefined; + } + _k172 = getNextArrayKey( _a172, _k172 ); + } + self setstance( "crouch" ); + wait 0,25; + self cancelgoal( "bomb" ); + self setstance( "stand" ); +} + +bot_dem_guard( zone, nodes, origin ) +{ + self cancelgoal( "bomb" ); + enemy = self bot_dem_enemy_interacting( origin ); + if ( isDefined( enemy ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( enemy.origin ); + self addgoal( enemy.origin, 128, 3, "dem_guard" ); + return; + } + enemy = self bot_dem_enemy_nearby( origin ); + if ( isDefined( enemy ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( enemy.origin ); + self addgoal( enemy.origin, 128, 3, "dem_guard" ); + return; + } + if ( self hasgoal( "dem_guard" ) && !self atgoal( "dem_guard" ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + return; + } + node = random( nodes ); + self addgoal( node, 24, 2, "dem_guard" ); +} + +bot_dem_defuse( zone ) +{ + self cancelgoal( "dem_guard" ); + if ( !self hasgoal( "bomb" ) ) + { + self.bomb_goal = self dem_get_bomb_goal( zone.visuals[ 0 ] ); + if ( isDefined( self.bomb_goal ) ) + { + self addgoal( self.bomb_goal, 48, 2, "bomb" ); + } + return; + } + if ( !self atgoal( "bomb" ) ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_smoke( self.bomb_goal ) ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( self.bomb_goal ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( self.bomb_goal ); + } + } + if ( ( self.goal_flag.detonatetime - getTime() ) < 12000 ) + { + self addgoal( self.bomb_goal, 48, 4, "bomb" ); + } + return; + } + self addgoal( self.bomb_goal, 48, 4, "bomb" ); + if ( cointoss() ) + { + self setstance( "crouch" ); + } + else + { + self setstance( "prone" ); + } + self pressusebutton( level.defusetime + 1 ); + wait 0,5; + if ( is_true( self.isdefusing ) ) + { + wait ( level.defusetime + 1 ); + } + self pressusebutton( 0 ); + self setstance( "crouch" ); + wait 0,25; + self cancelgoal( "bomb" ); + self setstance( "stand" ); +} + +bot_dem_enemy_interacting( origin ) +{ + enemies = maps/mp/bots/_bot::bot_get_enemies(); + _a288 = enemies; + _k288 = getFirstArrayKey( _a288 ); + while ( isDefined( _k288 ) ) + { + enemy = _a288[ _k288 ]; + if ( distancesquared( enemy.origin, origin ) > 65536 ) + { + } + else + { + if ( is_true( enemy.isdefusing ) || is_true( enemy.isplanting ) ) + { + return enemy; + } + } + _k288 = getNextArrayKey( _a288, _k288 ); + } + return undefined; +} + +bot_dem_friend_interacting( origin ) +{ + friends = maps/mp/bots/_bot::bot_get_friends(); + _a308 = friends; + _k308 = getFirstArrayKey( _a308 ); + while ( isDefined( _k308 ) ) + { + friend = _a308[ _k308 ]; + if ( distancesquared( friend.origin, origin ) > 65536 ) + { + } + else + { + if ( is_true( friend.isdefusing ) || is_true( friend.isplanting ) ) + { + return 1; + } + } + _k308 = getNextArrayKey( _a308, _k308 ); + } + return 0; +} + +bot_dem_enemy_nearby( origin ) +{ + enemy = maps/mp/bots/_bot::bot_get_closest_enemy( origin, 1 ); + if ( isDefined( enemy ) ) + { + if ( distancesquared( enemy.origin, origin ) < 1048576 ) + { + return enemy; + } + } + return undefined; +} + +dem_get_alive_zones() +{ + zones = []; + _a343 = level.bombzones; + _k343 = getFirstArrayKey( _a343 ); + while ( isDefined( _k343 ) ) + { + zone = _a343[ _k343 ]; + if ( is_true( zone.bombexploded ) ) + { + } + else + { + zones[ zones.size ] = zone; + } + _k343 = getNextArrayKey( _a343, _k343 ); + } + return zones; +} + +dem_get_bomb_goal( ent ) +{ + while ( !isDefined( ent.bot_goals ) ) + { + goals = []; + ent.bot_goals = []; + dir = anglesToForward( ent.angles ); + dir = vectorScale( dir, 32 ); + goals[ 0 ] = ent.origin + dir; + goals[ 1 ] = ent.origin - dir; + dir = anglesToRight( ent.angles ); + dir = vectorScale( dir, 48 ); + goals[ 2 ] = ent.origin + dir; + goals[ 3 ] = ent.origin - dir; + _a375 = goals; + _k375 = getFirstArrayKey( _a375 ); + while ( isDefined( _k375 ) ) + { + goal = _a375[ _k375 ]; + start = goal + vectorScale( ( 0, 0, 1 ), 128 ); + trace = bullettrace( start, goal, 0, undefined ); + ent.bot_goals[ ent.bot_goals.size ] = trace[ "position" ]; + _k375 = getNextArrayKey( _a375, _k375 ); + } + } + goals = array_randomize( ent.bot_goals ); + _a386 = goals; + _k386 = getFirstArrayKey( _a386 ); + while ( isDefined( _k386 ) ) + { + goal = _a386[ _k386 ]; + if ( findpath( self.origin, goal, 0 ) ) + { + return goal; + } + _k386 = getNextArrayKey( _a386, _k386 ); + } + return undefined; +} diff --git a/patch_mp/maps/mp/bots/_bot_dom.gsc b/patch_mp/maps/mp/bots/_bot_dom.gsc new file mode 100644 index 0000000..e48b59b --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_dom.gsc @@ -0,0 +1,483 @@ +#include maps/mp/bots/_bot; +#include maps/mp/bots/_bot_combat; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/dom; + +bot_dom_think() +{ + time = getTime(); + if ( time < self.bot.update_objective ) + { + return; + } + self.bot.update_objective = time + randomintrange( 500, 1500 ); + if ( self bot_is_capturing_flag() ) + { + flag = self dom_get_closest_flag(); + self bot_capture_flag( flag ); + return; + } + flag = self dom_get_closest_flag(); + if ( flag getflagteam() != self.team && distance2dsquared( self.origin, flag.origin ) < 147456 && !bot_has_flag_goal( flag ) ) + { + self bot_move_to_flag( flag ); + return; + } + flag = dom_get_weighted_flag( "neutral" ); + if ( !isDefined( flag ) ) + { + flag = dom_get_best_flag( self.team ); + } + if ( dom_has_two_flags( self.team ) ) + { + flag = dom_get_best_flag( self.team ); + } + if ( !isDefined( flag ) ) + { + return; + } + if ( !bot_has_flag_goal( flag ) && !self bot_goal_is_enemy_flag() ) + { + self bot_move_to_flag( flag ); + } + else + { + if ( !dom_is_game_start() ) + { + self bot_flag_grenade( flag ); + } + if ( distancesquared( self.origin, flag.origin ) < ( flag.radius * flag.radius ) && self istouching( flag.useobj.trigger ) ) + { + self bot_capture_flag( flag ); + } + } +} + +bot_move_to_flag( flag ) +{ + if ( level.script == "mp_frostbite" ) + { + nodes = getnodesinradius( flag.origin, flag.radius, 0, 32 ); + } + else + { + nodes = getnodesinradius( flag.origin, flag.radius, 0 ); + } +/# + assert( nodes.size ); +#/ + node = random( nodes ); + self addgoal( node, 24, 3, "dom_flag" ); +} + +bot_is_capturing_flag() +{ + return self atgoal( "dom_flag" ); +} + +bot_has_flag_goal( flag ) +{ + origin = self getgoal( "dom_flag" ); + if ( isDefined( origin ) ) + { + if ( distancesquared( flag.origin, origin ) < ( flag.radius * flag.radius ) ) + { + return 1; + } + } + return 0; +} + +bot_has_no_goal() +{ + origin = self getgoal( "dom_flag" ); + if ( isDefined( origin ) ) + { + return 0; + } + return 1; +} + +bot_goal_is_enemy_flag() +{ + origin = self getgoal( "dom_flag" ); + while ( isDefined( origin ) ) + { + _a130 = level.flags; + _k130 = getFirstArrayKey( _a130 ); + while ( isDefined( _k130 ) ) + { + flag = _a130[ _k130 ]; + if ( distancesquared( flag.origin, origin ) < ( flag.radius * flag.radius ) ) + { + if ( flag getflagteam() != self.team || dom_is_flag_contested( flag ) ) + { + return 1; + } + } + _k130 = getNextArrayKey( _a130, _k130 ); + } + } + return 0; +} + +bot_flag_grenade( flag ) +{ + if ( flag getflagteam() != self.team ) + { + if ( bot_tactical_insertion( flag ) ) + { + return; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_smoke( flag.origin ); + } + if ( !dom_is_flag_contested( flag ) ) + { + return; + } + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( flag.origin ) ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_tactical( flag.origin ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( flag.origin ); + } + } +} + +bot_get_look_at( flag ) +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 0 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + spawn = random( level.spawn_all ); + node = getvisiblenode( self.origin, spawn.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + return flag.origin; +} + +bot_capture_flag( flag ) +{ + time = getTime(); + if ( flag getflagteam() != self.team ) + { + if ( self getstance() == "prone" ) + { + self addgoal( self.origin, 24, 4, "dom_flag" ); + } + else + { + self addgoal( self.origin, 24, 3, "dom_flag" ); + } + if ( time > self.bot.update_lookat ) + { + origin = self bot_get_look_at( flag ); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + self.bot.update_lookat = time + randomintrange( 1500, 3000 ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + self maps/mp/bots/_bot_combat::bot_combat_toss_frag( self.origin ); + self maps/mp/bots/_bot_combat::bot_combat_toss_flash( self.origin ); + if ( !dom_is_game_start() ) + { + weapon = self getcurrentweapon(); + if ( weapon == "riotshield_mp" || weapon == "minigun_mp" ) + { + if ( cointoss() ) + { + self addgoal( self.origin, 24, 4, "dom_flag" ); + self setstance( "crouch" ); + } + } + else + { + if ( cointoss() && !bot_friend_in_radius( self.origin, 384 ) ) + { + self addgoal( self.origin, 24, 4, "dom_flag" ); + wait randomfloatrange( 0,5, 1 ); + self setstance( "prone" ); + self.bot.update_lookat += 5000; + } + } + } + } + else + { + if ( !dom_is_game_start() ) + { + if ( self getstance() == "stand" ) + { + wait randomfloatrange( 0,5, 1 ); + self setstance( "crouch" ); + } + } + } + } + else self clearlookat(); + self cancelgoal( "dom_flag" ); + if ( self getstance() == "crouch" ) + { + self setstance( "stand" ); + wait 0,25; + } + else + { + if ( self getstance() == "prone" ) + { + self setstance( "crouch" ); + wait 0,25; + self setstance( "stand" ); + wait 0,25; + } + } +} + +dom_is_game_start() +{ +/# + assert( isDefined( level.flags ) ); +#/ + _a289 = level.flags; + _k289 = getFirstArrayKey( _a289 ); + while ( isDefined( _k289 ) ) + { + flag = _a289[ _k289 ]; + if ( flag getflagteam() != "neutral" ) + { + return 0; + } + _k289 = getNextArrayKey( _a289, _k289 ); + } + return 1; +} + +dom_get_closest_flag() +{ + flags = arraysort( level.flags, self.origin ); + return flags[ 0 ]; +} + +dom_get_weighted_flag( owner ) +{ +/# + assert( isDefined( level.flags ) ); +#/ + best = undefined; + distsq = 9999999; + _a313 = level.flags; + _k313 = getFirstArrayKey( _a313 ); + while ( isDefined( _k313 ) ) + { + flag = _a313[ _k313 ]; + if ( isDefined( owner ) && flag getflagteam() != owner ) + { + } + else + { + d = distancesquared( self.origin, flag.origin ); + if ( distsq != 9999999 && d < distsq || randomint( 100 ) < 70 && randomint( 100 ) > 70 ) + { + best = flag; + distsq = d; + } + } + _k313 = getNextArrayKey( _a313, _k313 ); + } + return best; +} + +dom_get_weighted_enemy_flag( team ) +{ +/# + assert( isDefined( level.flags ) ); +#/ + best = undefined; + distsq = 9999999; + _a339 = level.flags; + _k339 = getFirstArrayKey( _a339 ); + while ( isDefined( _k339 ) ) + { + flag = _a339[ _k339 ]; + if ( flag getflagteam() == team ) + { + } + else + { + d = distancesquared( self.origin, flag.origin ); + if ( distsq != 9999999 && d < distsq || randomint( 100 ) < 80 && randomint( 100 ) > 80 ) + { + best = flag; + distsq = d; + } + } + _k339 = getNextArrayKey( _a339, _k339 ); + } + return best; +} + +dom_is_flag_contested( flag ) +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( flag.origin, 0 ); + if ( isDefined( enemy ) ) + { + return distancesquared( enemy.origin, flag.origin ) < 147456; + } +} + +dom_has_two_flags( team ) +{ + count = 0; + _a368 = level.flags; + _k368 = getFirstArrayKey( _a368 ); + while ( isDefined( _k368 ) ) + { + flag = _a368[ _k368 ]; + if ( dom_is_flag_contested( flag ) ) + { + } + else + { + if ( flag getflagteam() == team ) + { + count++; + } + } + _k368 = getNextArrayKey( _a368, _k368 ); + } + return count >= 2; +} + +dom_get_weighted_contested_flag( team ) +{ +/# + assert( isDefined( level.flags ) ); +#/ + best = undefined; + distsq = 9999999; + _a391 = level.flags; + _k391 = getFirstArrayKey( _a391 ); + while ( isDefined( _k391 ) ) + { + flag = _a391[ _k391 ]; + if ( !dom_is_flag_contested( flag ) ) + { + } + else + { + d = distancesquared( self.origin, flag.origin ); + if ( distsq != 9999999 && d < distsq || randomint( 100 ) < 80 && randomint( 100 ) > 80 ) + { + best = flag; + distsq = d; + } + } + _k391 = getNextArrayKey( _a391, _k391 ); + } + return best; +} + +dom_get_random_flag( owner ) +{ +/# + assert( isDefined( level.flags ) ); +#/ + flagindex = randomintrange( 0, level.flags.size ); + if ( !isDefined( owner ) ) + { + return level.flags[ flagindex ]; + } + i = 0; + while ( i < level.flags.size ) + { + if ( level.flags[ flagindex ] getflagteam() == owner ) + { + return level.flags[ flagindex ]; + } + flagindex = ( flagindex + 1 ) % level.flags.size; + i++; + } + return undefined; +} + +dom_get_best_flag( team ) +{ + flag1 = dom_get_weighted_enemy_flag( team ); + flag2 = dom_get_weighted_contested_flag( team ); + if ( !isDefined( flag1 ) ) + { + return flag2; + } + if ( !isDefined( flag2 ) ) + { + return flag1; + } + offchance = randomint( 100 ) > 80; + if ( distancesquared( self.origin, flag1.origin ) < distancesquared( self.origin, flag2.origin ) ) + { + if ( !offchance ) + { + return flag1; + } + else + { + return flag2; + } + } + if ( !offchance ) + { + return flag2; + } + else + { + return flag1; + } +} + +bot_tactical_insertion( flag ) +{ + if ( self getweaponammostock( "tactical_insertion_mp" ) <= 0 ) + { + return 0; + } + dist = self getlookaheaddist(); + dir = self getlookaheaddir(); + if ( !isDefined( dist ) || !isDefined( dir ) ) + { + return 0; + } + node = bot_nearest_node( flag.origin ); + mine = bot_nearest_node( self.origin ); + if ( isDefined( mine ) && !nodesvisible( mine, node ) ) + { + origin = self.origin + vectorScale( dir, dist ); + next = bot_nearest_node( origin ); + if ( next isdangerous( self.team ) ) + { + return 0; + } + if ( isDefined( next ) && nodesvisible( next, node ) ) + { + return bot_combat_tactical_insertion( self.origin ); + } + } + return 0; +} diff --git a/patch_mp/maps/mp/bots/_bot_hack.gsc b/patch_mp/maps/mp/bots/_bot_hack.gsc new file mode 100644 index 0000000..f28633c --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_hack.gsc @@ -0,0 +1,202 @@ +#include maps/mp/bots/_bot; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/ctf; + +bot_hack_tank_get_goal_origin( tank ) +{ + nodes = getnodesinradiussorted( tank.origin, 256, 0, 64, "Path" ); + _a11 = nodes; + _k11 = getFirstArrayKey( _a11 ); + while ( isDefined( _k11 ) ) + { + node = _a11[ _k11 ]; + dir = vectornormalize( node.origin - tank.origin ); + dir = vectorScale( dir, 32 ); + goal = tank.origin + dir; + if ( findpath( self.origin, goal, 0 ) ) + { + return goal; + } + _k11 = getNextArrayKey( _a11, _k11 ); + } + return undefined; +} + +bot_hack_has_goal( tank ) +{ + goal = self getgoal( "hack" ); + if ( isDefined( goal ) ) + { + if ( distancesquared( goal, tank.origin ) < 16384 ) + { + return 1; + } + } + return 0; +} + +bot_hack_at_goal() +{ + if ( self atgoal( "hack" ) ) + { + return 1; + } + goal = self getgoal( "hack" ); + while ( isDefined( goal ) ) + { + tanks = getentarray( "talon", "targetname" ); + tanks = arraysort( tanks, self.origin ); + _a56 = tanks; + _k56 = getFirstArrayKey( _a56 ); + while ( isDefined( _k56 ) ) + { + tank = _a56[ _k56 ]; + if ( distancesquared( goal, tank.origin ) < 16384 ) + { + if ( isDefined( tank.trigger ) && self istouching( tank.trigger ) ) + { + return 1; + } + } + _k56 = getNextArrayKey( _a56, _k56 ); + } + } + return 0; +} + +bot_hack_goal_pregame( tanks ) +{ + _a73 = tanks; + _k73 = getFirstArrayKey( _a73 ); + while ( isDefined( _k73 ) ) + { + tank = _a73[ _k73 ]; + if ( isDefined( tank.owner ) ) + { + } + else if ( isDefined( tank.team ) && tank.team == self.team ) + { + } + else + { + goal = self bot_hack_tank_get_goal_origin( tank ); + if ( isDefined( goal ) ) + { + if ( self addgoal( goal, 24, 2, "hack" ) ) + { + self.goal_flag = tank; + return; + } + } + } + _k73 = getNextArrayKey( _a73, _k73 ); + } +} + +bot_hack_think() +{ + if ( bot_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 ( !is_true( level.drones_spawned ) ) + { + self bot_hack_goal_pregame( tanks ); + } + else + { + _a122 = tanks; + _k122 = getFirstArrayKey( _a122 ); + while ( isDefined( _k122 ) ) + { + tank = _a122[ _k122 ]; + if ( isDefined( tank.owner ) && tank.owner == self ) + { + } + else + { + if ( !isDefined( tank.owner ) ) + { + if ( self bot_hack_has_goal( tank ) ) + { + return; + } + goal = self bot_hack_tank_get_goal_origin( tank ); + if ( isDefined( goal ) ) + { + self addgoal( goal, 24, 2, "hack" ); + return; + } + } + if ( tank.isstunned && distancesquared( self.origin, tank.origin ) < 262144 ) + { + goal = self bot_hack_tank_get_goal_origin( tank ); + if ( isDefined( goal ) ) + { + self addgoal( goal, 24, 3, "hack" ); + return; + } + } + } + _k122 = getNextArrayKey( _a122, _k122 ); + } + if ( !maps/mp/bots/_bot::bot_vehicle_weapon_ammo( "emp_grenade_mp" ) ) + { + ammo = getentarray( "weapon_scavenger_item_hack_mp", "classname" ); + ammo = arraysort( ammo, self.origin ); + _a162 = ammo; + _k162 = getFirstArrayKey( _a162 ); + while ( isDefined( _k162 ) ) + { + bag = _a162[ _k162 ]; + if ( findpath( self.origin, bag.origin, 0 ) ) + { + self addgoal( bag.origin, 24, 2, "hack" ); + return; + } + _k162 = getNextArrayKey( _a162, _k162 ); + } + return; + } + _a174 = tanks; + _k174 = getFirstArrayKey( _a174 ); + while ( isDefined( _k174 ) ) + { + tank = _a174[ _k174 ]; + if ( isDefined( tank.owner ) && tank.owner == self ) + { + } + else + { + if ( tank.isstunned ) + { + break; + } + else + { + if ( self throwgrenade( "emp_grenade_mp", tank.origin ) ) + { + self waittill( "grenade_fire" ); + goal = self bot_hack_tank_get_goal_origin( tank ); + if ( isDefined( goal ) ) + { + self addgoal( goal, 24, 3, "hack" ); + wait 0,5; + return; + } + } + } + } + _k174 = getNextArrayKey( _a174, _k174 ); + } + } +} diff --git a/patch_mp/maps/mp/bots/_bot_hq.gsc b/patch_mp/maps/mp/bots/_bot_hq.gsc new file mode 100644 index 0000000..dbf84bb --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_hq.gsc @@ -0,0 +1,373 @@ +#include maps/mp/bots/_bot_combat; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/koth; + +bot_hq_think() +{ + time = getTime(); + if ( time < self.bot.update_objective ) + { + return; + } + self.bot.update_objective = time + randomintrange( 500, 1500 ); + if ( bot_should_patrol_hq() ) + { + self bot_patrol_hq(); + } + else + { + if ( !bot_has_hq_goal() ) + { + self bot_move_to_hq(); + } + } + if ( self bot_is_capturing_hq() ) + { + self bot_capture_hq(); + } + bot_hq_tactical_insertion(); + bot_hq_grenade(); + if ( !bot_is_capturing_hq() && !self atgoal( "hq_patrol" ) ) + { + mine = getnearestnode( self.origin ); + node = hq_nearest_node(); + if ( isDefined( mine ) && nodesvisible( mine, node ) ) + { + self lookat( level.radio.baseorigin + vectorScale( ( 0, 0, 1 ), 30 ) ); + } + } +} + +bot_has_hq_goal() +{ + origin = self getgoal( "hq_radio" ); + while ( isDefined( origin ) ) + { + _a53 = level.radio.nodes; + _k53 = getFirstArrayKey( _a53 ); + while ( isDefined( _k53 ) ) + { + node = _a53[ _k53 ]; + if ( distancesquared( origin, node.origin ) < 4096 ) + { + return 1; + } + _k53 = getNextArrayKey( _a53, _k53 ); + } + } + return 0; +} + +bot_is_capturing_hq() +{ + return self atgoal( "hq_radio" ); +} + +bot_should_patrol_hq() +{ + if ( level.radio.gameobject.ownerteam == "neutral" ) + { + return 0; + } + if ( level.radio.gameobject.ownerteam != self.team ) + { + return 0; + } + if ( hq_is_contested() ) + { + return 0; + } + return 1; +} + +bot_patrol_hq() +{ + self cancelgoal( "hq_radio" ); + if ( self atgoal( "hq_patrol" ) ) + { + node = getnearestnode( self.origin ); + if ( node.type == "Path" ) + { + self setstance( "crouch" ); + } + else + { + self setstance( "stand" ); + } + if ( getTime() > self.bot.update_lookat ) + { + origin = self bot_get_look_at(); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + self.bot.update_lookat = getTime() + randomintrange( 1500, 3000 ); + } + goal = self getgoal( "hq_patrol" ); + nearest = hq_nearest_node(); + mine = getnearestnode( goal ); + if ( isDefined( mine ) && !nodesvisible( mine, nearest ) ) + { + self clearlookat(); + self cancelgoal( "hq_patrol" ); + } + if ( getTime() > self.bot.update_objective_patrol ) + { + self clearlookat(); + self cancelgoal( "hq_patrol" ); + } + return; + } + nearest = hq_nearest_node(); + if ( self hasgoal( "hq_patrol" ) ) + { + goal = self getgoal( "hq_patrol" ); + if ( distancesquared( self.origin, goal ) < 65536 ) + { + origin = self bot_get_look_at(); + self lookat( origin ); + } + if ( distancesquared( self.origin, goal ) < 16384 ) + { + self.bot.update_objective_patrol = getTime() + randomintrange( 3000, 6000 ); + } + mine = getnearestnode( goal ); + if ( isDefined( mine ) && !nodesvisible( mine, nearest ) ) + { + self clearlookat(); + self cancelgoal( "hq_patrol" ); + } + return; + } + nodes = getvisiblenodes( nearest ); +/# + assert( nodes.size ); +#/ + i = randomint( nodes.size ); + while ( i < nodes.size ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "hq_radio", nodes[ i ].origin, 128 ) == 0 ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "hq_patrol", nodes[ i ].origin, 256 ) == 0 ) + { + self addgoal( nodes[ i ], 24, 3, "hq_patrol" ); + return; + } + } + i++; + } +} + +bot_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; + } + nodes = array_randomize( level.radio.nodes ); + _a219 = nodes; + _k219 = getFirstArrayKey( _a219 ); + while ( isDefined( _k219 ) ) + { + node = _a219[ _k219 ]; + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "hq_radio", node.origin, 64 ) == 0 ) + { + self addgoal( node, 24, 3, "hq_radio" ); + return; + } + _k219 = getNextArrayKey( _a219, _k219 ); + } + self addgoal( random( nodes ), 24, 3, "hq_radio" ); +} + +bot_get_look_at() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + enemies = self maps/mp/bots/_bot::bot_get_enemies( 0 ); + if ( enemies.size ) + { + enemy = random( enemies ); + } + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + spawn = random( level.spawnpoints ); + node = getvisiblenode( self.origin, spawn.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + return level.radio.baseorigin; +} + +bot_capture_hq() +{ + self addgoal( self.origin, 24, 3, "hq_radio" ); + self setstance( "crouch" ); + if ( getTime() > self.bot.update_lookat ) + { + origin = self bot_get_look_at(); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + self.bot.update_lookat = getTime() + randomintrange( 1500, 3000 ); + } +} + +any_other_team_touching( skip_team ) +{ + _a307 = level.teams; + _k307 = getFirstArrayKey( _a307 ); + while ( isDefined( _k307 ) ) + { + team = _a307[ _k307 ]; + if ( team == skip_team ) + { + } + else + { + if ( level.radio.gameobject.numtouching[ team ] ) + { + return 1; + } + } + _k307 = getNextArrayKey( _a307, _k307 ); + } + return 0; +} + +is_hq_contested( skip_team ) +{ + if ( any_other_team_touching( skip_team ) ) + { + return 1; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( level.radio.baseorigin, 1 ); + if ( isDefined( enemy ) && distancesquared( enemy.origin, level.radio.baseorigin ) < 262144 ) + { + return 1; + } + return 0; +} + +bot_hq_grenade() +{ + enemies = bot_get_enemies(); + if ( !enemies.size ) + { + return; + } + if ( self atgoal( "hq_patrol" ) || self atgoal( "hq_radio" ) ) + { + if ( self getweaponammostock( "proximity_grenade_mp" ) > 0 ) + { + origin = bot_get_look_at(); + if ( self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ) ) + { + return; + } + } + } + if ( !is_hq_contested( self.team ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_smoke( level.radio.baseorigin ); + return; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( level.radio.baseorigin, 0 ); + if ( isDefined( enemy ) ) + { + origin = enemy.origin; + } + else + { + origin = level.radio.baseorigin; + } + dir = vectornormalize( self.origin - origin ); + dir = ( 0, dir[ 1 ], 0 ); + origin += vectorScale( dir, 128 ); + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( origin ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_tactical( origin ); + } +} + +bot_hq_tactical_insertion() +{ + if ( !self hasweapon( "tactical_insertion_mp" ) ) + { + return; + } + dist = self getlookaheaddist(); + dir = self getlookaheaddir(); + if ( !isDefined( dist ) || !isDefined( dir ) ) + { + return; + } + node = hq_nearest_node(); + mine = getnearestnode( self.origin ); + if ( isDefined( mine ) && !nodesvisible( mine, node ) ) + { + origin = self.origin + vectorScale( dir, dist ); + next = getnearestnode( origin ); + if ( isDefined( next ) && nodesvisible( next, node ) ) + { + bot_combat_tactical_insertion( self.origin ); + } + } +} + +hq_nearest_node() +{ + return random( level.radio.nodes ); +} + +hq_is_contested() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( level.radio.baseorigin, 0 ); + if ( isDefined( enemy ) ) + { + return distancesquared( enemy.origin, level.radio.baseorigin ) < ( level.radio.node_radius * level.radio.node_radius ); + } +} diff --git a/patch_mp/maps/mp/bots/_bot_koth.gsc b/patch_mp/maps/mp/bots/_bot_koth.gsc new file mode 100644 index 0000000..a3de5fa --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_koth.gsc @@ -0,0 +1,308 @@ +#include maps/mp/bots/_bot_combat; +#include maps/mp/_utility; +#include common_scripts/utility; +#include maps/mp/gametypes/koth; + +bot_koth_think() +{ + if ( !isDefined( level.zone.trig.goal_radius ) ) + { + maxs = level.zone.trig getmaxs(); + maxs = level.zone.trig.origin + maxs; + level.zone.trig.goal_radius = distance( level.zone.trig.origin, maxs ); +/# + println( "distance: " + level.zone.trig.goal_radius ); +#/ + ground = bullettrace( level.zone.gameobject.curorigin, level.zone.gameobject.curorigin - vectorScale( ( 0, 0, 1 ), 1024 ), 0, undefined ); + level.zone.trig.goal = ground[ "position" ] + vectorScale( ( 0, 0, 1 ), 8 ); + } + if ( !bot_has_hill_goal() ) + { + self bot_move_to_hill(); + } + if ( self bot_is_at_hill() ) + { + self bot_capture_hill(); + } + bot_hill_tactical_insertion(); + bot_hill_grenade(); +} + +bot_has_hill_goal() +{ + origin = self getgoal( "koth_hill" ); + if ( isDefined( origin ) ) + { + if ( distance2dsquared( level.zone.gameobject.curorigin, origin ) < ( level.zone.trig.goal_radius * level.zone.trig.goal_radius ) ) + { + return 1; + } + } + return 0; +} + +bot_is_at_hill() +{ + return self atgoal( "koth_hill" ); +} + +bot_move_to_hill() +{ + if ( getTime() < ( self.bot.update_objective + 4000 ) ) + { + return; + } + self clearlookat(); + self cancelgoal( "koth_hill" ); + if ( self getstance() == "prone" ) + { + self setstance( "crouch" ); + wait 0,25; + } + if ( self getstance() == "crouch" ) + { + self setstance( "stand" ); + wait 0,25; + } + nodes = getnodesinradiussorted( level.zone.trig.goal, level.zone.trig.goal_radius, 0, 128 ); + _a80 = nodes; + _k80 = getFirstArrayKey( _a80 ); + while ( isDefined( _k80 ) ) + { + node = _a80[ _k80 ]; + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "koth_hill", node.origin, 64 ) == 0 ) + { + if ( findpath( self.origin, node.origin, self, 0, 1 ) ) + { + self addgoal( node, 24, 3, "koth_hill" ); + self.bot.update_objective = getTime(); + return; + } + } + _k80 = getNextArrayKey( _a80, _k80 ); + } +} + +bot_get_look_at() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + } + enemies = self maps/mp/bots/_bot::bot_get_enemies( 0 ); + if ( enemies.size ) + { + enemy = random( enemies ); + } + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + } + spawn = random( level.spawnpoints ); + node = getvisiblenode( self.origin, spawn.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 1024 ) + { + return node.origin; + } + return level.zone.gameobject.curorigin; +} + +bot_capture_hill() +{ + self addgoal( self.origin, 24, 3, "koth_hill" ); + self setstance( "crouch" ); + if ( getTime() > self.bot.update_lookat ) + { + origin = self bot_get_look_at(); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + while ( cointoss() && lengthsquared( self getvelocity() ) < 2 ) + { + nodes = getnodesinradius( level.zone.trig.goal, level.zone.trig.goal_radius + 128, 0, 128 ); + i = randomintrange( 0, nodes.size ); + while ( i < nodes.size ) + { + node = nodes[ i ]; + if ( distancesquared( node.origin, self.origin ) > 1024 ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "koth_hill", node.origin, 128 ) == 0 ) + { + if ( findpath( self.origin, node.origin, self, 0, 1 ) ) + { + self addgoal( node, 24, 3, "koth_hill" ); + self.bot.update_objective = getTime(); + break; + } + } + } + else + { + i++; + } + } + } + self.bot.update_lookat = getTime() + randomintrange( 1500, 3000 ); + } +} + +any_other_team_touching( skip_team ) +{ + _a194 = level.teams; + _k194 = getFirstArrayKey( _a194 ); + while ( isDefined( _k194 ) ) + { + team = _a194[ _k194 ]; + if ( team == skip_team ) + { + } + else + { + if ( level.zone.gameobject.numtouching[ team ] ) + { + return 1; + } + } + _k194 = getNextArrayKey( _a194, _k194 ); + } + return 0; +} + +is_hill_contested( skip_team ) +{ + if ( any_other_team_touching( skip_team ) ) + { + return 1; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( level.zone.gameobject.curorigin, 1 ); + if ( isDefined( enemy ) && distancesquared( enemy.origin, level.zone.gameobject.curorigin ) < 262144 ) + { + return 1; + } + return 0; +} + +bot_hill_grenade() +{ + enemies = bot_get_enemies(); + if ( !enemies.size ) + { + return; + } + if ( self atgoal( "hill_patrol" ) || self atgoal( "koth_hill" ) ) + { + if ( self getweaponammostock( "proximity_grenade_mp" ) > 0 ) + { + origin = bot_get_look_at(); + if ( self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ) ) + { + return; + } + } + } + if ( !is_hill_contested( self.team ) ) + { + if ( !isDefined( level.next_smoke_time ) ) + { + level.next_smoke_time = 0; + } + if ( getTime() > level.next_smoke_time ) + { + if ( self maps/mp/bots/_bot_combat::bot_combat_throw_smoke( level.zone.gameobject.curorigin ) ) + { + level.next_smoke_time = getTime() + randomintrange( 60000, 120000 ); + } + } + return; + } + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( level.zone.gameobject.curorigin, 0 ); + if ( isDefined( enemy ) ) + { + origin = enemy.origin; + } + else + { + origin = level.zone.gameobject.curorigin; + } + dir = vectornormalize( self.origin - origin ); + dir = ( 0, dir[ 1 ], 0 ); + origin += vectorScale( dir, 128 ); + if ( maps/mp/bots/_bot::bot_get_difficulty() == "easy" ) + { + if ( !isDefined( level.next_grenade_time ) ) + { + level.next_grenade_time = 0; + } + if ( getTime() > level.next_grenade_time ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( origin ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_tactical( origin ); + } + else + { + level.next_grenade_time = getTime() + randomintrange( 60000, 120000 ); + } + } + } + else + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( origin ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_tactical( origin ); + } + } +} + +bot_hill_tactical_insertion() +{ + if ( !self hasweapon( "tactical_insertion_mp" ) ) + { + return; + } + dist = self getlookaheaddist(); + dir = self getlookaheaddir(); + if ( !isDefined( dist ) || !isDefined( dir ) ) + { + return; + } + node = hill_nearest_node(); + mine = getnearestnode( self.origin ); + if ( isDefined( mine ) && !nodesvisible( mine, node ) ) + { + origin = self.origin + vectorScale( dir, dist ); + next = getnearestnode( origin ); + if ( isDefined( next ) && nodesvisible( next, node ) ) + { + bot_combat_tactical_insertion( self.origin ); + } + } +} + +hill_nearest_node() +{ + nodes = getnodesinradiussorted( level.zone.gameobject.curorigin, 256, 0 ); +/# + assert( nodes.size ); +#/ + return nodes[ 0 ]; +} diff --git a/patch_mp/maps/mp/bots/_bot_loadout.gsc b/patch_mp/maps/mp/bots/_bot_loadout.gsc new file mode 100644 index 0000000..039dd8a --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_loadout.gsc @@ -0,0 +1,609 @@ +#include maps/mp/gametypes/_rank; +#include maps/mp/bots/_bot; +#include maps/mp/_utility; +#include common_scripts/utility; + +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; + } + else + { + player thread on_bot_connect(); + } + } +} + +on_bot_connect() +{ + self endon( "disconnect" ); + if ( isDefined( self.pers[ "bot_loadout" ] ) ) + { + return; + } + wait 0,1; + if ( ( self getentitynumber() % 2 ) == 0 ) + { + wait 0,05; + } + self maps/mp/bots/_bot::bot_set_rank(); + 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; + i = 1; + while ( i <= 3 ) + { + if ( self isitemlocked( maps/mp/gametypes/_rank::getitemindex( "feature_allocation_slot_" + i ) ) ) + { + max_allocation--; + + } + i++; + } + self bot_construct_loadout( max_allocation ); + self.pers[ "bot_loadout" ] = 1; +} + +bot_construct_loadout( allocation_max ) +{ + if ( self isitemlocked( maps/mp/gametypes/_rank::getitemindex( "feature_cac" ) ) ) + { + return; + } + pixbeginevent( "bot_construct_loadout" ); + item_list = bot_build_item_list(); + bot_construct_class( 0, item_list, allocation_max ); + bot_construct_class( 1, item_list, allocation_max ); + bot_construct_class( 2, item_list, allocation_max ); + bot_construct_class( 3, item_list, allocation_max ); + bot_construct_class( 4, item_list, allocation_max ); + killstreaks = item_list[ "killstreak1" ]; + if ( isDefined( item_list[ "killstreak2" ] ) ) + { + killstreaks = arraycombine( killstreaks, item_list[ "killstreak2" ], 1, 0 ); + } + if ( isDefined( item_list[ "killstreak3" ] ) ) + { + killstreaks = arraycombine( killstreaks, item_list[ "killstreak3" ], 1, 0 ); + } + if ( isDefined( killstreaks ) && killstreaks.size ) + { + bot_choose_weapon( 0, killstreaks ); + bot_choose_weapon( 0, killstreaks ); + bot_choose_weapon( 0, killstreaks ); + } + self.claimed_items = undefined; + pixendevent(); +} + +bot_construct_class( class, items, allocation_max ) +{ + allocation = 0; + claimed_count = bot_build_claimed_list( items ); + self.claimed_items = []; + while ( allocation < allocation_max ) + { + secondary_chance = 40; + remaining = allocation_max - allocation; + if ( remaining >= 1 && bot_make_choice( 95, claimed_count[ "primary" ], 1 ) ) + { + weapon = bot_choose_weapon( class, items[ "primary" ] ); + claimed_count[ "primary" ]++; + allocation++; + bot_choose_weapon_option( class, "camo", 0 ); + bot_choose_weapon_option( class, "reticle", 0 ); + allocation += bot_choose_primary_attachments( class, weapon, allocation, allocation_max ); + } + else + { + if ( !claimed_count[ "primary" ] ) + { + secondary_chance = 100; + } + } + remaining = allocation_max - allocation; + if ( remaining >= 1 && bot_make_choice( secondary_chance, claimed_count[ "secondary" ], 1 ) ) + { + if ( remaining >= 2 && randomint( 100 ) < 10 ) + { + self botclassadditem( class, "BONUSCARD_OVERKILL" ); + weapon = bot_choose_weapon( class, items[ "primary" ] ); + allocation++; + allocation++; + continue; + } + else + { + weapon = bot_choose_weapon( class, items[ "secondary" ] ); + bot_choose_weapon_option( class, "camo", 1 ); + } + allocation++; + claimed_count[ "secondary" ]++; + allocation += bot_choose_secondary_attachments( class, weapon, allocation, allocation_max ); + } + perks_chance = 50; + lethal_chance = 30; + tactical_chance = 20; + if ( claimed_count[ "specialty1" ] && claimed_count[ "specialty2" ] && claimed_count[ "specialty3" ] ) + { + perks_chance = 0; + } + if ( claimed_count[ "primarygrenade" ] ) + { + lethal_chance = 0; + } + if ( claimed_count[ "specialgrenade" ] ) + { + tactical_chance = 0; + } + if ( ( perks_chance + lethal_chance + tactical_chance ) <= 0 ) + { + return; + } + next_action = bot_chose_action( "perks", perks_chance, "lethal", lethal_chance, "tactical", tactical_chance, "none", 0 ); + if ( next_action == "perks" ) + { + remaining = allocation_max - allocation; + if ( remaining >= 3 && !claimed_count[ "specialty1" ] && randomint( 100 ) < 25 ) + { + self botclassadditem( class, "BONUSCARD_PERK_1_GREED" ); + bot_choose_weapon( class, items[ "specialty1" ] ); + bot_choose_weapon( class, items[ "specialty1" ] ); + claimed_count[ "specialty1" ] = 2; + allocation += 3; + } + remaining = allocation_max - allocation; + if ( remaining >= 3 && !claimed_count[ "specialty2" ] && randomint( 100 ) < 25 ) + { + self botclassadditem( class, "BONUSCARD_PERK_2_GREED" ); + bot_choose_weapon( class, items[ "specialty2" ] ); + bot_choose_weapon( class, items[ "specialty2" ] ); + claimed_count[ "specialty2" ] = 2; + allocation += 3; + } + remaining = allocation_max - allocation; + if ( remaining >= 3 && !claimed_count[ "specialty3" ] && randomint( 100 ) < 25 ) + { + self botclassadditem( class, "BONUSCARD_PERK_3_GREED" ); + bot_choose_weapon( class, items[ "specialty3" ] ); + bot_choose_weapon( class, items[ "specialty3" ] ); + claimed_count[ "specialty3" ] = 2; + allocation += 3; + } + remaining = allocation_max - allocation; + i = 0; + while ( i < 3 ) + { + perks = []; + remaining = allocation_max - allocation; + if ( remaining > 0 ) + { + if ( !claimed_count[ "specialty1" ] ) + { + perks[ perks.size ] = "specialty1"; + } + if ( !claimed_count[ "specialty2" ] ) + { + perks[ perks.size ] = "specialty2"; + } + if ( !claimed_count[ "specialty3" ] ) + { + perks[ perks.size ] = "specialty3"; + } + if ( perks.size ) + { + perk = random( perks ); + bot_choose_weapon( class, items[ perk ] ); + claimed_count[ perk ]++; + allocation++; + i++; + continue; + } + else + { + } + } + else i++; + } + } + else if ( next_action == "lethal" ) + { + remaining = allocation_max - allocation; + if ( remaining >= 2 && randomint( 100 ) < 50 ) + { + if ( !claimed_count[ "primarygrenade" ] ) + { + bot_choose_weapon( class, items[ "primarygrenade" ] ); + claimed_count[ "primarygrenade" ]++; + allocation++; + } + self botclassadditem( class, "BONUSCARD_DANGER_CLOSE" ); + allocation++; + } + else + { + if ( remaining >= 1 && !claimed_count[ "primarygrenade" ] ) + { + bot_choose_weapon( class, items[ "primarygrenade" ] ); + claimed_count[ "primarygrenade" ]++; + allocation++; + } + } + continue; + } + else if ( next_action == "tactical" ) + { + remaining = allocation_max - allocation; + if ( remaining >= 2 && !claimed_count[ "specialgrenade" ] && randomint( 100 ) < 50 ) + { + weapon = bot_choose_weapon( class, items[ "specialgrenade" ] ); + if ( weapon == "WEAPON_TACTICAL_INSERTION" || weapon == "WEAPON_WILLY_PETE" ) + { + claimed_count[ "specialgrenade" ] = 1; + allocation += 1; + } + else + { + self botclassadditem( class, weapon ); + claimed_count[ "specialgrenade" ] = 2; + allocation += 2; + } + break; + } + else + { + if ( remaining >= 1 && !claimed_count[ "specialgrenade" ] ) + { + bot_choose_weapon( class, items[ "specialgrenade" ] ); + claimed_count[ "specialgrenade" ]++; + allocation++; + } + } + } + } +} + +bot_make_choice( chance, claimed, max_claim ) +{ + if ( claimed < max_claim ) + { + return randomint( 100 ) < chance; + } +} + +bot_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 = []; + i = 0; + while ( i < chance1 ) + { + actions[ actions.size ] = action1; + i++; + } + i = 0; + while ( i < chance2 ) + { + actions[ actions.size ] = action2; + i++; + } + i = 0; + while ( i < chance3 ) + { + actions[ actions.size ] = action3; + i++; + } + i = 0; + while ( i < chance4 ) + { + actions[ actions.size ] = action4; + i++; + } + return random( actions ); +} + +bot_item_is_claimed( item ) +{ + _a370 = self.claimed_items; + _k370 = getFirstArrayKey( _a370 ); + while ( isDefined( _k370 ) ) + { + claim = _a370[ _k370 ]; + if ( claim == item ) + { + return 1; + } + _k370 = getNextArrayKey( _a370, _k370 ); + } + return 0; +} + +bot_choose_weapon( class, items ) +{ + if ( !isDefined( items ) || !items.size ) + { + return undefined; + } + start = randomint( items.size ); + i = 0; + while ( i < items.size ) + { + weapon = items[ start ]; + if ( !bot_item_is_claimed( weapon ) ) + { + break; + } + else + { + start = ( start + 1 ) % items.size; + i++; + } + } + self.claimed_items[ self.claimed_items.size ] = weapon; + self botclassadditem( class, weapon ); + return weapon; +} + +bot_build_weapon_options_list( optiontype ) +{ + level.botweaponoptionsid[ optiontype ] = []; + level.botweaponoptionsprob[ optiontype ] = []; + prob = 0; + row = 0; + while ( row < 255 ) + { + if ( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 1 ) == optiontype ) + { + index = level.botweaponoptionsid[ optiontype ].size; + level.botweaponoptionsid[ optiontype ][ index ] = int( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 0 ) ); + prob += int( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 15 ) ); + level.botweaponoptionsprob[ optiontype ][ index ] = prob; + } + row++; + } +} + +bot_choose_weapon_option( class, optiontype, primary ) +{ + if ( !isDefined( level.botweaponoptionsid ) ) + { + level.botweaponoptionsid = []; + level.botweaponoptionsprob = []; + bot_build_weapon_options_list( "camo" ); + bot_build_weapon_options_list( "reticle" ); + } + if ( !level.onlinegame && !level.systemlink ) + { + return; + } + 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 ) ); + i = 0; + while ( i < numoptions ) + { + if ( level.botweaponoptionsprob[ optiontype ][ i ] > rnd ) + { + self botclasssetweaponoption( class, primary, optiontype, level.botweaponoptionsid[ optiontype ][ i ] ); + return; + } + else + { + i++; + } + } +} + +bot_choose_primary_attachments( class, weapon, allocation, allocation_max ) +{ + attachments = getweaponattachments( weapon ); + remaining = allocation_max - allocation; + if ( !attachments.size || !remaining ) + { + return 0; + } + attachment_action = bot_chose_action( "3_attachments", 25, "2_attachments", 35, "1_attachments", 35, "none", 5 ); + if ( remaining >= 4 && attachment_action == "3_attachments" ) + { + a1 = random( attachments ); + self botclassaddattachment( class, weapon, a1, "primaryattachment1" ); + count = 1; + attachments = getweaponattachments( weapon, a1 ); + if ( attachments.size ) + { + a2 = random( attachments ); + self botclassaddattachment( class, weapon, a2, "primaryattachment2" ); + count++; + attachments = getweaponattachments( weapon, a1, a2 ); + if ( attachments.size ) + { + a3 = random( attachments ); + self botclassadditem( class, "BONUSCARD_PRIMARY_GUNFIGHTER" ); + self botclassaddattachment( class, weapon, a3, "primaryattachment3" ); + return 4; + } + } + return count; + } + else + { + if ( remaining >= 2 && attachment_action == "2_attachments" ) + { + a1 = random( attachments ); + self botclassaddattachment( class, weapon, a1, "primaryattachment1" ); + attachments = getweaponattachments( weapon, a1 ); + if ( attachments.size ) + { + a2 = random( attachments ); + self botclassaddattachment( class, weapon, a2, "primaryattachment2" ); + return 2; + } + return 1; + } + else + { + if ( remaining >= 1 && attachment_action == "1_attachments" ) + { + a = random( attachments ); + self botclassaddattachment( class, weapon, a, "primaryattachment1" ); + return 1; + } + } + } + return 0; +} + +bot_choose_secondary_attachments( class, weapon, allocation, allocation_max ) +{ + attachments = getweaponattachments( weapon ); + remaining = allocation_max - allocation; + if ( !attachments.size || !remaining ) + { + return 0; + } + attachment_action = bot_chose_action( "2_attachments", 10, "1_attachments", 40, "none", 50, "none", 0 ); + if ( remaining >= 3 && attachment_action == "2_attachments" ) + { + a1 = random( attachments ); + self botclassaddattachment( class, weapon, a1, "secondaryattachment1" ); + attachments = getweaponattachments( weapon, a1 ); + if ( attachments.size ) + { + a2 = random( attachments ); + self botclassadditem( class, "BONUSCARD_SECONDARY_GUNFIGHTER" ); + self botclassaddattachment( class, weapon, a2, "secondaryattachment2" ); + return 3; + } + return 1; + } + else + { + if ( remaining >= 1 && attachment_action == "1_attachments" ) + { + a = random( attachments ); + self botclassaddattachment( class, weapon, a, "secondaryattachment1" ); + return 1; + } + } + return 0; +} + +bot_build_item_list() +{ + pixbeginevent( "bot_build_item_list" ); + items = []; + i = 0; + while ( i < 256 ) + { + row = tablelookuprownum( level.statstableid, 0, i ); + if ( row > -1 ) + { + slot = tablelookupcolumnforrow( level.statstableid, row, 13 ); + if ( slot == "" ) + { + i++; + continue; + } + else number = int( tablelookupcolumnforrow( level.statstableid, row, 0 ) ); + if ( self isitemlocked( number ) ) + { + i++; + continue; + } + else allocation = int( tablelookupcolumnforrow( level.statstableid, row, 12 ) ); + if ( allocation < 0 ) + { + i++; + continue; + } + else name = tablelookupcolumnforrow( level.statstableid, row, 3 ); + if ( bot_item_is_banned( slot, name ) ) + { + i++; + continue; + } + else + { + if ( !isDefined( items[ slot ] ) ) + { + items[ slot ] = []; + } + items[ slot ][ items[ slot ].size ] = name; + } + } + i++; + } + pixendevent(); + return items; +} + +bot_item_is_banned( slot, item ) +{ + if ( item == "WEAPON_KNIFE_BALLISTIC" ) + { + return 1; + } + if ( getDvarInt( #"97A055DA" ) == 0 && item == "WEAPON_PEACEKEEPER" ) + { + return 1; + } + if ( slot != "killstreak1" && slot != "killstreak2" && slot != "killstreak3" ) + { + return 0; + } + _a633 = level.bot_banned_killstreaks; + _k633 = getFirstArrayKey( _a633 ); + while ( isDefined( _k633 ) ) + { + banned = _a633[ _k633 ]; + if ( item == banned ) + { + return 1; + } + _k633 = getNextArrayKey( _a633, _k633 ); + } + return 0; +} + +bot_build_claimed_list( items ) +{ + claimed = []; + keys = getarraykeys( items ); + _a649 = keys; + _k649 = getFirstArrayKey( _a649 ); + while ( isDefined( _k649 ) ) + { + key = _a649[ _k649 ]; + claimed[ key ] = 0; + _k649 = getNextArrayKey( _a649, _k649 ); + } + return claimed; +} diff --git a/patch_mp/maps/mp/bots/_bot_sd.gsc b/patch_mp/maps/mp/bots/_bot_sd.gsc new file mode 100644 index 0000000..e6c182a --- /dev/null +++ b/patch_mp/maps/mp/bots/_bot_sd.gsc @@ -0,0 +1,466 @@ +#include maps/mp/gametypes/_globallogic_utils; +#include maps/mp/bots/_bot_combat; +#include maps/mp/gametypes/_gameobjects; +#include maps/mp/_utility; +#include common_scripts/utility; + +bot_sd_think() +{ + _a8 = level.bombzones; + _k8 = getFirstArrayKey( _a8 ); + while ( isDefined( _k8 ) ) + { + zone = _a8[ _k8 ]; + if ( !isDefined( zone.nearest_node ) ) + { + nodes = getnodesinradiussorted( zone.trigger.origin, 256, 0 ); +/# + assert( nodes.size ); +#/ + zone.nearest_node = nodes[ 0 ]; + } + _k8 = getNextArrayKey( _a8, _k8 ); + } + zone = sd_get_planted_zone(); + if ( isDefined( zone ) ) + { + self bot_sd_defender( zone, 1 ); + } + else if ( self.team == game[ "attackers" ] ) + { + if ( level.multibomb ) + { + self.isbombcarrier = 1; + } + self bot_sd_attacker(); + } + else + { + zone = random( level.bombzones ); + self bot_sd_defender( zone ); + } +} + +bot_sd_attacker() +{ + level endon( "game_ended" ); + if ( !level.multibomb && !isDefined( level.sdbomb.carrier ) && !level.bombplanted ) + { + self cancelgoal( "sd_protect_carrier" ); + if ( !level.sdbomb maps/mp/gametypes/_gameobjects::isobjectawayfromhome() ) + { + if ( !self maps/mp/bots/_bot::bot_friend_goal_in_radius( "sd_pickup", level.sdbomb.curorigin, 64 ) ) + { + self addgoal( level.sdbomb.curorigin, 16, 4, "sd_pickup" ); + return; + } + } + else + { + self addgoal( level.sdbomb.curorigin, 16, 4, "sd_pickup" ); + return; + } + } + else + { + self cancelgoal( "sd_pickup" ); + } + if ( is_true( self.isbombcarrier ) ) + { + goal = self getgoal( "sd_plant" ); + if ( isDefined( goal ) ) + { + if ( distancesquared( self.origin, goal ) < 2304 ) + { + self setstance( "prone" ); + wait 0,5; + self pressusebutton( level.planttime + 1 ); + wait 0,5; + if ( is_true( self.isplanting ) ) + { + wait ( level.planttime + 1 ); + } + self pressusebutton( 0 ); + self setstance( "crouch" ); + wait 0,25; + self cancelgoal( "sd_plant" ); + self setstance( "stand" ); + } + return; + } + else + { + if ( getTime() > self.bot[ "patrol_update" ] ) + { + frac = sd_get_time_frac(); + if ( randomint( 100 ) < ( frac * 100 ) || frac > 0,85 ) + { + zone = sd_get_closest_bomb(); + goal = sd_get_bomb_goal( zone.visuals[ 0 ] ); + if ( isDefined( goal ) ) + { + if ( frac > 0,85 ) + { + self addgoal( goal, 24, 4, "sd_plant" ); + } + else + { + self addgoal( goal, 24, 3, "sd_plant" ); + } + } + } + self.bot[ "patrol_update" ] = getTime() + randomintrange( 2500, 5000 ); + } + } + } + else + { + if ( isDefined( level.sdbomb.carrier ) && !isplayer( level.sdbomb.carrier ) ) + { + if ( !isDefined( self.protectcarrier ) ) + { + if ( randomint( 100 ) > 70 ) + { + self.protectcarrier = 1; + } + else + { + self.protectcarrier = 0; + } + } + if ( self.protectcarrier ) + { + goal = level.sdbomb.carrier getgoal( "sd_plant" ); + if ( isDefined( goal ) ) + { + nodes = getnodesinradiussorted( goal, 256, 0 ); + if ( isDefined( nodes ) && nodes.size > 0 && !isDefined( self getgoal( "sd_protect_carrier" ) ) ) + { + self addgoal( nodes[ randomint( nodes.size ) ], 24, 3, "sd_protect_carrier" ); + } + } + } + } + } +} + +bot_sd_defender( zone, isplanted ) +{ + bot_sd_grenade(); + while ( isDefined( isplanted ) && isplanted && self hasgoal( "sd_defend" ) ) + { + goal = self getgoal( "sd_defend" ); + planted = sd_get_planted_zone(); + _a159 = level.bombzones; + _k159 = getFirstArrayKey( _a159 ); + while ( isDefined( _k159 ) ) + { + zone = _a159[ _k159 ]; + if ( planted != zone && distance2d( goal, zone.nearest_node.origin ) < distance2d( goal, planted.nearest_node.origin ) ) + { + self cancelgoal( "sd_defend" ); + } + _k159 = getNextArrayKey( _a159, _k159 ); + } + } + if ( self atgoal( "sd_defend" ) || self bot_need_to_defuse() ) + { + bot_sd_defender_think( zone ); + if ( self hasgoal( "sd_defend" ) ) + { + return; + } + } + if ( self hasgoal( "enemy_patrol" ) ) + { + goal = self getgoal( "enemy_patrol" ); + closezone = sd_get_closest_bomb(); + if ( distancesquared( goal, closezone.nearest_node.origin ) < 262144 ) + { + self clearlookat(); + self cancelgoal( "sd_defend" ); + return; + } + } + if ( self hasgoal( "sd_defend" ) ) + { + self.bot[ "patrol_update" ] = getTime() + randomintrange( 2500, 5000 ); + return; + } + if ( self hasgoal( "enemy_patrol" ) ) + { + return; + } + nodes = getvisiblenodes( zone.nearest_node ); + best = undefined; + highest = -100; + _a208 = nodes; + _k208 = getFirstArrayKey( _a208 ); + while ( isDefined( _k208 ) ) + { + node = _a208[ _k208 ]; + if ( node.type == "BAD NODE" ) + { + } + else if ( !canclaimnode( node, self.team ) ) + { + } + else if ( distancesquared( node.origin, self.origin ) < 65536 ) + { + } + else if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "sd_defend", node.origin, 256 ) > 0 ) + { + } + else + { + height = node.origin[ 2 ] - zone.nearest_node.origin[ 2 ]; + if ( isDefined( isplanted ) && isplanted ) + { + dist = distance2d( node.origin, zone.nearest_node.origin ); + score = ( 10000 - dist ) + height; + } + else + { + score = height; + } + if ( score > highest ) + { + highest = score; + best = node; + } + } + _k208 = getNextArrayKey( _a208, _k208 ); + } + if ( !isDefined( best ) ) + { + return; + } + self addgoal( best, 24, 3, "sd_defend" ); +} + +bot_get_look_at() +{ + enemy = self maps/mp/bots/_bot::bot_get_closest_enemy( self.origin, 1 ); + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + enemies = self maps/mp/bots/_bot::bot_get_enemies( 0 ); + if ( enemies.size ) + { + enemy = random( enemies ); + } + if ( isDefined( enemy ) ) + { + node = getvisiblenode( self.origin, enemy.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + } + zone = sd_get_closest_bomb(); + node = getvisiblenode( self.origin, zone.nearest_node.origin ); + if ( isDefined( node ) && distancesquared( self.origin, node.origin ) > 16384 ) + { + return node.origin; + } + forward = anglesToForward( self getplayerangles() ); + origin = self geteye() + ( forward * 1024 ); + return origin; +} + +bot_sd_defender_think( zone ) +{ + if ( self bot_need_to_defuse() ) + { + if ( self maps/mp/bots/_bot::bot_friend_goal_in_radius( "sd_defuse", level.sdbombmodel.origin, 16 ) > 0 ) + { + return; + } + self clearlookat(); + goal = self getgoal( "sd_defuse" ); + if ( isDefined( goal ) && distancesquared( self.origin, goal ) < 2304 ) + { + self setstance( "prone" ); + wait 0,5; + self pressusebutton( level.defusetime + 1 ); + wait 0,5; + if ( is_true( self.isdefusing ) ) + { + wait ( level.defusetime + 1 ); + } + self pressusebutton( 0 ); + self setstance( "crouch" ); + wait 0,25; + self cancelgoal( "sd_defuse" ); + self setstance( "stand" ); + return; + } + if ( !isDefined( goal ) && distance2dsquared( self.origin, level.sdbombmodel.origin ) < 1000000 ) + { + self addgoal( level.sdbombmodel.origin, 24, 4, "sd_defuse" ); + } + return; + } + if ( getTime() > self.bot[ "patrol_update" ] ) + { + if ( cointoss() ) + { + self clearlookat(); + self cancelgoal( "sd_defend" ); + return; + } + self.bot[ "patrol_update" ] = getTime() + randomintrange( 2500, 5000 ); + } + if ( self hasgoal( "enemy_patrol" ) ) + { + goal = self getgoal( "enemy_patrol" ); + zone = sd_get_closest_bomb(); + if ( distancesquared( goal, zone.nearest_node.origin ) < 262144 ) + { + self clearlookat(); + self cancelgoal( "sd_defend" ); + return; + } + } + if ( getTime() > self.bot[ "lookat_update" ] ) + { + origin = self bot_get_look_at(); + z = 20; + if ( distancesquared( origin, self.origin ) > 262144 ) + { + z = randomintrange( 16, 60 ); + } + self lookat( origin + ( 0, 0, z ) ); + self.bot[ "lookat_update" ] = getTime() + randomintrange( 1500, 3000 ); + if ( distancesquared( origin, self.origin ) > 65536 ) + { + dir = vectornormalize( self.origin - origin ); + dir = vectorScale( dir, 256 ); + origin += dir; + } + self maps/mp/bots/_bot_combat::bot_combat_throw_proximity( origin ); + } +} + +bot_need_to_defuse() +{ + if ( level.bombplanted ) + { + return self.team == game[ "defenders" ]; + } +} + +sd_get_bomb_goal( ent ) +{ + goals = []; + dir = anglesToForward( ent.angles ); + dir = vectorScale( dir, 32 ); + goals[ 0 ] = ent.origin + dir; + goals[ 1 ] = ent.origin - dir; + dir = anglesToRight( ent.angles ); + dir = vectorScale( dir, 48 ); + goals[ 2 ] = ent.origin + dir; + goals[ 3 ] = ent.origin - dir; + goals = array_randomize( goals ); + _a419 = goals; + _k419 = getFirstArrayKey( _a419 ); + while ( isDefined( _k419 ) ) + { + goal = _a419[ _k419 ]; + if ( findpath( self.origin, goal, 0 ) ) + { + return goal; + } + _k419 = getNextArrayKey( _a419, _k419 ); + } + return undefined; +} + +sd_get_time_frac() +{ + remaining = maps/mp/gametypes/_globallogic_utils::gettimeremaining(); + end = ( level.timelimit * 60 ) * 1000; + if ( end == 0 ) + { + end = self.spawntime + 120000; + remaining = end - getTime(); + } + return 1 - ( remaining / end ); +} + +sd_get_closest_bomb() +{ + best = undefined; + distsq = 9999999; + _a450 = level.bombzones; + _k450 = getFirstArrayKey( _a450 ); + while ( isDefined( _k450 ) ) + { + zone = _a450[ _k450 ]; + d = distancesquared( self.origin, zone.curorigin ); + if ( !isDefined( best ) ) + { + best = zone; + distsq = d; + } + else + { + if ( d < distsq ) + { + best = zone; + distsq = d; + } + } + _k450 = getNextArrayKey( _a450, _k450 ); + } + return best; +} + +sd_get_planted_zone() +{ + while ( level.bombplanted ) + { + _a475 = level.bombzones; + _k475 = getFirstArrayKey( _a475 ); + while ( isDefined( _k475 ) ) + { + zone = _a475[ _k475 ]; + if ( zone.interactteam == "none" ) + { + return zone; + } + _k475 = getNextArrayKey( _a475, _k475 ); + } + } + return undefined; +} + +bot_sd_grenade() +{ + enemies = bot_get_enemies(); + if ( !enemies.size ) + { + return; + } + zone = sd_get_closest_bomb(); + _a498 = enemies; + _k498 = getFirstArrayKey( _a498 ); + while ( isDefined( _k498 ) ) + { + enemy = _a498[ _k498 ]; + if ( distancesquared( enemy.origin, zone.nearest_node.origin ) < 147456 ) + { + if ( !self maps/mp/bots/_bot_combat::bot_combat_throw_lethal( enemy.origin ) ) + { + self maps/mp/bots/_bot_combat::bot_combat_throw_tactical( enemy.origin ); + } + return; + } + _k498 = getNextArrayKey( _a498, _k498 ); + } +}