#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", 0);
	
	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 thread difficulty();
	self thread teamWatch();
	self thread classWatch();

	self thread onBotSpawned();
	self thread onSpawned();

	self thread onDeath();
	self thread onGiveLoadout();

	self thread onKillcam();
}

/*
	Gets an exp amount for the bot that is nearish the host's xp.
*/
bot_get_rank()
{
	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 ) );

	return maps\mp\gametypes\_rank::getRankInfoMinXP( rank );
}

/*
	
*/
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;
}

/*
	
*/
getCardIcons()
{
	cards = [];

	for (i = 0; i < 300; 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;
}

/*
	
*/
isValidAttachmentCombo(att1, att2)
{
	colIndex = tableLookupRowNum( "mp/attachmentCombos.csv", 0, att1 );

	if (tableLookup( "mp/attachmentCombos.csv", 0, att2, colIndex ) == "no")
		return false;

	return true;
}

/*
	
*/
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;
}

/*
	
*/
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;
}

/*
	
*/
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;
}

/*
	
*/
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;
}

/*
	
*/
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;
}

/*
	
*/
getKillsNeededForStreak(streak)
{
	return int(tableLookup("mp/killstreakTable.csv", 1, streak, 4));
}

/*
	
*/
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;
}

/*
	
*/
chooseRandomPerk(perkkind)
{
	perks = getPerks(perkkind);
	rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData("experience") );
	allowOp = (getDvarInt("bots_loadout_allow_op") >= 1);

	while (true)
	{
		perk = random(perks);

		if (!allowOp)
		{
			if (perkkind == "perk4")
				return "specialty_null";

			if (perk == "specialty_pistoldeath")
				continue;
		}

		if (perk == "specialty_null")
			continue;

		if (!self isItemUnlocked(perk))
			continue;

		if (RandomFloatRange(0, 1) < (rank / level.maxRank))
			self.pers["bots"]["unlocks"]["upgraded_"+perk] = true;

		return perk;
	}
}

/*
	
*/
chooseRandomCamo()
{
	camos = getCamos();

	while (true)
	{
		camo = random(camos);

		if (camo == "gold" || camo == "prestige")
			continue;

		return camo;
	}
}

/*
	
*/
chooseRandomPrimary()
{
	primaries = getPrimaries();
	allowOp = (getDvarInt("bots_loadout_allow_op") >= 1);

	while (true)
	{
		primary = random(primaries);

		if (!allowOp)
		{
			if (primary == "riotshield")
				continue;	
		}

		if (!self isItemUnlocked(primary))
			continue;

		return primary;
	}
}

/*
	
*/
chooseRandomSecondary(perk1)
{
	if (perk1 == "specialty_onemanarmy")
		return "onemanarmy";

	secondaries = getSecondaries();
	allowOp = (getDvarInt("bots_loadout_allow_op") >= 1);

	while (true)
	{
		secondary = random(secondaries);

		if (!allowOp)
		{
			if (secondary == "at4" || secondary == "rpg" || secondary == "m79")
				continue;
		}

		if (!self isItemUnlocked(secondary))
			continue;

		if (secondary == "onemanarmy")
			continue;

		return secondary;
	}
}

/*
	
*/
chooseRandomAttachmentComboForGun(gun)
{
	atts = getAttachmentsForGun(gun);
	allowOp = (getDvarInt("bots_loadout_allow_op") >= 1);

	while (true)
	{
		att1 = random(atts);
		att2 = random(atts);

		if (!isValidAttachmentCombo(att1, att2))
			continue;

		if (!allowOp)
		{
			if (att1 == "gl" || att2 == "gl")
				continue;
		}

		retAtts = [];
		retAtts[0] = att1;
		retAtts[1] = att2;

		return retAtts;
	}
}

/*
	
*/
chooseRandomTactical()
{
	tacts = strTok("flash_grenade,smoke_grenade,concussion_grenade", ",");

	while (true)
	{
		tact = random(tacts);

		return tact;
	}
}

/*
	
*/
setClasses()
{
	rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData("experience") );

	if (RandomFloatRange(0, 1) < (rank / level.maxRank))
		self.pers["bots"]["unlocks"]["ghillie"] = true;

	for (i = 0; i < 5; i++)
	{
		equipment = chooseRandomPerk("equipment");
		perk1 = chooseRandomPerk("perk1");
		perk2 = chooseRandomPerk("perk2");
		perk3 = chooseRandomPerk("perk3");
		deathstreak = chooseRandomPerk("perk4");
		tactical = chooseRandomTactical();
		primary = chooseRandomPrimary();
		primaryAtts = chooseRandomAttachmentComboForGun(primary);
		primaryCamo = chooseRandomCamo();
		secondary = chooseRandomSecondary(perk1);
		secondaryAtts = chooseRandomAttachmentComboForGun(secondary);

		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);
	}
}

/*
	
*/
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;
}

/*
	
*/
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++;

	i = 0;
	while (i < chooseableStreaks)
	{
		slot = randomInt(3);

		if (killstreaks[slot] != "")
			continue;

		streak = random(allStreaks);

		if (isColidingKillstreak(killstreaks, streak))
			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;

	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;
}

/*
	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;
			}
		}
	}
}

/*
	
*/
onKillcam()
{
	level endon("game_ended");
	self endon("disconnect");
	
	for(;;)
	{
		self waittill("begin_killcam");

		self thread doKillcamStuff();
	}
}

/*
	
*/
doKillcamStuff()
{
	self endon("disconnect");
	self endon("killcam_ended");

	wait 0.5 + randomInt(3);

	if (randomInt(100) > 25)
		self notify("use_copycat");

	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 = "";
		rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData( "experience" ) ) + 1;
		if(rank < 4 || randomInt(100) < 2)
		{
			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);
		}
		
		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;
	}
}

/*
	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(;;)
	{
		wait 1;
		
		rankVar = GetDvarInt("bots_skill");
		
		if(rankVar == 9)
			continue;
			
		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"] = 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"]["behavior"]["strafe"] = 0;
				self.pers["bots"]["behavior"]["nade"] = 10;
				self.pers["bots"]["behavior"]["sprint"] = 10;
				self.pers["bots"]["behavior"]["camp"] = 5;
				self.pers["bots"]["behavior"]["follow"] = 5;
				self.pers["bots"]["behavior"]["crouch"] = 70;
				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"] = 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"]["behavior"]["strafe"] = 10;
				self.pers["bots"]["behavior"]["nade"] = 15;
				self.pers["bots"]["behavior"]["sprint"] = 15;
				self.pers["bots"]["behavior"]["camp"] = 5;
				self.pers["bots"]["behavior"]["follow"] = 5;
				self.pers["bots"]["behavior"]["crouch"] = 60;
				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"] = 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"]["behavior"]["strafe"] = 20;
				self.pers["bots"]["behavior"]["nade"] = 20;
				self.pers["bots"]["behavior"]["sprint"] = 20;
				self.pers["bots"]["behavior"]["camp"] = 5;
				self.pers["bots"]["behavior"]["follow"] = 5;
				self.pers["bots"]["behavior"]["crouch"] = 50;
				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"] = 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"]["behavior"]["strafe"] = 30;
				self.pers["bots"]["behavior"]["nade"] = 25;
				self.pers["bots"]["behavior"]["sprint"] = 30;
				self.pers["bots"]["behavior"]["camp"] = 5;
				self.pers["bots"]["behavior"]["follow"] = 5;
				self.pers["bots"]["behavior"]["crouch"] = 40;
				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"] = 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"]["behavior"]["strafe"] = 40;
				self.pers["bots"]["behavior"]["nade"] = 35;
				self.pers["bots"]["behavior"]["sprint"] = 40;
				self.pers["bots"]["behavior"]["camp"] = 5;
				self.pers["bots"]["behavior"]["follow"] = 5;
				self.pers["bots"]["behavior"]["crouch"] = 30;
				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"] = 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"]["behavior"]["strafe"] = 50;
				self.pers["bots"]["behavior"]["nade"] = 45;
				self.pers["bots"]["behavior"]["sprint"] = 50;
				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"] = 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"] = 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"]["behavior"]["strafe"] = 65;
				self.pers["bots"]["behavior"]["nade"] = 65;
				self.pers["bots"]["behavior"]["sprint"] = 65;
				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;
		}
	}
}

/*
	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);
			self.pers["bots"]["skill"]["dist"] = randomIntRange(500, 25000);
			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"]["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;
	}
}

/*
	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_perf_switch_weapon = undefined;

		self thread bot_dom_cap_think();
	}
}

/*
	
*/
onDeath()
{
	self endon("disconnect");

	for(;;)
	{
		self waittill("death");

		self.wantSafeSpawn = true;
	}
}

/*
	
*/
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;
	}
}

/*
	
*/
bot_inc_bots(obj, unreach)
{
	level endon("game_ended");
	
	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--;
}

/*
	
*/
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;
		}
	}
}

/*
	
*/
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;
}

/*
	
*/
getNearestWaypointOfWaypoints(waypoints)
{
	answer = undefined;
	for (i = 0; i < waypoints.size; i++)
	{
		waypoint = waypoints[i];

		if (isDefined(answer) && closer(self.origin, answer.origin, waypoint.origin))
			continue;

		answer = waypoint;
	}

	return answer;
}

/*
	
*/
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");
}

/*
	
*/
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");
}

/*
	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();
	}
}

/*
	
*/
start_bot_threads()
{
	self endon("disconnect");
	level endon("game_ended");
	self endon("death");
	
	gameFlagWait("prematch_done");

	self thread bot_killstreak_think();
	self thread bot_weapon_think();
	self thread bot_perk_think();

	self thread bot_target_vehicle();
	self thread bot_equipment_kill_think();
	self thread bot_turret_think();

	self thread bot_crate_think();
	self thread bot_revenge_think();

	self thread bot_uav_think();
	self thread bot_listen_to_steps();
	self thread follow_target();

	self thread bot_think_follow();
	self thread bot_think_camp();
	self thread bot_jav_loc_think();
	self thread bot_use_grenade_think();
	self thread bot_use_tube_think();
	self thread bot_use_equipment_think();

	self thread bot_dom_def_think();
	self thread bot_dom_spawn_kill_think();

	self thread bot_hq();

	self thread bot_cap();
}

/*
	
*/
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();
}

/*
	
*/
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 = [];
		for (i = 0; i < level.waypointsCamp.size; i++)
		{
			if (Distance(self.origin, level.waypointsCamp[i].origin) > 1024)
				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);
	}
}

/*
	
*/
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");
}

/*
	
*/
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");
}

/*
	
*/
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 = [];
		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 (Distance(player.origin, self.origin) > self.pers["bots"]["skill"]["help_dist"])
				continue;

			follows[follows.size] = player;
		}
		toFollow = random(follows);

		if (!isDefined(toFollow))
			continue;

		self thread killFollowAfterTime(randomIntRange(10,20));
		self followPlayer(toFollow);
	}
}

/*
	
*/
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");
}

/*
	
*/
killFollowAfterTime(time)
{
	self endon("death");
	self endon("disconnect");
	self endon("kill_follow_bot");

	wait time;

	self ClearScriptGoal();
	self ClearScriptAimPos();
	self notify("kill_follow_bot");
}

/*
	
*/
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) && Distance(myGoal, who.origin) < 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");
}

/*
	
*/
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");)
		{
			curWeap = self GetCurrentWeapon();
			if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
				break;

			if (self botIsClimbing())
				break;

			if(self IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
				break;

			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 = "";
			rank = self maps\mp\gametypes\_rank::getRankForXp( self getPlayerData( "experience" ) ) + 1;
			if(rank < 4 || randomInt(100) < 2)
			{
				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);
			}
			self.bot_oma_class = class;
			self notify("bot_force_check_switch");
			wait 0.1;

			if (self GetCurrentWeapon() != "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;
		}
	}
}

/*
	
*/
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 IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
			continue;
			
		if(self isDefusing() || self isPlanting())
			continue;

		curWeap = self GetCurrentWeapon();
		if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
			continue;

		if (self botIsClimbing())
			continue;

		if (self IsUsingRemote())
			continue;

		if (self InLastStand() && !self InFinalStand())
			continue;

		loc = undefined;

		if (!self nearAnyOfWaypoints(128, level.waypointsTube))
		{
			tubeWps = [];
			for (i = 0; i < level.waypointsTube.size; i++)
			{
				if (Distance(self.origin, level.waypointsTube[i].origin) > 1024)
					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;

		self.bot_perf_switch_weapon = tube;
		self notify("bot_force_check_switch");
		wait 0.1;

		if (self GetCurrentWeapon() == 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);
	}
}

/*
	
*/
fire_current_weapon()
{
	self endon("death");
	self endon("disconnect");
	self endon("weapon_change");
	self endon("stop_firing_weapon");

	for (;;)
	{
		self thread BotPressAttack(0.1);
		wait 0.05;
	}
}

/*
	
*/
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 (!isDefined(nade))
			continue;

		if (self HasThreat() || self HasBotJavelinLocation() || self HasScriptAimPos())
			continue;

		if(self BotIsFrozen())
			continue;
		
		if(self IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
			continue;
			
		if(self isDefusing() || self isPlanting())
			continue;

		curWeap = self GetCurrentWeapon();
		if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
			continue;

		if (self botIsClimbing())
			continue;

		if (self IsUsingRemote())
			continue;

		if (self inLastStand() && !self _hasPerk("specialty_laststandoffhand") && !self inFinalStand())
			continue;

		loc = undefined;

		if (!self nearAnyOfWaypoints(128, level.waypointsClay))
		{
			clayWps = [];
			for (i = 0; i < level.waypointsClay.size; i++)
			{
				if (Distance(self.origin, level.waypointsClay[i].origin) > 1024)
					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 throwBotGrenade(nade);

		self ClearScriptAimPos();
		self BotStopMoving(false);
	}
}

/*
	
*/
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 IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
			continue;
			
		if(self isDefusing() || self isPlanting())
			continue;

		curWeap = self GetCurrentWeapon();
		if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
			continue;

		if (self botIsClimbing())
			continue;

		if (self IsUsingRemote())
			continue;

		if (self inLastStand() && !self _hasPerk("specialty_laststandoffhand") && !self inFinalStand())
			continue;

		loc = undefined;

		if (!self nearAnyOfWaypoints(128, level.waypointsGren))
		{
			nadeWps = [];
			for (i = 0; i < level.waypointsGren.size; i++)
			{
				if (Distance(self.origin, level.waypointsGren[i].origin) > 1024)
					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 throwBotGrenade(nade, time);

		self ClearScriptAimPos();
		self BotStopMoving(false);
	}
}

/*
	
*/
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 IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
			continue;
			
		if(self isDefusing() || self isPlanting())
			continue;

		curWeap = self GetCurrentWeapon();
		if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
			continue;

		if (self botIsClimbing())
			continue;

		if (self IsUsingRemote())
			continue;

		if (self InLastStand() && !self InFinalStand())
			continue;

		if (self isEMPed())
			continue;

		loc = undefined;

		if (!self nearAnyOfWaypoints(128, level.waypointsJav))
		{
			javWps = [];
			for (i = 0; i < level.waypointsJav.size; i++)
			{
				if (Distance(self.origin, level.waypointsJav[i].origin) > 1024)
					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);
		self notify("bot_force_check_switch");

		wait 0.1;
		if (self GetCurrentWeapon() == "javelin_mp")
			self waittill_any_timeout(10, "missile_fire", "weapon_change");
			
		self ClearBotJavelinLocation();
	}
}

/*
	
*/
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.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))
		{
			if ( self HasScriptGoal() || self.bot_lock_goal )
				continue;

			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")
				continue;

			if (randomInt(100) > self.pers["bots"]["behavior"]["camp"] * 10)
				target.enemyTrigger notify("trigger", self);
			else
			{
				self thread killCampAfterTime(randomIntRange(10,20));
				self thread killCampAfterEntGone(target);
				self CampAtSpot(target.origin, target.origin + (0, 0, 42));
			}
			continue;
		}

		self SetScriptEnemy( target );
		self bot_equipment_attack(target);
		self ClearScriptEnemy();
	}
}

/*
	
*/
bot_equipment_attack(equ)
{
	wait_time = RandomIntRange( 7, 10 );

	for ( i = 0; i < wait_time; i++ )
	{
		wait( 1 );

		if ( !IsDefined( equ ) )
		{
			return;
		}
	}
}

/*
	
*/
bot_listen_to_steps()
{
	self endon("disconnect");
	self endon("death");
	
	for(;;)
	{
		wait 1;
		
		if(self HasScriptGoal() || self.bot_lock_goal)
			continue;
			
		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 ( player is_bot() && lengthsquared( player getBotVelocity() ) < 20000 )
				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;
		}
		
		self SetScriptGoal( heard.origin, 64 );

		if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal")
			self ClearScriptGoal();
	}
}

/*
	
*/
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;
			
			if(DistanceSquared(self.origin, player.origin) > dist)
				continue;
			
			if((!isSubStr(player getCurrentWeapon(), "_silencer_") && player.bots_firing) || (hasRadar && !player hasPerk("specialty_coldblooded")))
			{
				if (Distance(self.origin, player.origin) < self.pers["bots"]["skill"]["help_dist"] && 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;
			}
		}
	}
}

/*
	
*/
bot_revenge_think()
{
	self endon( "death" );
	self endon( "disconnect" );
	
	if(self.pers["bots"]["skill"]["base"] <= 1)
		return;
	
	if(!isDefined(self.killerLocation))
		return;
	
	for(;;)
	{
		wait( RandomIntRange( 1, 5 ) );
		
		if(self HasScriptGoal() || self.bot_lock_goal)
			return;
		
		if ( randomint( 100 ) < 75 )
			return;
		
		self SetScriptGoal( self.killerLocation, 64 );

		if (self waittill_any_return( "goal", "bad_path", "new_goal" ) != "new_goal")
			self ClearScriptGoal();
	}
}

/*
	
*/
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");
}

/*
	
*/
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;
	}
}

/*
	
*/
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(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, 15) );
		self bot_turret_attack(turret);
		self ClearScriptEnemy();
	}
}

/*
	
*/
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" );
		
		if ( RandomInt( 100 ) < 20 && ret != "crate_physics_done" )
			continue;
		
		if ( self HasScriptGoal() || self.bot_lock_goal )
		{
			wait 0.1;//because bot_crate_landed notify causes a same frame ClearScriptGoal
			
			if( self HasScriptGoal() || self.bot_lock_goal )
				continue;
		}

		if(self isDefusing() || self isPlanting())
			continue;

		if(self IsUsingRemote() || self BotIsFrozen())
			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.doingPhysics) || tempCrate.doingPhysics)
				continue;

			if ( !IsDefined( tempCrate.bots ) )
				tempCrate.bots = 0;
			
			if ( tempCrate.bots >= 3 )
				continue;

			if (isDefined(crate))
			{
				if (wantsClosest)
				{
					if (Distance(crate.origin, self.origin) < Distance(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;
		self SetScriptGoal(crate.origin, 64);
		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")
			continue;

		self _DisableWeapon();
		self BotFreezeControls(true);

		waitTime = 5;
		if (crate.owner == self)
			waitTime = 1.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 );
	}
}

/*
	
*/
bot_weapon_think()
{
	self endon("death");
	self endon("disconnect");
	level endon("game_ended");
	
	for(;;)
	{
		self waittill_any_timeout(randomIntRange(2, 4), "bot_force_check_switch");
		
		if(self IsBotReloading() || self IsBotFragging() || self botIsClimbing() || self IsBotKnifing())
			continue;

		if(self BotIsFrozen() || self.disabledWeapon)
			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 setSpawnWeapon(rocketAmmo);
				continue;
			}
		}

		if (self HasBotJavelinLocation() && self GetAmmoCount("javelin_mp"))
		{
			if (curWeap != "javelin_mp")
				self setSpawnWeapon("javelin_mp");

			continue;
		}

		if (isDefined(self.bot_oma_class))
		{
			if (curWeap != "onemanarmy_mp")
				self setSpawnWeapon("onemanarmy_mp");
			continue;
		}

		if (isDefined(self.bot_perf_switch_weapon))
		{
			if (curWeap != self.bot_perf_switch_weapon)
				self setSpawnWeapon(self.bot_perf_switch_weapon);

			self.bot_perf_switch_weapon = undefined;
			continue;
		}
		
		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 setSpawnWeapon(weap);
	}
}

/*
	
*/
getRocketAmmo()
{
	answer = self getLockonAmmo();

	if (isDefined(answer))
		return answer;

	if(self getAmmoCount("rpg_mp"))
		answer = "rpg_mp";

	return answer;
}

/*
	
*/
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;
}

/*
	
*/
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");
	}
}

/*
	
*/
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;
		}
	}
}

/*
	
*/
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;
}

/*
	
*/
clear_remote_on_death(isac130)
{
	self endon("disconnect");
	self endon("bot_clear_remote_on_death");
	level endon("game_ended");

	self waittill("death");

	if (isDefined(isac130) && isac130)
		level.ac130InUse = false;
	self ClearUsingRemote();
}

/*
	
*/
isAnyEnemyPlanes()
{
	for (i = 0; i < level.harriers.size; i++)
	{
		plane = level.harriers[i];

		if (level.teamBased && plane.team == self.team)
			continue;

		if (isDefined(plane.owner) && plane.owner == self)
			continue;

		return true;
	}

	return false;
}

/*
	
*/
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 IsBotReloading() || self IsBotFragging() || self IsBotKnifing())
			continue;
			
		if(self isDefusing() || self isPlanting())
			continue;

		curWeap = self GetCurrentWeapon();
		if (!isWeaponPrimary(curWeap) || self.disabledWeapon)
			continue;

		if (self isEMPed())
			continue;

		if (self botIsClimbing())
			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 );

		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 (self.pers["killstreaks"][0].lifeId == self.pers["deaths"] && !self HasScriptGoal() && !self.bot_lock_goal && streakName != "sentry" && !self nearAnyOfWaypoints(128, level.waypointsCamp))
			{
				campSpots = [];
				for (i = 0; i < level.waypointsCamp.size; i++)
				{
					if (Distance(self.origin, level.waypointsCamp[i].origin) > 1024)
						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")
			{
				myEye = self GetEye();
				angles = self GetPlayerAngles();

				forwardTrace = bulletTrace(myEye, myEye + AnglesToForward(angles)*1024, false, self);

				if (Distance(self.origin, forwardTrace["position"]) < 1000 && self.pers["bots"]["skill"]["base"] > 3)
					continue;

				self BotFreezeControls(true);
				wait 1;

				sentryGun = maps\mp\killstreaks\_autosentry::createSentryForPlayer( "sentry_minigun", self );
				sentryGun maps\mp\killstreaks\_autosentry::sentry_setPlaced();
				self notify( "sentry_placement_finished", sentryGun );

				self maps\mp\_matchdata::logKillstreakEvent( "sentry", self.origin );

				self maps\mp\killstreaks\_killstreaks::usedKillstreak( "sentry", true );
				self maps\mp\killstreaks\_killstreaks::shuffleKillStreaksFILO( "sentry" );
				self maps\mp\killstreaks\_killstreaks::giveOwnedKillstreakItem();
				wait 1;

				self BotFreezeControls(false);
			}
			else if (streakName == "predator_missile")
			{
				location = self getKillstreakTargetLocation();

				if(!isDefined(location))
					continue;

				self setUsingRemote( "remotemissile" );
				self setSpawnWeapon(ksWeap);
				self thread clear_remote_on_death();
				wait 1;
				self notify("bot_clear_remote_on_death");

				if (self isEMPed())
				{
					self ClearUsingRemote();
					self setSpawnWeapon(curWeap);
					continue;
				}
				
				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 = self.pers["killstreaks"][0].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 setSpawnWeapon(curWeap); 
			}
			else if (streakName == "ac130")
			{
				if ( isDefined( level.ac130player ) || level.ac130InUse )
					continue;

				level.ac130InUse = true;
				self setUsingRemote( "ac130" );
				self setSpawnWeapon(ksWeap);
				self thread clear_remote_on_death(true);
				wait 1;
				self notify("bot_clear_remote_on_death");

				if (self isEMPed()) // bcuz of the wait
				{
					level.ac130InUse = false;
					self ClearUsingRemote();
					self setSpawnWeapon(curWeap);
					continue;
				}

				self maps\mp\_matchdata::logKillstreakEvent( "ac130", self.origin );
	
				self.ac130LifeId = self.pers["killstreaks"][0].lifeId;
				level.ac130.planeModel.crashed = undefined;

				thread maps\mp\killstreaks\_ac130::setAC130Player( self );

				self maps\mp\killstreaks\_killstreaks::usedKillstreak( "ac130", true );
				self maps\mp\killstreaks\_killstreaks::shuffleKillStreaksFILO( "ac130" );
				self maps\mp\killstreaks\_killstreaks::giveOwnedKillstreakItem();

				self waittill( "stopped_using_remote" );

				wait 1;
				self setSpawnWeapon(curWeap);
			}
			else if (streakName == "helicopter_minigun")
			{
				if (isDefined( level.chopper ))
					continue;

				self setUsingRemote( "helicopter_minigun" );
				self setSpawnWeapon(ksWeap);
				self thread clear_remote_on_death();
				wait 1;
				self notify("bot_clear_remote_on_death");

				if (isDefined( level.chopper ) || self isEMPed())
				{
					self ClearUsingRemote();
					self setSpawnWeapon(curWeap);
					continue;
				}

				self thread maps\mp\killstreaks\_helicopter::startHelicopter(self.pers["killstreaks"][0].lifeId, "minigun");

				self maps\mp\killstreaks\_killstreaks::usedKillstreak( "helicopter_minigun", true );
				self maps\mp\killstreaks\_killstreaks::shuffleKillStreaksFILO( "helicopter_minigun" );
				self maps\mp\killstreaks\_killstreaks::giveOwnedKillstreakItem();

				self waittill( "stopped_using_remote" );

				wait 1;
				self setSpawnWeapon(curWeap);
			}
		}
		else
		{
			if (streakName == "airdrop_mega" || streakName == "airdrop_sentry_minigun" || streakName == "airdrop")
			{
				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 (Distance(self.origin, forwardTrace["position"]) < 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);
				if (self throwBotGrenade(ksWeap) != "grenade_fire")
				{
					self BotStopMoving(false);
					continue;
				}

				if (randomInt(100) < 80)
					self waittill_any_timeout( 15, "crate_physics_done" );

				self BotStopMoving(false);
			}
			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 BotFreezeControls(true);
						self setSpawnWeapon(ksWeap);
						wait 1;
						if (isDefined(location))
						{
							self notify( "confirm_location", location, directionYaw );
							wait 1;
						}
						self setSpawnWeapon(curWeap);
						self BotFreezeControls(false);
						break;
				}
			}
		}
	}
}

/*
	
*/
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();
	}
}

/*
	
*/
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");
}

/*
	
*/
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();
	}
}

/*
	
*/
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");
}

/*
	
*/
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 ( 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;
		for ( i = 0; i < level.flags.size; i++ )
		{
			if ( level.flags[i] maps\mp\gametypes\dom::getFlagTeam() == myTeam )
				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.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_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");
}

/*
	
*/
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 == "bad_path")
			{
				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");
}

/*
	
*/
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;
						continue;
					}
					
					self SetScriptGoal( origin, 256 );
					self thread bot_escort_obj(myflag, carrier);
					
					if(self waittill_any( "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();
		}
	}
}

/*
	
*/
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();
}