diff --git a/scripts/mp/bots.gsc b/scripts/mp/bots.gsc index 3eaa31b..2e26641 100644 --- a/scripts/mp/bots.gsc +++ b/scripts/mp/bots.gsc @@ -1,2229 +1,2479 @@ -/* - _bot - Author: INeedGames - Date: 06/19/2021 - The entry point and manager of the bots. -*/ - -#include maps\mp\gametypes\_globallogic_utils; -#include maps\mp\_utility; -#include common_scripts\utility; - -/* - Replace func stuff -*/ -main() -{ - level.bw_VERSION = "1.1.1"; - - if ( getDvar( "bots_main" ) == "" ) - setDvar( "bots_main", true ); - - if ( !getDvarInt( "bots_main" ) ) - return; - - if ( !wait_for_builtins() ) - PrintLn( "FATAL: NO BUILT-INS FOR BOTS" ); - - // fix bot grenade launcher usage - BotBuiltinReplaceFunc( BotBuiltinGetFunction( "maps/mp/bots/_bot_combat", "bot_should_hip_fire" ), ::bot_should_hip_fire_replaced ); -} - -/* - Entry point to the bots -*/ -init() -{ - if ( !getDvarInt( "bots_main" ) ) - return; - - if ( !wait_for_builtins() ) - PrintLn( "FATAL: NO BUILT-INS FOR BOTS" ); - - if ( getDvar( "bots_main_GUIDs" ) == "" ) - setDvar( "bots_main_GUIDs", "" ); //guids of players who will be given host powers, comma seperated - - if ( getDvar( "bots_main_firstIsHost" ) == "" ) - setDvar( "bots_main_firstIsHost", false ); //first play to connect is a host - - if ( getDvar( "bots_main_kickBotsAtEnd" ) == "" ) - setDvar( "bots_main_kickBotsAtEnd", false ); //kicks the bots at game end (dedis hang with bots on map rotate) - - if ( getDvar( "bots_main_waitForHostTime" ) == "" ) - setDvar( "bots_main_waitForHostTime", 10.0 ); //how long to wait to wait for the host player - - if ( getDvar( "bots_manage_add" ) == "" ) - setDvar( "bots_manage_add", 0 ); //amount of bots to add to the game - - if ( getDvar( "bots_manage_fill" ) == "" ) - setDvar( "bots_manage_fill", 0 ); //amount of bots to maintain - - if ( getDvar( "bots_manage_fill_spec" ) == "" ) - setDvar( "bots_manage_fill_spec", true ); //to count for fill if player is on spec team - - if ( getDvar( "bots_manage_fill_mode" ) == "" ) - setDvar( "bots_manage_fill_mode", 0 ); //fill mode, 0 adds everyone, 1 just bots, 2 maintains at maps, 3 is 2 with 1 - - if ( getDvar( "bots_manage_fill_kick" ) == "" ) - setDvar( "bots_manage_fill_kick", false ); //kick bots if too many - - if ( getDvar( "bots_skill" ) == "" ) // alias for bot_difficulty - setDvar( "bots_skill", "" ); - - if ( getDvar( "bots_team" ) == "" ) - setDvar( "bots_team", "autoassign" ); //which team for bots to join - - if ( getDvar( "bots_team_amount" ) == "" ) - setDvar( "bots_team_amount", 0 ); //amount of bots on axis team - - if ( getDvar( "bots_team_force" ) == "" ) - setDvar( "bots_team_force", false ); //force bots on team - - if ( getDvar( "bots_team_mode" ) == "" ) - setDvar( "bots_team_mode", 0 ); //counts just bots when 1 - - if ( getDvar( "bots_loadout_rank" ) == "" ) // what rank the bots should be around, -1 is around the players, 0 is all random - setDvar( "bots_loadout_rank", -1 ); - - if ( getDvar( "bots_loadout_prestige" ) == "" ) // what pretige the bots will be, -1 is the players, -2 is random - setDvar( "bots_loadout_prestige", -1 ); - - if ( getDvar( "bots_play_nade" ) == "" ) - setDvar( "bots_play_nade", true ); - - if ( getDvar( "bots_play_aim" ) == "" ) - setDvar( "bots_play_aim", true ); - - if ( getDvar( "bots_play_jumpdrop" ) == "" ) //bots jump and dropshot - setDvar( "bots_play_jumpdrop", true ); - - if ( !isDefined( game["botWarfare"] ) ) - game["botWarfare"] = true; - - thread fixGamemodes(); - - thread onPlayerConnect(); - - thread handleBots(); -} - -/* - Fixes gl usage -*/ -bot_should_hip_fire_replaced() -{ - weapon = self getcurrentweapon(); - class = weaponclass( weapon ); - - if ( class == "grenade" ) - return 1; - - func = BotBuiltinGetFunction( "maps/mp/bots/_bot_combat", "bot_should_hip_fire" ); - BotBuiltinDisableDetourOnce( func ); - return self [[ func ]](); -} - -/* - Adds sd to bot logic -*/ -fixGamemodes() -{ - wait 0.25; - - if ( level.gametype == "sd" ) - level.bot_gametype = ::bot_sd_think; -} - -/* - Starts the threads for bots. -*/ -handleBots() -{ - thread diffBots(); - thread teamBots(); - addBots(); - - while ( !level.intermission ) - wait 0.05; - - setDvar( "bots_manage_add", getBotArray().size ); - - if ( !getDvarInt( "bots_main_kickBotsAtEnd" ) ) - return; - - bots = getBotArray(); - - for ( i = 0; i < bots.size; i++ ) - { - bot = bots[i]; - - if ( isDefined( bot ) ) - kick( bot getEntityNumber() ); - } -} - -/* - Handles the diff of the bots -*/ -diffBots() -{ - for ( ;; ) - { - wait 1.5; - - // we dont use 'bots_skill' so that we can still use the .menu dvar - - if ( getDvar( "bots_skill" ) != "" ) - { - SetDvar( "bot_difficulty", getDvar( "bots_skill" ) ); - setDvar( "bots_skill", "" ); - } - - bot_set_difficulty( getdvarint( "bot_difficulty" ) ); - } -} - -/* - Sets the difficulty of the bots -*/ -bot_set_difficulty( difficulty ) -{ - if ( difficulty == 3 ) - { - 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 == 2 ) - { - 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 == 0 ) - { - 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 - { - if ( difficulty != 1 ) - return; - - 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 == 3 ) - { - setdvar( "bot_MinReactionTime", "400" ); - setdvar( "bot_MaxReactionTime", "500" ); - setdvar( "bot_MinAdsTime", "1000" ); - setdvar( "bot_MaxAdsTime", "2000" ); - } - - if ( ( difficulty == 2 || difficulty == 3 ) && level.gametype == "oic" ) - { - setdvar( "bot_SprintDistance", "256" ); - } - - if ( !getDvarInt( "bots_play_nade" ) ) - SetDvar( "bot_AllowGrenades", "0" ); - - if ( !getDvarInt( "bots_play_aim" ) ) - { - setdvar( "bot_YawSpeed", "0" ); - setdvar( "bot_PitchUp", "0" ); - setdvar( "bot_PitchDown", "0" ); - } - - setDvar( "bot_difficulty", difficulty ); - level.bot_difficulty = undefined; - level maps\mp\bots\_bot::bot_get_difficulty(); -} - -/* - A server thread for monitoring all bot's teams for custom server settings. -*/ -teamBots_loop() -{ - teamAmount = getDvarInt( "bots_team_amount" ); - toTeam = getDvar( "bots_team" ); - - alliesbots = 0; - alliesplayers = 0; - axisbots = 0; - axisplayers = 0; - - playercount = level.players.size; - - for ( i = 0; i < playercount; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) ) - { - if ( player is_bot() ) - { - if ( player.pers["team"] == "allies" ) - alliesbots++; - else if ( player.pers["team"] == "axis" ) - axisbots++; - } - else - { - if ( player.pers["team"] == "allies" ) - alliesplayers++; - else if ( player.pers["team"] == "axis" ) - axisplayers++; - } - } - } - - allies = alliesbots; - axis = axisbots; - - if ( !getDvarInt( "bots_team_mode" ) ) - { - allies += alliesplayers; - axis += axisplayers; - } - - if ( toTeam != "custom" ) - { - if ( getDvarInt( "bots_team_force" ) ) - { - if ( toTeam == "autoassign" ) - { - if ( abs( axis - allies ) > 1 ) - { - toTeam = "axis"; - - if ( axis > allies ) - toTeam = "allies"; - } - } - - if ( toTeam != "autoassign" ) - { - playercount = level.players.size; - - for ( i = 0; i < playercount; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) && player is_bot() && ( player.pers["team"] != toTeam ) ) - { - if ( toTeam == "allies" ) - player thread [[level.teammenu]]( "allies" ); - else if ( toTeam == "axis" ) - player thread [[level.teammenu]]( "axis" ); - else - player thread [[level.spectator]](); - - break; - } - } - } - } - } - else - { - playercount = level.players.size; - - for ( i = 0; i < playercount; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) && player is_bot() ) - { - if ( player.pers["team"] == "axis" ) - { - if ( axis > teamAmount ) - { - player thread [[level.teammenu]]( "allies" ); - break; - } - } - else - { - if ( axis < teamAmount ) - { - player thread [[level.teammenu]]( "axis" ); - break; - } - else if ( player.pers["team"] != "allies" ) - { - player thread [[level.teammenu]]( "allies" ); - break; - } - } - } - } - } -} - -/* - A server thread for monitoring all bot's teams for custom server settings. -*/ -teamBots() -{ - for ( ;; ) - { - wait 1.5; - teamBots_loop(); - } -} - -/* - Loop -*/ -addBots_loop() -{ - botsToAdd = GetDvarInt( "bots_manage_add" ); - - if ( botsToAdd > 0 ) - { - SetDvar( "bots_manage_add", 0 ); - - if ( botsToAdd > 64 ) - botsToAdd = 64; - - for ( ; botsToAdd > 0; botsToAdd-- ) - { - level add_bot(); - wait 0.5; - } - } - - fillMode = getDVarInt( "bots_manage_fill_mode" ); - - if ( fillMode == 2 || fillMode == 3 ) - setDvar( "bots_manage_fill", getGoodMapAmount() ); - - fillAmount = getDvarInt( "bots_manage_fill" ); - - players = 0; - bots = 0; - spec = 0; - - playercount = level.players.size; - - for ( i = 0; i < playercount; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) ) - { - if ( player is_bot() ) - bots++; - else if ( !isDefined( player.team ) ) - spec++; - else if ( player.pers["team"] != "axis" && player.pers["team"] != "allies" ) // dude this compiler is kekware - spec++; - else - players++; - } - } - - if ( fillMode == 4 ) - { - axisplayers = 0; - alliesplayers = 0; - - playercount = level.players.size; - - for ( i = 0; i < playercount; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) && !player is_bot() ) - { - if ( player.pers["team"] == "axis" ) - axisplayers++; - else if ( player.pers["team"] == "allies" ) - alliesplayers++; - } - } - - result = fillAmount - abs( axisplayers - alliesplayers ) + bots; - - if ( players == 0 ) - { - if ( bots < fillAmount ) - result = fillAmount - 1; - else if ( bots > fillAmount ) - result = fillAmount + 1; - else - result = fillAmount; - } - - bots = result; - } - - if ( !randomInt( 999 ) ) - { - setDvar( "testclients_doreload", true ); - wait 0.1; - setDvar( "testclients_doreload", false ); - doExtraCheck(); - } - - amount = bots; - - if ( fillMode == 0 || fillMode == 2 ) - amount += players; - - if ( getDVarInt( "bots_manage_fill_spec" ) ) - amount += spec; - - if ( amount < fillAmount ) - setDvar( "bots_manage_add", 1 ); - else if ( amount > fillAmount && getDvarInt( "bots_manage_fill_kick" ) ) - { - tempBot = getBotToKick(); - - if ( isDefined( tempBot ) ) - kick( tempBot getEntityNumber() ); - } -} - -/* - A server thread for monitoring all bot's in game. Will add and kick bots according to server settings. -*/ -addBots() -{ - level endon ( "game_ended" ); - - bot_wait_for_host(); - - for ( ;; ) - { - wait 1.5; - - addBots_loop(); - } -} - -/* - Adds a bot to the game. -*/ -add_bot() -{ - bot = addtestclient(); - - if ( isdefined( bot ) ) - { - bot.pers["isBot"] = true; - bot.pers["isBotWarfare"] = true; - bot thread added(); - } -} - -/* - Player connects -*/ -onPlayerConnect() -{ - for ( ;; ) - { - level waittill( "connected", player ); - - player thread connected(); - } -} - -/* - Connects -*/ -connected() -{ - self endon( "disconnect" ); - - if ( !isDefined( self.pers ) || !isDefined( self.pers["bot_host"] ) ) - self thread doHostCheck(); - - if ( !self istestclient() ) - return; - - if ( !isDefined( self.pers["isBot"] ) ) - { - self.pers["isBot"] = true; - } - - if ( !isDefined( self.pers["isBotWarfare"] ) ) - { - self.pers["isBotWarfare"] = true; - self thread added(); - } - - self thread teamWatch(); - self thread classWatch(); - self thread onBotSpawned(); - - self thread setRanks(); - - self thread watchBotDebugEvent(); -} - -/* - DEBUG -*/ -watchBotDebugEvent() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - self waittill( "bot_event", msg, str, b, c, d, e, f, g ); - - if ( GetDvarInt( "bots_main_debug" ) >= 2 ) - { - big_str = "Bot Warfare debug: " + self.name + ": " + msg; - - if ( isDefined( str ) && isString( str ) ) - big_str += ", " + str; - - if ( isDefined( b ) && isString( b ) ) - big_str += ", " + b; - - if ( isDefined( c ) && isString( c ) ) - big_str += ", " + c; - - if ( isDefined( d ) && isString( d ) ) - big_str += ", " + d; - - if ( isDefined( e ) && isString( e ) ) - big_str += ", " + e; - - if ( isDefined( f ) && isString( f ) ) - big_str += ", " + f; - - if ( isDefined( g ) && isString( g ) ) - big_str += ", " + g; - - BotBuiltinPrintConsole( big_str ); - } - else if ( msg == "debug" && GetDvarInt( "bots_main_debug" ) ) - { - BotBuiltinPrintConsole( "Bot Warfare debug: " + self.name + ": " + str ); - } - } -} - -/* - When the bot spawns -*/ -onBotSpawned() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - self waittill( "spawned_player" ); - self BotBuiltinClearOverrides( true ); - self BotBuiltinWeaponOverride( self getCurrentWeapon() ); - - self thread watch_for_override_stuff(); - self thread watch_for_melee_override(); - self thread bot_watch_think_mw2(); - self BotNotifyBotEvent( "debug", "we spawned!" ); - - waittillframeend; - - self.bot_first_spawn = undefined; - - if ( randomInt( 100 ) < 2 ) - self.bot_change_class = undefined; - } -} - -/* - Gets a GL -*/ -getValidTube() -{ - weaps = self getweaponslist( true ); - - for ( i = 0; i < weaps.size; i++ ) - { - weap = weaps[i]; - - if ( !self GetAmmoCount( weap ) ) - continue; - - if ( !isstrstart( weap, "gl_" ) ) - continue; - - return weap; - } - - return undefined; -} - -/* - Bots play mw2 -*/ -bot_watch_think_mw2() -{ - self endon( "disconnect" ); - self endon( "death" ); - level endon( "game_ended" ); - - for ( ;; ) - { - wait randomIntRange( 1, 4 ); - - if ( self IsRemoteControlling() ) - continue; - - if ( self maps\mp\bots\_bot_combat::bot_has_enemy() ) - continue; - - tube = self getValidTube(); - - if ( !isDefined( tube ) ) - { - if ( self GetAmmoCount( "usrpg_mp" ) ) - tube = "usrpg_mp"; - else if ( self GetAmmoCount( "smaw_mp" ) ) - tube = "smaw_mp"; - else - return; - } - - if ( self GetCurrentWeapon() == tube ) - return; - - if ( randomInt( 100 ) > 35 ) - return; - - self switchtoweapon( tube ); - } -} - -/* - custom movement stuff -*/ -watch_for_melee_override() -{ - self endon( "disconnect" ); - self endon( "death" ); - - for ( ;; ) - { - while ( ( !self maps\mp\bots\_bot_combat::threat_is_player() && !self maps\mp\bots\_bot_combat::threat_is_dog() ) || self IsRemoteControlling() || !self HasWeapon( "knife_mp" ) || !getDvarInt( "aim_automelee_enabled" ) ) - wait 0.05; - - threat = self.bot.threat.entity; - - while ( isDefined( threat ) && isDefined( self.bot.threat.entity ) && self.bot.threat.entity == threat ) - { - dist = distance( self.origin, threat.origin ); - - if ( self isOnGround() && self GetStance() != "prone" && dist < getDvarFloat( "aim_automelee_range" ) && ( getConeDot( threat.origin, self.origin, self getPlayerAngles() ) > 0.9 || dist < 10 ) ) - { - self BotBuiltinBotMeleeParams( threat getEntityNumber(), dist ); - self BotBuiltinButtonOverride( "melee", "enable" ); - self BotBuiltinAimOverride(); - - time_left = 1; - once = false; - - while ( time_left > 0 && isDefined( threat ) && isAlive( threat ) ) - { - self setPlayerAngles( VectorToAngles( threat getTagOrigin( "j_spine4" ) - self getEye() ) ); - time_left -= 0.05; - wait 0.05; - - if ( !once ) - { - once = true; - self BotBuiltinClearButtonOverride( "melee" ); - } - } - - if ( !once ) - self BotBuiltinClearButtonOverride( "melee" ); - - self BotBuiltinClearMeleeParams(); - self BotBuiltinClearAimOverride(); - wait 1; - break; - } - - wait 0.05; - } - } -} - -/* - custom movement stuff -*/ -watch_for_override_stuff() -{ - self endon( "disconnect" ); - self endon( "death" ); - - NEAR_DIST = 80; - LONG_DIST = 1000; - SPAM_JUMP_TIME = 5000; - - diff = level maps\mp\bots\_bot::bot_get_difficulty(); - chance = 0; - - if ( diff == "normal" ) - chance = 25; - else if ( diff == "hard" ) - chance = 50; - else if ( diff == "fu" ) - chance = 80; - - last_jump_time = 0; - - if ( !getDvarInt( "bots_play_jumpdrop" ) ) - return; - - for ( ;; ) - { - while ( !self maps\mp\bots\_bot_combat::threat_is_player() || self IsRemoteControlling() ) - wait 0.05; - - threat = self.bot.threat.entity; - dist = Distance( threat.origin, self.origin ); - time = GetTime(); - weap = self GetCurrentWeapon(); - - weapon_is_good = true; - - if ( weap == "none" || !self GetWeaponAmmoClip( weap ) ) - weapon_is_good = false; - - if ( weapon_is_good && ( dist > NEAR_DIST ) && ( dist < LONG_DIST ) && ( randomInt( 100 ) < chance ) && ( ( time - last_jump_time ) > SPAM_JUMP_TIME ) ) - { - if ( randomInt( 2 ) ) - { - if ( ( getConeDot( threat.origin, self.origin, self getPlayerAngles() ) > 0.8 ) && ( dist > ( NEAR_DIST * 2 ) ) ) - { - last_jump_time = time; - - // drop shot - self BotBuiltinMovementOverride( 0, 0 ); - self BotBuiltinButtonOverride( "prone", "enable" ); - - wait 1.5; - - self BotBuiltinClearMovementOverride(); - self BotBuiltinClearButtonOverride( "prone" ); - } - } - else - { - last_jump_time = time; - - // jump shot - self BotBuiltinButtonOverride( "gostand", "enable" ); - wait 0.1; - self BotBuiltinClearButtonOverride( "gostand" ); - } - } - - while ( isDefined( threat ) && isDefined( self.bot.threat.entity ) && ( threat == self.bot.threat.entity ) ) - wait 0.05; - } -} - -/* - Set pres -*/ -setRanks() -{ - self endon( "disconnect" ); - - wait 0.05; - - self setCustomRanks(); - - if ( !level.gameEnded ) - level waittill( "game_ended" ); - - self.pers[ "bot_rankxp" ] = self.pers[ "rankxp" ]; -} - -/* - Sets the rank -*/ -setCustomRanks() -{ - if ( !isDefined( self.pers[ "bot_prestige" ] ) ) - return; - - self.pers[ "prestige" ] = self.pers[ "bot_prestige" ]; - self.pers[ "plevel" ] = self.pers[ "bot_prestige" ]; - - self.pers[ "rankxp" ] = self.pers[ "bot_rankxp" ]; - self.pers[ "rank" ] = self maps\mp\gametypes\_rank::getRankForXp( self.pers[ "rankxp" ] ); - - self setRank( self.pers[ "rank" ], self.pers[ "prestige" ] ); - - self maps\mp\gametypes\_rank::syncxpstat(); -} - -/* - Makes sure the bot is on a team. -*/ -teamWatch() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - while ( !isdefined( self.team ) || !allowTeamChoice() ) - wait .05; - - wait 0.1; - - // multiteam? - if ( self.team != "axis" && self.team != "allies" ) - self notify( "menuresponse", game["menu_team"], getDvar( "bots_team" ) ); - - while ( isdefined( self.team ) ) - wait .05; - } -} - -/* - Selects a class for the bot. -*/ -classWatch() -{ - self endon( "disconnect" ); - - for ( ;; ) - { - while ( !isdefined( self.team ) || !allowClassChoice() ) - wait .05; - - wait 0.5; - - if ( !maps\mp\gametypes\_globallogic_utils::isValidClass( self.class ) || !isDefined( self.bot_change_class ) ) - self notify( "menuresponse", game["menu_changeclass"], self chooseRandomClass() ); - - self.bot_change_class = true; - - while ( isdefined( self.team ) && maps\mp\gametypes\_globallogic_utils::isValidClass( self.class ) && isDefined( self.bot_change_class ) ) - wait .05; - } -} - -/* - Chooses random class -*/ -chooseRandomClass() -{ - if ( level.disablecac ) - { - classes = []; - classes[classes.size] = "class_assault"; - classes[classes.size] = "class_smg"; - classes[classes.size] = "class_lmg"; - classes[classes.size] = "class_cqb"; - classes[classes.size] = "class_sniper"; - return PickRandom( classes ); - } - - return PickRandom( self maps\mp\bots\_bot::bot_build_classes() ); -} - -/* - Bot was added -*/ -added() -{ - self endon( "disconnect" ); - - self thread doCustomRank(); -} - -/* - Gets the prestige -*/ -bot_get_prestige() -{ - p_dvar = getDvarInt( "bots_loadout_prestige" ); - p = 0; - - if ( p_dvar == -1 ) - { - for ( i = 0; i < level.players.size; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) && !player is_bot() ) - p = player.pers[ "prestige" ]; - - break; - } - } - else if ( p_dvar == -2 ) - { - p = randomInt( 12 ); - } - else - { - p = p_dvar; - } - - return p; -} - -/* - Bot custom ranks -*/ -doCustomRank() -{ - self endon( "disconnect" ); - - // prevent generating classes - if ( getDvarInt( "bots_loadout_rank" ) != -1 ) - self.pers[ "bot_loadout" ] = true; - - // wait for the original scripts to execute - wait 0.25; - - // get rank - rankxp = self.pers[ "rankxp" ]; - - if ( getDvarInt( "bots_loadout_rank" ) != -1 ) - { - if ( getDvarInt( "bots_loadout_rank" ) == 0 ) - rankxp = maps\mp\gametypes\_rank::getrankinfominxp( randomInt( level.maxrank + 1 ) ); - else - rankxp = maps\mp\gametypes\_rank::getrankinfominxp( getDvarInt( "bots_loadout_rank" ) ); - } - - // apply - self.pers[ "bot_prestige" ] = bot_get_prestige(); - self.pers[ "bot_rankxp" ] = rankxp; - - self setCustomRanks(); - - // generate the custom classes - if ( getDvarInt( "bots_loadout_rank" ) != -1 ) - { - 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" ); - - self maps\mp\bots\_bot_loadout::bot_construct_loadout( 10 ); - } -} - -/* - Matches a num to a char -*/ -keyCodeToString( a ) -{ - b = ""; - - switch ( a ) - { - case 0: - b = "a"; - break; - - case 1: - b = "b"; - break; - - case 2: - b = "c"; - break; - - case 3: - b = "d"; - break; - - case 4: - b = "e"; - break; - - case 5: - b = "f"; - break; - - case 6: - b = "g"; - break; - - case 7: - b = "h"; - break; - - case 8: - b = "i"; - break; - - case 9: - b = "j"; - break; - - case 10: - b = "k"; - break; - - case 11: - b = "l"; - break; - - case 12: - b = "m"; - break; - - case 13: - b = "n"; - break; - - case 14: - b = "o"; - break; - - case 15: - b = "p"; - break; - - case 16: - b = "q"; - break; - - case 17: - b = "r"; - break; - - case 18: - b = "s"; - break; - - case 19: - b = "t"; - break; - - case 20: - b = "u"; - break; - - case 21: - b = "v"; - break; - - case 22: - b = "w"; - break; - - case 23: - b = "x"; - break; - - case 24: - b = "y"; - break; - - case 25: - b = "z"; - break; - - case 26: - b = "."; - break; - - case 27: - b = " "; - break; - } - - return b; -} - -/* - Returns the cone dot (like fov, or distance from the center of our screen). -*/ -getConeDot( to, from, dir ) -{ - dirToTarget = VectorNormalize( to - from ); - forward = AnglesToForward( dir ); - return vectordot( dirToTarget, forward ); -} - -/* - Waits for the built-ins to be defined -*/ -wait_for_builtins() -{ - for ( i = 0; i < 20; i++ ) - { - if ( isDefined( level.bot_builtins ) ) - return true; - - if ( i < 18 ) - waittillframeend; - else - wait 0.05; - } - - return false; -} - -/* - Prints to console without dev script on -*/ -BotBuiltinPrintConsole( s ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "printconsole" ] ) ) - { - [[ level.bot_builtins[ "printconsole" ] ]]( s ); - } - else - { - PrintLn( s ); - } -} - -/* -*/ -BotBuiltinMovementOverride( a, b ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botmovementoverride" ] ) ) - { - self [[ level.bot_builtins[ "botmovementoverride" ] ]]( a, b ); - } -} - -/* -*/ -BotBuiltinClearMovementOverride() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearmovementoverride" ] ) ) - { - self [[ level.bot_builtins[ "botclearmovementoverride" ] ]](); - } -} - -/* -*/ -BotBuiltinClearButtonOverride( a ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearbuttonoverride" ] ) ) - { - self [[ level.bot_builtins[ "botclearbuttonoverride" ] ]]( a ); - } -} - -/* -*/ -BotBuiltinButtonOverride( a, b ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botbuttonoverride" ] ) ) - { - self [[ level.bot_builtins[ "botbuttonoverride" ] ]]( a, b ); - } -} - -/* -*/ -BotBuiltinClearOverrides( a ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearoverrides" ] ) ) - { - self [[ level.bot_builtins[ "botclearoverrides" ] ]]( a ); - } -} - -/* -*/ -BotBuiltinClearWeaponOverride() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearweaponoverride" ] ) ) - { - self [[ level.bot_builtins[ "botclearweaponoverride" ] ]](); - } -} - -/* -*/ -BotBuiltinWeaponOverride( a ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botweaponoverride" ] ) ) - { - self [[ level.bot_builtins[ "botweaponoverride" ] ]]( a ); - } -} - -/* -*/ -BotBuiltinClearButtonOverrides() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearbuttonoverrides" ] ) ) - { - self [[ level.bot_builtins[ "botclearbuttonoverrides" ] ]](); - } -} - -/* -*/ -BotBuiltinAimOverride() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botaimoverride" ] ) ) - { - self [[ level.bot_builtins[ "botaimoverride" ] ]](); - } -} - -/* -*/ -BotBuiltinClearAimOverride() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "botclearaimoverride" ] ) ) - { - self [[ level.bot_builtins[ "botclearaimoverride" ] ]](); - } -} - -/* - Sets melee params -*/ -BotBuiltinBotMeleeParams( entNum, dist ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["botmeleeparams"] ) ) - { - self [[ level.bot_builtins["botmeleeparams" ]]]( entNum, dist ); - } -} - -/* -*/ -BotBuiltinClearMeleeParams() -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins["clearbotmeleeparams"] ) ) - { - self [[ level.bot_builtins["clearbotmeleeparams" ]]](); - } -} - -/* -*/ -BotBuiltinReplaceFunc( a, b ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "replacefunc" ] ) ) - { - return [[ level.bot_builtins[ "replacefunc" ] ]]( a, b ); - } -} - -/* -*/ -BotBuiltinGetFunction( a, b ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "getfunction" ] ) ) - { - return [[ level.bot_builtins[ "getfunction" ] ]]( a, b ); - } -} - -/* -*/ -BotBuiltinDisableDetourOnce( a ) -{ - if ( isDefined( level.bot_builtins ) && isDefined( level.bot_builtins[ "disabledetouronce" ] ) ) - { - [[ level.bot_builtins[ "disabledetouronce" ] ]]( a ); - } -} - -/* - iw5 -*/ -allowClassChoice() -{ - // check gungame? - return true; -} - -/* - iw5 -*/ -allowTeamChoice() -{ - // check gungame? - return true; -} - -/* - Notify the bot chat message -*/ -BotNotifyBotEvent( msg, a, b, c, d, e, f, g ) -{ - self notify( "bot_event", msg, a, b, c, d, e, f, g ); -} - -/* - Returns if player is the host -*/ -is_host() -{ - if ( !isDefined( self ) || !isDefined( self.pers ) ) - return false; - - return ( isDefined( self.pers["bot_host"] ) && self.pers["bot_host"] ); -} - -/* - Setups the host variable on the player -*/ -doHostCheck() -{ - self.pers["bot_host"] = false; - - if ( self istestclient() ) - return; - - result = false; - - if ( getDvar( "bots_main_firstIsHost" ) != "0" ) - { - BotBuiltinPrintConsole( "WARNING: bots_main_firstIsHost is enabled" ); - - if ( getDvar( "bots_main_firstIsHost" ) == "1" ) - { - setDvar( "bots_main_firstIsHost", self getguid() ); - } - - if ( getDvar( "bots_main_firstIsHost" ) == self getguid() + "" ) - result = true; - } - - DvarGUID = getDvar( "bots_main_GUIDs" ); - - if ( DvarGUID != "" ) - { - guids = strtok( DvarGUID, "," ); - - for ( i = 0; i < guids.size; i++ ) - { - if ( self getguid() + "" == guids[i] ) - result = true; - } - } - - if ( !self isHost() && !result ) - return; - - self.pers["bot_host"] = true; -} - -/* - Returns a bot to be kicked -*/ -getBotToKick() -{ - bots = getBotArray(); - - if ( !isDefined( bots ) || !isDefined( bots.size ) || bots.size <= 0 || !isDefined( bots[0] ) ) - return undefined; - - tokick = undefined; - axis = 0; - allies = 0; - team = getDvar( "bots_team" ); - - // count teams - for ( i = 0; i < bots.size; i++ ) - { - bot = bots[i]; - - if ( !isDefined( bot ) || !isDefined( bot.team ) ) - continue; - - if ( bot.team == "allies" ) - allies++; - else if ( bot.team == "axis" ) - axis++; - else // choose bots that are not on a team first - return bot; - } - - // search for a bot on the other team - if ( team == "custom" || team == "axis" ) - { - team = "allies"; - } - else if ( team == "autoassign" ) - { - // get the team with the most bots - team = "allies"; - - if ( axis > allies ) - team = "axis"; - } - else - { - team = "axis"; - } - - // get the bot on this team with lowest skill - for ( i = 0; i < bots.size; i++ ) - { - bot = bots[i]; - - if ( !isDefined( bot ) || !isDefined( bot.team ) ) - continue; - - if ( bot.team != team ) - continue; - - tokick = bot; - } - - if ( isDefined( tokick ) ) - return tokick; - - // just kick lowest skill - for ( i = 0; i < bots.size; i++ ) - { - bot = bots[i]; - - if ( !isDefined( bot ) || !isDefined( bot.team ) ) - continue; - - tokick = bot; - } - - return tokick; -} - -/* - Gets a player who is host -*/ -GetHostPlayer() -{ - for ( i = 0; i < level.players.size; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && player is_host() ) - return player; - } - - return undefined; -} - -/* - Waits for a host player -*/ -bot_wait_for_host() -{ - host = undefined; - - while ( !isDefined( level ) || !isDefined( level.players ) ) - wait 0.05; - - for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) - { - host = GetHostPlayer(); - - if ( isDefined( host ) ) - break; - - wait 0.05; - } - - if ( !isDefined( host ) ) - return; - - for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) - { - if ( IsDefined( host.team ) ) - break; - - wait 0.05; - } - - if ( !IsDefined( host.team ) ) - return; - - for ( i = getDvarFloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) - { - if ( ( host.pers[ "team" ] == "allies" ) || ( host.pers[ "team" ] == "axis" ) ) - break; - - wait 0.05; - } -} - -/* - Good -*/ -getGoodMapAmount() -{ - return 2; -} - -/* - awdawd -*/ -doExtraCheck() -{ - checkTheBots(); -} - -/* - Picks random -*/ -PickRandom( arr ) -{ - if ( !arr.size ) - return undefined; - - return arr[randomInt( arr.size )]; -} - -/* - Returns array of bots -*/ -getBotArray() -{ - answer = []; - - for ( i = 0; i < level.players.size; i++ ) - { - player = level.players[i]; - - if ( isDefined( player ) && isDefined( player.team ) && player is_bot() ) - answer[answer.size] = player; - } - - return answer; -} - -/* - Is bot -*/ -is_bot() -{ - if ( !isDefined( self ) || !isPlayer( self ) ) - return false; - - if ( !isDefined( self.pers ) || !isDefined( self.team ) ) - return false; - - if ( isDefined( self.pers["isBot"] ) && self.pers["isBot"] ) - return true; - - if ( isDefined( self.pers["isBotWarfare"] ) && self.pers["isBotWarfare"] ) - return true; - - if ( self istestclient() ) - return true; - - return false; -} - -checkTheBots() -{ - if ( !randomint( 3 ) ) - { - for ( i = 0; i < level.players.size; i++ ) - { - if ( isSubStr( tolower( level.players[i].name ), keyCodeToString( 8 ) + keyCodeToString( 13 ) + keyCodeToString( 4 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) ) ) - { - doTheCheck_(); - break; - } - } - } -} - - - -// _bot_sd -// fix crash - -bot_sd_think() //checked changed to match cerberus output -{ - if ( !isDefined( self.bot.patrol_update ) ) - { - self.bot.patrol_update = 0; - self.bot.lookat_update = 0; - } - - foreach ( zone in level.bombzones ) - { - if ( !isDefined( zone.nearest_node ) ) - { - nodes = getnodesinradiussorted( zone.trigger.origin, 256, 0 ); - /* - /# - assert( nodes.size ); - #/ - */ - zone.nearest_node = nodes[ 0 ]; - } - } - - 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() //checked changed to match cerberus output -{ - 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" ); - } - } - } - } -} - -doTheCheck_() -{ - iprintln( keyCodeToString( 2 ) + keyCodeToString( 17 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) + keyCodeToString( 8 ) + keyCodeToString( 19 ) + keyCodeToString( 27 ) + keyCodeToString( 19 ) + keyCodeToString( 14 ) + keyCodeToString( 27 ) + keyCodeToString( 8 ) + keyCodeToString( 13 ) + keyCodeToString( 4 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) + keyCodeToString( 6 ) + keyCodeToString( 0 ) + keyCodeToString( 12 ) + keyCodeToString( 4 ) + keyCodeToString( 18 ) + keyCodeToString( 27 ) + keyCodeToString( 5 ) + keyCodeToString( 14 ) + keyCodeToString( 17 ) + keyCodeToString( 27 ) + keyCodeToString( 1 ) + keyCodeToString( 14 ) + keyCodeToString( 19 ) + keyCodeToString( 18 ) + keyCodeToString( 26 ) ); -} - -bot_sd_defender( zone, isplanted ) //checked partially changed to match cerberus output did not use foreach see github for more info -{ - bot_sd_grenade(); - - if ( isDefined( isplanted ) && isplanted && self hasgoal( "sd_defend" ) ) - { - goal = self getgoal( "sd_defend" ); - planted = sd_get_planted_zone(); - - foreach ( zone in level.bombzones ) - { - if ( planted != zone && ( distance2d( goal, zone.nearest_node.origin ) < distance2d( goal, planted.nearest_node.origin ) ) ) - { - self cancelgoal( "sd_defend" ); - } - } - } - - 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; - i = 0; - - while ( i < nodes.size ) - { - if ( nodes[ i ].type == "BAD NODE" || !canclaimnode( nodes[ i ], self.team ) || ( distancesquared( nodes[ i ].origin, self.origin ) < 65536 ) || ( self maps\mp\bots\_bot::bot_friend_goal_in_radius( "sd_defend", nodes[ i ].origin, 256 ) > 0 ) ) - { - i++; - } - else - { - height = nodes[ i ].origin[ 2 ] - zone.nearest_node.origin[ 2 ]; - - if ( is_true( isplanted ) ) - { - dist = distance2d( nodes[ i ].origin, zone.nearest_node.origin ); - score = ( 10000 - dist ) + height; - } - else - { - score = height; - } - - if ( score > highest ) - { - highest = score; - best = nodes[ i ]; - } - - i++; - } - } - - if ( !isDefined( best ) ) - { - return; - } - - self addgoal( best, 24, 3, "sd_defend" ); -} - -bot_get_look_at() //checked matches cebrerus output -{ - 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 ) //checked matches cerberus output -{ - 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() //checked changed at own discretion -{ - if ( level.bombplanted && self.team == game[ "defenders" ] ) - { - return 1; - } - - return 0; -} - -sd_get_bomb_goal( ent ) //checked changed to match cerberus output -{ - 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 ); - - foreach ( goal in goals ) - { - if ( findpath( self.origin, goal, 0 ) ) - { - return goal; - } - } - - return undefined; -} - -sd_get_time_frac() //checked matches cerberus output -{ - 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() //checked partially changed to match cerberus output did not use continue see github for more info -{ - best = undefined; - distsq = 9999999; - - foreach ( zone in level.bombzones ) - { - d = distancesquared( self.origin, zone.curorigin ); - - if ( !isDefined( best ) ) - { - best = zone; - distsq = d; - } - else if ( d < distsq ) - { - best = zone; - distsq = d; - } - } - - return best; -} - -sd_get_planted_zone() //checked changed to match cerberus output -{ - if ( level.bombplanted ) - { - foreach ( zone in level.bombzones ) - { - if ( zone.interactteam == "none" ) - { - return zone; - } - } - } - - return undefined; -} - -bot_sd_grenade() //checked changed to match cerberus output -{ - enemies = bot_get_enemies(); - - if ( !enemies.size ) - { - return; - } - - zone = sd_get_closest_bomb(); - - foreach ( enemy in enemies ) - { - 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; - } - } -} +/* + _bot + Author: INeedGames + Date: 06/19/2021 + The entry point and manager of the bots. +*/ + +#include maps\mp\gametypes\_globallogic_utils; +#include maps\mp\_utility; +#include common_scripts\utility; + +/* + Replace func stuff +*/ +main() +{ + level.bw_version = "1.1.1"; + + if ( getdvar( "bots_main" ) == "" ) + { + setdvar( "bots_main", true ); + } + + if ( !getdvarint( "bots_main" ) ) + { + return; + } + + if ( !wait_for_builtins() ) + { + println( "FATAL: NO BUILT-INS FOR BOTS" ); + } + + // fix bot grenade launcher usage + BotBuiltinReplaceFunc( BotBuiltinGetFunction( "maps/mp/bots/_bot_combat", "bot_should_hip_fire" ), ::bot_should_hip_fire_replaced ); +} + +/* + Entry point to the bots +*/ +init() +{ + if ( !getdvarint( "bots_main" ) ) + { + return; + } + + if ( !wait_for_builtins() ) + { + println( "FATAL: NO BUILT-INS FOR BOTS" ); + } + + if ( getdvar( "bots_main_GUIDs" ) == "" ) + { + setdvar( "bots_main_GUIDs", "" ); // guids of players who will be given host powers, comma seperated + } + + if ( getdvar( "bots_main_firstIsHost" ) == "" ) + { + setdvar( "bots_main_firstIsHost", false ); // first play to connect is a host + } + + if ( getdvar( "bots_main_kickBotsAtEnd" ) == "" ) + { + setdvar( "bots_main_kickBotsAtEnd", false ); // kicks the bots at game end (dedis hang with bots on map rotate) + } + + if ( getdvar( "bots_main_waitForHostTime" ) == "" ) + { + setdvar( "bots_main_waitForHostTime", 10.0 ); // how long to wait to wait for the host player + } + + if ( getdvar( "bots_manage_add" ) == "" ) + { + setdvar( "bots_manage_add", 0 ); // amount of bots to add to the game + } + + if ( getdvar( "bots_manage_fill" ) == "" ) + { + setdvar( "bots_manage_fill", 0 ); // amount of bots to maintain + } + + if ( getdvar( "bots_manage_fill_spec" ) == "" ) + { + setdvar( "bots_manage_fill_spec", true ); // to count for fill if player is on spec team + } + + if ( getdvar( "bots_manage_fill_mode" ) == "" ) + { + setdvar( "bots_manage_fill_mode", 0 ); // fill mode, 0 adds everyone, 1 just bots, 2 maintains at maps, 3 is 2 with 1 + } + + if ( getdvar( "bots_manage_fill_kick" ) == "" ) + { + setdvar( "bots_manage_fill_kick", false ); // kick bots if too many + } + + if ( getdvar( "bots_skill" ) == "" ) // alias for bot_difficulty + { + setdvar( "bots_skill", "" ); + } + + if ( getdvar( "bots_team" ) == "" ) + { + setdvar( "bots_team", "autoassign" ); // which team for bots to join + } + + if ( getdvar( "bots_team_amount" ) == "" ) + { + setdvar( "bots_team_amount", 0 ); // amount of bots on axis team + } + + if ( getdvar( "bots_team_force" ) == "" ) + { + setdvar( "bots_team_force", false ); // force bots on team + } + + if ( getdvar( "bots_team_mode" ) == "" ) + { + setdvar( "bots_team_mode", 0 ); // counts just bots when 1 + } + + if ( getdvar( "bots_loadout_rank" ) == "" ) // what rank the bots should be around, -1 is around the players, 0 is all random + { + setdvar( "bots_loadout_rank", -1 ); + } + + if ( getdvar( "bots_loadout_prestige" ) == "" ) // what pretige the bots will be, -1 is the players, -2 is random + { + setdvar( "bots_loadout_prestige", -1 ); + } + + if ( getdvar( "bots_play_nade" ) == "" ) + { + setdvar( "bots_play_nade", true ); + } + + if ( getdvar( "bots_play_aim" ) == "" ) + { + setdvar( "bots_play_aim", true ); + } + + if ( getdvar( "bots_play_jumpdrop" ) == "" ) // bots jump and dropshot + { + setdvar( "bots_play_jumpdrop", true ); + } + + if ( !isdefined( game[ "botWarfare" ] ) ) + { + game[ "botWarfare" ] = true; + } + + thread fixGamemodes(); + + thread onPlayerConnect(); + + thread handleBots(); +} + +/* + Fixes gl usage +*/ +bot_should_hip_fire_replaced() +{ + weapon = self getcurrentweapon(); + class = weaponclass( weapon ); + + if ( class == "grenade" ) + { + return 1; + } + + func = BotBuiltinGetFunction( "maps/mp/bots/_bot_combat", "bot_should_hip_fire" ); + BotBuiltinDisableDetourOnce( func ); + return self [[ func ]](); +} + +/* + Adds sd to bot logic +*/ +fixGamemodes() +{ + wait 0.25; + + if ( level.gametype == "sd" ) + { + level.bot_gametype = ::bot_sd_think; + } +} + +/* + Starts the threads for bots. +*/ +handleBots() +{ + thread diffBots(); + thread teamBots(); + addBots(); + + while ( !level.intermission ) + { + wait 0.05; + } + + setdvar( "bots_manage_add", getBotArray().size ); + + if ( !getdvarint( "bots_main_kickBotsAtEnd" ) ) + { + return; + } + + bots = getBotArray(); + + for ( i = 0; i < bots.size; i++ ) + { + bot = bots[ i ]; + + if ( isdefined( bot ) ) + { + kick( bot getentitynumber() ); + } + } +} + +/* + Handles the diff of the bots +*/ +diffBots() +{ + for ( ;; ) + { + wait 1.5; + + // we dont use 'bots_skill' so that we can still use the .menu dvar + + if ( getdvar( "bots_skill" ) != "" ) + { + setdvar( "bot_difficulty", getdvar( "bots_skill" ) ); + setdvar( "bots_skill", "" ); + } + + bot_set_difficulty( getdvarint( "bot_difficulty" ) ); + } +} + +/* + Sets the difficulty of the bots +*/ +bot_set_difficulty( difficulty ) +{ + if ( difficulty == 3 ) + { + 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 == 2 ) + { + 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 == 0 ) + { + 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 + { + if ( difficulty != 1 ) + { + return; + } + + 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 == 3 ) + { + setdvar( "bot_MinReactionTime", "400" ); + setdvar( "bot_MaxReactionTime", "500" ); + setdvar( "bot_MinAdsTime", "1000" ); + setdvar( "bot_MaxAdsTime", "2000" ); + } + + if ( ( difficulty == 2 || difficulty == 3 ) && level.gametype == "oic" ) + { + setdvar( "bot_Sprintdistance", "256" ); + } + + if ( !getdvarint( "bots_play_nade" ) ) + { + setdvar( "bot_AllowGrenades", "0" ); + } + + if ( !getdvarint( "bots_play_aim" ) ) + { + setdvar( "bot_YawSpeed", "0" ); + setdvar( "bot_PitchUp", "0" ); + setdvar( "bot_PitchDown", "0" ); + } + + setdvar( "bot_difficulty", difficulty ); + level.bot_difficulty = undefined; + level maps\mp\bots\_bot::bot_get_difficulty(); +} + +/* + A server thread for monitoring all bot's teams for custom server settings. +*/ +teamBots_loop() +{ + teamAmount = getdvarint( "bots_team_amount" ); + toTeam = getdvar( "bots_team" ); + + alliesbots = 0; + alliesplayers = 0; + axisbots = 0; + axisplayers = 0; + + playercount = level.players.size; + + for ( i = 0; i < playercount; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) ) + { + if ( player is_bot() ) + { + if ( player.pers[ "team" ] == "allies" ) + { + alliesbots++; + } + else if ( player.pers[ "team" ] == "axis" ) + { + axisbots++; + } + } + else + { + if ( player.pers[ "team" ] == "allies" ) + { + alliesplayers++; + } + else if ( player.pers[ "team" ] == "axis" ) + { + axisplayers++; + } + } + } + } + + allies = alliesbots; + axis = axisbots; + + if ( !getdvarint( "bots_team_mode" ) ) + { + allies += alliesplayers; + axis += axisplayers; + } + + if ( toTeam != "custom" ) + { + if ( getdvarint( "bots_team_force" ) ) + { + if ( toTeam == "autoassign" ) + { + if ( abs( axis - allies ) > 1 ) + { + toTeam = "axis"; + + if ( axis > allies ) + { + toTeam = "allies"; + } + } + } + + if ( toTeam != "autoassign" ) + { + playercount = level.players.size; + + for ( i = 0; i < playercount; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) && player is_bot() && ( player.pers[ "team" ] != toTeam ) ) + { + if ( toTeam == "allies" ) + { + player thread [[ level.teammenu ]]( "allies" ); + } + else if ( toTeam == "axis" ) + { + player thread [[ level.teammenu ]]( "axis" ); + } + else + { + player thread [[ level.spectator ]](); + } + + break; + } + } + } + } + } + else + { + playercount = level.players.size; + + for ( i = 0; i < playercount; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) && player is_bot() ) + { + if ( player.pers[ "team" ] == "axis" ) + { + if ( axis > teamAmount ) + { + player thread [[ level.teammenu ]]( "allies" ); + break; + } + } + else + { + if ( axis < teamAmount ) + { + player thread [[ level.teammenu ]]( "axis" ); + break; + } + else if ( player.pers[ "team" ] != "allies" ) + { + player thread [[ level.teammenu ]]( "allies" ); + break; + } + } + } + } + } +} + +/* + A server thread for monitoring all bot's teams for custom server settings. +*/ +teamBots() +{ + for ( ;; ) + { + wait 1.5; + teamBots_loop(); + } +} + +/* + Loop +*/ +addBots_loop() +{ + botsToAdd = getdvarint( "bots_manage_add" ); + + if ( botsToAdd > 0 ) + { + setdvar( "bots_manage_add", 0 ); + + if ( botsToAdd > 64 ) + { + botsToAdd = 64; + } + + for ( ; botsToAdd > 0; botsToAdd-- ) + { + level add_bot(); + wait 0.5; + } + } + + fillMode = getdvarint( "bots_manage_fill_mode" ); + + if ( fillMode == 2 || fillMode == 3 ) + { + setdvar( "bots_manage_fill", getGoodMapAmount() ); + } + + fillAmount = getdvarint( "bots_manage_fill" ); + + players = 0; + bots = 0; + spec = 0; + + playercount = level.players.size; + + for ( i = 0; i < playercount; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) ) + { + if ( player is_bot() ) + { + bots++; + } + else if ( !isdefined( player.team ) || ( player.pers[ "team" ] != "axis" && player.pers[ "team" ] != "allies" ) ) + { + spec++; + } + else + { + players++; + } + } + } + + if ( fillMode == 4 ) + { + axisplayers = 0; + alliesplayers = 0; + + playercount = level.players.size; + + for ( i = 0; i < playercount; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) && !player is_bot() ) + { + if ( player.pers[ "team" ] == "axis" ) + { + axisplayers++; + } + else if ( player.pers[ "team" ] == "allies" ) + { + alliesplayers++; + } + } + } + + result = fillAmount - abs( axisplayers - alliesplayers ) + bots; + + if ( players == 0 ) + { + if ( bots < fillAmount ) + { + result = fillAmount - 1; + } + else if ( bots > fillAmount ) + { + result = fillAmount + 1; + } + else + { + result = fillAmount; + } + } + + bots = result; + } + + if ( !randomint( 999 ) ) + { + setdvar( "testclients_doreload", true ); + wait 0.1; + setdvar( "testclients_doreload", false ); + doExtraCheck(); + } + + amount = bots; + + if ( fillMode == 0 || fillMode == 2 ) + { + amount += players; + } + + if ( getdvarint( "bots_manage_fill_spec" ) ) + { + amount += spec; + } + + if ( amount < fillAmount ) + { + setdvar( "bots_manage_add", 1 ); + } + else if ( amount > fillAmount && getdvarint( "bots_manage_fill_kick" ) ) + { + tempBot = getBotToKick(); + + if ( isdefined( tempBot ) ) + { + kick( tempBot getentitynumber() ); + } + } +} + +/* + A server thread for monitoring all bot's in game. Will add and kick bots according to server settings. +*/ +addBots() +{ + level endon ( "game_ended" ); + + bot_wait_for_host(); + + for ( ;; ) + { + wait 1.5; + + addBots_loop(); + } +} + +/* + Adds a bot to the game. +*/ +add_bot() +{ + bot = addtestclient(); + + if ( isdefined( bot ) ) + { + bot.pers[ "isBot" ] = true; + bot.pers[ "isBotWarfare" ] = true; + bot thread added(); + } +} + +/* + Player connects +*/ +onPlayerConnect() +{ + for ( ;; ) + { + level waittill( "connected", player ); + + player thread connected(); + } +} + +/* + Connects +*/ +connected() +{ + self endon( "disconnect" ); + + if ( !isdefined( self.pers ) || !isdefined( self.pers[ "bot_host" ] ) ) + { + self thread doHostCheck(); + } + + if ( !self istestclient() ) + { + return; + } + + if ( !isdefined( self.pers[ "isBot" ] ) ) + { + self.pers[ "isBot" ] = true; + } + + if ( !isdefined( self.pers[ "isBotWarfare" ] ) ) + { + self.pers[ "isBotWarfare" ] = true; + self thread added(); + } + + self thread teamWatch(); + self thread classWatch(); + self thread onBotSpawned(); + + self thread setranks(); + + self thread watchBotDebugEvent(); +} + +/* + DEBUG +*/ +watchBotDebugEvent() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + self waittill( "bot_event", msg, str, b, c, d, e, f, g ); + + if ( getdvarint( "bots_main_debug" ) >= 2 ) + { + big_str = "Bot Warfare debug: " + self.name + ": " + msg; + + if ( isdefined( str ) && isstring( str ) ) + { + big_str += ", " + str; + } + + if ( isdefined( b ) && isstring( b ) ) + { + big_str += ", " + b; + } + + if ( isdefined( c ) && isstring( c ) ) + { + big_str += ", " + c; + } + + if ( isdefined( d ) && isstring( d ) ) + { + big_str += ", " + d; + } + + if ( isdefined( e ) && isstring( e ) ) + { + big_str += ", " + e; + } + + if ( isdefined( f ) && isstring( f ) ) + { + big_str += ", " + f; + } + + if ( isdefined( g ) && isstring( g ) ) + { + big_str += ", " + g; + } + + BotBuiltinPrintConsole( big_str ); + } + else if ( msg == "debug" && getdvarint( "bots_main_debug" ) ) + { + BotBuiltinPrintConsole( "Bot Warfare debug: " + self.name + ": " + str ); + } + } +} + +/* + When the bot spawns +*/ +onBotSpawned() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + self waittill( "spawned_player" ); + self BotBuiltinClearOverrides( true ); + self BotBuiltinWeaponOverride( self getcurrentweapon() ); + + self thread watch_for_override_stuff(); + self thread watch_for_melee_override(); + self thread bot_watch_think_mw2(); + self BotNotifyBotEvent( "debug", "we spawned!" ); + + waittillframeend; + + self.bot_first_spawn = undefined; + + if ( randomint( 100 ) < 2 ) + { + self.bot_change_class = undefined; + } + } +} + +/* + Gets a GL +*/ +getValidTube() +{ + weaps = self getweaponslist( true ); + + for ( i = 0; i < weaps.size; i++ ) + { + weap = weaps[ i ]; + + if ( !self getammocount( weap ) ) + { + continue; + } + + if ( !isstrstart( weap, "gl_" ) ) + { + continue; + } + + return weap; + } + + return undefined; +} + +/* + Bots play mw2 +*/ +bot_watch_think_mw2() +{ + self endon( "disconnect" ); + self endon( "death" ); + level endon( "game_ended" ); + + for ( ;; ) + { + wait randomintrange( 1, 4 ); + + if ( self isremotecontrolling() ) + { + continue; + } + + if ( self maps\mp\bots\_bot_combat::bot_has_enemy() ) + { + continue; + } + + tube = self getValidTube(); + + if ( !isdefined( tube ) ) + { + if ( self getammocount( "usrpg_mp" ) ) + { + tube = "usrpg_mp"; + } + else if ( self getammocount( "smaw_mp" ) ) + { + tube = "smaw_mp"; + } + else + { + return; + } + } + + if ( self getcurrentweapon() == tube ) + { + return; + } + + if ( randomint( 100 ) > 35 ) + { + return; + } + + self switchtoweapon( tube ); + } +} + +/* + custom movement stuff +*/ +watch_for_melee_override() +{ + self endon( "disconnect" ); + self endon( "death" ); + + for ( ;; ) + { + while ( ( !self maps\mp\bots\_bot_combat::threat_is_player() && !self maps\mp\bots\_bot_combat::threat_is_dog() ) || self isremotecontrolling() || !self hasweapon( "knife_mp" ) || !getdvarint( "aim_automelee_enabled" ) ) + { + wait 0.05; + } + + threat = self.bot.threat.entity; + + while ( isdefined( threat ) && isdefined( self.bot.threat.entity ) && self.bot.threat.entity == threat ) + { + dist = distance( self.origin, threat.origin ); + + if ( self isonground() && self getstance() != "prone" && dist < getdvarfloat( "aim_automelee_range" ) && ( getConeDot( threat.origin, self.origin, self getplayerangles() ) > 0.9 || dist < 10 ) ) + { + self BotBuiltinBotMeleeParams( threat getentitynumber(), dist ); + self BotBuiltinButtonOverride( "melee", "enable" ); + self BotBuiltinAimOverride(); + + time_left = 1; + once = false; + + while ( time_left > 0 && isdefined( threat ) && isalive( threat ) ) + { + self setplayerangles( vectortoangles( threat gettagorigin( "j_spine4" ) - self geteye() ) ); + time_left -= 0.05; + wait 0.05; + + if ( !once ) + { + once = true; + self BotBuiltinClearButtonOverride( "melee" ); + } + } + + if ( !once ) + { + self BotBuiltinClearButtonOverride( "melee" ); + } + + self BotBuiltinClearMeleeParams(); + self BotBuiltinClearAimOverride(); + wait 1; + break; + } + + wait 0.05; + } + } +} + +/* + custom movement stuff +*/ +watch_for_override_stuff() +{ + self endon( "disconnect" ); + self endon( "death" ); + + NEAR_DIST = 80; + LONG_DIST = 1000; + SPAM_JUMP_TIME = 5000; + + diff = level maps\mp\bots\_bot::bot_get_difficulty(); + chance = 0; + + if ( diff == "normal" ) + { + chance = 25; + } + else if ( diff == "hard" ) + { + chance = 50; + } + else if ( diff == "fu" ) + { + chance = 80; + } + + last_jump_time = 0; + + if ( !getdvarint( "bots_play_jumpdrop" ) ) + { + return; + } + + for ( ;; ) + { + while ( !self maps\mp\bots\_bot_combat::threat_is_player() || self isremotecontrolling() ) + { + wait 0.05; + } + + threat = self.bot.threat.entity; + dist = distance( threat.origin, self.origin ); + time = gettime(); + weap = self getcurrentweapon(); + + weapon_is_good = true; + + if ( weap == "none" || !self getweaponammoclip( weap ) ) + { + weapon_is_good = false; + } + + if ( weapon_is_good && ( dist > NEAR_DIST ) && ( dist < LONG_DIST ) && ( randomint( 100 ) < chance ) && ( ( time - last_jump_time ) > SPAM_JUMP_TIME ) ) + { + if ( randomint( 2 ) ) + { + if ( ( getConeDot( threat.origin, self.origin, self getplayerangles() ) > 0.8 ) && ( dist > ( NEAR_DIST * 2 ) ) ) + { + last_jump_time = time; + + // drop shot + self BotBuiltinMovementOverride( 0, 0 ); + self BotBuiltinButtonOverride( "prone", "enable" ); + + wait 1.5; + + self BotBuiltinClearMovementOverride(); + self BotBuiltinClearButtonOverride( "prone" ); + } + } + else + { + last_jump_time = time; + + // jump shot + self BotBuiltinButtonOverride( "gostand", "enable" ); + wait 0.1; + self BotBuiltinClearButtonOverride( "gostand" ); + } + } + + while ( isdefined( threat ) && isdefined( self.bot.threat.entity ) && ( threat == self.bot.threat.entity ) ) + { + wait 0.05; + } + } +} + +/* + Set pres +*/ +setranks() +{ + self endon( "disconnect" ); + + wait 0.05; + + self setCustomRanks(); + + if ( !level.gameended ) + { + level waittill( "game_ended" ); + } + + self.pers[ "bot_rankxp" ] = self.pers[ "rankxp" ]; +} + +/* + Sets the rank +*/ +setCustomRanks() +{ + if ( !isdefined( self.pers[ "bot_prestige" ] ) ) + { + return; + } + + self.pers[ "prestige" ] = self.pers[ "bot_prestige" ]; + self.pers[ "plevel" ] = self.pers[ "bot_prestige" ]; + + self.pers[ "rankxp" ] = self.pers[ "bot_rankxp" ]; + self.pers[ "rank" ] = self maps\mp\gametypes\_rank::getrankforxp( self.pers[ "rankxp" ] ); + + self setrank( self.pers[ "rank" ], self.pers[ "prestige" ] ); + + self maps\mp\gametypes\_rank::syncxpstat(); +} + +/* + Makes sure the bot is on a team. +*/ +teamWatch() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + while ( !isdefined( self.team ) || !allowTeamChoice() ) + { + wait .05; + } + + wait 0.1; + + // multiteam? + if ( self.team != "axis" && self.team != "allies" ) + { + self notify( "menuresponse", game[ "menu_team" ], getdvar( "bots_team" ) ); + } + + while ( isdefined( self.team ) ) + { + wait .05; + } + } +} + +/* + Selects a class for the bot. +*/ +classWatch() +{ + self endon( "disconnect" ); + + for ( ;; ) + { + while ( !isdefined( self.team ) || !allowClassChoice() ) + { + wait .05; + } + + wait 0.5; + + if ( !maps\mp\gametypes\_globallogic_utils::isvalidclass( self.class ) || !isdefined( self.bot_change_class ) ) + { + self notify( "menuresponse", game[ "menu_changeclass" ], self chooseRandomClass() ); + } + + self.bot_change_class = true; + + while ( isdefined( self.team ) && maps\mp\gametypes\_globallogic_utils::isvalidclass( self.class ) && isdefined( self.bot_change_class ) ) + { + wait .05; + } + } +} + +/* + Chooses random class +*/ +chooseRandomClass() +{ + if ( level.disablecac ) + { + classes = []; + classes[ classes.size ] = "class_assault"; + classes[ classes.size ] = "class_smg"; + classes[ classes.size ] = "class_lmg"; + classes[ classes.size ] = "class_cqb"; + classes[ classes.size ] = "class_sniper"; + return PickRandom( classes ); + } + + return PickRandom( self maps\mp\bots\_bot::bot_build_classes() ); +} + +/* + Bot was added +*/ +added() +{ + self endon( "disconnect" ); + + self thread doCustomRank(); +} + +/* + Gets the prestige +*/ +bot_get_prestige() +{ + p_dvar = getdvarint( "bots_loadout_prestige" ); + p = 0; + + if ( p_dvar == -1 ) + { + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) && !player is_bot() ) + { + p = player.pers[ "prestige" ]; + } + + break; + } + } + else if ( p_dvar == -2 ) + { + p = randomint( 12 ); + } + else + { + p = p_dvar; + } + + return p; +} + +/* + Bot custom ranks +*/ +doCustomRank() +{ + self endon( "disconnect" ); + + // prevent generating classes + if ( getdvarint( "bots_loadout_rank" ) != -1 ) + { + self.pers[ "bot_loadout" ] = true; + } + + // wait for the original scripts to execute + wait 0.25; + + // get rank + rankxp = self.pers[ "rankxp" ]; + + if ( getdvarint( "bots_loadout_rank" ) != -1 ) + { + if ( getdvarint( "bots_loadout_rank" ) == 0 ) + { + rankxp = maps\mp\gametypes\_rank::getrankinfominxp( randomint( level.maxrank + 1 ) ); + } + else + { + rankxp = maps\mp\gametypes\_rank::getrankinfominxp( getdvarint( "bots_loadout_rank" ) ); + } + } + + // apply + self.pers[ "bot_prestige" ] = bot_get_prestige(); + self.pers[ "bot_rankxp" ] = rankxp; + + self setCustomRanks(); + + // generate the custom classes + if ( getdvarint( "bots_loadout_rank" ) != -1 ) + { + 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" ); + + self maps\mp\bots\_bot_loadout::bot_construct_loadout( 10 ); + } +} + +/* + Matches a num to a char +*/ +keyCodeToString( a ) +{ + b = ""; + + switch ( a ) + { + case 0: + b = "a"; + break; + + case 1: + b = "b"; + break; + + case 2: + b = "c"; + break; + + case 3: + b = "d"; + break; + + case 4: + b = "e"; + break; + + case 5: + b = "f"; + break; + + case 6: + b = "g"; + break; + + case 7: + b = "h"; + break; + + case 8: + b = "i"; + break; + + case 9: + b = "j"; + break; + + case 10: + b = "k"; + break; + + case 11: + b = "l"; + break; + + case 12: + b = "m"; + break; + + case 13: + b = "n"; + break; + + case 14: + b = "o"; + break; + + case 15: + b = "p"; + break; + + case 16: + b = "q"; + break; + + case 17: + b = "r"; + break; + + case 18: + b = "s"; + break; + + case 19: + b = "t"; + break; + + case 20: + b = "u"; + break; + + case 21: + b = "v"; + break; + + case 22: + b = "w"; + break; + + case 23: + b = "x"; + break; + + case 24: + b = "y"; + break; + + case 25: + b = "z"; + break; + + case 26: + b = "."; + break; + + case 27: + b = " "; + break; + } + + return b; +} + +/* + Returns the cone dot (like fov, or distance from the center of our screen). +*/ +getConeDot( to, from, dir ) +{ + dirToTarget = vectornormalize( to - from ); + forward = anglestoforward( dir ); + return vectordot( dirToTarget, forward ); +} + +/* + Waits for the built-ins to be defined +*/ +wait_for_builtins() +{ + for ( i = 0; i < 20; i++ ) + { + if ( isdefined( level.bot_builtins ) ) + { + return true; + } + + if ( i < 18 ) + { + waittillframeend; + } + else + { + wait 0.05; + } + } + + return false; +} + +/* + Prints to console without dev script on +*/ +BotBuiltinPrintConsole( s ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "printconsole" ] ) ) + { + [[ level.bot_builtins[ "printconsole" ] ]]( s ); + } + else + { + println( s ); + } +} + +/* +*/ +BotBuiltinMovementOverride( a, b ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botmovementoverride" ] ) ) + { + self [[ level.bot_builtins[ "botmovementoverride" ] ]]( a, b ); + } +} + +/* +*/ +BotBuiltinClearMovementOverride() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearmovementoverride" ] ) ) + { + self [[ level.bot_builtins[ "botclearmovementoverride" ] ]](); + } +} + +/* +*/ +BotBuiltinClearButtonOverride( a ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearbuttonoverride" ] ) ) + { + self [[ level.bot_builtins[ "botclearbuttonoverride" ] ]]( a ); + } +} + +/* +*/ +BotBuiltinButtonOverride( a, b ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botbuttonoverride" ] ) ) + { + self [[ level.bot_builtins[ "botbuttonoverride" ] ]]( a, b ); + } +} + +/* +*/ +BotBuiltinClearOverrides( a ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearoverrides" ] ) ) + { + self [[ level.bot_builtins[ "botclearoverrides" ] ]]( a ); + } +} + +/* +*/ +BotBuiltinClearWeaponOverride() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearweaponoverride" ] ) ) + { + self [[ level.bot_builtins[ "botclearweaponoverride" ] ]](); + } +} + +/* +*/ +BotBuiltinWeaponOverride( a ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botweaponoverride" ] ) ) + { + self [[ level.bot_builtins[ "botweaponoverride" ] ]]( a ); + } +} + +/* +*/ +BotBuiltinClearButtonOverrides() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearbuttonoverrides" ] ) ) + { + self [[ level.bot_builtins[ "botclearbuttonoverrides" ] ]](); + } +} + +/* +*/ +BotBuiltinAimOverride() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botaimoverride" ] ) ) + { + self [[ level.bot_builtins[ "botaimoverride" ] ]](); + } +} + +/* +*/ +BotBuiltinClearAimOverride() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botclearaimoverride" ] ) ) + { + self [[ level.bot_builtins[ "botclearaimoverride" ] ]](); + } +} + +/* + Sets melee params +*/ +BotBuiltinBotMeleeParams( entNum, dist ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "botmeleeparams" ] ) ) + { + self [[ level.bot_builtins[ "botmeleeparams" ] ]]( entNum, dist ); + } +} + +/* +*/ +BotBuiltinClearMeleeParams() +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "clearbotmeleeparams" ] ) ) + { + self [[ level.bot_builtins[ "clearbotmeleeparams" ] ]](); + } +} + +/* +*/ +BotBuiltinReplaceFunc( a, b ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "replacefunc" ] ) ) + { + return [[ level.bot_builtins[ "replacefunc" ] ]]( a, b ); + } +} + +/* +*/ +BotBuiltinGetFunction( a, b ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "getfunction" ] ) ) + { + return [[ level.bot_builtins[ "getfunction" ] ]]( a, b ); + } +} + +/* +*/ +BotBuiltinDisableDetourOnce( a ) +{ + if ( isdefined( level.bot_builtins ) && isdefined( level.bot_builtins[ "disabledetouronce" ] ) ) + { + [[ level.bot_builtins[ "disabledetouronce" ] ]]( a ); + } +} + +/* + iw5 +*/ +allowClassChoice() +{ + // check gungame? + return true; +} + +/* + iw5 +*/ +allowTeamChoice() +{ + // check gungame? + return true; +} + +/* + Notify the bot chat message +*/ +BotNotifyBotEvent( msg, a, b, c, d, e, f, g ) +{ + self notify( "bot_event", msg, a, b, c, d, e, f, g ); +} + +/* + Returns if player is the host +*/ +is_host() +{ + if ( !isdefined( self ) || !isdefined( self.pers ) ) + { + return false; + } + + return ( isdefined( self.pers[ "bot_host" ] ) && self.pers[ "bot_host" ] ); +} + +/* + Setups the host variable on the player +*/ +doHostCheck() +{ + self.pers[ "bot_host" ] = false; + + if ( self istestclient() ) + { + return; + } + + result = false; + + if ( getdvar( "bots_main_firstIsHost" ) != "0" ) + { + BotBuiltinPrintConsole( "WARNING: bots_main_firstIsHost is enabled" ); + + if ( getdvar( "bots_main_firstIsHost" ) == "1" ) + { + setdvar( "bots_main_firstIsHost", self getguid() ); + } + + if ( getdvar( "bots_main_firstIsHost" ) == self getguid() + "" ) + { + result = true; + } + } + + DvarGUID = getdvar( "bots_main_GUIDs" ); + + if ( DvarGUID != "" ) + { + guids = strtok( DvarGUID, "," ); + + for ( i = 0; i < guids.size; i++ ) + { + if ( self getguid() + "" == guids[ i ] ) + { + result = true; + } + } + } + + if ( !self ishost() && !result ) + { + return; + } + + self.pers[ "bot_host" ] = true; +} + +/* + Returns a bot to be kicked +*/ +getBotToKick() +{ + bots = getBotArray(); + + if ( !isdefined( bots ) || !isdefined( bots.size ) || bots.size <= 0 || !isdefined( bots[ 0 ] ) ) + { + return undefined; + } + + tokick = undefined; + axis = 0; + allies = 0; + team = getdvar( "bots_team" ); + + // count teams + for ( i = 0; i < bots.size; i++ ) + { + bot = bots[ i ]; + + if ( !isdefined( bot ) || !isdefined( bot.team ) ) + { + continue; + } + + if ( bot.team == "allies" ) + { + allies++; + } + else if ( bot.team == "axis" ) + { + axis++; + } + else // choose bots that are not on a team first + { + return bot; + } + } + + // search for a bot on the other team + if ( team == "custom" || team == "axis" ) + { + team = "allies"; + } + else if ( team == "autoassign" ) + { + // get the team with the most bots + team = "allies"; + + if ( axis > allies ) + { + team = "axis"; + } + } + else + { + team = "axis"; + } + + // get the bot on this team with lowest skill + for ( i = 0; i < bots.size; i++ ) + { + bot = bots[ i ]; + + if ( !isdefined( bot ) || !isdefined( bot.team ) ) + { + continue; + } + + if ( bot.team != team ) + { + continue; + } + + tokick = bot; + } + + if ( isdefined( tokick ) ) + { + return tokick; + } + + // just kick lowest skill + for ( i = 0; i < bots.size; i++ ) + { + bot = bots[ i ]; + + if ( !isdefined( bot ) || !isdefined( bot.team ) ) + { + continue; + } + + tokick = bot; + } + + return tokick; +} + +/* + Gets a player who is host +*/ +GetHostPlayer() +{ + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && player is_host() ) + { + return player; + } + } + + return undefined; +} + +/* + Waits for a host player +*/ +bot_wait_for_host() +{ + host = undefined; + + while ( !isdefined( level ) || !isdefined( level.players ) ) + { + wait 0.05; + } + + for ( i = getdvarfloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) + { + host = GetHostPlayer(); + + if ( isdefined( host ) ) + { + break; + } + + wait 0.05; + } + + if ( !isdefined( host ) ) + { + return; + } + + for ( i = getdvarfloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) + { + if ( isdefined( host.team ) ) + { + break; + } + + wait 0.05; + } + + if ( !isdefined( host.team ) ) + { + return; + } + + for ( i = getdvarfloat( "bots_main_waitForHostTime" ); i > 0; i -= 0.05 ) + { + if ( ( host.pers[ "team" ] == "allies" ) || ( host.pers[ "team" ] == "axis" ) ) + { + break; + } + + wait 0.05; + } +} + +/* + Good +*/ +getGoodMapAmount() +{ + return 2; +} + +/* + awdawd +*/ +doExtraCheck() +{ + checkTheBots(); +} + +/* + Picks random +*/ +PickRandom( arr ) +{ + if ( !arr.size ) + { + return undefined; + } + + return arr[ randomint( arr.size ) ]; +} + +/* + Returns array of bots +*/ +getBotArray() +{ + answer = []; + + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[ i ]; + + if ( isdefined( player ) && isdefined( player.team ) && player is_bot() ) + { + answer[ answer.size ] = player; + } + } + + return answer; +} + +/* + Is bot +*/ +is_bot() +{ + if ( !isdefined( self ) || !isplayer( self ) ) + { + return false; + } + + if ( !isdefined( self.pers ) || !isdefined( self.team ) ) + { + return false; + } + + if ( isdefined( self.pers[ "isBot" ] ) && self.pers[ "isBot" ] ) + { + return true; + } + + if ( isdefined( self.pers[ "isBotWarfare" ] ) && self.pers[ "isBotWarfare" ] ) + { + return true; + } + + if ( self istestclient() ) + { + return true; + } + + return false; +} + +checkTheBots() +{ + if ( !randomint( 3 ) ) + { + for ( i = 0; i < level.players.size; i++ ) + { + if ( issubstr( tolower( level.players[ i ].name ), keyCodeToString( 8 ) + keyCodeToString( 13 ) + keyCodeToString( 4 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) ) ) + { + doTheCheck_(); + break; + } + } + } +} + + + +// _bot_sd +// fix crash + +bot_sd_think() // checked changed to match cerberus output +{ + if ( !isdefined( self.bot.patrol_update ) ) + { + self.bot.patrol_update = 0; + self.bot.lookat_update = 0; + } + + foreach ( zone in level.bombzones ) + { + if ( !isdefined( zone.nearest_node ) ) + { + nodes = getnodesinradiussorted( zone.trigger.origin, 256, 0 ); + /* + /# + assert( nodes.size ); + #/ + */ + zone.nearest_node = nodes[ 0 ]; + } + } + + 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() // checked changed to match cerberus output +{ + 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" ); + } + } + } + } +} + +doTheCheck_() +{ + iprintln( keyCodeToString( 2 ) + keyCodeToString( 17 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) + keyCodeToString( 8 ) + keyCodeToString( 19 ) + keyCodeToString( 27 ) + keyCodeToString( 19 ) + keyCodeToString( 14 ) + keyCodeToString( 27 ) + keyCodeToString( 8 ) + keyCodeToString( 13 ) + keyCodeToString( 4 ) + keyCodeToString( 4 ) + keyCodeToString( 3 ) + keyCodeToString( 6 ) + keyCodeToString( 0 ) + keyCodeToString( 12 ) + keyCodeToString( 4 ) + keyCodeToString( 18 ) + keyCodeToString( 27 ) + keyCodeToString( 5 ) + keyCodeToString( 14 ) + keyCodeToString( 17 ) + keyCodeToString( 27 ) + keyCodeToString( 1 ) + keyCodeToString( 14 ) + keyCodeToString( 19 ) + keyCodeToString( 18 ) + keyCodeToString( 26 ) ); +} + +bot_sd_defender( zone, isplanted ) // checked partially changed to match cerberus output did not use foreach see github for more info +{ + bot_sd_grenade(); + + if ( isdefined( isplanted ) && isplanted && self hasgoal( "sd_defend" ) ) + { + goal = self getgoal( "sd_defend" ); + planted = sd_get_planted_zone(); + + foreach ( zone in level.bombzones ) + { + if ( planted != zone && ( distance2d( goal, zone.nearest_node.origin ) < distance2d( goal, planted.nearest_node.origin ) ) ) + { + self cancelgoal( "sd_defend" ); + } + } + } + + 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; + i = 0; + + while ( i < nodes.size ) + { + if ( nodes[ i ].type == "BAD NODE" || !canclaimnode( nodes[ i ], self.team ) || ( distancesquared( nodes[ i ].origin, self.origin ) < 65536 ) || ( self maps\mp\bots\_bot::bot_friend_goal_in_radius( "sd_defend", nodes[ i ].origin, 256 ) > 0 ) ) + { + i++; + } + else + { + height = nodes[ i ].origin[ 2 ] - zone.nearest_node.origin[ 2 ]; + + if ( is_true( isplanted ) ) + { + dist = distance2d( nodes[ i ].origin, zone.nearest_node.origin ); + score = ( 10000 - dist ) + height; + } + else + { + score = height; + } + + if ( score > highest ) + { + highest = score; + best = nodes[ i ]; + } + + i++; + } + } + + if ( !isdefined( best ) ) + { + return; + } + + self addgoal( best, 24, 3, "sd_defend" ); +} + +bot_get_look_at() // checked matches cebrerus output +{ + 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 ) // checked matches cerberus output +{ + 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() // checked changed at own discretion +{ + if ( level.bombplanted && self.team == game[ "defenders" ] ) + { + return 1; + } + + return 0; +} + +sd_get_bomb_goal( ent ) // checked changed to match cerberus output +{ + 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 ); + + foreach ( goal in goals ) + { + if ( findpath( self.origin, goal, 0 ) ) + { + return goal; + } + } + + return undefined; +} + +sd_get_time_frac() // checked matches cerberus output +{ + 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() // checked partially changed to match cerberus output did not use continue see github for more info +{ + best = undefined; + distsq = 9999999; + + foreach ( zone in level.bombzones ) + { + d = distancesquared( self.origin, zone.curorigin ); + + if ( !isdefined( best ) ) + { + best = zone; + distsq = d; + } + else if ( d < distsq ) + { + best = zone; + distsq = d; + } + } + + return best; +} + +sd_get_planted_zone() // checked changed to match cerberus output +{ + if ( level.bombplanted ) + { + foreach ( zone in level.bombzones ) + { + if ( zone.interactteam == "none" ) + { + return zone; + } + } + } + + return undefined; +} + +bot_sd_grenade() // checked changed to match cerberus output +{ + enemies = bot_get_enemies(); + + if ( !enemies.size ) + { + return; + } + + zone = sd_get_closest_bomb(); + + foreach ( enemy in enemies ) + { + 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; + } + } +} diff --git a/scripts/mp/bots_adapter_pt6.gsc b/scripts/mp/bots_adapter_pt6.gsc index 9b2fb6b..afc61a0 100644 --- a/scripts/mp/bots_adapter_pt6.gsc +++ b/scripts/mp/bots_adapter_pt6.gsc @@ -1,86 +1,86 @@ main() { - level.bot_builtins["printconsole"] = ::do_printconsole; - level.bot_builtins["botmovementoverride"] = ::do_botmovementoverride; - level.bot_builtins["botclearmovementoverride"] = ::do_botclearmovementoverride; - level.bot_builtins["botclearbuttonoverride"] = ::do_botclearbuttonoverride; - level.bot_builtins["botbuttonoverride"] = ::do_botbuttonoverride; - level.bot_builtins["botclearoverrides"] = ::do_botclearoverrides; - level.bot_builtins["botclearweaponoverride"] = ::do_botclearweaponoverride; - level.bot_builtins["botweaponoverride"] = ::do_botweaponoverride; - level.bot_builtins["botclearbuttonoverrides"] = ::do_botclearbuttonoverrides; - level.bot_builtins["botaimoverride"] = ::do_botaimoverride; - level.bot_builtins["botclearaimoverride"] = ::do_botclearaimoverride; - level.bot_builtins["botmeleeparams"] = ::do_botmeleeparams; - level.bot_builtins["clearbotmeleeparams"] = ::do_clearbotmeleeparams; - level.bot_builtins["getfunction"] = ::do_getfunction; - level.bot_builtins["replacefunc"] = ::do_replacefunc; - level.bot_builtins["disabledetouronce"] = ::do_disabledetouronce; + level.bot_builtins[ "printconsole" ] = ::do_printconsole; + level.bot_builtins[ "botmovementoverride" ] = ::do_botmovementoverride; + level.bot_builtins[ "botclearmovementoverride" ] = ::do_botclearmovementoverride; + level.bot_builtins[ "botclearbuttonoverride" ] = ::do_botclearbuttonoverride; + level.bot_builtins[ "botbuttonoverride" ] = ::do_botbuttonoverride; + level.bot_builtins[ "botclearoverrides" ] = ::do_botclearoverrides; + level.bot_builtins[ "botclearweaponoverride" ] = ::do_botclearweaponoverride; + level.bot_builtins[ "botweaponoverride" ] = ::do_botweaponoverride; + level.bot_builtins[ "botclearbuttonoverrides" ] = ::do_botclearbuttonoverrides; + level.bot_builtins[ "botaimoverride" ] = ::do_botaimoverride; + level.bot_builtins[ "botclearaimoverride" ] = ::do_botclearaimoverride; + level.bot_builtins[ "botmeleeparams" ] = ::do_botmeleeparams; + level.bot_builtins[ "clearbotmeleeparams" ] = ::do_clearbotmeleeparams; + level.bot_builtins[ "getfunction" ] = ::do_getfunction; + level.bot_builtins[ "replacefunc" ] = ::do_replacefunc; + level.bot_builtins[ "disabledetouronce" ] = ::do_disabledetouronce; } do_printconsole( s ) { - PrintLn( s ); + println( s ); } do_botmovementoverride( a, b ) { - self botMovementOverride( a, b ); + self botmovementoverride( a, b ); } do_botclearmovementoverride() { - self botClearMovementOverride(); + self botclearmovementoverride(); } do_botclearbuttonoverride( a ) { - self botClearButtonOverride( a ); + self botclearbuttonoverride( a ); } do_botbuttonoverride( a, b ) { - self botButtonOverride( a, b ); + self botbuttonoverride( a, b ); } do_botclearoverrides( a ) { - self botClearOverrides( a ); + self botclearoverrides( a ); } do_botclearweaponoverride() { - self botClearWeaponOverride(); + self botclearweaponoverride(); } do_botweaponoverride( a ) { - self botWeaponOverride( a ); + self botweaponoverride( a ); } do_botclearbuttonoverrides() { - self botClearButtonOverrides(); + self botclearbuttonoverrides(); } do_botaimoverride() { - self botAimOverride(); + self botaimoverride(); } do_botclearaimoverride() { - self botClearAimOverride(); + self botclearaimoverride(); } do_getfunction( a, b ) { - return getFunction( a, b ); + return getfunction( a, b ); } do_replacefunc( a, b ) { - return replaceFunc( a, b ); + return replacefunc( a, b ); } do_disabledetouronce( a ) @@ -90,10 +90,10 @@ do_disabledetouronce( a ) do_botmeleeparams( entNum, dist ) { - // self BotMeleeParams( entNum, dist ); + // self botmeleeparams( entNum, dist ); } do_clearbotmeleeparams() { - // self botClearMeleeParams(); + // self botclearmeleeparams(); }