#include maps/mp/gametypes/_rank;
#include maps/mp/bots/_bot;
#include maps/mp/_utility;
#include common_scripts/utility;

init() //checked changed to match cerberus output
{
	level endon( "game_ended" );
	level.bot_banned_killstreaks = array( "KILLSTREAK_RCBOMB", "KILLSTREAK_QRDRONE", "KILLSTREAK_REMOTE_MISSILE", "KILLSTREAK_REMOTE_MORTAR", "KILLSTREAK_HELICOPTER_GUNNER" );
	for ( ;; )
	{
		level waittill( "connected", player );
		if ( !player istestclient() )
		{
			continue;
		}
		player thread on_bot_connect();
	}
}

on_bot_connect() //checked changed to match cerberus output
{
	self endon( "disconnect" );
	if ( isDefined( self.pers[ "bot_loadout" ] ) )
	{
		return;
	}
	wait 0.1;
	if ( ( self getentitynumber() % 2 ) == 0 )
	{
		wait 0.05;
	}
	self maps/mp/bots/_bot::bot_set_rank();
	if ( level.onlinegame && !sessionmodeisprivate() )
	{
		self botsetdefaultclass( 5, "class_assault" );
		self botsetdefaultclass( 6, "class_smg" );
		self botsetdefaultclass( 7, "class_lmg" );
		self botsetdefaultclass( 8, "class_cqb" );
		self botsetdefaultclass( 9, "class_sniper" );
	}
	else
	{
		self botsetdefaultclass( 5, "class_assault" );
		self botsetdefaultclass( 6, "class_smg" );
		self botsetdefaultclass( 7, "class_lmg" );
		self botsetdefaultclass( 8, "class_cqb" );
		self botsetdefaultclass( 9, "class_sniper" );
	}
	max_allocation = 10;
	for ( i = 1; i <= 3; i++ )
	{
		if ( self isitemlocked( maps/mp/gametypes/_rank::getitemindex( "feature_allocation_slot_" + i ) ) )
		{
			max_allocation--;
		}
	}
	self bot_construct_loadout( max_allocation );
	self.pers[ "bot_loadout" ] = 1;
}

bot_construct_loadout( allocation_max ) //checked matches cerberus output
{
	if ( self isitemlocked( maps/mp/gametypes/_rank::getitemindex( "feature_cac" ) ) )
	{
		return;
	}
	pixbeginevent( "bot_construct_loadout" );
	item_list = bot_build_item_list();
	bot_construct_class( 0, item_list, allocation_max );
	bot_construct_class( 1, item_list, allocation_max );
	bot_construct_class( 2, item_list, allocation_max );
	bot_construct_class( 3, item_list, allocation_max );
	bot_construct_class( 4, item_list, allocation_max );
	killstreaks = item_list[ "killstreak1" ];
	if ( isDefined( item_list[ "killstreak2" ] ) )
	{
		killstreaks = arraycombine( killstreaks, item_list[ "killstreak2" ], 1, 0 );
	}
	if ( isDefined( item_list[ "killstreak3" ] ) )
	{
		killstreaks = arraycombine( killstreaks, item_list[ "killstreak3" ], 1, 0 );
	}
	if ( isDefined( killstreaks ) && killstreaks.size )
	{
		bot_choose_weapon( 0, killstreaks );
		bot_choose_weapon( 0, killstreaks );
		bot_choose_weapon( 0, killstreaks );
	}
	self.claimed_items = undefined;
	pixendevent();
}

bot_construct_class( class, items, allocation_max ) //checked partially changed to match cerberus output did not change while loop to for loop see github for more info
{
	allocation = 0;
	claimed_count = bot_build_claimed_list( items );
	self.claimed_items = [];
	while ( allocation < allocation_max )
	{
		secondary_chance = 40;
		remaining = allocation_max - allocation;
		if ( remaining >= 1 && bot_make_choice( 95, claimed_count[ "primary" ], 1 ) )
		{
			weapon = bot_choose_weapon( class, items[ "primary" ] );
			claimed_count[ "primary" ]++;
			allocation++;
			bot_choose_weapon_option( class, "camo", 0 );
			bot_choose_weapon_option( class, "reticle", 0 );
			allocation += bot_choose_primary_attachments( class, weapon, allocation, allocation_max );
		}
		else if ( !claimed_count[ "primary" ] )
		{
			secondary_chance = 100;
		}
		remaining = allocation_max - allocation;
		if ( remaining >= 1 && bot_make_choice( secondary_chance, claimed_count[ "secondary" ], 1 ) )
		{
			if ( remaining >= 2 && randomint( 100 ) < 10 )
			{
				self botclassadditem( class, "BONUSCARD_OVERKILL" );
				weapon = bot_choose_weapon( class, items[ "primary" ] );
				allocation++;
				continue;
			}
			else
			{
				weapon = bot_choose_weapon( class, items[ "secondary" ] );
				bot_choose_weapon_option( class, "camo", 1 );
			}
			allocation++;
			claimed_count[ "secondary" ]++;
			allocation += bot_choose_secondary_attachments( class, weapon, allocation, allocation_max );
		}
		perks_chance = 50;
		lethal_chance = 30;
		tactical_chance = 20;
		if ( claimed_count[ "specialty1" ] && claimed_count[ "specialty2" ] && claimed_count[ "specialty3" ] )
		{
			perks_chance = 0;
		}
		if ( claimed_count[ "primarygrenade" ] )
		{
			lethal_chance = 0;
		}
		if ( claimed_count[ "specialgrenade" ] )
		{
			tactical_chance = 0;
		}
		if ( ( perks_chance + lethal_chance + tactical_chance ) <= 0 )
		{
			return;
		}
		next_action = bot_chose_action( "perks", perks_chance, "lethal", lethal_chance, "tactical", tactical_chance, "none", 0 );
		if ( next_action == "perks" )
		{
			remaining = allocation_max - allocation;
			if ( remaining >= 3 && !claimed_count[ "specialty1" ] && randomint( 100 ) < 25 )
			{
				self botclassadditem( class, "BONUSCARD_PERK_1_GREED" );
				bot_choose_weapon( class, items[ "specialty1" ] );
				bot_choose_weapon( class, items[ "specialty1" ] );
				claimed_count[ "specialty1" ] = 2;
				allocation += 3;
			}
			remaining = allocation_max - allocation;
			if ( remaining >= 3 && !claimed_count[ "specialty2" ] && randomint( 100 ) < 25 )
			{
				self botclassadditem( class, "BONUSCARD_PERK_2_GREED" );
				bot_choose_weapon( class, items[ "specialty2" ] );
				bot_choose_weapon( class, items[ "specialty2" ] );
				claimed_count[ "specialty2" ] = 2;
				allocation += 3;
			}
			remaining = allocation_max - allocation;
			if ( remaining >= 3 && !claimed_count[ "specialty3" ] && randomint( 100 ) < 25 )
			{
				self botclassadditem( class, "BONUSCARD_PERK_3_GREED" );
				bot_choose_weapon( class, items[ "specialty3" ] );
				bot_choose_weapon( class, items[ "specialty3" ] );
				claimed_count[ "specialty3" ] = 2;
				allocation += 3;
			}
			remaining = allocation_max - allocation;
			i = 0;
			while ( i < 3 )
			{
				perks = [];
				remaining = allocation_max - allocation;
				if ( remaining > 0 )
				{
					if ( !claimed_count[ "specialty1" ] )
					{
						perks[ perks.size ] = "specialty1";
					}
					if ( !claimed_count[ "specialty2" ] )
					{
						perks[ perks.size ] = "specialty2";
					}
					if ( !claimed_count[ "specialty3" ] )
					{
						perks[ perks.size ] = "specialty3";
					}
					if ( perks.size )
					{
						perk = random( perks );
						bot_choose_weapon( class, items[ perk ] );
						claimed_count[ perk ]++;
						allocation++;
						i++;
						continue;
					}
				}
				i++;
			}
		}
		else if ( next_action == "lethal" )
		{
			remaining = allocation_max - allocation;
			if ( remaining >= 2 && randomint( 100 ) < 50 )
			{
				if ( !claimed_count[ "primarygrenade" ] )
				{
					bot_choose_weapon( class, items[ "primarygrenade" ] );
					claimed_count[ "primarygrenade" ]++;
					allocation++;
				}
				self botclassadditem( class, "BONUSCARD_DANGER_CLOSE" );
				allocation++;
			}
			else if ( remaining >= 1 && !claimed_count[ "primarygrenade" ] )
			{
				bot_choose_weapon( class, items[ "primarygrenade" ] );
				claimed_count[ "primarygrenade" ]++;
				allocation++;
			}
		}
		else if ( next_action == "tactical" )
		{
			remaining = allocation_max - allocation;
			if ( remaining >= 2 && !claimed_count[ "specialgrenade" ] && randomint( 100 ) < 50 )
			{
				weapon = bot_choose_weapon( class, items[ "specialgrenade" ] );
				if ( weapon == "WEAPON_TACTICAL_INSERTION" || weapon == "WEAPON_WILLY_PETE" )
				{
					claimed_count[ "specialgrenade" ] = 1;
					allocation += 1;
				}
				else
				{
					self botclassadditem( class, weapon );
					claimed_count[ "specialgrenade" ] = 2;
					allocation += 2;
				}
			}
			else if ( remaining >= 1 && !claimed_count[ "specialgrenade" ] )
			{
				bot_choose_weapon( class, items[ "specialgrenade" ] );
				claimed_count[ "specialgrenade" ]++;
				allocation++;
			}
		}
	}
}

bot_make_choice( chance, claimed, max_claim ) //checked changed at own discretion
{
	if ( claimed < max_claim && randomint( 100 ) < chance )
	{
		return 1;
	}
	return 0;
}

bot_chose_action( action1, chance1, action2, chance2, action3, chance3, action4, chance4 ) //checked changed to match cerberus output
{
	chance1 = int( chance1 / 10 );
	chance2 = int( chance2 / 10 );
	chance3 = int( chance3 / 10 );
	chance4 = int( chance4 / 10 );
	actions = [];
	for ( i = 0; i < chance1; i++ )
	{
		actions[ actions.size ] = action1;
	}
	for ( i = 0; i < chance2; i++ )
	{
		actions[ actions.size ] = action2;
	}
	for ( i = 0; i < chance3; i++ )
	{
		actions[ actions.size ] = action3;
	}
	for ( i = 0; i < chance4; i++ )
	{
		actions[ actions.size ] = action4;
	}
	return random( actions );
}

bot_item_is_claimed( item ) //checked changed to match cerberus output
{
	foreach ( claim in self.claimed_items )
	{
		if ( claim == item )
		{
			return 1;
		}
	}
	return 0;
}

bot_choose_weapon( class, items ) //checked changed to match cerberus output
{
	if ( !isDefined( items ) || !items.size )
	{
		return undefined;
	}
	start = randomint( items.size );
	for ( i = 0; i < items.size; i++ )
	{
		weapon = items[ start ];
		if ( !bot_item_is_claimed( weapon ) )
		{
			break;
		}
		start = ( start + 1 ) % items.size;
	}
	self.claimed_items[ self.claimed_items.size ] = weapon;
	self botclassadditem( class, weapon );
	return weapon;
}

bot_build_weapon_options_list( optiontype ) //checked changed to match cerberus output
{
	level.botweaponoptionsid[ optiontype ] = [];
	level.botweaponoptionsprob[ optiontype ] = [];
	prob = 0;
	for ( row = 0; row < 255; row++ )
	{
		if ( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 1 ) == optiontype )
		{
			index = level.botweaponoptionsid[ optiontype ].size;
			level.botweaponoptionsid[ optiontype ][ index ] = int( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 0 ) );
			prob += int( tablelookupcolumnforrow( "mp/attachmentTable.csv", row, 15 ) );
			level.botweaponoptionsprob[ optiontype ][ index ] = prob;
		}
	}
}

bot_choose_weapon_option( class, optiontype, primary ) //checked changed to match cerberus output may need to review order of operations
{
	if ( !isDefined( level.botweaponoptionsid ) )
	{
		level.botweaponoptionsid = [];
		level.botweaponoptionsprob = [];
		bot_build_weapon_options_list( "camo" );
		bot_build_weapon_options_list( "reticle" );
	}
	if ( !level.onlinegame && !level.systemlink )
	{
		return;
	}
	numoptions = level.botweaponoptionsprob[ optiontype ].size;
	maxprob = level.botweaponoptionsprob[ optiontype ][ numoptions - 1 ];
	if ( !level.systemlink && self.pers[ "rank" ] < 20 )
	{
		maxprob += ( 4 * maxprob ) * ( ( 20 - self.pers[ "rank" ] ) / 20 );
	}
	rnd = randomint( int( maxprob ) );
	for ( i = 0; i < numoptions; i++ )
	{
		if ( level.botweaponoptionsprob[ optiontype ][ i ] > rnd )
		{
			self botclasssetweaponoption( class, primary, optiontype, level.botweaponoptionsid[ optiontype ][ i ] );
			return;
		}
	}
}

bot_choose_primary_attachments( class, weapon, allocation, allocation_max ) //checked changed to match cerberus output
{
	attachments = getweaponattachments( weapon );
	remaining = allocation_max - allocation;
	if ( !attachments.size || !remaining )
	{
		return 0;
	}
	attachment_action = bot_chose_action( "3_attachments", 25, "2_attachments", 35, "1_attachments", 35, "none", 5 );
	if ( remaining >= 4 && attachment_action == "3_attachments" )
	{
		a1 = random( attachments );
		self botclassaddattachment( class, weapon, a1, "primaryattachment1" );
		count = 1;
		attachments = getweaponattachments( weapon, a1 );
		if ( attachments.size )
		{
			a2 = random( attachments );
			self botclassaddattachment( class, weapon, a2, "primaryattachment2" );
			count++;
			attachments = getweaponattachments( weapon, a1, a2 );
			if ( attachments.size )
			{
				a3 = random( attachments );
				self botclassadditem( class, "BONUSCARD_PRIMARY_GUNFIGHTER" );
				self botclassaddattachment( class, weapon, a3, "primaryattachment3" );
				return 4;
			}
		}
		return count;
	}
	else if ( remaining >= 2 && attachment_action == "2_attachments" )
	{
		a1 = random( attachments );
		self botclassaddattachment( class, weapon, a1, "primaryattachment1" );
		attachments = getweaponattachments( weapon, a1 );
		if ( attachments.size )
		{
			a2 = random( attachments );
			self botclassaddattachment( class, weapon, a2, "primaryattachment2" );
			return 2;
		}
		return 1;
	}
	else if ( remaining >= 1 && attachment_action == "1_attachments" )
	{
		a = random( attachments );
		self botclassaddattachment( class, weapon, a, "primaryattachment1" );
		return 1;
	}
	return 0;
}

bot_choose_secondary_attachments( class, weapon, allocation, allocation_max ) //checked changed to match cerberus output
{
	attachments = getweaponattachments( weapon );
	remaining = allocation_max - allocation;
	if ( !attachments.size || !remaining )
	{
		return 0;
	}
	attachment_action = bot_chose_action( "2_attachments", 10, "1_attachments", 40, "none", 50, "none", 0 );
	if ( remaining >= 3 && attachment_action == "2_attachments" )
	{
		a1 = random( attachments );
		self botclassaddattachment( class, weapon, a1, "secondaryattachment1" );
		attachments = getweaponattachments( weapon, a1 );
		if ( attachments.size )
		{
			a2 = random( attachments );
			self botclassadditem( class, "BONUSCARD_SECONDARY_GUNFIGHTER" );
			self botclassaddattachment( class, weapon, a2, "secondaryattachment2" );
			return 3;
		}
		return 1;
	}
	else if ( remaining >= 1 && attachment_action == "1_attachments" )
	{
		a = random( attachments );
		self botclassaddattachment( class, weapon, a, "secondaryattachment1" );
		return 1;
	}
	return 0;
}

bot_build_item_list() //checked partially changed to match cerberus output did not change while loop to for loop see github for more info
{
	pixbeginevent( "bot_build_item_list" );
	items = [];
	i = 0;
	while ( i < 256 )
	{
		row = tablelookuprownum( level.statstableid, 0, i );
		if ( row > -1 )
		{
			slot = tablelookupcolumnforrow( level.statstableid, row, 13 );
			if ( slot == "" )
			{
				i++;
				continue;
			}
			number = int( tablelookupcolumnforrow( level.statstableid, row, 0 ) );
			if ( self isitemlocked( number ) )
			{
				i++;
				continue;
			}
			allocation = int( tablelookupcolumnforrow( level.statstableid, row, 12 ) );
			if ( allocation < 0 )
			{
				i++;
				continue;
			}
			name = tablelookupcolumnforrow( level.statstableid, row, 3 );
			if ( bot_item_is_banned( slot, name ) )
			{
				i++;
				continue;
			}
			if ( !isDefined( items[ slot ] ) )
			{
				items[ slot ] = [];
			}
			items[ slot ][ items[ slot ].size ] = name;
		}
		i++;
	}
	pixendevent();
	return items;
}

bot_item_is_banned( slot, item ) //checked changed to match cerberus output
{
	if ( item == "WEAPON_KNIFE_BALLISTIC" )
	{
		return 1;
	}
	if ( /* getDvarInt( #"97A055DA" ) == 0 && */ item == "WEAPON_PEACEKEEPER" )
	{
		return 1;
	}
	if ( slot != "killstreak1" && slot != "killstreak2" && slot != "killstreak3" )
	{
		return 0;
	}
	foreach ( banned in level.bot_banned_killstreaks )
	{
		if ( item == banned )
		{
			return 1;
		}
	}
	return 0;
}

bot_build_claimed_list( items ) //checked changed to match cerberus output
{
	claimed = [];
	keys = getarraykeys( items );
	foreach ( key in keys )
	{
		claimed[ key ] = 0;
	}
	return claimed;
}

