diff --git a/maps/mp/bots/_bot.gsc b/maps/mp/bots/_bot.gsc index 9d6219e..478baa0 100644 --- a/maps/mp/bots/_bot.gsc +++ b/maps/mp/bots/_bot.gsc @@ -158,7 +158,7 @@ onPlayerDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, if(self is_bot()) { self maps\mp\bots\_bot_internal::onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); - //self maps\mp\bots\_bot_script::onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); + self maps\mp\bots\_bot_script::onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); } self [[level.prevCallbackPlayerDamage]](eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset); @@ -172,7 +172,7 @@ onPlayerKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHi if(self is_bot()) { self maps\mp\bots\_bot_internal::onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); - //self maps\mp\bots\_bot_script::onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); + self maps\mp\bots\_bot_script::onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); } self [[level.prevCallbackPlayerKilled]](eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration); @@ -368,7 +368,7 @@ connected() } self thread maps\mp\bots\_bot_internal::connected(); - //self thread maps\mp\bots\_bot_script::connected(); + self thread maps\mp\bots\_bot_script::connected(); level.bots[level.bots.size] = self; self thread onDisconnect(); @@ -384,7 +384,7 @@ added() self endon("disconnect"); self thread maps\mp\bots\_bot_internal::added(); - //self thread maps\mp\bots\_bot_script::added(); + self thread maps\mp\bots\_bot_script::added(); } /* diff --git a/maps/mp/bots/_bot_script.gsc b/maps/mp/bots/_bot_script.gsc new file mode 100644 index 0000000..c512a45 --- /dev/null +++ b/maps/mp/bots/_bot_script.gsc @@ -0,0 +1,6452 @@ +/* + _bot_script + Author: INeedGames + Date: 05/11/2021 + Tells the bots what to do. + Similar to t5's _bot +*/ + +#include common_scripts\utility; +#include maps\mp\_utility; +#include maps\mp\gametypes\_hud_util; +#include maps\mp\bots\_bot_utility; + +/* + When the bot gets added into the game. +*/ +added() +{ + self endon("disconnect"); + + self setPlayerData("experience", self bot_get_rank()); + self setPlayerData("prestige", self bot_get_prestige()); + + self setPlayerData("cardTitle", random(getCardTitles())); + self setPlayerData("cardIcon", random(getCardIcons())); + + //self setClasses(); + //self setKillstreaks(); + + self set_diff(); +} + +/* + When the bot connects to the game. +*/ +connected() +{ + self endon("disconnect"); + + self.killerLocation = undefined; + self.lastKiller = undefined; + + self thread difficulty(); + self thread teamWatch(); + self thread classWatch(); + + self thread onBotSpawned(); + self thread onSpawned(); + + self thread onDeath(); + self thread onGiveLoadout(); + + self thread onKillcam(); +} + +/* + 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.team)) + continue; + + if (player is_bot()) + continue; + + p = player getPlayerData("prestige"); + break; + } + } + else if (p_dvar == -2) + { + p = randomInt(12); + } + else + { + p = p_dvar; + } + + return p; +} + +/* + Gets an exp amount for the bot that is nearish the host's xp. +*/ +bot_get_rank() +{ + rank = 1; + rank_dvar = getDvarInt("bots_loadout_rank"); + + if (rank_dvar == -1) + { + ranks = []; + bot_ranks = []; + human_ranks = []; + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[i]; + + if ( player == self ) + continue; + + if ( !IsDefined( player.pers[ "rank" ] ) ) + continue; + + if ( player is_bot() ) + { + bot_ranks[ bot_ranks.size ] = player.pers[ "rank" ]; + } + else + { + human_ranks[ human_ranks.size ] = player.pers[ "rank" ]; + } + } + + if( !human_ranks.size ) + human_ranks[ human_ranks.size ] = Round( random_normal_distribution( 45, 20, 0, level.maxRank ) ); + + human_avg = array_average( human_ranks ); + + while ( bot_ranks.size + human_ranks.size < 5 ) + { + // add some random ranks for better random number distribution + rank = human_avg + RandomIntRange( -10, 10 ); + human_ranks[ human_ranks.size ] = rank; + } + + ranks = array_combine( human_ranks, bot_ranks ); + + avg = array_average( ranks ); + s = array_std_deviation( ranks, avg ); + + rank = Round( random_normal_distribution( avg, s, 0, level.maxRank ) ); + } + else if (rank_dvar == 0) + { + rank = Round( random_normal_distribution( 45, 20, 0, level.maxRank ) ); + } + else + { + rank = Round( random_normal_distribution( rank_dvar, 5, 0, level.maxRank ) ); + } + + return maps\mp\gametypes\_rank::getRankInfoMinXP( rank ); +} + +/* + returns an array of all card titles +*/ +getCardTitles() +{ + cards = []; + + for (i = 0; i < 600; i++) + { + card_name = tableLookupByRow( "mp/cardTitleTable.csv", i, 0 ); + + if (card_name == "") + continue; + + if (!isSubStr(card_name, "cardtitle_")) + continue; + + cards[cards.size] = card_name; + } + + return cards; +} + +/* + returns an array of all card icons +*/ +getCardIcons() +{ + cards = []; + + for (i = 0; i < 400; i++) + { + card_name = tableLookupByRow( "mp/cardIconTable.csv", i, 0 ); + + if (card_name == "") + continue; + + if (!isSubStr(card_name, "cardicon_")) + continue; + + cards[cards.size] = card_name; + } + + return cards; +} + +/* + returns if attachment is valid with attachment 2 +*/ +isValidAttachmentCombo(att1, att2) +{ + colIndex = tableLookupRowNum( "mp/attachmentCombos.csv", 0, att1 ); + + if (tableLookup( "mp/attachmentCombos.csv", 0, att2, colIndex ) == "no") + return false; + + return true; +} + +/* + returns all attachments for the given gun +*/ +getAttachmentsForGun(gun) +{ + row = tableLookupRowNum( "mp/statStable.csv", 4, gun ); + + attachments = []; + for ( h = 0; h < 10; h++ ) + { + attachmentName = tableLookupByRow( "mp/statStable.csv", row, h + 11 ); + + if( attachmentName == "" ) + { + attachments[attachments.size] = "none"; + break; + } + + attachments[attachments.size] = attachmentName; + } + + return attachments; +} + +/* + returns all primaries +*/ +getPrimaries() +{ + primaries = []; + + for (i = 0; i < 160; i++) + { + weapon_type = tableLookupByRow( "mp/statstable.csv", i, 2 ); + + if (weapon_type != "weapon_assault" && weapon_type != "weapon_riot" && weapon_type != "weapon_smg" && weapon_type != "weapon_sniper" && weapon_type != "weapon_lmg") + continue; + + weapon_name = tableLookupByRow( "mp/statstable.csv", i, 4 ); + + primaries[primaries.size] = weapon_name; + } + + return primaries; +} + +/* + returns all secondaries +*/ +getSecondaries() +{ + secondaries = []; + + for (i = 0; i < 160; i++) + { + weapon_type = tableLookupByRow( "mp/statstable.csv", i, 2 ); + + if (weapon_type != "weapon_pistol" && weapon_type != "weapon_machine_pistol" && weapon_type != "weapon_projectile" && weapon_type != "weapon_shotgun") + continue; + + weapon_name = tableLookupByRow( "mp/statstable.csv", i, 4 ); + + if (weapon_name == "gl") + continue; + + secondaries[secondaries.size] = weapon_name; + } + + return secondaries; +} + +/* + returns all camos +*/ +getCamos() +{ + camos = []; + + for (i = 0; i < 15; i++) + { + camo_name = tableLookupByRow( "mp/camoTable.csv", i, 1 ); + + if (camo_name == "") + continue; + + camos[camos.size] = camo_name; + } + + return camos; +} + +/* + returns all perks for the given type +*/ +getPerks(perktype) +{ + perks = []; + for (i = 0; i < 50; i++) + { + perk_type = tableLookupByRow( "mp/perktable.csv", i, 5 ); + + if (perk_type != perktype) + continue; + + perk_name = tableLookupByRow( "mp/perktable.csv", i, 1 ); + + if (perk_name == "specialty_c4death") + continue; + + if (perk_name == "_specialty_blastshield") + continue; + + perks[perks.size] = perk_name; + } + + return perks; +} + +/* + returns kill cost for a streak +*/ +getKillsNeededForStreak(streak) +{ + return int(tableLookup("mp/killstreakTable.csv", 1, streak, 4)); +} + +/* + returns all killstreaks +*/ +getKillstreaks() +{ + killstreaks = []; + for (i = 0; i < 40; i++) + { + streak_name = tableLookupByRow( "mp/killstreakTable.csv", i, 1 ); + + if(streak_name == "" || streak_name == "none") + continue; + + if(streak_name == "b1") + continue; + + if(streak_name == "sentry") // theres an airdrop version + continue; + + if (isSubstr(streak_name, "KILLSTREAKS_")) + continue; + + killstreaks[killstreaks.size] = streak_name; + } + return killstreaks; +} + +/* + bots chooses a random perk +*/ +chooseRandomPerk(perkkind, primary, primaryAtts) +{ + perks = getPerks(perkkind); + rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData("experience") ); + allowOp = (getDvarInt("bots_loadout_allow_op") >= 1); + reasonable = getDvarInt("bots_loadout_reasonable"); + + while (true) + { + perk = random(perks); + + if (!allowOp) + { + if (perkkind == "perk4") + return "specialty_null"; + + if (perk == "specialty_pistoldeath") + continue; + + if (perk == "specialty_coldblooded") + continue; + + if (perk == "specialty_localjammer") + continue; + } + + if (reasonable) + { + if (perk == "specialty_bling") + continue; + if (perk == "specialty_localjammer") + continue; + if (perk == "throwingknife_mp") + continue; + if (perk == "specialty_blastshield") + continue; + if (perk == "frag_grenade_mp") + continue; + if (perk == "specialty_copycat") + continue; + + if (perkkind == "perk1") + { + if (perk == "specialty_onemanarmy") + { + if (primaryAtts[0] != "gl"/* && primaryAtts[1] != "gl"*/) + continue; + } + } + + if (perkkind == "perk2") + { + if (perk != "specialty_bulletdamage") + { + if (perk == "specialty_explosivedamage") + { + if (primaryAtts[0] != "gl"/* && primaryAtts[1] != "gl"*/) + continue; + } + else + { + if (randomInt(100) < 10) + continue; + + if (primary == "cheytac") + continue; + if (primary == "rpd") + continue; + if (primary == "ak47" && randomInt(100) < 80) + continue; + if (primary == "aug") + continue; + if (primary == "barrett" && randomInt(100) < 80) + continue; + if (primary == "tavor" && randomInt(100) < 80) + continue; + if (primary == "scar") + continue; + if (primary == "masada" && randomInt(100) < 60) + continue; + if (primary == "m4" && randomInt(100) < 80) + continue; + if (primary == "m16") + continue; + if (primary == "fal") + continue; + if (primary == "famas") + continue; + } + } + } + } + + if (perk == "specialty_null") + continue; + + if (!self isItemUnlocked(perk)) + continue; + + if (RandomFloatRange(0, 1) < ((rank / level.maxRank) + 0.1)) + self.pers["bots"]["unlocks"]["upgraded_"+perk] = true; + + return perk; + } +} + +/* + choose a random camo +*/ +chooseRandomCamo() +{ + camos = getCamos(); + + while (true) + { + camo = random(camos); + + if (camo == "gold" || camo == "prestige") + continue; + + return camo; + } +} + +/* + choose a random primary +*/ +chooseRandomPrimary() +{ + primaries = getPrimaries(); + allowOp = (getDvarInt("bots_loadout_allow_op") >= 1); + reasonable = getDvarInt("bots_loadout_reasonable"); + + while (true) + { + primary = random(primaries); + + if (!allowOp) + { + if (primary == "riotshield") + continue; + } + + if (reasonable) + { + if (primary == "riotshield") + continue; + if (primary == "wa2000") + continue; + if (primary == "uzi") + continue; + if (primary == "sa80") + continue; + if (primary == "fn2000") + continue; + if (primary == "m240") + continue; + if (primary == "mg4") + continue; + } + + if (!self isItemUnlocked(primary)) + continue; + + return primary; + } +} + +/* + choose a random secondary +*/ +chooseRandomSecondary(perk1) +{ + if (perk1 == "specialty_onemanarmy") + return "onemanarmy"; + + secondaries = getSecondaries(); + allowOp = (getDvarInt("bots_loadout_allow_op") >= 1); + reasonable = getDvarInt("bots_loadout_reasonable"); + + while (true) + { + secondary = random(secondaries); + + if (!allowOp) + { + if (secondary == "at4" || secondary == "rpg" || secondary == "m79") + continue; + } + + if (reasonable) + { + if (secondary == "ranger") + continue; + if (secondary == "model1887") + continue; + } + + if (!self isItemUnlocked(secondary)) + continue; + + if (secondary == "onemanarmy") + continue; + + return secondary; + } +} + +/* + chooses random attachements for a gun +*/ +chooseRandomAttachmentComboForGun(gun) +{ + atts = getAttachmentsForGun(gun); + rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData("experience") ); + allowOp = (getDvarInt("bots_loadout_allow_op") >= 1); + reasonable = getDvarInt("bots_loadout_reasonable"); + + if (RandomFloatRange(0, 1) >= ((rank / level.maxRank) + 0.1)) + { + retAtts = []; + retAtts[0] = "none"; + retAtts[1] = "none"; + + return retAtts; + } + + while (true) + { + att1 = random(atts); + att2 = random(atts); + + if (!isValidAttachmentCombo(att1, att2)) + continue; + + if (!allowOp) + { + if (att1 == "gl" || att2 == "gl") + continue; + } + + if (reasonable) + { + if (att1 == "shotgun" || att2 == "shotgun") + continue; + + if (att1 == "akimbo" || att2 == "akimbo") + { + if (gun != "ranger" && gun != "model1887" && gun != "glock") + continue; + } + + if (att1 == "acog" || att2 == "acog") + continue; + if (att1 == "thermal" || att2 == "thermal") + continue; + if (att1 == "rof" || att2 == "rof") + continue; + + if (att1 == "silencer" || att2 == "silencer") + { + if (gun == "spas12" || gun == "aa12" || gun == "striker" || gun == "rpd" || gun == "m1014" || gun == "cheytac" || gun == "barrett" || gun == "aug" || gun == "m240" || gun == "mg4" || gun == "sa80" || gun == "wa2000") + continue; + } + } + + retAtts = []; + retAtts[0] = att1; + retAtts[1] = att2; + + return retAtts; + } +} + +/* + choose a random tacticle grenade +*/ +chooseRandomTactical() +{ + tacts = strTok("flash_grenade,smoke_grenade,concussion_grenade", ","); + reasonable = getDvarInt("bots_loadout_reasonable"); + + while (true) + { + tact = random(tacts); + + if (reasonable) + { + if (tact == "smoke_grenade") + continue; + } + + return tact; + } +} + +/* + sets up all classes for a bot +*/ +setClasses() +{ + n = 5; + if (!self is_bot()) + n = 15; + + rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData("experience") ); + + if (RandomFloatRange(0, 1) < ((rank / level.maxRank) + 0.1)) + { + self.pers["bots"]["unlocks"]["ghillie"] = true; + self.pers["bots"]["behavior"]["quickscope"] = true; + } + + for (i = 0; i < n; i++) + { + equipment = chooseRandomPerk("equipment"); + perk3 = chooseRandomPerk("perk3"); + deathstreak = chooseRandomPerk("perk4"); + tactical = chooseRandomTactical(); + primary = chooseRandomPrimary(); + primaryAtts = chooseRandomAttachmentComboForGun(primary); + perk1 = chooseRandomPerk("perk1", primary, primaryAtts); + + if (perk1 != "specialty_bling") + primaryAtts[1] = "none"; + + perk2 = chooseRandomPerk("perk2", primary, primaryAtts); + primaryCamo = chooseRandomCamo(); + secondary = chooseRandomSecondary(perk1); + secondaryAtts = chooseRandomAttachmentComboForGun(secondary); + + if (perk1 != "specialty_bling" || !isDefined(self.pers["bots"]["unlocks"]["upgraded_specialty_bling"])) + secondaryAtts[1] = "none"; + + self setPlayerData("customClasses", i, "weaponSetups", 0, "weapon", primary); + self setPlayerData("customClasses", i, "weaponSetups", 0, "attachment", 0, primaryAtts[0]); + self setPlayerData("customClasses", i, "weaponSetups", 0, "attachment", 1, primaryAtts[1]); + self setPlayerData("customClasses", i, "weaponSetups", 0, "camo", primaryCamo); + + self setPlayerData("customClasses", i, "weaponSetups", 1, "weapon", secondary); + self setPlayerData("customClasses", i, "weaponSetups", 1, "attachment", 0, secondaryAtts[0]); + self setPlayerData("customClasses", i, "weaponSetups", 1, "attachment", 1, secondaryAtts[1]); + + self setPlayerData("customClasses", i, "perks", 0, equipment); + self setPlayerData("customClasses", i, "perks", 1, perk1); + self setPlayerData("customClasses", i, "perks", 2, perk2); + self setPlayerData("customClasses", i, "perks", 3, perk3); + self setPlayerData("customClasses", i, "perks", 4, deathstreak); + self setPlayerData("customClasses", i, "specialGrenade", tactical); + } +} + +/* + returns if killstreak is going to have the same kill cost +*/ +isColidingKillstreak(killstreaks, killstreak) +{ + ksVal = getKillsNeededForStreak(killstreak); + + for (i = 0; i < killstreaks.size; i++) + { + ks = killstreaks[i]; + + if (ks == "") + continue; + + if (ks == "none") + continue; + + ksV = getKillsNeededForStreak(ks); + + if (ksV <= 0) + continue; + + if (ksV != ksVal) + continue; + + return true; + } + + return false; +} + +/* + bots set their killstreaks +*/ +setKillstreaks() +{ + rankId = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData( "experience" ) ) + 1; + + allStreaks = getKillstreaks(); + + killstreaks = []; + killstreaks[0] = ""; + killstreaks[1] = ""; + killstreaks[2] = ""; + + chooseableStreaks = 0; + if (rankId >= 10) + chooseableStreaks++; + if (rankId >= 15) + chooseableStreaks++; + if (rankId >= 22) + chooseableStreaks++; + + reasonable = getDvarInt("bots_loadout_reasonable"); + op = getDvarInt("bots_loadout_allow_op"); + + i = 0; + while (i < chooseableStreaks) + { + slot = randomInt(3); + + if (killstreaks[slot] != "") + continue; + + streak = random(allStreaks); + + if (isColidingKillstreak(killstreaks, streak)) + continue; + + if (reasonable) + { + if (streak == "stealth_airstrike") + continue; + if (streak == "airdrop_mega") + continue; + if (streak == "emp") + continue; + if (streak == "airdrop_sentry_minigun") + continue; + if (streak == "airdrop") + continue; + if (streak == "precision_airstrike") + continue; + if (streak == "helicopter") + continue; + } + + if (op) + { + if (streak == "nuke") + continue; + } + + killstreaks[slot] = streak; + i++; + } + + if (killstreaks[0] == "") + killstreaks[0] = "uav"; + if (killstreaks[1] == "") + killstreaks[1] = "airdrop"; + if (killstreaks[2] == "") + killstreaks[2] = "predator_missile"; + + self setPlayerData("killstreaks", 0, killstreaks[0]); + self setPlayerData("killstreaks", 1, killstreaks[1]); + self setPlayerData("killstreaks", 2, killstreaks[2]); +} + +/* + The callback for when the bot gets killed. +*/ +onKilled(eInflictor, eAttacker, iDamage, sMeansOfDeath, sWeapon, vDir, sHitLoc, timeOffset, deathAnimDuration) +{ + self.killerLocation = undefined; + self.lastKiller = undefined; + + if(!IsDefined( self ) || !isDefined(self.team)) + return; + + if ( sMeansOfDeath == "MOD_FALLING" || sMeansOfDeath == "MOD_SUICIDE" ) + return; + + if ( iDamage <= 0 ) + return; + + if(!IsDefined( eAttacker ) || !isDefined(eAttacker.team)) + return; + + if(eAttacker == self) + return; + + if(level.teamBased && eAttacker.team == self.team) + return; + + if ( !IsDefined( eInflictor ) || eInflictor.classname != "player" ) + return; + + if(!isAlive(eAttacker)) + return; + + self.killerLocation = eAttacker.origin; + self.lastKiller = eAttacker; +} + +/* + The callback for when the bot gets damaged. +*/ +onDamage(eInflictor, eAttacker, iDamage, iDFlags, sMeansOfDeath, sWeapon, vPoint, vDir, sHitLoc, timeOffset) +{ + if(!IsDefined( self ) || !isDefined(self.team)) + return; + + if(!isAlive(self)) + return; + + if ( sMeansOfDeath == "MOD_FALLING" || sMeansOfDeath == "MOD_SUICIDE" ) + return; + + if ( iDamage <= 0 ) + return; + + if(!IsDefined( eAttacker ) || !isDefined(eAttacker.team)) + return; + + if(eAttacker == self) + return; + + if(level.teamBased && eAttacker.team == self.team) + return; + + if ( !IsDefined( eInflictor ) || eInflictor.classname != "player" ) + return; + + if(!isAlive(eAttacker)) + return; + + if (!isSubStr(sWeapon, "_silencer_")) + self bot_cry_for_help( eAttacker ); + + self SetAttacker( eAttacker ); +} + +/* + When the bot gets attacked, have the bot ask for help from teammates. +*/ +bot_cry_for_help( attacker ) +{ + if ( !level.teamBased ) + { + return; + } + + theTime = GetTime(); + if ( IsDefined( self.help_time ) && theTime - self.help_time < 1000 ) + { + return; + } + + self.help_time = theTime; + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[i]; + + if ( !player is_bot() ) + { + continue; + } + + if(!isDefined(player.team)) + continue; + + if ( !IsAlive( player ) ) + { + continue; + } + + if ( player == self ) + { + continue; + } + + if ( player.team != self.team ) + { + continue; + } + + dist = player.pers["bots"]["skill"]["help_dist"]; + dist *= dist; + if ( DistanceSquared( self.origin, player.origin ) > dist ) + { + continue; + } + + if ( RandomInt( 100 ) < 50 ) + { + self SetAttacker( attacker ); + + if ( RandomInt( 100 ) > 70 ) + { + break; + } + } + } +} + +/* + watches when the bot enters a killcam +*/ +onKillcam() +{ + level endon("game_ended"); + self endon("disconnect"); + + for(;;) + { + self waittill("begin_killcam"); + + self thread doKillcamStuff(); + } +} + +/* + bots use copy cat and skip killcams +*/ +doKillcamStuff() +{ + self endon("disconnect"); + self endon("killcam_ended"); + + wait 0.5 + randomInt(3); + + wait 0.1; + + self notify("abort_killcam"); +} + +/* + Selects a class for the bot. +*/ +classWatch() +{ + self endon("disconnect"); + + for(;;) + { + while(!isdefined(self.pers["team"])) + wait .05; + + wait 0.5; + + class = self chooseRandomClass(); + + self notify("menuresponse", game["menu_changeclass"], class); + self.bot_change_class = true; + + while(isdefined(self.pers["team"]) && isdefined(self.pers["class"]) && isDefined(self.bot_change_class)) + wait .05; + } +} + +/* + Chooses a random class +*/ +chooseRandomClass() +{ + reasonable = getDvarInt("bots_loadout_reasonable"); + class = ""; + rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData( "experience" ) ) + 1; + if(rank < 4 || (randomInt(100) < 2 && !reasonable)) + { + while(class == "") + { + switch(randomInt(5)) + { + case 0: + class = "class0"; + break; + case 1: + class = "class1"; + break; + case 2: + class = "class2"; + break; + case 3: + if(rank >= 2) + class = "class3"; + break; + case 4: + if(rank >= 3) + class = "class4"; + break; + } + } + } + else + { + class = "custom"+(randomInt(5)+1); + } + + return class; +} + +/* + Makes sure the bot is on a team. +*/ +teamWatch() +{ + self endon("disconnect"); + + for(;;) + { + while(!isdefined(self.pers["team"])) + wait .05; + + wait 0.05; + self notify("menuresponse", game["menu_team"], getDvar("bots_team")); + + while(isdefined(self.pers["team"])) + wait .05; + } +} + +/* + Updates the bot's difficulty variables. +*/ +difficulty() +{ + self endon("disconnect"); + + for(;;) + { + rankVar = GetDvarInt("bots_skill"); + + if(rankVar != 9) + { + switch(self.pers["bots"]["skill"]["base"]) + { + case 1: + self.pers["bots"]["skill"]["aim_time"] = 0.6; + self.pers["bots"]["skill"]["init_react_time"] = 1500; + self.pers["bots"]["skill"]["reaction_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 600; + self.pers["bots"]["skill"]["remember_time"] = 750; + self.pers["bots"]["skill"]["fov"] = 0.7; + self.pers["bots"]["skill"]["dist_max"] = 2500; + self.pers["bots"]["skill"]["dist_start"] = 1000; + self.pers["bots"]["skill"]["spawn_time"] = 0.75; + self.pers["bots"]["skill"]["help_dist"] = 0; + self.pers["bots"]["skill"]["semi_time"] = 0.9; + self.pers["bots"]["skill"]["shoot_after_time"] = 1; + self.pers["bots"]["skill"]["aim_offset_time"] = 1.5; + self.pers["bots"]["skill"]["aim_offset_amount"] = 4; + self.pers["bots"]["skill"]["bone_update_interval"] = 2; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 0; + self.pers["bots"]["behavior"]["nade"] = 10; + self.pers["bots"]["behavior"]["sprint"] = 30; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 20; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 0; + break; + case 2: + self.pers["bots"]["skill"]["aim_time"] = 0.55; + self.pers["bots"]["skill"]["init_react_time"] = 1000; + self.pers["bots"]["skill"]["reaction_time"] = 800; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 1250; + self.pers["bots"]["skill"]["remember_time"] = 1500; + self.pers["bots"]["skill"]["fov"] = 0.65; + self.pers["bots"]["skill"]["dist_max"] = 3000; + self.pers["bots"]["skill"]["dist_start"] = 1500; + self.pers["bots"]["skill"]["spawn_time"] = 0.65; + self.pers["bots"]["skill"]["help_dist"] = 500; + self.pers["bots"]["skill"]["semi_time"] = 0.75; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.75; + self.pers["bots"]["skill"]["aim_offset_time"] = 1; + self.pers["bots"]["skill"]["aim_offset_amount"] = 3; + self.pers["bots"]["skill"]["bone_update_interval"] = 1.5; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 10; + self.pers["bots"]["behavior"]["nade"] = 15; + self.pers["bots"]["behavior"]["sprint"] = 45; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 15; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 10; + break; + case 3: + self.pers["bots"]["skill"]["aim_time"] = 0.4; + self.pers["bots"]["skill"]["init_react_time"] = 750; + self.pers["bots"]["skill"]["reaction_time"] = 500; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1000; + self.pers["bots"]["skill"]["no_trace_look_time"] = 1500; + self.pers["bots"]["skill"]["remember_time"] = 2000; + self.pers["bots"]["skill"]["fov"] = 0.6; + self.pers["bots"]["skill"]["dist_max"] = 4000; + self.pers["bots"]["skill"]["dist_start"] = 2250; + self.pers["bots"]["skill"]["spawn_time"] = 0.5; + self.pers["bots"]["skill"]["help_dist"] = 750; + self.pers["bots"]["skill"]["semi_time"] = 0.65; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.65; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.75; + self.pers["bots"]["skill"]["aim_offset_amount"] = 2.5; + self.pers["bots"]["skill"]["bone_update_interval"] = 1; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 20; + self.pers["bots"]["behavior"]["nade"] = 20; + self.pers["bots"]["behavior"]["sprint"] = 50; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 25; + break; + case 4: + self.pers["bots"]["skill"]["aim_time"] = 0.3; + self.pers["bots"]["skill"]["init_react_time"] = 600; + self.pers["bots"]["skill"]["reaction_time"] = 400; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 1500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 2000; + self.pers["bots"]["skill"]["remember_time"] = 3000; + self.pers["bots"]["skill"]["fov"] = 0.55; + self.pers["bots"]["skill"]["dist_max"] = 5000; + self.pers["bots"]["skill"]["dist_start"] = 3350; + self.pers["bots"]["skill"]["spawn_time"] = 0.35; + self.pers["bots"]["skill"]["help_dist"] = 1000; + self.pers["bots"]["skill"]["semi_time"] = 0.5; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.5; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.5; + self.pers["bots"]["skill"]["aim_offset_amount"] = 2; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.75; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_spineupper,j_ankle_le,j_ankle_ri,j_head,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 30; + self.pers["bots"]["behavior"]["nade"] = 25; + self.pers["bots"]["behavior"]["sprint"] = 55; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 35; + break; + case 5: + self.pers["bots"]["skill"]["aim_time"] = 0.25; + self.pers["bots"]["skill"]["init_react_time"] = 500; + self.pers["bots"]["skill"]["reaction_time"] = 300; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 2500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 3000; + self.pers["bots"]["skill"]["remember_time"] = 4000; + self.pers["bots"]["skill"]["fov"] = 0.5; + self.pers["bots"]["skill"]["dist_max"] = 7500; + self.pers["bots"]["skill"]["dist_start"] = 5000; + self.pers["bots"]["skill"]["spawn_time"] = 0.25; + self.pers["bots"]["skill"]["help_dist"] = 1500; + self.pers["bots"]["skill"]["semi_time"] = 0.4; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.35; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.35; + self.pers["bots"]["skill"]["aim_offset_amount"] = 1.5; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.5; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 40; + self.pers["bots"]["behavior"]["nade"] = 35; + self.pers["bots"]["behavior"]["sprint"] = 60; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 50; + break; + case 6: + self.pers["bots"]["skill"]["aim_time"] = 0.2; + self.pers["bots"]["skill"]["init_react_time"] = 250; + self.pers["bots"]["skill"]["reaction_time"] = 150; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 2500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 4000; + self.pers["bots"]["skill"]["remember_time"] = 5000; + self.pers["bots"]["skill"]["fov"] = 0.45; + self.pers["bots"]["skill"]["dist_max"] = 10000; + self.pers["bots"]["skill"]["dist_start"] = 7500; + self.pers["bots"]["skill"]["spawn_time"] = 0.2; + self.pers["bots"]["skill"]["help_dist"] = 2000; + self.pers["bots"]["skill"]["semi_time"] = 0.25; + self.pers["bots"]["skill"]["shoot_after_time"] = 0.25; + self.pers["bots"]["skill"]["aim_offset_time"] = 0.25; + self.pers["bots"]["skill"]["aim_offset_amount"] = 1; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.25; + self.pers["bots"]["skill"]["bones"] = "j_spineupper,j_head,j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 50; + self.pers["bots"]["behavior"]["nade"] = 45; + self.pers["bots"]["behavior"]["sprint"] = 65; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 10; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 75; + break; + case 7: + self.pers["bots"]["skill"]["aim_time"] = 0.1; + self.pers["bots"]["skill"]["init_react_time"] = 100; + self.pers["bots"]["skill"]["reaction_time"] = 50; + self.pers["bots"]["skill"]["no_trace_ads_time"] = 2500; + self.pers["bots"]["skill"]["no_trace_look_time"] = 4000; + self.pers["bots"]["skill"]["remember_time"] = 7500; + self.pers["bots"]["skill"]["fov"] = 0.4; + self.pers["bots"]["skill"]["dist_max"] = 15000; + self.pers["bots"]["skill"]["dist_start"] = 10000; + self.pers["bots"]["skill"]["spawn_time"] = 0.05; + self.pers["bots"]["skill"]["help_dist"] = 3000; + self.pers["bots"]["skill"]["semi_time"] = 0.1; + self.pers["bots"]["skill"]["shoot_after_time"] = 0; + self.pers["bots"]["skill"]["aim_offset_time"] = 0; + self.pers["bots"]["skill"]["aim_offset_amount"] = 0; + self.pers["bots"]["skill"]["bone_update_interval"] = 0.05; + self.pers["bots"]["skill"]["bones"] = "j_head"; + self.pers["bots"]["skill"]["ads_fov_multi"] = 0.5; + self.pers["bots"]["skill"]["ads_aimspeed_multi"] = 0.5; + + self.pers["bots"]["behavior"]["strafe"] = 65; + self.pers["bots"]["behavior"]["nade"] = 65; + self.pers["bots"]["behavior"]["sprint"] = 70; + self.pers["bots"]["behavior"]["camp"] = 5; + self.pers["bots"]["behavior"]["follow"] = 5; + self.pers["bots"]["behavior"]["crouch"] = 5; + self.pers["bots"]["behavior"]["switch"] = 2; + self.pers["bots"]["behavior"]["class"] = 2; + self.pers["bots"]["behavior"]["jump"] = 90; + break; + } + } + + wait 5; + } +} + +/* + Sets the bot difficulty. +*/ +set_diff() +{ + rankVar = GetDvarInt("bots_skill"); + + switch(rankVar) + { + case 0: + self.pers["bots"]["skill"]["base"] = Round( random_normal_distribution( 3.5, 1.75, 1, 7 ) ); + break; + case 8: + break; + case 9: + self.pers["bots"]["skill"]["base"] = randomIntRange(1, 7); + self.pers["bots"]["skill"]["aim_time"] = 0.05 * randomIntRange(1, 20); + self.pers["bots"]["skill"]["init_react_time"] = 50 * randomInt(100); + self.pers["bots"]["skill"]["reaction_time"] = 50 * randomInt(100); + self.pers["bots"]["skill"]["remember_time"] = 50 * randomInt(100); + self.pers["bots"]["skill"]["no_trace_ads_time"] = 50 * randomInt(100); + self.pers["bots"]["skill"]["no_trace_look_time"] = 50 * randomInt(100); + self.pers["bots"]["skill"]["fov"] = randomFloatRange(-1, 1); + + randomNum = randomIntRange(500, 25000); + self.pers["bots"]["skill"]["dist_start"] = randomNum; + self.pers["bots"]["skill"]["dist_max"] = randomNum * 2; + + self.pers["bots"]["skill"]["spawn_time"] = 0.05 * randomInt(20); + self.pers["bots"]["skill"]["help_dist"] = randomIntRange(500, 25000); + self.pers["bots"]["skill"]["semi_time"] = randomFloatRange(0.05, 1); + self.pers["bots"]["skill"]["shoot_after_time"] = randomFloatRange(0.05, 1); + self.pers["bots"]["skill"]["aim_offset_time"] = randomFloatRange(0.05, 1); + self.pers["bots"]["skill"]["aim_offset_amount"] = randomFloatRange(0.05, 1); + self.pers["bots"]["skill"]["bone_update_interval"] = randomFloatRange(0.05, 1); + self.pers["bots"]["skill"]["bones"] = "j_head,j_spineupper,j_ankle_le,j_ankle_ri"; + + self.pers["bots"]["behavior"]["strafe"] = randomInt(100); + self.pers["bots"]["behavior"]["nade"] = randomInt(100); + self.pers["bots"]["behavior"]["sprint"] = randomInt(100); + self.pers["bots"]["behavior"]["camp"] = randomInt(100); + self.pers["bots"]["behavior"]["follow"] = randomInt(100); + self.pers["bots"]["behavior"]["crouch"] = randomInt(100); + self.pers["bots"]["behavior"]["switch"] = randomInt(100); + self.pers["bots"]["behavior"]["class"] = randomInt(100); + self.pers["bots"]["behavior"]["jump"] = randomInt(100); + break; + default: + self.pers["bots"]["skill"]["base"] = rankVar; + break; + } +} + +/* + Allows the bot to spawn when force respawn is disabled + Watches when the bot dies +*/ +onDeath() +{ + self endon("disconnect"); + + for(;;) + { + self waittill("death"); + + self.wantSafeSpawn = true; + } +} + +/* + Watches when the bot is given a loadout +*/ +onGiveLoadout() +{ + self endon("disconnect"); + + for(;;) + { + self waittill("giveLoadout"); + + class = self.class; + if (isDefined(self.bot_oma_class)) + class = self.bot_oma_class; + + //self botGiveLoadout(self.team, class, !isDefined(self.bot_oma_class)); + self.bot_oma_class = undefined; + } +} + +/* + When the bot spawns. +*/ +onSpawned() +{ + self endon("disconnect"); + + for(;;) + { + self waittill("spawned_player"); + + if(randomInt(100) <= self.pers["bots"]["behavior"]["class"]) + self.bot_change_class = undefined; + + self.bot_lock_goal = false; + self.bot_oma_class = undefined; + self.help_time = undefined; + self.bot_was_follow_script_update = undefined; + self.bot_stuck_on_carepackage = undefined; + + if (getDvarInt("bots_play_obj")) + self thread bot_dom_cap_think(); + } +} + +/* + When the bot spawned, after the difficulty wait. Start the logic for the bot. +*/ +onBotSpawned() +{ + self endon("disconnect"); + level endon("game_ended"); + + for(;;) + { + self waittill("bot_spawned"); + + self thread start_bot_threads(); + } +} + +/* + Starts all the bot thinking +*/ +start_bot_threads() +{ + self endon("disconnect"); + level endon("game_ended"); + self endon("death"); + + gameFlagWait("prematch_done"); + + // inventory usage + if (getDvarInt("bots_play_killstreak")) + self thread bot_killstreak_think(); + + self thread bot_weapon_think(); + self thread doReloadCancel(); + self thread bot_perk_think(); + + // script targeting + if (getDvarInt("bots_play_target_other")) + { + self thread bot_target_vehicle(); + self thread bot_equipment_kill_think(); + self thread bot_turret_think(); + } + + // airdrop + if (getDvarInt("bots_play_take_carepackages")) + { + self thread bot_watch_stuck_on_crate(); + self thread bot_crate_think(); + } + + // awareness + self thread bot_revenge_think(); + self thread bot_uav_think(); + self thread bot_listen_to_steps(); + self thread follow_target(); + + // camp and follow + if (getDvarInt("bots_play_camp")) + { + self thread bot_think_follow(); + self thread bot_think_camp(); + } + + // nades + if (getDvarInt("bots_play_nade")) + { + self thread bot_jav_loc_think(); + self thread bot_use_tube_think(); + self thread bot_use_grenade_think(); + self thread bot_use_equipment_think(); + self thread bot_watch_riot_weapons(); + self thread bot_watch_think_mw2(); // bots play mw2 + } + + // obj + if (getDvarInt("bots_play_obj")) + { + self thread bot_dom_def_think(); + self thread bot_dom_spawn_kill_think(); + + self thread bot_hq(); + + self thread bot_cap(); + + self thread bot_sab(); + + self thread bot_sd_defenders(); + self thread bot_sd_attackers(); + + self thread bot_dem_attackers(); + self thread bot_dem_defenders(); + + self thread bot_gtnw(); + self thread bot_oneflag(); + self thread bot_arena(); + self thread bot_vip(); + } + + self thread bot_think_revive(); +} + +/* + Increments the number of bots approching the obj, decrements when needed + Used for preventing too many bots going to one obj, or unreachable objs +*/ +bot_inc_bots(obj, unreach) +{ + level endon("game_ended"); + self endon("bot_inc_bots"); + + if (!isDefined(obj)) + return; + + if (!isDefined(obj.bots)) + obj.bots = 0; + + obj.bots++; + + ret = self waittill_any_return("death", "disconnect", "bad_path", "goal", "new_goal"); + + if (isDefined(obj) && (ret != "bad_path" || !isDefined(unreach))) + obj.bots--; +} + +/* + Watches when the bot is touching the obj and calls 'goal' +*/ +bots_watch_touch_obj(obj) +{ + self endon ("death"); + self endon ("disconnect"); + self endon ("bad_path"); + self endon ("goal"); + self endon ("new_goal"); + + for (;;) + { + wait 0.5; + + if (!isDefined(obj)) + { + self notify("bad_path"); + return; + } + + if (self IsTouching(obj)) + { + self notify("goal"); + return; + } + } +} + +/* + Is bot near any of the given waypoints +*/ +nearAnyOfWaypoints(dist, waypoints) +{ + dist *= dist; + for (i = 0; i < waypoints.size; i++) + { + waypoint = waypoints[i]; + + if (DistanceSquared(waypoint.origin, self.origin) > dist) + continue; + + return true; + } + + return false; +} + +/* + Returns nearest waypoint of waypoints +*/ +getNearestWaypointOfWaypoints(waypoints) +{ + answer = undefined; + closestDist = 2147483647; + for (i = 0; i < waypoints.size; i++) + { + waypoint = waypoints[i]; + thisDist = DistanceSquared(self.origin, waypoint.origin); + + if (isDefined(answer) && thisDist > closestDist) + continue; + + answer = waypoint; + closestDist = thisDist; + } + + return answer; +} + +/* + Watches while the obj is being carried, calls 'goal' when complete +*/ +bot_escort_obj(obj, carrier) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (!isDefined(obj)) + break; + + if (!isDefined(obj.carrier) || carrier == obj.carrier) + break; + } + + self notify("goal"); +} + +/* + Watches while the obj is not being carried, calls 'goal' when complete +*/ +bot_get_obj(obj) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (!isDefined(obj)) + break; + + if (isDefined(obj.carrier)) + break; + } + + self notify("goal"); +} + +/* + bots will defend their site from a planter/defuser +*/ +bot_defend_site(site) +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (!site isInUse()) + break; + } + + self notify("bad_path"); +} + +/* + Bots will go plant the bomb +*/ +bot_go_plant(plant) +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 1; + + if (level.bombPlanted) + break; + + if (self isTouching(plant.trigger)) + break; + } + + if(level.bombPlanted) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Bots will go defuse the bomb +*/ +bot_go_defuse(plant) +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 1; + + if (!level.bombPlanted) + break; + + if (self isTouching(plant.trigger)) + break; + } + + if(!level.bombPlanted) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Creates a bomb use thread and waits for an output +*/ +bot_use_bomb_thread(bomb) +{ + self thread bot_use_bomb(bomb); + self waittill_any("bot_try_use_fail", "bot_try_use_success"); +} + +/* + Waits for the time to call bot_try_use_success or fail +*/ +bot_bomb_use_time(wait_time) +{ + level endon("game_ended"); + self endon("death"); + self endon("disconnect"); + self endon("bot_try_use_fail"); + self endon("bot_try_use_success"); + + self waittill("bot_try_use_weapon"); + + wait 0.05; + elapsed = 0; + while(wait_time > elapsed) + { + wait 0.05;//wait first so waittill can setup + elapsed += 0.05; + + if(self InLastStand()) + { + self notify("bot_try_use_fail"); + return;//needed? + } + } + + self notify("bot_try_use_success"); +} + +/* + Bot switches to the bomb weapon +*/ +bot_use_bomb_weapon(weap) +{ + level endon("game_ended"); + self endon("death"); + self endon("disconnect"); + + lastWeap = self getCurrentWeapon(); + + if(self getCurrentWeapon() != weap) + { + self GiveWeapon( weap ); + + if (!self ChangeToWeapon(weap)) + { + self notify("bot_try_use_fail"); + return; + } + } + else + { + wait 0.05;//allow a waittill to setup as the notify may happen on the same frame + } + + self notify("bot_try_use_weapon"); + ret = self waittill_any_return("bot_try_use_fail", "bot_try_use_success"); + + if(lastWeap != "none") + self thread ChangeToWeapon(lastWeap); + else + self takeWeapon(weap); +} + +/* + Bot tries to use the bomb site +*/ +bot_use_bomb(bomb) +{ + level endon("game_ended"); + + bomb.inUse = true; + + myteam = self.team; + + self BotFreezeControls(true); + + bomb [[bomb.onBeginUse]](self); + + self clientClaimTrigger( bomb.trigger ); + self.claimTrigger = bomb.trigger; + + self thread bot_bomb_use_time(bomb.useTime / 1000); + self thread bot_use_bomb_weapon(bomb.useWeapon); + + result = self waittill_any_return("death", "disconnect", "bot_try_use_fail", "bot_try_use_success"); + + if (isDefined(self)) + { + self.claimTrigger = undefined; + self BotFreezeControls(false); + } + + bomb [[bomb.onEndUse]](myteam, self, (result == "bot_try_use_success")); + bomb.trigger releaseClaimedTrigger(); + + if(result == "bot_try_use_success") + bomb [[bomb.onUse]](self); + + bomb.inUse = false; +} + +/* + Fires the bots weapon until told to stop +*/ +fire_current_weapon() +{ + self endon("death"); + self endon("disconnect"); + self endon("weapon_change"); + self endon("stop_firing_weapon"); + + for (;;) + { + self thread BotPressAttack(0.05); + wait 0.1; + } +} + +/* + Changes to the weap +*/ +changeToWeapon(weap) +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + if (!self HasWeapon(weap)) + return false; + + if (self GetCurrentWeapon() == weap) + return true; + + self BotChangeToWeapon(weap); + + self waittill_any_timeout(5, "weapon_change"); + + return (self GetCurrentWeapon() == weap); +} + +/* + Bots throw the grenade +*/ +botThrowGrenade(nade, time) +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + if (!self GetAmmoCount(nade)) + return false; + + if (isSecondaryGrenade(nade)) + self thread BotPressSmoke(time); + else + self thread BotPressFrag(time); + + ret = self waittill_any_timeout(5, "grenade_fire"); + + return (ret == "grenade_fire"); +} + +/* + Gets the object thats the closest in the array +*/ +bot_array_nearest_curorigin(array) +{ + result = undefined; + + for(i = 0; i < array.size; i++) + if(!isDefined(result) || DistanceSquared(self.origin,array[i].curorigin) < DistanceSquared(self.origin,result.curorigin)) + result = array[i]; + + return result; +} + +/* + Returns an weapon thats a rocket with ammo +*/ +getRocketAmmo() +{ + answer = self getLockonAmmo(); + + if (isDefined(answer)) + return answer; + + if(self getAmmoCount("rpg_mp")) + answer = "rpg_mp"; + + return answer; +} + +/* + Returns a weapon thats lockon with ammo +*/ +getLockonAmmo() +{ + answer = undefined; + + if(self getAmmoCount("at4_mp")) + answer = "at4_mp"; + + if(self getAmmoCount("stinger_mp")) + answer = "stinger_mp"; + + if(self getAmmoCount("javelin_mp")) + answer = "javelin_mp"; + + return answer; +} + +/* + Clears goal when events death +*/ +stop_go_target_on_death(tar) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "new_goal" ); + self endon( "bad_path" ); + self endon( "goal" ); + + tar waittill_either("death", "disconnect"); + + self ClearScriptGoal(); +} + +/* + Goes to the target's location if it had one +*/ +follow_target() +{ + self endon( "death" ); + self endon( "disconnect" ); + + for(;;) + { + wait 1; + + if ( self HasScriptGoal() || self.bot_lock_goal ) + continue; + + if ( !self HasThreat() ) + continue; + + threat = self GetThreat(); + + if (!isPlayer(threat)) + continue; + + if(randomInt(100) > self.pers["bots"]["behavior"]["follow"]*5) + continue; + + self thread stop_go_target_on_death(threat); + + self SetScriptGoal(threat.origin, 64); + if (self waittill_any_return("new_goal", "goal", "bad_path") != "new_goal") + self ClearScriptGoal(); + } +} + +/* + Bot logic for bot determining to camp. +*/ +bot_think_camp() +{ + self endon( "death" ); + self endon( "disconnect" ); + + for(;;) + { + wait randomintrange(4,7); + + if ( self HasScriptGoal() || self.bot_lock_goal || self HasScriptAimPos() ) + continue; + + if(randomInt(100) > self.pers["bots"]["behavior"]["camp"]) + continue; + + campSpots = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsCamp.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsCamp[i].origin) > distSq) + continue; + + campSpots[campSpots.size] = level.waypointsCamp[i]; + } + campSpot = random(campSpots); + + if (!isDefined(campSpot)) + continue; + + self SetScriptGoal(campSpot.origin, 16); + + ret = self waittill_any_return("new_goal", "goal", "bad_path"); + + if (ret != "new_goal") + self ClearScriptGoal(); + + if (ret != "goal") + continue; + + self thread killCampAfterTime(randomIntRange(10,20)); + self CampAtSpot(campSpot.origin, campSpot.origin + AnglesToForward(campSpot.angles) * 2048); + } +} + +/* + Kills the camping thread when time +*/ +killCampAfterTime(time) +{ + self endon("death"); + self endon("disconnect"); + self endon("kill_camp_bot"); + + wait time + 0.05; + self ClearScriptGoal(); + self ClearScriptAimPos(); + + self notify("kill_camp_bot"); +} + +/* + Kills the camping thread when ent gone +*/ +killCampAfterEntGone(ent) +{ + self endon("death"); + self endon("disconnect"); + self endon("kill_camp_bot"); + + for (;;) + { + wait 0.05; + + if (!isDefined(ent)) + break; + } + + self ClearScriptGoal(); + self ClearScriptAimPos(); + + self notify("kill_camp_bot"); +} + +/* + Camps at the spot +*/ +CampAtSpot(origin, anglePos) +{ + self endon("kill_camp_bot"); + + self SetScriptGoal(origin, 64); + if (isDefined(anglePos)) + { + self SetScriptAimPos(anglePos); + } + + self waittill("new_goal"); + self ClearScriptAimPos(); + + self notify("kill_camp_bot"); +} + +/* + Bot logic for bot determining to follow another player. +*/ +bot_think_follow() +{ + self endon( "death" ); + self endon( "disconnect" ); + + for(;;) + { + wait randomIntRange(3,5); + + if ( self HasScriptGoal() || self.bot_lock_goal || self HasScriptAimPos() ) + continue; + + if(randomInt(100) > self.pers["bots"]["behavior"]["follow"]) + continue; + + if (!level.teamBased) + continue; + + follows = []; + distSq = self.pers["bots"]["skill"]["help_dist"] * self.pers["bots"]["skill"]["help_dist"]; + for (i = level.players.size - 1; i >= 0; i--) + { + player = level.players[i]; + + if (player == self) + continue; + + if(!isReallyAlive(player)) + continue; + + if (player.team != self.team) + continue; + + if (DistanceSquared(player.origin, self.origin) > distSq) + continue; + + follows[follows.size] = player; + } + toFollow = random(follows); + + if (!isDefined(toFollow)) + continue; + + self thread killFollowAfterTime(randomIntRange(10,20)); + self followPlayer(toFollow); + } +} + +/* + Kills follow when new goal +*/ +watchForFollowNewGoal() +{ + self endon("death"); + self endon("disconnect"); + self endon("kill_follow_bot"); + + for (;;) + { + self waittill("new_goal"); + + if (!isDefined(self.bot_was_follow_script_update)) + break; + } + + self ClearScriptAimPos(); + self notify("kill_follow_bot"); +} + +/* + Kills follow when time +*/ +killFollowAfterTime(time) +{ + self endon("death"); + self endon("disconnect"); + self endon("kill_follow_bot"); + + wait time; + + self ClearScriptGoal(); + self ClearScriptAimPos(); + self notify("kill_follow_bot"); +} + +/* + Determine bot to follow a player +*/ +followPlayer(who) +{ + self endon("kill_follow_bot"); + + self thread watchForFollowNewGoal(); + + for (;;) + { + wait 0.05; + + if (!isDefined(who) || !isReallyAlive(who)) + break; + + self SetScriptAimPos(who.origin + (0, 0, 42)); + myGoal = self GetScriptGoal(); + + if (isDefined(myGoal) && DistanceSquared(myGoal, who.origin) < 64*64) + continue; + + self.bot_was_follow_script_update = true; + self SetScriptGoal(who.origin, 32); + waittillframeend; + self.bot_was_follow_script_update = undefined; + + self waittill_either("goal", "bad_path"); + } + + self ClearScriptGoal(); + self ClearScriptAimPos(); + + self notify("kill_follow_bot"); +} + +/* + Bots thinking of using one man army and blast shield +*/ +bot_perk_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + for (;;) + { + wait randomIntRange(5,7); + + if (self IsUsingRemote()) + continue; + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + for (;self _hasPerk("specialty_blastshield");) + { + if (!self _hasPerk("_specialty_blastshield")) + { + if (randomInt(100) < 65) + break; + + self _setPerk("_specialty_blastshield"); + } + else + { + if (randomInt(100) < 90) + break; + + self _unsetPerk("_specialty_blastshield"); + } + + break; + } + + for (;self _hasPerk("specialty_onemanarmy") && self hasWeapon("onemanarmy_mp");) + { + if (self HasThreat() || self HasBotJavelinLocation()) + break; + + if (self InLastStand() && !self InFinalStand()) + break; + + anyWeapout = false; + weaponsList = self GetWeaponsListAll(); + for (i = 0; i < weaponsList.size; i++) + { + weap = weaponsList[i]; + + if (self getAmmoCount(weap) || weap == "onemanarmy_mp") + continue; + + anyWeapout = true; + } + + if ((!anyWeapout && randomInt(100) < 90) || randomInt(100) < 10) + break; + + class = self chooseRandomClass(); + self.bot_oma_class = class; + + if (!self changeToWeapon("onemanarmy_mp")) + { + self.bot_oma_class = undefined; + break; + } + + self BotFreezeControls(true); + wait 1; + self BotFreezeControls(false); + + self notify ( "menuresponse", game["menu_onemanarmy"], self.bot_oma_class ); + + self waittill_any_timeout ( 10, "changed_kit" ); + break; + } + } +} + +/* + Bots thinking of using a noobtube +*/ +bot_use_tube_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + doFastContinue = false; + + for (;;) + { + if (doFastContinue) + doFastContinue = false; + else + { + wait randomintRange(3, 7); + + chance = self.pers["bots"]["behavior"]["nade"] / 2; + if (chance > 20) + chance = 20; + + if (randomInt(100) > chance) + continue; + } + + tube = self getValidTube(); + if (!isDefined(tube)) + continue; + + if (self HasThreat() || self HasBotJavelinLocation() || self HasScriptAimPos()) + continue; + + if(self BotIsFrozen()) + continue; + + if (self IsBotFragging() || self IsBotSmoking()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + loc = undefined; + + if (!self nearAnyOfWaypoints(128, level.waypointsTube)) + { + tubeWps = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsTube.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsTube[i].origin) > distSq) + continue; + + tubeWps[tubeWps.size] = level.waypointsTube[i]; + } + tubeWp = random(tubeWps); + + myEye = self GetEye(); + if (!isDefined(tubeWp) || self HasScriptGoal() || self.bot_lock_goal) + { + traceForward = BulletTrace(myEye, myEye + AnglesToForward(self GetPlayerAngles()) * 900 * 5, false, self); + + loc = traceForward["position"]; + dist = DistanceSquared(self.origin, loc); + if (dist < level.bots_minGrenadeDistance || dist > level.bots_maxGrenadeDistance * 5) + continue; + + if (!bulletTracePassed(self.origin + (0, 0, 5), self.origin + (0, 0, 2048), false, self)) + continue; + + if (!bulletTracePassed(loc + (0, 0, 5), loc + (0, 0, 2048), false, self)) + continue; + + loc += (0, 0, dist/16000); + } + else + { + self SetScriptGoal(tubeWp.origin, 16); + + ret = self waittill_any_return("new_goal", "goal", "bad_path"); + + if (ret != "new_goal") + self ClearScriptGoal(); + + if (ret != "goal") + continue; + + doFastContinue = true; + continue; + } + } + else + { + tubeWp = self getNearestWaypointOfWaypoints(level.waypointsTube); + loc = tubeWp.origin + AnglesToForward(tubeWp.angles) * 2048; + } + + if (!isDefined(loc)) + continue; + + self SetScriptAimPos(loc); + self BotStopMoving(true); + wait 1; + + if (self changeToWeapon(tube)) + { + self thread fire_current_weapon(); + self waittill_any_timeout(5, "missile_fire", "weapon_change"); + self notify("stop_firing_weapon"); + } + + self ClearScriptAimPos(); + self BotStopMoving(false); + } +} + +/* + Bots thinking of using claymores and TIs +*/ +bot_use_equipment_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + doFastContinue = false; + + for (;;) + { + if (doFastContinue) + doFastContinue = false; + else + { + wait randomintRange(2, 4); + + chance = self.pers["bots"]["behavior"]["nade"] / 2; + if (chance > 20) + chance = 20; + + if (randomInt(100) > chance) + continue; + } + + nade = undefined; + if (self GetAmmoCount("claymore_mp")) + nade = "claymore_mp"; + if (self GetAmmoCount("flare_mp")) + nade = "flare_mp"; + if (self GetAmmoCount("c4_mp")) + nade = "c4_mp"; + + if (!isDefined(nade)) + continue; + + if (self HasThreat() || self HasBotJavelinLocation() || self HasScriptAimPos()) + continue; + + if(self BotIsFrozen()) + continue; + + if(self IsBotFragging() || self IsBotSmoking()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self inLastStand() && !self _hasPerk("specialty_laststandoffhand") && !self inFinalStand()) + continue; + + loc = undefined; + + if (!self nearAnyOfWaypoints(128, level.waypointsClay)) + { + clayWps = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsClay.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsClay[i].origin) > distSq) + continue; + + clayWps[clayWps.size] = level.waypointsClay[i]; + } + clayWp = random(clayWps); + + if (!isDefined(clayWp) || self HasScriptGoal() || self.bot_lock_goal) + { + myEye = self GetEye(); + loc = myEye + AnglesToForward(self GetPlayerAngles()) * 256; + + if (!bulletTracePassed(myEye, loc, false, self)) + continue; + } + else + { + self SetScriptGoal(clayWp.origin, 16); + + ret = self waittill_any_return("new_goal", "goal", "bad_path"); + + if (ret != "new_goal") + self ClearScriptGoal(); + + if (ret != "goal") + continue; + + doFastContinue = true; + continue; + } + } + else + { + clayWp = self getNearestWaypointOfWaypoints(level.waypointsClay); + loc = clayWp.origin + AnglesToForward(clayWp.angles) * 2048; + } + + if (!isDefined(loc)) + continue; + + self SetScriptAimPos(loc); + self BotStopMoving(true); + wait 1; + + self botThrowGrenade(nade, 0.05); + + self ClearScriptAimPos(); + self BotStopMoving(false); + } +} + +/* + Bots thinking of using grenades +*/ +bot_use_grenade_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + doFastContinue = false; + + for (;;) + { + if (doFastContinue) + doFastContinue = false; + else + { + wait randomintRange(4, 7); + + chance = self.pers["bots"]["behavior"]["nade"] / 2; + if (chance > 20) + chance = 20; + + if (randomInt(100) > chance) + continue; + } + + nade = self getValidGrenade(); + if (!isDefined(nade)) + continue; + + if (self HasThreat() || self HasBotJavelinLocation() || self HasScriptAimPos()) + continue; + + if(self BotIsFrozen()) + continue; + + if(self IsBotFragging() || self IsBotSmoking()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self inLastStand() && !self _hasPerk("specialty_laststandoffhand") && !self inFinalStand()) + continue; + + loc = undefined; + + if (!self nearAnyOfWaypoints(128, level.waypointsGren)) + { + nadeWps = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsGren.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsGren[i].origin) > distSq) + continue; + + nadeWps[nadeWps.size] = level.waypointsGren[i]; + } + nadeWp = random(nadeWps); + + myEye = self GetEye(); + if (!isDefined(nadeWp) || self HasScriptGoal() || self.bot_lock_goal) + { + traceForward = BulletTrace(myEye, myEye + AnglesToForward(self GetPlayerAngles()) * 900, false, self); + + loc = traceForward["position"]; + dist = DistanceSquared(self.origin, loc); + if (dist < level.bots_minGrenadeDistance || dist > level.bots_maxGrenadeDistance) + continue; + + if (!bulletTracePassed(self.origin + (0, 0, 5), self.origin + (0, 0, 2048), false, self)) + continue; + + if (!bulletTracePassed(loc + (0, 0, 5), loc + (0, 0, 2048), false, self)) + continue; + + loc += (0, 0, dist/3000); + } + else + { + self SetScriptGoal(nadeWp.origin, 16); + + ret = self waittill_any_return("new_goal", "goal", "bad_path"); + + if (ret != "new_goal") + self ClearScriptGoal(); + + if (ret != "goal") + continue; + + doFastContinue = true; + continue; + } + } + else + { + nadeWp = self getNearestWaypointOfWaypoints(level.waypointsGren); + loc = nadeWp.origin + AnglesToForward(nadeWp.angles) * 2048; + } + + if (!isDefined(loc)) + continue; + + self SetScriptAimPos(loc); + self BotStopMoving(true); + wait 1; + + time = 0.5; + if (nade == "frag_grenade_mp") + time = 2; + self botThrowGrenade(nade, time); + + self ClearScriptAimPos(); + self BotStopMoving(false); + } +} + +/* + Bots play mw2 +*/ +bot_watch_think_mw2() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + for (;;) + { + wait randomIntRange(1, 4); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + if (self HasThreat()) + continue; + + tube = self getValidTube(); + if (!isDefined(tube)) + { + if (self GetAmmoCount("at4_mp")) + tube = "at4_mp"; + else if (self GetAmmoCount("rpg_mp")) + tube = "rpg_mp"; + else + continue; + } + + if (self GetCurrentWeapon() == tube) + continue; + + chance = self.pers["bots"]["behavior"]["nade"]; + + if (randomInt(100) > chance) + continue; + + self ChangeToWeapon(tube); + } +} + +/* + Bots will use gremades/wweapons while having a target while using a shield +*/ +bot_watch_riot_weapons() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + for (;;) + { + wait randomIntRange(2, 4); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + if (!self HasThreat()) + continue; + + if (!self.hasRiotShieldEquipped) + continue; + + threat = self GetThreat(); + dist = DistanceSquared(threat.origin, self.origin); + curWeap = self GetCurrentWeapon(); + + if (randomInt(2)) + { + nade = self getValidGrenade(); + + if (!isDefined(nade)) + continue; + + if (dist <= level.bots_minGrenadeDistance || dist >= level.bots_maxGrenadeDistance) + continue; + + if (randomInt(100) > self.pers["bots"]["behavior"]["nade"]) + continue; + + self botThrowGrenade(nade); + } + else + { + if (randomInt(100) > self.pers["bots"]["behavior"]["switch"] * 10) + continue; + + weaponslist = self getweaponslistall(); + weap = ""; + while(weaponslist.size) + { + weapon = weaponslist[randomInt(weaponslist.size)]; + weaponslist = array_remove(weaponslist, weapon); + + if(!self getAmmoCount(weapon)) + continue; + + if (!isWeaponPrimary(weapon)) + continue; + + if(curWeap == weapon || weapon == "none" || weapon == "" || weapon == "javelin_mp" || weapon == "stinger_mp" || weapon == "onemanarmy_mp") + continue; + + weap = weapon; + break; + } + + if(weap == "") + continue; + + self ChangeToWeapon(weap); + } + } +} + +/* + BOts thinking of using javelins +*/ +bot_jav_loc_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + doFastContinue = false; + for (;;) + { + if (doFastContinue) + doFastContinue = false; + else + { + wait randomintRange(2, 4); + + chance = self.pers["bots"]["behavior"]["nade"] / 2; + if (chance > 20) + chance = 20; + + if (randomInt(100) > chance) + continue; + } + + if (!self GetAmmoCount("javelin_mp")) + continue; + + if (self HasThreat() || self HasBotJavelinLocation() || self HasScriptAimPos()) + continue; + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + if (self isEMPed()) + continue; + + loc = undefined; + + if (!self nearAnyOfWaypoints(128, level.waypointsJav)) + { + javWps = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsJav.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsJav[i].origin) > distSq) + continue; + + javWps[javWps.size] = level.waypointsJav[i]; + } + javWp = random(javWps); + + if (!isDefined(javWp) || self HasScriptGoal() || self.bot_lock_goal) + { + traceForward = self maps\mp\_javelin::EyeTraceForward(); + if (!isDefined(traceForward)) + continue; + + loc = traceForward[0]; + if (self maps\mp\_javelin::TargetPointTooClose(loc)) + continue; + + if (!bulletTracePassed(self.origin + (0, 0, 5), self.origin + (0, 0, 2048), false, self)) + continue; + + if (!bulletTracePassed(loc + (0, 0, 5), loc + (0, 0, 2048), false, self)) + continue; + } + else + { + self SetScriptGoal(javWp.origin, 16); + + ret = self waittill_any_return("new_goal", "goal", "bad_path"); + + if (ret != "new_goal") + self ClearScriptGoal(); + + if (ret != "goal") + continue; + + doFastContinue = true; + continue; + } + } + else + { + javWp = self getNearestWaypointOfWaypoints(level.waypointsJav); + loc = javWp.jav_point; + } + + if (!isDefined(loc)) + continue; + + self SetBotJavelinLocation(loc); + + if (self changeToWeapon("javelin_mp")) + { + self waittill_any_timeout(10, "missile_fire", "weapon_change"); + } + + self ClearBotJavelinLocation(); + } +} + +/* + Bots thinking of targeting equipment, c4, claymores and TIs +*/ +bot_equipment_kill_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon ( "game_ended" ); + + myteam = self.pers[ "team" ]; + + for ( ;; ) + { + wait( RandomIntRange( 1, 3 ) ); + + if(self HasScriptEnemy()) + continue; + + if(self.pers["bots"]["skill"]["base"] <= 1) + continue; + + hasSitrep = self _HasPerk( "specialty_detectexplosive" ); + grenades = getEntArray( "grenade", "classname" ); + myEye = self getEye(); + myAngles = self getPlayerAngles(); + dist = 512*512; + target = undefined; + + for ( i = 0; i < grenades.size; i++ ) + { + item = grenades[i]; + + if (!isDefined(item)) + continue; + + if ( !IsDefined( item.name ) ) + continue; + + if ( IsDefined( item.owner ) && ((level.teamBased && item.owner.team == self.team) || item.owner == self) ) + continue; + + if (item.name != "c4_mp" && item.name != "claymore_mp") + continue; + + if(!hasSitrep && !bulletTracePassed(myEye, item.origin, false, item)) + continue; + + if(getConeDot(item.origin, self.origin, myAngles) < 0.6) + continue; + + if ( DistanceSquared( item.origin, self.origin ) < dist ) + { + target = item; + break; + } + } + + if ( !IsDefined( target ) ) + { + for ( i = 0; i < level.players.size; i++ ) + { + player = level.players[i]; + + if ( player == self ) + continue; + + if(!isDefined(player.team)) + continue; + + if ( level.teamBased && player.team == myteam ) + continue; + + ti = player.setSpawnPoint; + if(!isDefined(ti)) + continue; + + if(!isDefined(ti.bots)) + ti.bots = 0; + + if(ti.bots >= 2) + continue; + + if(!hasSitrep && !bulletTracePassed(myEye, ti.origin, false, ti)) + continue; + + if(getConeDot(ti.origin, self.origin, myAngles) < 0.6) + continue; + + if ( DistanceSquared( ti.origin, self.origin ) < dist ) + { + target = ti; + break; + } + } + } + + if ( !IsDefined( target ) ) + continue; + + if (isDefined(target.enemyTrigger) && !self HasScriptGoal() && !self.bot_lock_goal) + { + self SetScriptGoal(target.origin, 64); + self thread bot_inc_bots(target, true); + self thread bots_watch_touch_obj( target ); + + path = self waittill_any_return("bad_path", "goal", "new_goal"); + + if (path != "new_goal") + self ClearScriptGoal(); + + if (path != "goal" || !isDefined(target)) + continue; + + if (randomInt(100) < self.pers["bots"]["behavior"]["camp"] * 8) + { + self thread killCampAfterTime(randomIntRange(10,20)); + self thread killCampAfterEntGone(target); + self CampAtSpot(target.origin, target.origin + (0, 0, 42)); + } + + if (isDefined(target)) + target.enemyTrigger notify("trigger", self); + + continue; + } + + self SetScriptEnemy( target ); + self bot_equipment_attack(target); + self ClearScriptEnemy(); + } +} + +/* + Bots target the equipment for a time then stop +*/ +bot_equipment_attack(equ) +{ + wait_time = RandomIntRange( 7, 10 ); + + for ( i = 0; i < wait_time; i++ ) + { + wait( 1 ); + + if ( !IsDefined( equ ) ) + { + return; + } + } +} + +/* + Bots will listen to foot steps and target nearby targets +*/ +bot_listen_to_steps() +{ + self endon("disconnect"); + self endon("death"); + + for(;;) + { + wait 1; + + if(self.pers["bots"]["skill"]["base"] < 3) + continue; + + dist = level.bots_listenDist; + if(self _hasPerk("specialty_selectivehearing")) + dist *= 1.4; + + dist *= dist; + + heard = undefined; + for(i = level.players.size-1 ; i >= 0; i--) + { + player = level.players[i]; + + if(player == self) + continue; + if(level.teamBased && self.team == player.team) + continue; + if(player.sessionstate != "playing") + continue; + if(!isReallyAlive(player)) + continue; + + if( lengthsquared( player getVelocity() ) < 20000 ) + continue; + + if( distanceSquared(player.origin, self.origin) > dist ) + continue; + + if( player _hasPerk("specialty_quieter")) + continue; + + heard = player; + break; + } + + hasHeartbeat = (isSubStr(self GetCurrentWeapon(), "_heartbeat_") && !self IsEMPed()); + heartbeatDist = 350*350; + + if(!IsDefined(heard) && hasHeartbeat) + { + for(i = level.players.size-1 ; i >= 0; i--) + { + player = level.players[i]; + + if(player == self) + continue; + if(level.teamBased && self.team == player.team) + continue; + if(player.sessionstate != "playing") + continue; + if(!isReallyAlive(player)) + continue; + + if (player _hasPerk("specialty_heartbreaker")) + continue; + + if (distanceSquared(player.origin, self.origin) > heartbeatDist) + continue; + + if (GetConeDot(player.origin, self.origin, self GetPlayerAngles()) < 0.6) + continue; + + heard = player; + break; + } + } + + if(!IsDefined(heard)) + continue; + + if(bulletTracePassed(self getEye(), heard getTagOrigin( "j_spineupper" ), false, heard)) + { + self setAttacker(heard); + continue; + } + + if (self HasScriptGoal() || self.bot_lock_goal) + continue; + + self SetScriptGoal( heard.origin, 64 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } +} + +/* + Bots will look at the uav and target targets +*/ +bot_uav_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + + for(;;) + { + wait 0.75; + + if(self.pers["bots"]["skill"]["base"] <= 1) + continue; + + if (self isEMPed() || self.bot_isScrambled) + continue; + + if (self _hasPerk("_specialty_blastshield")) + continue; + + if ((level.teamBased && level.activeCounterUAVs[level.otherTeam[self.team]]) || (!level.teamBased && self.isRadarBlocked)) + continue; + + hasRadar = ((level.teamBased && level.activeUAVs[self.team]) || (!level.teamBased && level.activeUAVs[self.guid])); + if( level.hardcoreMode && !hasRadar ) + continue; + + dist = self.pers["bots"]["skill"]["help_dist"]; + dist *= dist * 8; + + for ( i = level.players.size - 1; i >= 0; i-- ) + { + player = level.players[i]; + + if(player == self) + continue; + + if(!isDefined(player.team)) + continue; + + if(player.sessionstate != "playing") + continue; + + if(level.teambased && player.team == self.team) + continue; + + if(!isReallyAlive(player)) + continue; + + distFromPlayer = DistanceSquared(self.origin, player.origin); + if(distFromPlayer > dist) + continue; + + if((!isSubStr(player getCurrentWeapon(), "_silencer_") && player.bots_firing) || (hasRadar && !player _hasPerk("specialty_coldblooded"))) + { + distSq = self.pers["bots"]["skill"]["help_dist"] * self.pers["bots"]["skill"]["help_dist"]; + if (distFromPlayer < distSq && bulletTracePassed(self getEye(), player getTagOrigin( "j_spineupper" ), false, player)) + { + self SetAttacker(player); + } + + if (!self HasScriptGoal() && !self.bot_lock_goal) + { + self thread stop_go_target_on_death(player); + self SetScriptGoal( player.origin, 128 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } + break; + } + } + } +} + +/* + bots will go to their target's kill location +*/ +bot_revenge_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if(self.pers["bots"]["skill"]["base"] <= 1) + return; + + if (isDefined(self.lastKiller) && isReallyAlive(self.lastKiller)) + { + if(bulletTracePassed(self getEye(), self.lastKiller getTagOrigin( "j_spineupper" ), false, self.lastKiller)) + { + self setAttacker(self.lastKiller); + } + } + + if(!isDefined(self.killerLocation)) + return; + + loc = self.killerLocation; + + for(;;) + { + wait( RandomIntRange( 1, 5 ) ); + + if(self HasScriptGoal() || self.bot_lock_goal) + return; + + if ( randomint( 100 ) < 75 ) + return; + + self SetScriptGoal( loc, 64 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } +} + +/* + Watches the target's health, calls 'bad_path' +*/ +turret_death_monitor(turret) +{ + self endon ("death"); + self endon ("disconnect"); + self endon ("bad_path"); + self endon ("goal"); + self endon ("new_goal"); + + for (;;) + { + wait 0.5; + + if (!isDefined(turret)) + break; + + if (turret.health <= 20000) + break; + + if (isDefined(turret.carriedBy)) + break; + } + + self notify("bad_path"); +} + +/* + Bots will target the turret for a time +*/ +bot_turret_attack( enemy ) +{ + wait_time = RandomIntRange( 7, 10 ); + + for ( i = 0; i < wait_time; i++ ) + { + wait( 1 ); + + if ( !IsDefined( enemy ) ) + return; + + if(enemy.health <= 20000) + return; + + if (isDefined(enemy.carriedBy)) + return; + + //if ( !BulletTracePassed( self getEye(), enemy.origin + ( 0, 0, 15 ), false, enemy ) ) + // return; + } +} + +/* + Bots will think when to target a turret +*/ +bot_turret_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon ( "game_ended" ); + + myteam = self.pers[ "team" ]; + + for ( ;; ) + { + wait( 1 ); + + turrets = level.turrets; + turretsKeys = getArrayKeys(turrets); + if ( turretsKeys.size == 0 ) + { + wait( randomintrange( 3, 5 ) ); + continue; + } + + if(self.pers["bots"]["skill"]["base"] <= 1) + continue; + + if (self HasScriptEnemy() || self IsUsingRemote()) + continue; + + myEye = self GetEye(); + turret = undefined; + for (i = turretsKeys.size - 1; i >= 0; i--) + { + tempTurret = turrets[turretsKeys[i]]; + + if (!isDefined(tempTurret)) + continue; + + if(tempTurret.health <= 20000) + continue; + if (isDefined(tempTurret.carriedBy)) + continue; + if(isDefined(tempTurret.owner) && tempTurret.owner == self) + continue; + if(tempTurret.team == self.pers["team"] && level.teamBased) + continue; + if(!bulletTracePassed(myEye, tempTurret.origin + (0, 0, 15), false, tempTurret)) + continue; + + turret = tempTurret; + } + + if (!isDefined(turret)) + continue; + + forward = AnglesToForward( turret.angles ); + forward = VectorNormalize( forward ); + + delta = self.origin - turret.origin; + delta = VectorNormalize( delta ); + + dot = VectorDot( forward, delta ); + + facing = true; + if ( dot < 0.342 ) // cos 70 degrees + facing = false; + if ( turret isStunned() ) + facing = false; + if(self _hasPerk("specialty_coldblooded")) + facing = false; + + if ( facing && !BulletTracePassed( myEye, turret.origin + ( 0, 0, 15 ), false, turret ) ) + continue; + + if ( !IsDefined( turret.bots ) ) + turret.bots = 0; + if ( turret.bots >= 2 ) + continue; + + if(!facing && !self HasScriptGoal() && !self.bot_lock_goal) + { + self SetScriptGoal(turret.origin, 32); + self thread bot_inc_bots(turret, true); + self thread turret_death_monitor( turret ); + self thread bots_watch_touch_obj( turret ); + + if(self waittill_any_return("bad_path", "goal", "new_goal") != "new_goal") + self ClearScriptGoal(); + } + + if(!isDefined(turret)) + continue; + + self SetScriptEnemy( turret, (0, 0, 25) ); + self bot_turret_attack(turret); + self ClearScriptEnemy(); + } +} + +/* + Checks if the bot is stuck on a carepackage +*/ +bot_watch_stuck_on_crate() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + for (;;) + { + wait 3; + + if (self HasThreat()) + continue; + + crates = getEntArray( "care_package", "targetname" ); + if ( crates.size == 0 ) + continue; + + crate = undefined; + for (i = crates.size - 1; i >= 0; i--) + { + tempCrate = crates[i]; + + if (!isDefined(tempCrate)) + continue; + + if (!isDefined(tempCrate.doingPhysics) || tempCrate.doingPhysics) + continue; + + if (isDefined(crate) && DistanceSquared(crate.origin, self.origin) < DistanceSquared(tempCrate.origin, self.origin)) + continue; + + crate = tempCrate; + } + + if (!isDefined(crate)) + continue; + + radius = GetDvarFloat( "player_useRadius" ); + if (DistanceSquared(crate.origin, self.origin) > radius * radius) + continue; + + self.bot_stuck_on_carepackage = crate; + self notify("crate_physics_done"); + } +} + +/* + Bots will capture carepackages +*/ +bot_crate_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + myteam = self.pers[ "team" ]; + + first = true; + + for ( ;; ) + { + ret = "crate_physics_done"; + if(first) + first = false; + else + ret = self waittill_any_timeout( randomintrange( 3, 5 ), "crate_physics_done" ); + + crate = self.bot_stuck_on_carepackage; + self.bot_stuck_on_carepackage = undefined; + if (!isDefined(crate)) + { + if ( RandomInt( 100 ) < 20 && ret != "crate_physics_done" ) + continue; + + if ( self HasScriptGoal() || self.bot_lock_goal ) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if(self IsUsingRemote() || self BotIsFrozen()) + continue; + + if (self inLastStand()) + continue; + + crates = getEntArray( "care_package", "targetname" ); + if ( crates.size == 0 ) + continue; + + wantsClosest = randomint(2); + + crate = undefined; + for (i = crates.size - 1; i >= 0; i--) + { + tempCrate = crates[i]; + + if (!isDefined(tempCrate)) + continue; + + if (!isDefined(tempCrate.doingPhysics) || tempCrate.doingPhysics) + continue; + + if ( !IsDefined( tempCrate.bots ) ) + tempCrate.bots = 0; + + if ( tempCrate.bots >= 3 ) + continue; + + if (isDefined(crate)) + { + if (wantsClosest) + { + if (DistanceSquared(crate.origin, self.origin) < DistanceSquared(tempCrate.origin, self.origin)) + continue; + } + else + { + if (maps\mp\killstreaks\_killstreaks::getStreakCost(crate.crateType) > maps\mp\killstreaks\_killstreaks::getStreakCost(tempCrate.crateType)) + continue; + } + } + + crate = tempCrate; + } + + if (!isDefined(crate)) + continue; + + self.bot_lock_goal = true; + + radius = GetDvarFloat( "player_useRadius" ); + self SetScriptGoal(crate.origin, radius); + self thread bot_inc_bots(crate, true); + self thread bots_watch_touch_obj(crate); + + path = self waittill_any_return("bad_path", "goal", "new_goal"); + + self.bot_lock_goal = false; + + if (path != "new_goal") + self ClearScriptGoal(); + + if (path != "goal" || !isDefined(crate) || DistanceSquared(self.origin, crate.origin) > radius*radius) + continue; + } + + self _DisableWeapon(); + self BotFreezeControls(true); + + waitTime = 3; + if (isDefined(crate.owner) && crate.owner == self) + waitTime = 0.5; + + crate waittill_notify_or_timeout("captured", waitTime); + + self _EnableWeapon(); + self BotFreezeControls(false); + + self notify("bot_force_check_switch"); + + if (!isDefined(crate)) + continue; + + crate notify ( "captured", self ); + } +} + +/* + Reload cancels +*/ +doReloadCancel() +{ + self endon("disconnect"); + self endon("death"); + + for (;;) + { + ret = self waittill_any_return("reload", "weapon_change"); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + curWeap = self GetCurrentWeapon(); + + if (!maps\mp\gametypes\_weapons::isPrimaryWeapon(curWeap)) + continue; + + if (ret == "reload") + { + // check single reloads + if (self GetWeaponAmmoClip(curWeap) < WeaponClipSize(curWeap)) + continue; + } + + // check difficulty + if (self.pers["bots"]["skill"]["base"] <= 3) + continue; + + // check if got another weapon + weaponslist = self GetWeaponsListPrimaries(); + weap = ""; + while(weaponslist.size) + { + weapon = weaponslist[randomInt(weaponslist.size)]; + weaponslist = array_remove(weaponslist, weapon); + + if (!maps\mp\gametypes\_weapons::isPrimaryWeapon(weapon)) + continue; + + if(curWeap == weapon || weapon == "none" || weapon == "") + continue; + + weap = weapon; + break; + } + + if(weap == "") + continue; + + // do the cancel + wait 0.1; + self BotChangeToWeapon(weap); + wait 0.25; + self BotChangeToWeapon(curWeap); + wait 2; + } +} + +/* + Bots will think to switch weapons +*/ +bot_weapon_think() +{ + self endon("death"); + self endon("disconnect"); + level endon("game_ended"); + + first = true; + + for(;;) + { + self waittill_any_timeout(randomIntRange(2, 4), "bot_force_check_switch"); + + if(self BotIsFrozen()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + curWeap = self GetCurrentWeapon(); + hasTarget = self hasThreat(); + if(hasTarget) + { + threat = self getThreat(); + rocketAmmo = self getRocketAmmo(); + + if(entIsVehicle(threat) && isDefined(rocketAmmo)) + { + if (curWeap != rocketAmmo) + self thread ChangeToWeapon(rocketAmmo); + continue; + } + } + + if (self HasBotJavelinLocation() && self GetAmmoCount("javelin_mp")) + { + if (curWeap != "javelin_mp") + self thread ChangeToWeapon("javelin_mp"); + + continue; + } + + if (isDefined(self.bot_oma_class)) + { + if (curWeap != "onemanarmy_mp") + self thread ChangeToWeapon("onemanarmy_mp"); + continue; + } + + if (first) + { + first = false; + + if(randomInt(100) > self.pers["bots"]["behavior"]["initswitch"]) + continue; + } + else + { + if(curWeap != "none" && self getAmmoCount(curWeap) && curWeap != "stinger_mp" && curWeap != "javelin_mp" && curWeap != "onemanarmy_mp") + { + if(randomInt(100) > self.pers["bots"]["behavior"]["switch"]) + continue; + + if(hasTarget) + continue; + } + } + + weaponslist = self getweaponslistall(); + weap = ""; + while(weaponslist.size) + { + weapon = weaponslist[randomInt(weaponslist.size)]; + weaponslist = array_remove(weaponslist, weapon); + + if(!self getAmmoCount(weapon)) + continue; + + if (!isWeaponPrimary(weapon)) + continue; + + if(curWeap == weapon || weapon == "none" || weapon == "" || weapon == "javelin_mp" || weapon == "stinger_mp" || weapon == "onemanarmy_mp") + continue; + + weap = weapon; + break; + } + + if(weap == "") + continue; + + self thread ChangeToWeapon(weap); + } +} + +/* + Bots think when to target vehicles +*/ +bot_target_vehicle() +{ + self endon("disconnect"); + self endon("death"); + + for (;;) + { + wait randomIntRange(2, 4); + + if(self.pers["bots"]["skill"]["base"] <= 1) + continue; + + if(self HasScriptEnemy()) + continue; + + if (self IsUsingRemote()) + continue; + + rocketAmmo = self getRocketAmmo(); + if(!isDefined(rocketAmmo) && self BotGetRandom() < 90) + continue; + + if (isDefined(rocketAmmo) && rocketAmmo == "javelin_mp" && self isEMPed()) + continue; + + targets = maps\mp\_stinger::GetTargetList(); + + if (!targets.size) + continue; + + lockOnAmmo = self getLockonAmmo(); + myEye = self GetEye(); + target = undefined; + for (i = targets.size - 1; i >= 0; i--) + { + tempTarget = targets[i]; + + if (isDefined(tempTarget.owner) && tempTarget.owner == self) + continue; + + if(!bulletTracePassed( myEye, tempTarget.origin, false, tempTarget )) + continue; + + if (tempTarget.health <= 0) + continue; + + if (tempTarget.classname != "script_vehicle" && !isDefined(lockOnAmmo)) + continue; + + target = tempTarget; + } + + if (!isDefined(target)) + continue; + + self SetScriptEnemy( target, (0, 0, 0) ); + self bot_attack_vehicle( target ); + self ClearScriptEnemy(); + self notify("bot_force_check_switch"); + } +} + +/* + Bots target the killstreak for a time and stops +*/ +bot_attack_vehicle( target ) +{ + target endon("death"); + + wait_time = RandomIntRange( 7, 10 ); + + for ( i = 0; i < wait_time; i++ ) + { + self notify("bot_force_check_switch"); + wait( 1 ); + + if ( !IsDefined( target ) ) + { + return; + } + } +} + +/* + Returns an origin thats good to use for a kill streak +*/ +getKillstreakTargetLocation() +{ + location = undefined; + players = []; + for(i = level.players.size - 1; i >= 0; i--) + { + player = level.players[i]; + + if(player == self) + continue; + if(!isDefined(player.team)) + continue; + if(level.teamBased && self.team == player.team) + continue; + if(player.sessionstate != "playing") + continue; + if(!isReallyAlive(player)) + continue; + if(player _hasPerk("specialty_coldblooded")) + continue; + if(!bulletTracePassed(player.origin, player.origin+(0,0,2048), false, player) && self.pers["bots"]["skill"]["base"] > 3) + continue; + + players[players.size] = player; + } + + target = random(players); + + if(isDefined(target)) + location = target.origin + (randomIntRange((8-self.pers["bots"]["skill"]["base"])*-75, (8-self.pers["bots"]["skill"]["base"])*75), randomIntRange((8-self.pers["bots"]["skill"]["base"])*-75, (8-self.pers["bots"]["skill"]["base"])*75), 0); + else if(self.pers["bots"]["skill"]["base"] <= 3) + location = self.origin + (randomIntRange(-512, 512), randomIntRange(-512, 512), 0); + + return location; +} + +/* + Clears remote usage when bot dies +*/ +clear_remote_on_death(isac130) +{ + self endon("bot_clear_remote_on_death"); + level endon("game_ended"); + + self waittill_either("death", "disconnect"); + + if (isDefined(isac130) && isac130) + level.ac130InUse = false; + + if (isDefined(self)) + self ClearUsingRemote(); +} + +/* + Returns if any harriers exists that is an enemy +*/ +isAnyEnemyPlanes() +{ + if (!isDefined(level.harriers)) + return false; + + for (i = 0; i < level.harriers.size; i++) + { + plane = level.harriers[i]; + + if (!isDefined(plane)) + continue; + + if (level.teamBased && plane.team == self.team) + continue; + + if (isDefined(plane.owner) && plane.owner == self) + continue; + + return true; + } + + return false; +} + +/* + Bots think to use killstreaks +*/ +bot_killstreak_think() +{ + self endon("disconnect"); + self endon("death"); + level endon("game_ended"); + + doFastContinue = false; + + for (;;) + { + if (doFastContinue) + doFastContinue = false; + else + { + wait randomIntRange(1, 3); + } + + if ( !isDefined( self.pers["killstreaks"][0] ) ) + continue; + + if(self BotIsFrozen()) + continue; + + if(self HasThreat() || self HasBotJavelinLocation()) + continue; + + if(self isDefusing() || self isPlanting()) + continue; + + if (self isEMPed()) + continue; + + if (self IsUsingRemote()) + continue; + + if (self InLastStand() && !self InFinalStand()) + continue; + + streakName = self.pers["killstreaks"][0].streakName; + + if (level.inGracePeriod && maps\mp\killstreaks\_killstreaks::deadlyKillstreak(streakName)) + continue; + + ksWeap = maps\mp\killstreaks\_killstreaks::getKillstreakWeapon( streakName ); + curWeap = self GetCurrentWeapon(); + + if (curWeap == "none" || !isWeaponPrimary(curWeap)) + curWeap = self GetLastWeapon(); + + lifeId = self.pers["killstreaks"][0].lifeId; + if (!isDefined(lifeId)) + lifeId = -1; + + if (isStrStart(streakName, "helicopter_") && self isAnyEnemyPlanes() && self.pers["bots"]["skill"]["base"] > 3) + continue; + + if (maps\mp\killstreaks\_killstreaks::isRideKillstreak(streakName) || maps\mp\killstreaks\_killstreaks::isCarryKillstreak(streakName)) + { + if (self inLastStand()) + continue; + + if (lifeId == self.deaths && !self HasScriptGoal() && !self.bot_lock_goal && streakName != "sentry" && !self nearAnyOfWaypoints(128, level.waypointsCamp)) + { + campSpots = []; + distSq = 1024*1024; + for (i = 0; i < level.waypointsCamp.size; i++) + { + if (DistanceSquared(self.origin, level.waypointsCamp[i].origin) > distSq) + continue; + + campSpots[campSpots.size] = level.waypointsCamp[i]; + } + campSpot = random(campSpots); + + if (isDefined(campSpot)) + { + self SetScriptGoal(campSpot.origin, 16); + + if (self waittill_any_return("new_goal", "goal", "bad_path") != "new_goal") + self ClearScriptGoal(); + + doFastContinue = true; + continue; + } + } + + if (streakName == "sentry") + { + if (self HasScriptAimPos()) + continue; + + myEye = self GetEye(); + angles = self GetPlayerAngles(); + + forwardTrace = bulletTrace(myEye, myEye + AnglesToForward(angles)*1024, false, self); + + if (DistanceSquared(self.origin, forwardTrace["position"]) < 1000*1000 && self.pers["bots"]["skill"]["base"] > 3) + continue; + + self BotStopMoving(true); + self SetScriptAimPos(forwardTrace["position"]); + + if (!self changeToWeapon(ksWeap)) + { + self BotStopMoving(false); + self ClearScriptAimPos(); + continue; + } + + wait 1; + self notify("place_sentry"); + wait 0.05; + self notify("cancel_sentry"); + wait 0.5; + + self thread changeToWeapon(curWeap); + + self BotStopMoving(false); + self ClearScriptAimPos(); + } + else if (streakName == "predator_missile") + { + location = self getKillstreakTargetLocation(); + + if(!isDefined(location)) + continue; + + self setUsingRemote( "remotemissile" ); + self thread clear_remote_on_death(); + self BotStopMoving(true); + + if (!self changeToWeapon(ksWeap)) + { + self ClearUsingRemote(); + self notify("bot_clear_remote_on_death"); + self BotStopMoving(false); + continue; + } + + wait 1; + self notify("bot_clear_remote_on_death"); + self BotStopMoving(false); + + if (self isEMPed()) + { + self ClearUsingRemote(); + self thread changeToWeapon(curWeap); + continue; + } + + self BotFreezeControls(true); + + self maps\mp\killstreaks\_killstreaks::usedKillstreak( "predator_missile", true ); + //self maps\mp\killstreaks\_killstreaks::shuffleKillStreaksFILO( "predator_missile" ); + self maps\mp\killstreaks\_killstreaks::giveOwnedKillstreakItem(); + + rocket = MagicBullet( "remotemissile_projectile_mp", self.origin + (0.0,0.0,7000.0 - (self.pers["bots"]["skill"]["base"] * 400)), location, self ); + rocket.lifeId = lifeId; + rocket.type = "remote"; + + rocket thread maps\mp\gametypes\_weapons::AddMissileToSightTraces( self.pers["team"] ); + //rocket thread maps\mp\killstreaks\_remotemissile::handleDamage(); + thread maps\mp\killstreaks\_remotemissile::MissileEyes( self, rocket ); + + self waittill( "stopped_using_remote" ); + + wait 1; + self BotFreezeControls(false); + self thread changeToWeapon(curWeap); + } + else if (streakName == "ac130") + { + if ( isDefined( level.ac130player ) || level.ac130InUse ) + continue; + + self BotStopMoving(true); + self changeToWeapon(ksWeap); + self BotStopMoving(false); + + wait 3; + + if ( !isDefined( level.ac130player ) || level.ac130player != self ) + self thread changeToWeapon(curWeap); + } + else if (streakName == "helicopter_minigun") + { + if (isDefined( level.chopper )) + continue; + + self BotStopMoving(true); + self changeToWeapon(ksWeap); + self BotStopMoving(false); + + wait 3; + + if ( !isDefined( level.chopper ) || !isDefined( level.chopper.gunner ) || level.chopper.gunner != self ) + self thread changeToWeapon(curWeap); + } + } + else + { + if (streakName == "airdrop_mega" || streakName == "airdrop_sentry_minigun" || streakName == "airdrop") + { + if (self HasScriptAimPos()) + continue; + + if (streakName != "airdrop_mega" && level.littleBirds > 2) + continue; + + if(!bulletTracePassed(self.origin, self.origin+(0,0,2048), false, self) && self.pers["bots"]["skill"]["base"] > 3) + continue; + + myEye = self GetEye(); + angles = self GetPlayerAngles(); + + forwardTrace = bulletTrace(myEye, myEye + AnglesToForward(angles)*256, false, self); + + if (DistanceSquared(self.origin, forwardTrace["position"]) < 96*96 && self.pers["bots"]["skill"]["base"] > 3) + continue; + + if (!bulletTracePassed(forwardTrace["position"], forwardTrace["position"]+(0,0,2048), false, self) && self.pers["bots"]["skill"]["base"] > 3) + continue; + + self BotStopMoving(true); + self SetScriptAimPos(forwardTrace["position"]); + + if (!self changeToWeapon(ksWeap)) + { + self BotStopMoving(false); + self ClearScriptAimPos(); + continue; + } + + self thread fire_current_weapon(); + + ret = self waittill_any_timeout( 5, "grenade_fire" ); + + self notify("stop_firing_weapon"); + self thread changeToWeapon(curWeap); + + if (ret == "timeout") + { + self BotStopMoving(false); + self ClearScriptAimPos(); + continue; + } + + if (randomInt(100) < 80 && !self HasScriptGoal() && !self.bot_lock_goal) + self waittill_any_timeout( 15, "crate_physics_done", "new_goal" ); + + self BotStopMoving(false); + self ClearScriptAimPos(); + } + else + { + if (streakName == "harrier_airstrike" && level.planes > 1) + continue; + + if (streakName == "nuke" && isDefined( level.nukeIncoming )) + continue; + + if (streakName == "counter_uav" && self.pers["bots"]["skill"]["base"] > 3 && ((level.teamBased && level.activeCounterUAVs[self.team]) || (!level.teamBased && level.activeCounterUAVs[self.guid]))) + continue; + + if (streakName == "uav" && self.pers["bots"]["skill"]["base"] > 3 && ((level.teamBased && (level.activeUAVs[self.team] || level.activeCounterUAVs[level.otherTeam[self.team]])) || (!level.teamBased && (level.activeUAVs[self.guid] || self.isRadarBlocked)))) + continue; + + if (streakName == "emp" && self.pers["bots"]["skill"]["base"] > 3 && ((level.teamBased && level.teamEMPed[level.otherTeam[self.team]]) || (!level.teamBased && isDefined( level.empPlayer )))) + continue; + + location = undefined; + directionYaw = undefined; + switch (streakName) + { + case "harrier_airstrike": + case "stealth_airstrike": + case "precision_airstrike": + location = self getKillstreakTargetLocation(); + directionYaw = randomInt(360); + + if (!isDefined(location)) + continue; + case "helicopter": + case "helicopter_flares": + case "uav": + case "nuke": + case "counter_uav": + case "emp": + self BotStopMoving(true); + + if (self changeToWeapon(ksWeap)) + { + wait 1; + + if (isDefined(location)) + { + self BotFreezeControls(true); + + self notify( "confirm_location", location, directionYaw ); + wait 1; + + self BotFreezeControls(false); + } + + self thread changeToWeapon(curWeap); + } + + self BotStopMoving(false); + break; + } + } + } + } +} + +/* + Bots hang around the enemy's flag to spawn kill em +*/ +bot_dom_spawn_kill_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( level.gametype != "dom" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + for ( ;; ) + { + wait( randomintrange( 10, 20 ) ); + + if ( randomint( 100 ) < 20 ) + continue; + + if ( self HasScriptGoal() || self.bot_lock_goal) + continue; + + myFlagCount = maps\mp\gametypes\dom::getTeamFlagCount( myTeam ); + + if ( myFlagCount == level.flags.size ) + continue; + + otherFlagCount = maps\mp\gametypes\dom::getTeamFlagCount( otherTeam ); + + if (myFlagCount <= otherFlagCount || otherFlagCount != 1) + continue; + + flag = undefined; + for ( i = 0; i < level.flags.size; i++ ) + { + if ( level.flags[i] maps\mp\gametypes\dom::getFlagTeam() == myTeam ) + continue; + } + + if(!isDefined(flag)) + continue; + + if(DistanceSquared(self.origin, flag.origin) < 2048*2048) + continue; + + self SetScriptGoal( flag.origin, 1024 ); + + self thread bot_dom_watch_flags(myFlagCount, myTeam); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } +} + +/* + Calls 'bad_path' when the flag count changes +*/ +bot_dom_watch_flags(count, myTeam) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (maps\mp\gametypes\dom::getTeamFlagCount( myTeam ) != count) + break; + } + + self notify("bad_path"); +} + +/* + Bots watches their own flags and protects them when they are under capture +*/ +bot_dom_def_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( level.gametype != "dom" ) + return; + + myTeam = self.pers[ "team" ]; + + for ( ;; ) + { + wait( randomintrange( 1, 3 ) ); + + if ( randomint( 100 ) < 35 ) + continue; + + if ( self HasScriptGoal() || self.bot_lock_goal ) + continue; + + flag = undefined; + for ( i = 0; i < level.flags.size; i++ ) + { + if ( level.flags[i] maps\mp\gametypes\dom::getFlagTeam() != myTeam ) + continue; + + if ( !level.flags[i].useObj.objPoints[myTeam].isFlashing ) + continue; + + if ( !isDefined(flag) || DistanceSquared(self.origin,level.flags[i].origin) < DistanceSquared(self.origin,flag.origin) ) + flag = level.flags[i]; + } + + if ( !isDefined(flag) ) + continue; + + self SetScriptGoal( flag.origin, 128 ); + + self thread bot_dom_watch_for_flashing(flag, myTeam); + self thread bots_watch_touch_obj(flag); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } +} + +/* + Watches while the flag is under capture +*/ +bot_dom_watch_for_flashing(flag, myTeam) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (!isDefined(flag)) + break; + + if (flag maps\mp\gametypes\dom::getFlagTeam() != myTeam || !flag.useObj.objPoints[myTeam].isFlashing) + break; + } + + self notify("bad_path"); +} + +/* + Bots capture dom flags +*/ +bot_dom_cap_think() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( level.gametype != "dom" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + for ( ;; ) + { + wait( randomintrange( 3, 12 ) ); + + if ( self.bot_lock_goal ) + { + continue; + } + + if ( !isDefined(level.flags) || level.flags.size == 0 ) + continue; + + myFlagCount = maps\mp\gametypes\dom::getTeamFlagCount( myTeam ); + + if ( myFlagCount == level.flags.size ) + continue; + + otherFlagCount = maps\mp\gametypes\dom::getTeamFlagCount( otherTeam ); + + if (game["teamScores"][myteam] >= game["teamScores"][otherTeam]) + { + if ( myFlagCount < otherFlagCount ) + { + if ( randomint( 100 ) < 15 ) + continue; + } + else if ( myFlagCount == otherFlagCount ) + { + if ( randomint( 100 ) < 35 ) + continue; + } + else if ( myFlagCount > otherFlagCount ) + { + if ( randomint( 100 ) < 95 ) + continue; + } + } + + flag = undefined; + flags = []; + for ( i = 0; i < level.flags.size; i++ ) + { + if ( level.flags[i] maps\mp\gametypes\dom::getFlagTeam() == myTeam ) + continue; + + flags[flags.size] = level.flags[i]; + } + + if (randomInt(100) > 30) + { + for ( i = 0; i < flags.size; i++ ) + { + if ( !isDefined(flag) || DistanceSquared(self.origin,level.flags[i].origin) < DistanceSquared(self.origin,flag.origin) ) + flag = level.flags[i]; + } + } + else if (flags.size) + { + flag = random(flags); + } + + if ( !isDefined(flag) ) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( flag.origin, 64 ); + + self thread bot_dom_go_cap_flag(flag, myteam); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if (event != "goal") + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + while ( flag maps\mp\gametypes\dom::getFlagTeam() != myTeam && self isTouching(flag) ) + { + cur = flag.useObj.curProgress; + wait 0.5; + + if(flag.useObj.curProgress == cur) + break;//some enemy is near us, kill him + } + + self ClearScriptGoal(); + + self.bot_lock_goal = false; + } +} + +/* + Bot goes to the flag, watching while they don't have the flag +*/ +bot_dom_go_cap_flag(flag, myteam) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait randomintrange(2,4); + + if (!isDefined(flag)) + break; + + if (flag maps\mp\gametypes\dom::getFlagTeam() == myTeam) + break; + + if (self isTouching(flag)) + break; + } + + if (flag maps\mp\gametypes\dom::getFlagTeam() == myTeam) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Bots play headquarters +*/ +bot_hq() +{ + self endon( "death" ); + self endon( "disconnect" ); + + if ( level.gametype != "koth" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self.bot_lock_goal ) + { + continue; + } + + if(!isDefined(level.radio)) + continue; + + if(!isDefined(level.radio.gameobject)) + continue; + + radio = level.radio; + gameobj = radio.gameobject; + origin = ( radio.origin[0], radio.origin[1], radio.origin[2]+5 ); + + //if neut or enemy + if(gameobj.ownerTeam != myTeam) + { + if(gameobj.interactTeam == "none")//wait for it to become active + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + //capture it + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_hq_go_cap(gameobj, radio); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if (event != "goal") + { + self.bot_lock_goal = false; + continue; + } + + if(!self isTouching(gameobj.trigger) || level.radio != radio) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + while(self isTouching(gameobj.trigger) && gameobj.ownerTeam != myTeam && level.radio == radio) + { + cur = gameobj.curProgress; + wait 0.5; + + if(cur == gameobj.curProgress) + break;//no prog made, enemy must be capping + } + + self ClearScriptGoal(); + self.bot_lock_goal = false; + } + else//we own it + { + if(gameobj.objPoints[myteam].isFlashing)//underattack + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_hq_watch_flashing(gameobj, radio); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } + } +} + +/* + Waits until not touching the trigger and it is the current radio. +*/ +bot_hq_go_cap(obj, radio) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait randomintrange(2,4); + + if (!isDefined(obj)) + break; + + if (self isTouching(obj.trigger)) + break; + + if (level.radio != radio) + break; + } + + if(level.radio != radio) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Waits while the radio is under attack. +*/ +bot_hq_watch_flashing(obj, radio) +{ + self endon( "death" ); + self endon( "disconnect" ); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + myteam = self.team; + + for (;;) + { + wait 0.5; + + if (!isDefined(obj)) + break; + + if (!obj.objPoints[myteam].isFlashing) + break; + + if (level.radio != radio) + break; + } + + self notify("bad_path"); +} + +/* + Bots play capture the flag +*/ +bot_cap() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "ctf" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if(!isDefined(level.capZones)) + continue; + + if(!isDefined(level.teamFlags)) + continue; + + myflag = level.teamFlags[myteam]; + myzone = level.capZones[myteam]; + + theirflag = level.teamFlags[otherTeam]; + theirzone = level.capZones[otherTeam]; + + if(!myflag maps\mp\gametypes\_gameobjects::isHome()) + { + carrier = myflag.carrier; + + if(!isDefined(carrier))//someone doesnt has our flag + { + if(!isDefined(theirflag.carrier) && DistanceSquared(self.origin, theirflag.curorigin) < DistanceSquared(self.origin, myflag.curorigin)) //no one has their flag and its closer + self bot_cap_get_flag(theirflag); + else//go get it + self bot_cap_get_flag(myflag); + + continue; + } + else + { + if(theirflag maps\mp\gametypes\_gameobjects::isHome() && randomint(100) < 50) + { //take their flag + self bot_cap_get_flag(theirflag); + } + else + { + if(self HasScriptGoal()) + continue; + + if(!isDefined(theirzone.bots)) + theirzone.bots = 0; + + origin = theirzone.curorigin; + + if(theirzone.bots > 2 || randomInt(100) < 45) + { + //kill carrier + if(carrier _hasPerk( "specialty_coldblooded" )) + continue; + + origin = carrier.origin; + + self SetScriptGoal( origin, 64 ); + self thread bot_escort_obj(myflag, carrier); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + self thread bot_inc_bots(theirzone); + + //camp their zone + if(DistanceSquared(origin, self.origin) <= 1024*1024) + { + wait 4; + self notify("bot_inc_bots"); theirzone.bots--; + continue; + } + + self SetScriptGoal( origin, 256 ); + self thread bot_inc_bots(theirzone); + self thread bot_escort_obj(myflag, carrier); + + if(self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } + } + } + else//our flag is ok + { + if(self isFlagCarrier())//if have flag + { + //go cap + origin = myzone.curorigin; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 32 ); + + self thread bot_get_obj(myflag); + evt = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + wait 1; + if (evt != "new_goal") + self ClearScriptGoal(); + self.bot_lock_goal = false; + continue; + } + + carrier = theirflag.carrier; + + if(!isDefined(carrier))//if no one has enemy flag + { + self bot_cap_get_flag(theirflag); + continue; + } + + //escort them + + if(self HasScriptGoal()) + continue; + + origin = carrier.origin; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_escort_obj(theirflag, carrier); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + } + } +} + +/* + Bots go and get the flag +*/ +bot_cap_get_flag(flag) +{ + origin = flag.curorigin; + + //go get it + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 32 ); + + self thread bot_get_obj(flag); + + evt = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + wait 1; + + self.bot_lock_goal = false; + if (evt != "new_goal") + self ClearScriptGoal(); +} + +/* + Bots play sab +*/ +bot_sab() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "sab" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if(!isDefined(level.sabBomb)) + continue; + + if(!isDefined(level.bombZones) || !level.bombZones.size) + continue; + + if (self IsPlanting() || self isDefusing()) + continue; + + bomb = level.sabBomb; + bombteam = bomb.ownerTeam; + carrier = bomb.carrier; + timeleft = maps\mp\gametypes\_gamelogic::getTimeRemaining()/1000; + + // the bomb is ours, we are on the offence + if(bombteam == myTeam) + { + site = level.bombZones[otherTeam]; + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + // protect our planted bomb + if(level.bombPlanted) + { + // kill defuser + if(site isInUse()) //somebody is defusing our bomb we planted + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + self.bot_lock_goal = false; + continue; + } + + // we are not the carrier + if(!self isBombCarrier()) + { + // lets escort the bomb carrier + if(self HasScriptGoal()) + continue; + + origin = carrier.origin; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_escort_obj(bomb, carrier); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + // we are the carrier of the bomb, lets check if we need to plant + timepassed = getTimePassed()/1000; + + if(timepassed < 120 && timeleft >= 90 && randomInt(100) < 98) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 1 ); + + self thread bot_go_plant(site); + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || level.bombPlanted || !self isTouching(site.trigger) || site IsInUse() || self inLastStand() || self HasThreat()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(site); + wait 1; + + self ClearScriptGoal(); + self.bot_lock_goal = false; + } + else if(bombteam == otherTeam) // the bomb is theirs, we are on the defense + { + site = level.bombZones[myteam]; + + if(!isDefined(site.bots)) + site.bots = 0; + + // protect our site from planters + if(!level.bombPlanted) + { + //kill bomb carrier + if(site.bots > 2 || randomInt(100) < 45) + { + if(self HasScriptGoal()) + continue; + + if(carrier _hasPerk( "specialty_coldblooded" )) + continue; + + origin = carrier.origin; + + self SetScriptGoal( origin, 64 ); + self thread bot_escort_obj(bomb, carrier); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + //protect bomb site + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + self thread bot_inc_bots(site); + + if(site isInUse())//somebody is planting + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_inc_bots(site); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + if(DistanceSquared(origin, self.origin) <= 1024*1024) + { + wait 4; + self notify("bot_inc_bots"); site.bots--; + continue; + } + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + self thread bot_inc_bots(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + self.bot_lock_goal = false; + continue; + } + + // bomb is planted we need to defuse + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + // someone else is defusing, lets just hang around + if(site.bots > 1) + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_go_defuse(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + // lets go defuse + self.bot_lock_goal = true; + + self SetScriptGoal( origin, 1 ); + self thread bot_inc_bots(site); + self thread bot_go_defuse(site); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || !level.bombPlanted || site IsInUse() || !self isTouching(site.trigger) || self InLastStand() || self HasThreat()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(site); + wait 1; + self ClearScriptGoal(); + + self.bot_lock_goal = false; + } + else // we need to go get the bomb! + { + origin = ( bomb.curorigin[0], bomb.curorigin[1], bomb.curorigin[2]+5 ); + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + + self thread bot_get_obj(bomb); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + } +} + +/* + Bots play sd defenders +*/ +bot_sd_defenders() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "sd" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + if(myTeam == game["attackers"]) + return; + + rand = self BotGetRandom(); + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if (self IsPlanting() || self isDefusing()) + continue; + + // bomb not planted, lets protect our sites + if(!level.bombPlanted) + { + timeleft = maps\mp\gametypes\_gamelogic::getTimeRemaining()/1000; + + if(timeleft >= 90) + continue; + + // check for a bomb carrier, and camp the bomb + if(!level.multiBomb && isDefined(level.sdBomb)) + { + bomb = level.sdBomb; + carrier = level.sdBomb.carrier; + + if(!isDefined(carrier)) + { + origin = ( bomb.curorigin[0], bomb.curorigin[1], bomb.curorigin[2]+5 ); + + //hang around the bomb + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + + self thread bot_get_obj(bomb); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + } + + // pick a site to protect + if(!isDefined(level.bombZones) || !level.bombZones.size) + continue; + + sites = []; + for(i = 0; i < level.bombZones.size; i++) + { + sites[sites.size] = level.bombZones[i]; + } + + if(!sites.size) + continue; + + if (rand > 50) + site = self bot_array_nearest_curorigin(sites); + else + site = random(sites); + + if(!isDefined(site)) + continue; + + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + if(site isInUse())//somebody is planting + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + // bomb is planted, we need to defuse + if(!isDefined(level.defuseObject)) + continue; + + defuse = level.defuseObject; + + if(!isDefined(defuse.bots)) + defuse.bots = 0; + + origin = ( defuse.curorigin[0], defuse.curorigin[1], defuse.curorigin[2]+5 ); + + // someone is going to go defuse ,lets just hang around + if(defuse.bots > 1) + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_go_defuse(defuse); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + // lets defuse + self.bot_lock_goal = true; + self SetScriptGoal( origin, 1 ); + self thread bot_inc_bots(defuse); + self thread bot_go_defuse(defuse); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || !level.bombPlanted || defuse isInUse() || !self isTouching(defuse.trigger) || self InLastStand() || self HasThreat()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(defuse); + wait 1; + self ClearScriptGoal(); + self.bot_lock_goal = false; + } +} + +/* + Bots play sd attackers +*/ +bot_sd_attackers() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "sd" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + if(myTeam != game["attackers"]) + return; + + rand = self BotGetRandom(); + + first = true; + + for ( ;; ) + { + if(first) + first = false; + else + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + //bomb planted + if(level.bombPlanted) + { + if(!isDefined(level.defuseObject)) + continue; + + site = level.defuseObject; + + origin = ( site.curorigin[0], site.curorigin[1], site.curorigin[2]+5 ); + + if(site IsInUse())//somebody is defusing + { + self.bot_lock_goal = true; + + self SetScriptGoal( origin, 64 ); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + timeleft = maps\mp\gametypes\_gamelogic::getTimeRemaining()/1000; + timepassed = getTimePassed()/1000; + + //dont have a bomb + if(!self IsBombCarrier() && !level.multiBomb) + { + if(!isDefined(level.sdBomb)) + continue; + + bomb = level.sdBomb; + carrier = level.sdBomb.carrier; + + //bomb is picked up + if(isDefined(carrier)) + { + //escort the bomb carrier + if(self HasScriptGoal()) + continue; + + origin = carrier.origin; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_escort_obj(bomb, carrier); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + if(!isDefined(bomb.bots)) + bomb.bots = 0; + + origin = ( bomb.curorigin[0], bomb.curorigin[1], bomb.curorigin[2]+5 ); + + //hang around the bomb if other is going to go get it + if(bomb.bots > 1) + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + + self thread bot_get_obj(bomb); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + // go get the bomb + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + self thread bot_inc_bots(bomb); + self thread bot_get_obj(bomb); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + // check if to plant + if(timepassed < 120 && timeleft >= 90 && randomInt(100) < 98) + continue; + + if(!isDefined(level.bombZones) || !level.bombZones.size) + continue; + + sites = []; + for(i = 0; i < level.bombZones.size; i++) + { + sites[sites.size] = level.bombZones[i]; + } + + if(!sites.size) + continue; + + if(rand > 50) + plant = self bot_array_nearest_curorigin(sites); + else + plant = random(sites); + + if(!isDefined(plant)) + continue; + + origin = ( plant.curorigin[0]+50, plant.curorigin[1]+50, plant.curorigin[2]+5 ); + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 1 ); + self thread bot_go_plant(plant); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || level.bombPlanted || plant.visibleTeam == "none" || !self isTouching(plant.trigger) || self InLastStand() || self HasThreat() || plant IsInUse()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(plant); + wait 1; + + self ClearScriptGoal(); + self.bot_lock_goal = false; + } +} + +/* + Bots go plant the demo bomb +*/ +bot_dem_go_plant(plant) +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if ((plant.label == "_b" && level.bombBPlanted) || (plant.label == "_a" && level.bombAPlanted)) + break; + + if (self isTouching(plant.trigger)) + break; + } + + if((plant.label == "_b" && level.bombBPlanted) || (plant.label == "_a" && level.bombAPlanted)) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Bots spawn kill dom attackers +*/ +bot_dem_attack_spawnkill() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + l1 = level.bombAPlanted; + l2 = level.bombBPlanted; + + for (;;) + { + wait 0.5; + + if (l1 != level.bombAPlanted || l2 != level.bombBPlanted) + break; + } + + self notify("bad_path"); +} + +/* + Bots play demo attackers +*/ +bot_dem_attackers() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "dd" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + if(myTeam != game["attackers"]) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if(!isDefined(level.bombZones) || !level.bombZones.size) + continue; + + bombs = [];//sites with bombs + sites = [];//sites to bomb at + bombed = 0;//exploded sites + for ( i = 0; i < level.bombZones.size; i++ ) + { + bomb = level.bombZones[i]; + + if(isDefined(bomb.bombExploded) && bomb.bombExploded) + { + bombed++; + continue; + } + + if(bomb.label == "_a") + { + if(level.bombAPlanted) + bombs[bombs.size] = bomb; + else + sites[sites.size] = bomb; + + continue; + } + + if(bomb.label == "_b") + { + if(level.bombBPlanted) + bombs[bombs.size] = bomb; + else + sites[sites.size] = bomb; + + continue; + } + } + timeleft = maps\mp\gametypes\_gamelogic::getTimeRemaining()/1000; + + shouldLet = (game["teamScores"][myteam] > game["teamScores"][otherTeam] && timeleft < 90 && bombed == 1); + //spawnkill conditions + //if we have bombed one site or 1 bomb is planted with lots of time left, spawn kill + //if we want the other team to win for overtime and they do not need to defuse, spawn kill + if(((bombed + bombs.size == 1 && timeleft >= 90) || (shouldLet && !bombs.size)) && randomInt(100) < 95) + { + if(self HasScriptGoal()) + continue; + + spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dd_spawn_defender_start" ); + + if(!spawnPoints.size) + continue; + + spawnpoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints ); + + if(DistanceSquared(spawnpoint.origin, self.origin) <= 2048*2048) + continue; + + self SetScriptGoal( spawnpoint.origin, 1024 ); + + self thread bot_dem_attack_spawnkill(); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + //let defuse conditions + //if enemy is going to lose and lots of time left, let them defuse to play longer + //or if want to go into overtime near end of the extended game + if(((bombs.size + bombed == 2 && timeleft >= 90) || (shouldLet && bombs.size)) && randomInt(100) < 95) + { + spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dd_spawn_attacker_start" ); + + if(!spawnPoints.size) + continue; + + spawnpoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints ); + + if(DistanceSquared(spawnpoint.origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( spawnpoint.origin, 512 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //defend bomb conditions + //if time is running out and we have a bomb planted + if(bombs.size && timeleft < 90 && (!sites.size || randomInt(100) < 95)) + { + site = self bot_array_nearest_curorigin(bombs); + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + if(site IsInUse())//somebody is defusing + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else go plant + if(!sites.size) + continue; + + plant = self bot_array_nearest_curorigin(sites); + + if(!isDefined(plant)) + continue; + + if(!isDefined(plant.bots)) + plant.bots = 0; + + origin = ( plant.curorigin[0]+50, plant.curorigin[1]+50, plant.curorigin[2]+5 ); + + //hang around the site if lots of time left + if(plant.bots > 1 && timeleft >= 60) + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + self thread bot_dem_go_plant(plant); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + self.bot_lock_goal = true; + + self SetScriptGoal( origin, 1 ); + self thread bot_inc_bots(plant); + self thread bot_dem_go_plant(plant); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || (plant.label == "_b" && level.bombBPlanted) || (plant.label == "_a" && level.bombAPlanted) || plant IsInUse() || !self isTouching(plant.trigger) || self InLastStand() || self HasThreat()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(plant); + wait 1; + + self ClearScriptGoal(); + + self.bot_lock_goal = false; + } +} + +/* + Bots play demo defenders +*/ +bot_dem_defenders() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "dd" ) + return; + + myTeam = self.pers[ "team" ]; + otherTeam = getOtherTeam( myTeam ); + + if(myTeam == game["attackers"]) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if(!isDefined(level.bombZones) || !level.bombZones.size) + continue; + + bombs = [];//sites with bombs + sites = [];//sites to bomb at + bombed = 0;//exploded sites + for ( i = 0; i < level.bombZones.size; i++ ) + { + bomb = level.bombZones[i]; + + if(isDefined(bomb.bombExploded) && bomb.bombExploded) + { + bombed++; + continue; + } + + if(bomb.label == "_a") + { + if(level.bombAPlanted) + bombs[bombs.size] = bomb; + else + sites[sites.size] = bomb; + + continue; + } + + if(bomb.label == "_b") + { + if(level.bombBPlanted) + bombs[bombs.size] = bomb; + else + sites[sites.size] = bomb; + + continue; + } + } + timeleft = maps\mp\gametypes\_gamelogic::getTimeRemaining()/1000; + + shouldLet = (timeleft < 60 && ((bombed == 0 && bombs.size != 2) || (game["teamScores"][myteam] > game["teamScores"][otherTeam] && bombed == 1)) && randomInt(100) < 98); + + //spawnkill conditions + //if nothing to defuse with a lot of time left, spawn kill + //or letting a bomb site to explode but a bomb is planted, so spawnkill + if((!bombs.size && timeleft >= 60 && randomInt(100) < 95) || (shouldLet && bombs.size == 1)) + { + if(self HasScriptGoal()) + continue; + + spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dd_spawn_attacker_start" ); + + if(!spawnPoints.size) + continue; + + spawnpoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints ); + + if(DistanceSquared(spawnpoint.origin, self.origin) <= 2048*2048) + continue; + + self SetScriptGoal( spawnpoint.origin, 1024 ); + + self thread bot_dem_defend_spawnkill(); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + //let blow up conditions + //let enemy blow up at least one to extend play time + //or if want to go into overtime after extended game + if(shouldLet) + { + spawnPoints = maps\mp\gametypes\_spawnlogic::getSpawnpointArray( "mp_dd_spawn_defender_start" ); + + if(!spawnPoints.size) + continue; + + spawnpoint = maps\mp\gametypes\_spawnlogic::getSpawnpoint_Random( spawnPoints ); + + if(DistanceSquared(spawnpoint.origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( spawnpoint.origin, 512 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //defend conditions + //if no bombs planted with little time left + if(!bombs.size && timeleft < 60 && randomInt(100) < 95 && sites.size) + { + site = self bot_array_nearest_curorigin(sites); + origin = ( site.curorigin[0]+50, site.curorigin[1]+50, site.curorigin[2]+5 ); + + if(site IsInUse())//somebody is planting + { + self.bot_lock_goal = true; + self SetScriptGoal( origin, 64 ); + + self thread bot_defend_site(site); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else hang around the site + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self.bot_lock_goal = true; + self SetScriptGoal( origin, 256 ); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + + self.bot_lock_goal = false; + continue; + } + + //else go defuse + + if(!bombs.size) + continue; + + defuse = self bot_array_nearest_curorigin(bombs); + + if(!isDefined(defuse)) + continue; + + if(!isDefined(defuse.bots)) + defuse.bots = 0; + + origin = ( defuse.curorigin[0]+50, defuse.curorigin[1]+50, defuse.curorigin[2]+5 ); + + //hang around the site if not in danger of losing + if(defuse.bots > 1 && bombed + bombs.size != 2) + { + if(self HasScriptGoal()) + continue; + + if(DistanceSquared(origin, self.origin) <= 1024*1024) + continue; + + self SetScriptGoal( origin, 256 ); + + self thread bot_dem_go_defuse(defuse); + + if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal") + self ClearScriptGoal(); + continue; + } + + self.bot_lock_goal = true; + + self SetScriptGoal( origin, 1 ); + self thread bot_inc_bots(defuse); + self thread bot_dem_go_defuse(defuse); + + event = self waittill_any_return( "goal", "bad_path", "new_goal" ); + + if (event != "new_goal") + self ClearScriptGoal(); + + if(event != "goal" || (defuse.label == "_b" && !level.bombBPlanted) || (defuse.label == "_a" && !level.bombAPlanted) || defuse IsInUse() || !self isTouching(defuse.trigger) || self InLastStand() || self HasThreat()) + { + self.bot_lock_goal = false; + continue; + } + + self SetScriptGoal( self.origin, 64 ); + + self bot_use_bomb_thread(defuse); + wait 1; + + self ClearScriptGoal(); + + self.bot_lock_goal = false; + } +} + +/* + Bots go defuse +*/ +bot_dem_go_defuse(defuse) +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (self isTouching(defuse.trigger)) + break; + + if ((defuse.label == "_b" && !level.bombBPlanted) || (defuse.label == "_a" && !level.bombAPlanted)) + break; + } + + if((defuse.label == "_b" && !level.bombBPlanted) || (defuse.label == "_a" && !level.bombAPlanted)) + self notify("bad_path"); + else + self notify("goal"); +} + +/* + Bots go spawn kill +*/ +bot_dem_defend_spawnkill() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + self endon( "goal" ); + self endon( "bad_path" ); + self endon( "new_goal" ); + + for (;;) + { + wait 0.5; + + if (level.bombBPlanted || level.bombAPlanted) + break; + } + + self notify("bad_path"); +} + +/* + Bots think to revive +*/ +bot_think_revive() +{ +} + +/* + Bots play the Global thermonuclear warfare +*/ +bot_gtnw() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "gtnw" ) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if (!isDefined(level.nukeSite)) + continue; + + + } +} + +/* + Bots play oneflag +*/ +bot_oneflag() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "oneflag" ) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + if (!isDefined(level.capZones) || !isDefined(level.teamFlags)) + continue; + + /*if(isDefined(level.capZones) && isDefined(level.teamFlags)) + { + if(self.pers["team"] == game["attackers"])//attacking + { + if(isDefined(self.carryFlag))//has flag + { + self.bots_objDoing = "cap"; + self thread bots\talk::bots_oneflag_capFlag(); + self bots_goToLoc(level.capZones[self.team].trigger.origin, ::bots_nullFunc, 0, 0, 0); + self.bots_objDoing = "none"; + self thread bots\talk::bots_oneflag_doneCap(); + } + else//doesn't have flag + { + if(!isDefined(level.teamFlags[game["defenders"]].carrier))//no one has flag + { + self.bots_objDoing = "flag"; + self thread bots\talk::bots_oneflag_getFlag(); + self bots_goToLoc(level.teamFlags[game["defenders"]].trigger.origin, ::bots_oneFlagGet, 0, 0, 0); + self.bots_objDoing = "none"; + self thread bots\talk::bots_oneflag_doneGetFlag(); + } + else + { + if(self.bots_traitRandom) + { + self thread bots\talk::bots_oneflag_protectCarrier(level.teamFlags[game["defenders"]].carrier); + self bots_goFollow(level.teamFlags[game["defenders"]].carrier, 30, false); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_nullFunc, 0, 0, 0); + } + } + } + } + else//defending + { + if(isDefined(level.teamFlags[self.team].carrier))//some one has flag + { + if(self.bots_traitRandom < 2) + { + self thread bots\talk::bots_oneflag_killCarrier(level.teamFlags[self.team].carrier);//kill carrier + self bots_goFollow(level.teamFlags[self.team].carrier, 30, false); + } + else + { + wps = bots_getWaypointsNear(level.capZones[game["attackers"]].trigger.origin, randomFloatRange(100,1000)); + wp = undefined; + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp)) + { + self thread bots\talk::bots_oneflag_protectCapzone();//hang around cap zone + + self bots_goToLoc(level.waypoints[wp].origin, ::bots_defendOneFlagCap, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_defendOneFlagCap, 0, 0, 0); + } + } + } + else + { + if(level.teamFlags[self.team] maps\mp\gametypes\_gameobjects::isHome()) + { + wps = bots_getWaypointsNear(level.teamFlags[self.team].trigger.origin, randomFloatRange(100,1000)); + wp = undefined;//hang around flag area + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp)) + { + self thread bots\talk::bots_oneflag_protectFlag(); + + self bots_goToLoc(level.waypoints[wp].origin, ::bots_defendOneFlag, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_defendOneFlag, 0, 0, 0); + } + } + else + { + self.bots_objDoing = "flag"; + self thread bots\talk::bots_oneflag_returnFlag();//return flag + self bots_campAtEnt(level.teamFlags[self.team].trigger, false, ::bots_oneFlagGetDefend, 0, 0, 0); + self.bots_objDoing = "none"; + self thread bots\talk::bots_oneflag_returnFlagDone(); + } + } + }*/ + } +} + +/* + Bots play arena +*/ +bot_arena() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "arena" ) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + /*case "arena"://iw's hidden gametypes. + if(isDefined(level.arenaFlag)) + { + self.bots_objDoing = "flag"; + self thread bots\talk::bots_arena_capFlag(); + self bots_campAtEnt(level.arenaFlag.trigger, false, ::bots_nullFunc, 0, 0, 0); + self.bots_objDoing = "none"; + self thread bots\talk::bots_arena_capFlagDone(); + } + else + { + wps = bots_getWaypointsNear(level.bots_goalPoint.origin, level.bots_goalRad); + wp = undefined; + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp) && self.bots_traitRandom != 3) + { + self bots_goToLoc(level.waypoints[wp].origin, ::bots_nullFunc, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_nullFunc, 0, 0, 0); + } + } + break; + */ + } +} + +/* + Bots play arena +*/ +bot_vip() +{ + self endon( "death" ); + self endon( "disconnect" ); + level endon("game_ended"); + + if ( level.gametype != "vip" ) + return; + + for ( ;; ) + { + wait( randomintrange( 3, 5 ) ); + + if ( self IsUsingRemote() || self.bot_lock_goal ) + { + continue; + } + + /*case "vip"://maybe used at gaming events. (ya right, this is not even finished) + if(isDefined(level.extractionZone)) + { + if(self.team == game["defenders"]) + { + if(isDefined(self.isVip) && self.isVip) + { + if(!isDefined(level.extractionTime)) + { + self.bots_objDoing = "vip"; + self thread bots\talk::bots_vip_extract(); + self bots_goToLoc(level.extractionZone.trigger.origin, ::bots_nullFunc, 0, 0, 0); + if(distance(level.extractionZone.trigger.origin, self.origin) <= level.bots_useNear) + level.extractionZone [[level.extractionZone.onUse]](self); + + self thread bots\talk::bots_vip_extractDone(); + self.bots_objDoing = "none"; + } + else + { + wps = bots_getWaypointsNear(level.bots_goalPoint.origin, level.bots_goalRad); + wp = undefined; + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp) && self.bots_traitRandom != 3) + { + self bots_goToLoc(level.waypoints[wp].origin, ::bots_nullFunc, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_nullFunc, 0, 0, 0); + } + } + } + else + { + if(self.bots_traitRandom) + { + tarPlay = undefined; + foreach(player in level.players) + { + if(!isDefined(player.isVip) || !player.isVip) + continue; + + if(!bots_isReallyAlive(player)) + continue; + + tarPlay = player; + break; + } + + self thread bots\talk::bots_vip_protect(tarPlay); + self bots_goFollow(tarPlay, 30, false); + } + else + { + wps = bots_getWaypointsNear(level.bots_goalPoint.origin, level.bots_goalRad); + wp = undefined; + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp) && self.bots_traitRandom != 3) + { + self bots_goToLoc(level.waypoints[wp].origin, ::bots_nullFunc, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_nullFunc, 0, 0, 0); + } + } + } + } + else + { + tarPlay = undefined; + foreach(player in level.players) + { + if(!isDefined(player.isVip) || !player.isVip) + continue; + + if(!bots_isReallyAlive(player)) + continue; + + tarPlay = player; + break; + } + + if((!isDefined(level.extractionTime) || self.bots_traitRandom < 2) && isDefined(tarPlay)) + { + self thread bots\talk::bots_vip_kill(tarPlay); + self bots_goFollow(tarPlay, 30, false); + } + else + { + wps = bots_getWaypointsNear(level.extractionZone.trigger.origin, randomFloatRange(100,1000)); + wp = undefined; + if(wps.size > 0) + { + wp = wps[randomint(wps.size)]; + } + if(isDefined(wp) && self.bots_traitRandom != 3) + { + self thread bots\talk::bots_vip_hangaround(); + self bots_goToLoc(level.waypoints[wp].origin, ::bots_nullFunc, 0, 0, 0); + } + else + { + self bots_goToLoc(level.waypoints[randomint(level.waypointCount)].origin, ::bots_nullFunc, 0, 0, 0); + } + } + } + } + break;*/ + } +}