#include maps\mp\_utility;
#include common_scripts\utility;
#include maps\mp\gametypes\_hud_util;

CRATE_KILLCAM_OFFSET = ( 0, 0, -200);
GRAVITY_UNITS_PER_SECOND = 800;
CRATE_ICON_Z_OFFSET = 60;

init()
{
	if ( GetDvarInt( "virtuallobbyactive", 0 ) )
		return;
	
	level._effect[ "airdrop_crate_destroy" ] = loadfx( "vfx/explosion/vehicle_pdrone_explosion" );
	level._effect[ "airdrop_crate_trap_explode" ] = LoadFX( "vfx/explosion/frag_grenade_default" );
	
	setAirDropCrateCollision( "airdrop_crate" ); 	// old care package entities
	setAirDropCrateCollision( "care_package" );		// new care package entities
	assert( IsDefined(level.airDropCrateCollision) );
	
	level.numDropCrates = 0;
	level.crateTypes 	= [];
	
	level.killstreakWieldWeapons["airdrop_trap_explosive_mp"] = "airdrop_assault";
		
	level.killStreakFuncs["airdrop_reinforcement_common"] 	= ::tryUseReinforcementCommon;
	level.killStreakFuncs["airdrop_reinforcement_uncommon"] = ::tryUseReinforcementUncommon;
	level.killStreakFuncs["airdrop_reinforcement_rare"] 	= ::tryUseReinforcementRare;
	level.killStreakFuncs["airdrop_reinforcement_practice"]	= ::tryUseReinforcementPractice;
	
	addCrateTypes_standard();
	
	level.secondaryReinforcementHintText = [];
	level.secondaryReinforcementHintText[ "specialty_extended_battery" ] = &"PERKS_EXO_BATTERY";
	level.secondaryReinforcementHintText[ "specialty_class_lowprofile" ] = &"PERKS_LOWPROFILE";
	level.secondaryReinforcementHintText[ "specialty_class_flakjacket" ] = &"PERKS_FLAKJACKET";
	level.secondaryReinforcementHintText[ "specialty_class_lightweight" ] = &"PERKS_LIGHTWEIGHT";
	level.secondaryReinforcementHintText[ "specialty_class_blindeye" ] = &"PERKS_BLINDEYE";
	level.secondaryReinforcementHintText[ "specialty_class_coldblooded" ] = &"PERKS_COLDBLOODED";
	level.secondaryReinforcementHintText[ "specialty_class_peripherals" ] = &"PERKS_PERIPHERALS";
	level.secondaryReinforcementHintText[ "specialty_class_fasthands" ] = &"PERKS_FASTHANDS";
	level.secondaryReinforcementHintText[ "specialty_class_dexterity" ] = &"PERKS_DEXTERITY";
	level.secondaryReinforcementHintText[ "specialty_exo_blastsuppressor" ] = &"PERKS_EXO_BLASTSUPPRESSOR";
	level.secondaryReinforcementHintText[ "specialty_class_hardwired" ] = &"PERKS_HARDWIRED";
	level.secondaryReinforcementHintText[ "specialty_class_toughness" ] = &"PERKS_TOUGHNESS";
	level.secondaryReinforcementHintText[ "specialty_class_scavenger" ] = &"PERKS_SCAVENGER";
	level.secondaryReinforcementHintText[ "specialty_class_hardline" ] = &"PERKS_HARDLINE";

	if( IsDefined(level.customCrateFunc) )
		[[level.customCrateFunc]]();
	
	generateMaxWeightedCrateValue();

	/#
	SetDevDvarIfUninitialized( "scr_crateOverride", "" );
	SetDevDvarIfUninitialized( "scr_crateTypeOverride", "" );
	#/
	
	level.mapKillstreakAutoDropIndex = RandomInt( 4 );
}

addCrateTypes_standard()
{
	mapStreak = level.mapKillStreak;
	if ( IsDefined( level.mapStreaksDisabled ) && level.mapStreaksDisabled )
		mapStreak = undefined;
	
	//   			dropType 	      			crateType 							crateWeight		crateFunc 					hint string			  				streak ref					module 1						module 2						module 3
	addCrateType( 	"airdrop_assault", 			mapStreak, 							168, 			::killstreakCrateThink, 	level.mapKillstreakPickupString, 	mapStreak );
	addCrateType(	"airdrop_assault", 			"b", 								168, 			::killstreakCrateThink, 	&"MP_SENTRY_PICKUP", 				"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_heavy_resistance" );
	addCrateType(	"airdrop_assault", 			"c", 								168, 			::killstreakCrateThink, 	&"MP_SENTRY_PICKUP", 				"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_rippable", 				"sentry_rocket_turret" );
	addCrateType( 	"airdrop_assault", 			"d", 								168, 			::killstreakCrateThink, 	&"MP_MISSILE_STRIKE_PICKUP", 		"missile_strike", 			"missile_strike_extra_1" );
	addCrateType( 	"airdrop_assault", 			"e", 								168, 			::killstreakCrateThink, 	&"MP_MISSILE_STRIKE_PICKUP", 		"missile_strike", 			"missile_strike_chem", 			"missile_strike_extra_1" );
	addCrateType( 	"airdrop_assault", 			"f", 								168, 			::killstreakCrateThink, 	&"MP_RECON_UGV_PICKUP", 			"recon_ugv", 				"recon_ugv_cloak", 				"recon_ugv_assist_points" );
	addCrateType( 	"airdrop_assault", 			"g", 								168, 			::killstreakCrateThink, 	&"MP_RECON_UGV_PICKUP", 			"recon_ugv", 				"recon_ugv_paint_grenade", 		"recon_ugv_assist_points" );
	addCrateType( 	"airdrop_assault", 			"h", 								98, 			::killstreakCrateThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_beam"	);
	addCrateType( 	"airdrop_assault", 			"i", 								98, 			::killstreakCrateThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_width", 	"orbital_strike_laser_duration"	);
	addCrateType( 	"airdrop_assault", 			"j", 								100, 			::killstreakCrateThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_enemy_direction", 			"uav_orbit" );
	addCrateType( 	"airdrop_assault", 			"k", 								100, 			::killstreakCrateThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_scrambler", 				"uav_increased_time", 			"uav_paint_outline" );
	addCrateType( 	"airdrop_assault", 			"l", 								40, 			::killstreakCrateThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_rockets", 				"warbird_coop_offensive" );
	addCrateType( 	"airdrop_assault", 			"m", 								40, 			::killstreakCrateThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_ai_attack", 			"warbird_flares", 				"warbird_time" );
	addCrateType( 	"airdrop_assault", 			"n", 								30, 			::killstreakCrateThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_mg",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_assault", 			"o", 								30, 			::killstreakCrateThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_ai",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_assault", 			"p", 								20, 			::killstreakCrateThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_turret", 		"orbitalsupport_coop_offensive","orbitalsupport_ammo" );
	addCrateType( 	"airdrop_assault", 			"q", 								20, 			::killstreakCrateThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_rockets", 		"orbitalsupport_flares",		"orbitalsupport_time" );
	addCrateType( 	"airdrop_assault", 			"r", 								20, 			::killstreakCrateThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_two", 	"strafing_run_airstrike_flares" );
	addCrateType( 	"airdrop_assault", 			"s", 								20, 			::killstreakCrateThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_stealth" );
	addCrateType( 	"airdrop_assault", 			"t", 								10, 			::killstreakCrateThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_assist", 					"emp_flash" );
	addCrateType( 	"airdrop_assault", 			"u", 								10, 			::killstreakCrateThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_streak_kill", 				"emp_equipment_kill", 			"emp_time_1" );
	addCrateType( 	"airdrop_assault", 			"v", 								10, 			::killstreakCrateThink, 	&"MP_HEAVY_EXO_PICKUP", 			"heavy_exosuit", 			"heavy_exosuit_radar", 	 		"heavy_exosuit_punch"	);
	addCrateType( 	"airdrop_assault", 			"w", 								10, 			::killstreakCrateThink, 	&"MP_HEAVY_EXO_PICKUP", 			"heavy_exosuit", 			"heavy_exosuit_trophy", 		"heavy_exosuit_rockets", 		"heavy_exosuit_eject"	);

	//   			dropType 	      			crateType 							crateWeight		crateFunc 					hint string			  				streak ref					module 1						module 2						module 3
	addCrateType( 	"airdrop_assault_odds", 	mapStreak, 							136, 			::killstreakCrateThink, 	level.mapKillstreakPickupString, 	mapStreak );
	addCrateType(	"airdrop_assault_odds", 	"b", 								136, 			::killstreakCrateThink, 	&"MP_SENTRY_PICKUP", 				"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_heavy_resistance" );
	addCrateType(	"airdrop_assault_odds", 	"c", 								136, 			::killstreakCrateThink, 	&"MP_SENTRY_PICKUP", 				"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_rippable", 				"sentry_rocket_turret" );
	addCrateType( 	"airdrop_assault_odds", 	"d", 								136, 			::killstreakCrateThink, 	&"MP_MISSILE_STRIKE_PICKUP", 		"missile_strike", 			"missile_strike_extra_1" );
	addCrateType( 	"airdrop_assault_odds", 	"e", 								136, 			::killstreakCrateThink, 	&"MP_MISSILE_STRIKE_PICKUP", 		"missile_strike", 			"missile_strike_chem", 			"missile_strike_extra_1" );
	addCrateType( 	"airdrop_assault_odds", 	"f", 								136, 			::killstreakCrateThink, 	&"MP_RECON_UGV_PICKUP", 			"recon_ugv", 				"recon_ugv_cloak", 				"recon_ugv_assist_points" );
	addCrateType( 	"airdrop_assault_odds", 	"g", 								136, 			::killstreakCrateThink, 	&"MP_RECON_UGV_PICKUP", 			"recon_ugv", 				"recon_ugv_paint_grenade", 		"recon_ugv_assist_points" );
	addCrateType( 	"airdrop_assault_odds", 	"h", 								116, 			::killstreakCrateThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_beam"	);
	addCrateType( 	"airdrop_assault_odds", 	"i", 								116, 			::killstreakCrateThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_width", 	"orbital_strike_laser_duration"	);
	addCrateType( 	"airdrop_assault_odds", 	"j", 								100, 			::killstreakCrateThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_enemy_direction", 			"uav_orbit" );
	addCrateType( 	"airdrop_assault_odds", 	"k", 								100, 			::killstreakCrateThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_scrambler", 				"uav_increased_time", 			"uav_paint_outline" );
	addCrateType( 	"airdrop_assault_odds", 	"l", 								60, 			::killstreakCrateThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_rockets", 				"warbird_coop_offensive" );
	addCrateType( 	"airdrop_assault_odds", 	"m", 								60, 			::killstreakCrateThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_ai_attack", 			"warbird_flares", 				"warbird_time" );
	addCrateType( 	"airdrop_assault_odds", 	"n", 								50, 			::killstreakCrateThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_mg",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_assault_odds", 	"o", 								50, 			::killstreakCrateThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_ai",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_assault_odds", 	"p", 								40, 			::killstreakCrateThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_turret", 		"orbitalsupport_coop_offensive","orbitalsupport_ammo" );
	addCrateType( 	"airdrop_assault_odds", 	"q", 								40, 			::killstreakCrateThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_rockets", 		"orbitalsupport_flares",		"orbitalsupport_time" );
	addCrateType( 	"airdrop_assault_odds", 	"r", 								40, 			::killstreakCrateThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_two", 	"strafing_run_airstrike_flares" );
	addCrateType( 	"airdrop_assault_odds", 	"s", 								40, 			::killstreakCrateThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_stealth" );
	addCrateType( 	"airdrop_assault_odds", 	"t", 								30, 			::killstreakCrateThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_assist", 					"emp_flash" );
	addCrateType( 	"airdrop_assault_odds", 	"u", 								30, 			::killstreakCrateThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_streak_kill", 				"emp_equipment_kill", 			"emp_time_1" );
	addCrateType( 	"airdrop_assault_odds", 	"v", 								30, 			::killstreakCrateThink, 	&"MP_HEAVY_EXO_PICKUP", 			"heavy_exosuit", 			"heavy_exosuit_radar",	 		"heavy_exosuit_punch"	);
	addCrateType( 	"airdrop_assault_odds", 	"w", 								30, 			::killstreakCrateThink, 	&"MP_HEAVY_EXO_PICKUP",				"heavy_exosuit", 			"heavy_exosuit_trophy", 		"heavy_exosuit_rockets", 		"heavy_exosuit_eject"	);

	// dropType 	      								crateType 					crateWeight		crateFunc 								hint string			  						streak ref					module 1						module 2						module 3
	addCrateType( "airdrop_reinforcement_common", 		"a", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_SENTRY_PICKUP", 						"remote_mg_sentry_turret", 	"sentry_guardian" );
	addCrateType( "airdrop_reinforcement_common", 		"b", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_UAV_PICKUP", 							"uav", 						"uav_enemy_direction" );
	addCrateType( "airdrop_reinforcement_common", 		"c", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_RECON_UGV_PICKUP", 					"recon_ugv",				"recon_ugv_assist_points" );
	
	// dropType 	      								crateType 					crateWeight		crateFunc 								hint string			  						streak ref					module 1						module 2						module 3
	addCrateType( "airdrop_reinforcement_uncommon", 	"a", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_EMP_PICKUP", 							"emp" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"b", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 			"assault_ugv",				"assault_ugv_rockets" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"c", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_SENTRY_PICKUP", 						"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_rippable", 				"sentry_rocket_turret" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"d", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_MISSILE_STRIKE_PICKUP", 				"missile_strike", 			"missile_strike_chem", 			"missile_strike_extra_1" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"e", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_UAV_PICKUP", 							"uav", 						"uav_scrambler", 				"uav_increased_time" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"f", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_RECON_UGV_PICKUP", 					"recon_ugv", 				"recon_ugv_paint_grenade", 		"recon_ugv_assist_points" );
	
	addCrateType( "airdrop_reinforcement_uncommon", 	"g", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_EXO_BATTERY", 						"specialty_extended_battery" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"h", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_LOWPROFILE", 						"specialty_class_lowprofile" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"j", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_FLAKJACKET", 						"specialty_class_flakjacket" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"k", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_LIGHTWEIGHT", 						"specialty_class_lightweight" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"l", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_BLINDEYE", 							"specialty_class_blindeye" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"m", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_COLDBLOODED", 						"specialty_class_coldblooded" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"n", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_PERIPHERALS", 						"specialty_class_peripherals" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"o", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_FASTHANDS", 						"specialty_class_fasthands" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"p", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_DEXTERITY", 						"specialty_class_dexterity" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"r", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_EXO_BLASTSUPPRESSOR", 				"specialty_exo_blastsuppressor" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"s", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_HARDWIRED", 						"specialty_class_hardwired" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"t", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_TOUGHNESS", 						"specialty_class_toughness" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"u", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_SCAVENGER", 						"specialty_class_scavenger" );
	addCrateType( "airdrop_reinforcement_uncommon", 	"v", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_HARDLINE", 							"specialty_class_hardline" );
		
	// dropType 	      								crateType 					crateWeight		crateFunc 								hint string			  						streak ref					module 1						module 2						module 3
	addCrateType( "airdrop_reinforcement_rare", 		"a", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_HEAVY_EXO_PICKUP", 					"heavy_exosuit", 			"heavy_exosuit_radar" );
	addCrateType( "airdrop_reinforcement_rare", 		"b", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITALSUPPORT_PICKUP", 				"orbitalsupport",			"orbitalsupport_turret" );
	addCrateType( "airdrop_reinforcement_rare", 		"c", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_AIRSTRIKE_PICKUP", 					"strafing_run_airstrike", 	"strafing_run_airstrike_flares" );
	addCrateType( "airdrop_reinforcement_rare", 		"d", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_WARBIRD_PICKUP", 						"warbird", 					"warbird_ai_attack", 			"warbird_flares" );
	addCrateType( "airdrop_reinforcement_rare", 		"e", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 			"orbital_strike_laser", 	"orbital_strike_laser_width", 	"orbital_strike_laser_duration"	);
	addCrateType( "airdrop_reinforcement_rare", 		"f", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_UAV_PICKUP", 							"uav", 						"uav_scrambler", 				"uav_increased_time", 			"uav_paint_outline" );
	addCrateType( "airdrop_reinforcement_rare", 		"g", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_RECON_UGV_PICKUP", 					"recon_ugv", 				"recon_ugv_paint_grenade", 		"recon_ugv_assist_points",		"recon_ugv_stun" );
	
	addCrateType( "airdrop_reinforcement_rare", 		"h", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_EXO_BATTERY", 						"specialty_extended_battery" );
	addCrateType( "airdrop_reinforcement_rare", 		"i", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_LOWPROFILE", 						"specialty_class_lowprofile" );
	addCrateType( "airdrop_reinforcement_rare", 		"k", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_FLAKJACKET", 						"specialty_class_flakjacket" );
	addCrateType( "airdrop_reinforcement_rare", 		"l", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_LIGHTWEIGHT", 						"specialty_class_lightweight" );
	addCrateType( "airdrop_reinforcement_rare", 		"m", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_BLINDEYE", 							"specialty_class_blindeye" );
	addCrateType( "airdrop_reinforcement_rare", 		"n", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_COLDBLOODED", 						"specialty_class_coldblooded" );
	addCrateType( "airdrop_reinforcement_rare", 		"o", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_PERIPHERALS", 						"specialty_class_peripherals" );
	addCrateType( "airdrop_reinforcement_rare", 		"p", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_FASTHANDS", 						"specialty_class_fasthands" );
	addCrateType( "airdrop_reinforcement_rare", 		"q", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_DEXTERITY", 						"specialty_class_dexterity" );
	addCrateType( "airdrop_reinforcement_rare", 		"s", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_EXO_BLASTSUPPRESSOR", 				"specialty_exo_blastsuppressor" );
	addCrateType( "airdrop_reinforcement_rare", 		"t", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_HARDWIRED", 						"specialty_class_hardwired" );
	addCrateType( "airdrop_reinforcement_rare", 		"u", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_TOUGHNESS", 						"specialty_class_toughness" );
	addCrateType( "airdrop_reinforcement_rare", 		"v", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_SCAVENGER", 						"specialty_class_scavenger" );
	addCrateType( "airdrop_reinforcement_rare", 		"w", 						100, 			::reinforcementCrateSpecialtyThink, 	&"PERKS_HARDLINE", 							"specialty_class_hardline" );
	
	// dropType 	      								crateType 					crateWeight		crateFunc 								hint string			  				streak ref					module 1						module 2						module 3
	addCrateType(	"airdrop_reinforcement_practice", 	"a", 						168, 			::reinforcementCrateKillstreakThink, 	&"MP_SENTRY_PICKUP", 				"remote_mg_sentry_turret", 	"sentry_guardian", 				"sentry_rippable", 				"sentry_rocket_turret" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"b", 						168, 			::reinforcementCrateKillstreakThink, 	&"MP_MISSILE_STRIKE_PICKUP", 		"missile_strike", 			"missile_strike_extra_1" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"c", 						168, 			::reinforcementCrateKillstreakThink, 	&"MP_RECON_UGV_PICKUP", 			"recon_ugv", 				"recon_ugv_paint_grenade", 		"recon_ugv_assist_points" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"d", 						98, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_beam"	);
	addCrateType( 	"airdrop_reinforcement_practice", 	"e", 						98, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITAL_STRIKE_LASER_PICKUP", 	"orbital_strike_laser", 	"orbital_strike_laser_width", 	"orbital_strike_laser_duration"	);
	addCrateType( 	"airdrop_reinforcement_practice", 	"f", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_enemy_direction", 			"uav_orbit" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"g", 						100, 			::reinforcementCrateKillstreakThink, 	&"MP_UAV_PICKUP", 					"uav", 						"uav_scrambler", 				"uav_increased_time", 			"uav_paint_outline" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"h", 						40, 			::reinforcementCrateKillstreakThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_rockets", 				"warbird_coop_offensive" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"i", 						40, 			::reinforcementCrateKillstreakThink, 	&"MP_WARBIRD_PICKUP", 				"warbird", 					"warbird_ai_attack", 			"warbird_flares", 				"warbird_time" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"j", 						30, 			::reinforcementCrateKillstreakThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_mg",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"k", 						30, 			::reinforcementCrateKillstreakThink, 	&"MP_GROUND_ASSAULT_DRONE_PICKUP", 	"assault_ugv", 				"assault_ugv_ai",				"assault_ugv_rockets" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"l", 						20, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_turret", 		"orbitalsupport_coop_offensive","orbitalsupport_ammo" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"m", 						20, 			::reinforcementCrateKillstreakThink, 	&"MP_ORBITALSUPPORT_PICKUP", 		"orbitalsupport",			"orbitalsupport_rockets", 		"orbitalsupport_flares",		"orbitalsupport_time" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"n", 						20, 			::reinforcementCrateKillstreakThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_flares" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"o", 						20, 			::reinforcementCrateKillstreakThink, 	&"MP_AIRSTRIKE_PICKUP", 			"strafing_run_airstrike", 	"strafing_run_airstrike_stealth" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"p", 						10, 			::reinforcementCrateKillstreakThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_assist", 					"emp_flash" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"q", 						10, 			::reinforcementCrateKillstreakThink, 	&"MP_EMP_PICKUP", 					"emp", 						"emp_streak_kill", 				"emp_equipment_kill", 			"emp_time_1" );
	addCrateType( 	"airdrop_reinforcement_practice", 	"r", 						10, 			::reinforcementCrateKillstreakThink, 	&"MP_HEAVY_EXO_PICKUP", 			"heavy_exosuit", 			"heavy_exosuit_radar",	 		"heavy_exosuit_punch"	);
	addCrateType( 	"airdrop_reinforcement_practice", 	"s", 						10, 			::reinforcementCrateKillstreakThink, 	&"MP_HEAVY_EXO_PICKUP", 			"heavy_exosuit", 			"heavy_exosuit_trophy", 		"heavy_exosuit_rockets", 		"heavy_exosuit_eject"	);
}

generateMaxWeightedCrateValue()
{
	foreach( dropType, dropTypeArray in level.crateTypes )
	{
		level.crateMaxVal[ dropType ] = 0;	
		foreach( crateType in dropTypeArray )
		{
			type = crateType.type;
			if( !level.crateTypes[ dropType ][ type ].raw_weight )
			{
				level.crateTypes[ dropType ][ type ].weight = level.crateTypes[ dropType ][ type ].raw_weight;
				continue;
			}

			level.crateMaxVal[ dropType ] += level.crateTypes[ dropType ][ type ].raw_weight;
			level.crateTypes[ dropType ][ type ].weight = level.crateMaxVal[ dropType ];
		}
	}
}

changeCrateWeight(dropType, crateType, crateWeight)
{
	if(!IsDefined(level.crateTypes[ dropType ]) || !IsDefined(level.crateTypes[ dropType ][ crateType ]))
	   return;
	   
	level.crateTypes[ dropType ][ crateType ].raw_weight = crateWeight;
	generateMaxWeightedCrateValue();
}

setAirDropCrateCollision( carePackageName )
{
	airDropCrates = GetEntArray( carePackageName, "targetname" );
	
	if( !IsDefined(airDropCrates) || (airDropCrates.size == 0 ) )
	{
		return;
	}
	
	level.airDropCrateCollision = GetEnt( airDropCrates[0].target, "targetname" );
	
	foreach( crate in airDropCrates )
	{
		crate deleteCrate();
	}
}

addCrateType( dropType, crateType, crateWeight, crateFunc, hintString, streakRef, moduleRef1, moduleRef2, moduleRef3 )
{
	if( !IsDefined(crateType) )
		return;
		
	level.crateTypes[ dropType ][ crateType ] = SpawnStruct();
	
	level.crateTypes[ dropType ][ crateType ].dropType 		= dropType;
	level.crateTypes[ dropType ][ crateType ].type 			= crateType;
	level.crateTypes[ dropType ][ crateType ].raw_weight 	= crateWeight;
	level.crateTypes[ dropType ][ crateType ].weight 		= crateWeight;
	level.crateTypes[ dropType ][ crateType ].func 			= crateFunc;
	level.crateTypes[ dropType ][ crateType ].streakRef 	= streakRef;
	level.crateTypes[ dropType ][ crateType ].modules = [];
	level.crateTypes[ dropType ][ crateType ].modules[ level.crateTypes[ dropType ][ crateType ].modules.size ] = moduleRef1;
	level.crateTypes[ dropType ][ crateType ].modules[ level.crateTypes[ dropType ][ crateType ].modules.size ] = moduleRef2;
	level.crateTypes[ dropType ][ crateType ].modules[ level.crateTypes[ dropType ][ crateType ].modules.size ] = moduleRef3;
	
	if( IsDefined( hintString ) )
		game[ "strings" ][ dropType + crateType + "_hint" ] = hintString;
}

getStreakForCrate( dropType, crateType )
{
	if ( IsDefined( level.crateTypes[ dropType ] ) && IsDefined( level.crateTypes[ dropType ][ crateType ] ) && IsDefined( level.crateTypes[ dropType ][ crateType ].streakRef ) )
		return level.crateTypes[ dropType ][ crateType ].streakRef;
	
	return crateType;
}

getModulesForCrate( dropType, crateType )
{
	if ( IsDefined( level.customKillstreakCrateModules ) )
		return [[level.customKillstreakCrateModules]]( dropType, crateType );

	return level.crateTypes[ dropType ][ crateType ].modules;
}

getRandomCrateType( dropType, excludeCrateTypes )
{
/#
	if ( GetDvar( "scr_setnextkillstreak", "" ) != "" )
	{
		killstreak = GetDvar( "scr_setnextkillstreak" );
		SetDvar( "scr_setnextkillstreak", "" );
		
		foreach( crateType in level.crateTypes[ dropType ] )
		{
			type = crateType.type;
			if ( level.crateTypes[ dropType ][ type ].streakRef == killstreak )
				return type;
		}
	}
#/
	if ( GetDvar ( "g_gametype" ) != "horde" )
	{
		typeHasMapKillstreak = IsDefined( level.mapKillStreak ) && isDefined(level.crateTypes[dropType][level.mapKillStreak]);
		canSpawnMapKillStreak = IsDefined(level.mapKillstreakAutoDropIndex) && level.numDropCrates >= level.mapKillstreakAutoDropIndex;
		if ( typeHasMapKillstreak && canSpawnMapKillStreak )
		{
			level.mapKillstreakAutoDropIndex = undefined;
			return level.mapKillStreak;
		}
	}
	
	value 				= RandomInt( level.crateMaxVal[ dropType ] );
	selectedCrateType 	= undefined;
	
	dropTypes = level.crateTypes[ dropType ];
	if( isDefined( excludeCrateTypes ) )
	{
		AssertEx( isArray( excludeCrateTypes ), "excludeCrateTypes must be an array" );
		dropTypeArray = level.crateTypes[ dropType ];
		foreach( ent in dropTypeArray )
		{
			if( crateTypeIsExcluded( ent.type, excludeCrateTypes ) )
			{
				dropTypeArray = special_array_remove( dropTypeArray, ent );
			}
		}
		dropTypes = dropTypeArray;
	}	
	
	foreach( crateType in dropTypes )
	{
		type = crateType.type;
		if( !level.crateTypes[ dropType ][ type ].weight )
			continue;

		selectedCrateType = type;

		if( level.crateTypes[ dropType ][ type ].weight > value )
			break;
	}
	
	return( selectedCrateType );
}

crateTypeIsExcluded( type, excludeCrateTypes )
{
	foreach( excludeType in excludeCrateTypes )
	{
		if( excludeType == type )
			return true;
	}
	
	return false;
}

special_array_remove( array, remove )
{
	new_array = [];
	foreach( ent in array )
	{
		if( ent != remove )
		{
			new_array[ent.type] = ent;
		}
	}
	return new_array;
}


getCrateTypeForDropType( dropType, excludedCrateTypes )
{
	switch	( dropType )
	{
		case "airdrop_assault":
		case "airdrop_assault_odds":
		default:
			return getRandomCrateType( dropType, excludedCrateTypes );
	}
}

/**********************************************************
*		 crate functions
***********************************************************/

deleteOnOwnerDeath( owner )
{
//	wait ( 0.25 );
	self linkTo( owner, "tag_origin", (0,0,0), (0,0,0) );

	owner waittill ( "death" );
	
	self delete();
}


crateTeamModelUpdater() // self == crate team model (the logo)
{
	self endon ( "death" );

	self hide();
	foreach ( player in level.players )
	{
		if ( player.team != "spectator" )
			self ShowToPlayer( player );
	}

	for ( ;; )
	{
		level waittill ( "joined_team" );
		
		self hide();
		foreach ( player in level.players )
		{
			if ( player.team != "spectator" )
				self ShowToPlayer( player );
		}
	}	
}


crateModelTeamUpdater( showForTeam, showToSpectator ) // self == crate model (friendly or enemy)
{
	self endon ( "death" );

	self hide();

	foreach ( player in level.players )
	{
		if( ( player.team == showForTeam ) || ( showToSpectator && ( player.team == "spectator" ) ) )
			self ShowToPlayer( player );
	}

	for ( ;; )
	{
		level waittill_any( "joined_team", "joined_spectators" );
		
		self hide();
		foreach ( player in level.players )
		{
			if ( ( player.team == showForTeam ) || ( showToSpectator && ( player.team == "spectator" ) ) )
				self ShowToPlayer( player );
		}
	}
}

// for FFA
crateModelPlayerUpdater( owner, friendly ) // self == crate model (friendly or enemy)
{
	self endon ( "death" );

	self hide();

	foreach ( player in level.players )
	{
		if( friendly && IsDefined( owner ) && player != owner )
			continue;
		if( !friendly && IsDefined( owner ) && player == owner )
			continue;

		self ShowToPlayer( player );
	}

	for ( ;; )
	{
		level waittill ( "joined_team" );

		self hide();
		foreach ( player in level.players )
		{
			if( friendly && IsDefined( owner ) && player != owner )
				continue;
			if( !friendly && IsDefined( owner ) && player == owner )
				continue;

			self ShowToPlayer( player );
		}
	}	
}

crateUseTeamUpdater( team )
{
	self endon ( "death" );

	for ( ;; )
	{
		setUsableByTeam( team );

		level waittill ( "joined_team" );
		
	}
}

crateUseJuggernautUpdater()
{
	streakRef = getStreakForCrate( self.dropType, self.crateType );
	if ( !isSubStr( streakRef, "juggernaut" ) )
		return;
	
	self endon( "death" );
	level endon( "game_ended" );
	
	for ( ;; )
	{
		level waittill ( "juggernaut_equipped", player );
		
		self disablePlayerUse( player );
		self thread crateUsePostJuggernautUpdater( player );
	}	
}

crateUsePostJuggernautUpdater( player )
{
	self endon( "death" );
	level endon( "game_ended" );
	player endon( "disconnect" );
	
	player waittill( "death" );
	self enablePlayerUse( player );
}

/*
=============
///ScriptDocBegin
"Name: createAirDropCrate( <owner> , <dropType> , <crateType> , <startPos> , <startAngles> )"
"Summary: "
"Module: Entity"
"CallOn: An entity"
"MandatoryArg: <owner>: owner of the crate"
"MandatoryArg: <dropType>: assault, trap, etc"
"MandatoryArg: <crateType>: uav, counter-uav, predator, etc"
"MandatoryArg: <startPos>: vector origin"
"OptionalArg: <startAngles>: vector angles"
"OptionalArg: <alreadyTrapped>: start as a trap to enemies even though it is normal to allies"
"OptionalArg: <setupCollision>: setup collision immediately, default to true"
"Example: dropCrate = createAirDropCrate( self.owner, dropType, crateType, startPos );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
createAirDropCrate( owner, dropType, crateType, startPos, startAngles, alreadyTrapped, setupCollision )
{
	if ( !IsDefined( startAngles ) )
		startAngles = ( 0, 0, 0 );
	
	if ( !IsDefined( alreadyTrapped ) )
		alreadyTrapped = false;
	
	if ( !IsDefined( setupCollision ) )
		setupCollision = true;
	
	dropCrate = spawn( "script_model", startPos );
	dropCrate.angles = startAngles;		//TODO: applying angles after the spawn() function call isn't fast enough. Need code help.
	
	dropCrate.curProgress = 0;
	dropCrate.useTime = 0;
	dropCrate.useRate = 0;
	dropCrate.team = self.team;
	
	if ( isDefined( owner ) )
		dropCrate.owner = owner;
	else
		dropCrate.owner = undefined;
	
	dropCrate.crateType = crateType;
	dropCrate.dropType = dropType;
	dropCrate.targetname = "care_package";
	dropCrate.isTrap = alreadyTrapped;
	
	if ( dropCrate.team == "any" )
	{
		dropCrate setModel("orbital_carepackage_pod_01_ai");
		//Need a friendly model and it need to not use cloned brush collision to work with check for trigger_hurt
		dropCrate.friendlyModel = spawn( "script_model", dropCrate.origin );
		dropCrate.friendlyModel SetModel("tag_origin");
		dropCrate.friendlyModel thread deleteOnOwnerDeath( dropCrate );
	}
	else
	{
		dropCrate setModel( maps\mp\gametypes\_teams::getTeamCrateModel( dropCrate.team ) );	
		dropCrate thread crateTeamModelUpdater();
	
		friendlyModelName = "orbital_carepackage_pod_01_ai";
		enemyModelName = "orbital_carepackage_pod_01_clr_01_ai";
		if ( crateType == "booby_trap" )
		{
			friendlyModelName = "orbital_carepackage_pod_01_logo_trap_ai";
			dropCrate thread trap_createBombSquadModel();
		}
		else if ( alreadyTrapped )
		{
			dropCrate thread trap_createBombSquadModel();
		}
		
		dropCrate.friendlyModel = spawn( "script_model", startPos );
		dropCrate.friendlyModel setModel( friendlyModelName );
		dropCrate.friendlyModel.parentCrate = dropCrate;
		dropCrate.friendlyModel NotSolid();
		dropCrate.enemyModel = spawn( "script_model", startPos );
		dropCrate.enemyModel setModel( enemyModelName );
		dropCrate.enemyModel.parentCrate = dropCrate;
		dropCrate.enemyModel NotSolid();
	
		dropCrate.friendlyModel thread deleteOnOwnerDeath( dropCrate );
		if( level.teambased )
			dropCrate.friendlyModel thread crateModelTeamUpdater( dropCrate.team, true );
		else
			dropCrate.friendlyModel thread crateModelPlayerUpdater( owner, true );
	
		dropCrate.enemyModel thread deleteOnOwnerDeath( dropCrate );
		
		if( level.teambased )
			dropCrate.enemyModel thread crateModelTeamUpdater( level.otherTeam[dropCrate.team], false );
		else
			dropCrate.enemyModel thread crateModelPlayerUpdater( owner, false );
	}
	
	dropCrate.inUse = false;
	
	if ( setupCollision )
		dropCrate CloneBrushmodelToScriptmodel( level.airDropCrateCollision );
	
	dropCrate.killCamEnt = Spawn( "script_model", dropCrate.origin + CRATE_KILLCAM_OFFSET );
	dropCrate.killCamEnt SetScriptMoverKillCam( "explosive" );
	dropCrate.killCamEnt SetContents(0);
	dropCrate.killCamEnt LinkTo( dropCrate );

	level.numDropCrates++;

	return dropCrate;
}

trap_createBombSquadModel() // self == crate
{
	bombSquadModel = spawn( "script_model", self.origin );
	bombSquadModel.angles = self.angles;
	bombSquadModel hide();

	bombSquadModel thread maps\mp\gametypes\_weapons::bombSquadVisibilityUpdater( self.owner );
	bombSquadModel setModel( "orbital_carepackage_pod_01_ai_bombsquad" );
	bombSquadModel linkTo( self );
	bombSquadModel SetContents( 0 );

	self waittill ( "death" );

	bombSquadModel delete();
}

crateSetupHintStrings( hintString, secondaryHintString )
{
	if ( IsDefined( secondaryHintString ) && IsDefined( self.owner ) )
	{
		self.ownerStringEnt = Spawn( "script_model", self.origin + ( 0, 0, 60 ) );
		self.ownerStringEnt setCursorHint( "HINT_NOICON" );
		self.ownerStringEnt setHintString( hintString );
		self.ownerStringEnt SetSecondaryHintString( secondaryHintString );
		
		self.otherStringEnt = Spawn( "script_model", self.origin + ( 0, 0, 60 ) );
		self.otherStringEnt setCursorHint( "HINT_NOICON" );
		self.otherStringEnt setHintString( hintString );
	}
	else
	{
		self setCursorHint( "HINT_NOICON" );
		self setHintString( hintString );
	}
}

onPlayerConnectHintString( otherStringEnt, ownerStringEnt )
{
	otherStringEnt endon( "death" );
	ownerStringEnt endon( "death" );
	
	while ( true )
	{
		level waittill( "connected", player );
		
		player thread onPlayerSpawnedHintString( otherStringEnt, ownerStringEnt );
	}
}

onPlayerSpawnedHintString( otherStringEnt, ownerStringEnt )
{
	otherStringEnt endon( "death" );
	ownerStringEnt endon( "death" );

	self waittill( "spawned" );
	
	otherStringEnt EnablePlayerUse( self );
	ownerStringEnt DisablePlayerUse( self );
}

// Setup of drop pod when it reaches the ground
crateSetupForUse( mode, icons )
{
	if( IsDefined ( level.isHorde ) && level.isHorde )
	{
		self waittill ( "drop_pod_cleared" );
	}
	
	if ( IsDefined( self.ownerStringEnt ) )
	{
		self.ownerStringEnt MakeUsable();
		self.otherStringEnt MakeUsable();
		
		foreach ( player in level.players )
		{
			if ( player != self.owner )
			{
				self.ownerStringEnt DisablePlayerUse( player );
				self.otherStringEnt EnablePlayerUse( player );
			}
			else
			{
				self.otherStringEnt DisablePlayerUse( player );
				self.ownerStringEnt EnablePlayerUse( player );
			}
		}
		
		thread onPlayerConnectHintString( self.otherStringEnt, self.ownerStringEnt );
	}
	else
	{
		self MakeUsable();
	}
	
	self.mode = mode;

	if ( self.team == "any" )
	{
		curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
		objective_add( curObjID, "invisible", (0,0,0) );
		objective_position( curObjID, self.origin );
		objective_state( curObjID, "active" );
		
		shaderName = "compass_objpoint_ammo_friendly";
		objective_icon( curObjID, shaderName );
		
		Objective_Team( curObjID, "none");
		
		self.objIdFriendly = curObjID;
	}
	else
	{
		//setup owner team
		if ( level.teamBased || IsDefined( self.owner ) )
		{
			curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
			objective_add( curObjID, "invisible", (0,0,0) );
			objective_position( curObjID, self.origin );
			objective_state( curObjID, "active" );
			
			shaderName = "compass_objpoint_ammo_friendly";
			if( mode == "trap" )
				shaderName = "compass_objpoint_trap_friendly";
			objective_icon( curObjID, shaderName );
			
			if ( !level.teamBased && IsDefined( self.owner ) )
				Objective_PlayerTeam( curObjId, self.owner GetEntityNumber() );
			else
				Objective_Team( curObjID, self.team );
			
			self.objIdFriendly = curObjID;
		}
	
		if( ! ( IsDefined ( level.isHorde ) && level.isHorde ) )
		{
			if ( !IsDefined( self.owner ) || !( IsDefined( self.moduleHide ) && self.moduleHide ) )
			{
				curObjID = maps\mp\gametypes\_gameobjects::getNextObjID();
				objective_add( curObjID, "invisible", (0,0,0) );
				objective_position( curObjID, self.origin );
				objective_state( curObjID, "active" );
				objective_icon( curObjID, "compass_objpoint_ammo_enemy" );
			
				if ( !level.teamBased && IsDefined( self.owner ) )
					Objective_PlayerEnemyTeam( curObjId, self.owner GetEntityNumber() );
				else
					Objective_Team( curObjID, level.otherTeam[self.team] );
			
				self.objIdEnemy = curObjID;
			}
		}
	}

	if ( self.team == "any" )
	{
		foreach(team in level.teamNameList)
		{
			if( isDefined( icons ) && isArray( icons ) )
			{
				self setHeadIcon_multiple( team, icons );
			}
			else
				self maps\mp\_entityheadIcons::setHeadIcon( team, icons, (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
		}
	}
	else if ( mode == "trap" )
	{	
		self thread crateUseTeamUpdater( getOtherTeam( self.team ) );
	}
	else
	{
		self thread crateUseTeamUpdater();
		
		streakRef = getStreakForCrate( self.dropType, self.crateType );
		if ( isSubStr( streakRef, "juggernaut" ) )
		{
			foreach ( player in level.players )
				if ( player isJuggernaut() )
					self thread crateUsePostJuggernautUpdater( player );
		}		

		if ( level.teamBased )
		{
			if( isDefined( icons ) && isArray( icons ) )
			{
				self setHeadIcon_multiple( self.team, icons );
			}
			else
				self maps\mp\_entityheadIcons::setHeadIcon( self.team, icons, (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
		}
		else if ( IsDefined( self.owner ) )
		{
			if( isDefined( icons ) && isArray( icons ) )
			{
				self setHeadIcon_multiple( self.owner, icons );
			}
			else
				self maps\mp\_entityheadIcons::setHeadIcon( self.owner, icons, (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
		}
	}
	
	self thread crateUseJuggernautUpdater();
}

setHeadIcon_multiple( showTo, icons )
{
	offset = 10;
	i = 0;
	self.iconEnts = [];
	foreach( icon in icons )
	{
		self.iconEnts[ icon ] = self spawn_tag_origin();
		self.iconEnts[ icon ] maps\mp\_entityheadIcons::setHeadIcon( showTo, icon, (0,0,(CRATE_ICON_Z_OFFSET - 5 ) + (i*offset)), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
		i++;
	}
}

setUsableByTeam( team )
{
	streakRef = getStreakForCrate( self.dropType, self.crateType );

	foreach ( player in level.players )
	{
		if ( isSubStr( streakRef, "juggernaut" ) && player isJuggernaut() )
		{
			self DisablePlayerUse( player );
		}
		else if ( !level.teamBased && self.mode == "trap" )
		{
			if ( IsDefined( self.owner ) && player == self.owner )
				self DisablePlayerUse( player );
			else
				self EnablePlayerUse( player );
		}
		else if ( !IsDefined( team ) || team == player.team )
			self EnablePlayerUse( player );
		else
			self DisablePlayerUse( player );
	}	
}

physicsWaiter( dropType, crateType )
{
	self waittill( "physics_finished" );

	self.droppingToGround = false;
	self thread [[ level.crateTypes[ dropType ][ crateType ].func ]]( dropType );
	level thread dropTimeOut( self, self.owner, crateType );
		
	killTriggers = getEntArray( "trigger_hurt", "classname" );
	foreach ( trigger in killTriggers )
	{
		if ( self.friendlyModel isTouching( trigger ) )
		{
			self deleteCrate();
			return;
		}
	}
		
	if( IsDefined(self.owner) && ( abs(self.origin[2] - self.owner.origin[2]) > 4000 ) )
	{
		self deleteCrate();
		return;		
	}
	
	if ( IsDefined(level.isZombieGame) && level.isZombieGame )
		self DisconnectPaths();
	
	// Moving platforms. 
	data = SpawnStruct();
	data.deathOverrideCallback = ::movingPlatformDeathFunc;
	data.touchingPlatformValid = ::movingPlatformTouchValid;
	self thread maps\mp\_movers::handle_moving_platforms( data );
}

movingPlatformDeathFunc( data )
{
	self deleteCrate( true, true );
}

movingPlatformTouchValid( platform )
{
	return ( carepackageAndCarepackageValid( platform ) && carepackageAndGoliathValid( platform ) && carepackageAndPlatformValid( platform ) );
}

carepackageAndGoliathValid( platform )
{
	return ( !IsDefined( self.targetname )|| !IsDefined( platform.crateType ) || self.targetname != "care_package" || platform.crateType != "juggernaut" );
}

carepackageAndCarepackageValid( platform )
{
	return ( !IsDefined( self.targetname )|| !IsDefined( platform.targetname ) || self.targetname != "care_package" || platform.targetname != "care_package" );
}

carepackageAndPlatformValid( platform )
{
	return ( !IsDefined( self.targetname ) || !IsDefined( platform.carepackageTouchValid ) || self.targetname != "care_package" || !platform.carepackageTouchValid );
}


//deletes if crate wasnt used after 90 seconds
dropTimeOut( dropCrate, owner, crateType )
{
	if( IsDefined(level.noCrateTimeOut) && (level.noCrateTimeOut) )
		return;
	
	level endon ( "game_ended" );
	dropCrate endon( "death" );
	
	if ( dropCrate.dropType == "nuke_drop" )
		return;	
	
	timeOut = 90.0;
	if ( crateType == "supply" )
		timeOut = 20.0;
	
	maps\mp\gametypes\_hostmigration::waitLongDurationWithHostMigrationPause( timeOut );
	
	while ( dropCrate.curProgress != 0 )
		wait 1;
	
	dropCrate deleteCrate( true, true );
}


/**********************************************************
*		 crate trigger functions
***********************************************************/

crateOtherCaptureThink( useText )
{
	self endon( "captured" );
	
	crate = self;
	if ( IsDefined( self.otherStringEnt ) )
		crate = self.otherStringEnt;
	
	while ( isDefined( self ) )
	{
		crate waittill( "trigger", player );
		
		if ( isDefined( self.owner ) && player == self.owner )
			continue;
		
		if ( player IsJumping() || ( IsDefined( player.exo_hover_on ) && player.exo_hover_on ) )
			continue;
			
		if ( !player IsOnGround() && !waitPlayerStuckOnCarepackageReturn( player ) )
			continue;

		if ( !self validateOpenConditions( player ) )
			continue;			

		player.isCapturingCrate = true;
		useEnt = self createUseEnt();
		result = false;
		if ( self.crateType == "booby_trap" )
		{
			result = useEnt useHoldThink( player, 500, useText );
		}
		else
		{
			result = useEnt useHoldThink( player, undefined, useText );
		}
		
		if ( isDefined( useEnt ) )
			useEnt delete();
		
		if ( !result )
		{
			if ( IsDefined(player) )
				player.isCapturingCrate = false;
			continue;
		}
			
		player.isCapturingCrate = false;
		
		//If the player is entering the juggernaut, ensure they successfully do so without falling into last stand
		if( IsDefined( level.isHorde ) && level.isHorde )
		{			
			if( self.crateType == "juggernaut" && !( IsDefined(player.lastStand) && player.lastStand ) )
				player SetDemiGod( true );
			
			//If the player has fallen into last stand, leave the crate there
			if( IsDefined(player.lastStand) && player.lastStand )
				continue;
		}
		
		self notify ( "captured", player );
	}
}

crateOwnerCaptureThink( useText )
{
	self endon( "captured" );
	
	if ( !IsDefined( self.owner ) )
		return;
	
	self.owner endon( "disconnect" );
	
	crate = self;
	if ( IsDefined( self.ownerStringEnt ) )
		crate = self.ownerStringEnt;
	
	useHoldTime = 500;
	if ( IsDefined( self.modulePickup ) && self.modulePickup )
		useHoldTime = 100;
	
	while ( isDefined( self ) )
	{
		crate waittill( "trigger", player );
		
		if ( isDefined( self.owner ) && player != self.owner )
			continue;
		
		if ( player IsJumping() || ( IsDefined( player.exo_hover_on ) && player.exo_hover_on ) )
			continue;
		
		if ( !player IsOnGround() && !waitPlayerStuckOnCarepackageReturn( player ) )
			continue;
		
		if ( !self validateOpenConditions( player ) )
			continue;
		
		player.isCapturingCrate = true;
		if ( !useHoldThink( player, useHoldTime, useText ) )
		{
			player.isCapturingCrate = false;
			continue;
		}
		
		player.isCapturingCrate = false;
		
		//If the player is entering the juggernaut, ensure they successfully do so without falling into last stand
		if( IsDefined( level.isHorde ) && level.isHorde )
		{			
			if( self.crateType == "juggernaut" && !( IsDefined(player.lastStand) && player.lastStand ) )
				player SetDemiGod( true );
			
			//If the player has fallen into last stand, leave the crate there
			if( IsDefined(player.lastStand) && player.lastStand )
				continue;
		}
		
		self notify ( "captured", player );
	}
}


waitPlayerStuckOnCarepackageReturn( player )
{
	if ( player IsOnGround() )
		return false;
	
	TIME_UNTIL_STUCK = 200;
	
	startOrg = player.origin;
	stuckTimeStart = GetTime();
	
	while ( IsDefined( player ) && isReallyAlive( player ) && !player IsOnGround() && startOrg == player.origin && player UseButtonPressed() )
	{
		curTimeButtonHeld = GetTime() - stuckTimeStart;
		if ( curTimeButtonHeld >= TIME_UNTIL_STUCK )
			return true;
		
		waitframe();
	}
	
	return false;
}


validateOpenConditions( opener )
{
	currentWeapon = opener GetCurrentPrimaryWeapon();
	
	if ( IsSubStr( currentWeapon, "turrethead" ) )
		return true;
	
	if( !(opener maps\mp\killstreaks\_killstreaks::canShuffleWithKillstreakWeapon()) )
		return false;
	
	if( IsDefined(opener.changingWeapon) && !(opener maps\mp\killstreaks\_killstreaks::canShuffleWithKillstreakWeapon()) )
		return false;

	return true;
}


killstreakCrateThink( dropType )
{
	self endon ( "death" );
	
	streakRef 	= getStreakForCrate( dropType, self.crateType );
	modules 	= getModulesForCrate( dropType, self.crateType );
	
	canReroll = isDefined(self.owner) && ( self.owner _hasPerk( "specialty_highroller" ) || ( IsDefined( self.moduleRoll ) && self.moduleRoll ) );
	
	secondaryHintString = undefined;
	if ( canReroll )
		secondaryHintString = &"MP_PACKAGE_REROLL";
	
	crateHint = undefined;
	if ( isDefined( game["strings"][dropType + self.crateType + "_hint"] ) )
		crateHint = game["strings"][dropType + self.crateType + "_hint"];
	else
		crateHint = &"PLATFORM_GET_KILLSTREAK";
	
	crateSetupHintStrings( crateHint, secondaryHintString );
	crateSetupForUse( "all", maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( streakRef, modules ) );
	
	if ( IsDefined(self.owner) )
		self thread crateOtherCaptureThink();
	self thread crateOwnerCaptureThink();
	if ( canReroll )
		self thread crateOwnerDoubleTapThink();
	
	if ( self.isTrap )
		self crateTrapSetupKillcam();
	
	for ( ;; )
	{
		self waittill ( "captured", player );
		
		streakRef 	= getStreakForCrate( dropType, self.crateType );
		modules 	= getModulesForCrate( dropType, self.crateType );

		if ( isDefined( self.owner ) && player != self.owner )
		{
			if ( !level.teamBased || player.team != self.team )
			{
				if ( self.isTrap )
				{
					player thread detonateTrap( self, self.owner );
					return;
				}
				else
				{
					player thread maps\mp\_events::hijackerEvent( self.owner );
				}
			}
			else
			{
				self.owner thread maps\mp\_events::sharedEvent();
			}
		}
		
		player playLocalSound( "orbital_pkg_use" );
		
		slotIndex = player maps\mp\killstreaks\_killstreaks::getNextKillstreakSlotIndex( streakRef, false );
		player thread maps\mp\gametypes\_hud_message::killstreakSplashNotify( streakRef, undefined, undefined, modules, slotIndex );
		player thread maps\mp\killstreaks\_killstreaks::giveKillstreak( streakRef, false, false, self.owner, modules );
		
		if( IsDefined(level.mapKillStreak) && (level.mapKillStreak == self.crateType) )
			player thread maps\mp\_events::mapKillStreakEvent();
		
		//If the player has stolen the care package and the player has the specialty_highroller Perk, spawn a trap crate replacement.
		shouldDestroyPlayVFX = true;
		shouldTrapPerk = ( player _hasPerk( "specialty_highroller" ) && ( !level.teamBased || player.team != self.team ) );
		packageHasModuleTrap = IsDefined( self.moduleTrap ) && self.moduleTrap;
		shouldTrapModule = ( packageHasModuleTrap && ( self.owner == player || ( level.teamBased && player.team == self.team ) ) );
		if ( shouldTrapPerk || shouldTrapModule )
		{
			newTrapCrate = player createAirDropCrate( player, "booby_trap", "booby_trap", self.origin, self.angles );
			if ( IsDefined( newTrapCrate.enemymodel ) )
				newTrapCrate.enemymodel thread maps\mp\killstreaks\_orbital_carepackage::orbitalAnimate( true );
			newTrapCrate thread boobyTrapCrateThink( self );
			level thread dropTimeOut( newTrapCrate, newTrapCrate.owner, newTrapCrate.crateType );
			shouldDestroyPlayVFX = false;

			// Make sure models are made solid again so that missiles are blocked by them
			if ( IsDefined( newTrapCrate.friendlyModel  ) )
				newTrapCrate.friendlyModel Solid();
			if ( IsDefined( newTrapCrate.enemyModel  ) )
				newTrapCrate.enemyModel Solid();	
		}
		
		self deleteCrate( shouldDestroyPlayVFX );
	}
}

detonateTrap( crate, owner )
{
	crate endon ( "death" );
	
	location = crate.origin + ( 0, 0, 50 );
	
	if ( level.teamBased )
		crate maps\mp\_entityheadIcons::setHeadIcon( self.team, "specialty_trap_crate", (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
	else
		crate maps\mp\_entityheadIcons::setHeadIcon( self, "specialty_trap_crate", (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
	
	thread play_sound_in_space( "orbital_pkg_trap_armed", location );
	wait (1.0);
	
	forward = ( location + ( 0, 0, 1 ) ) - location;
	playfx ( getfx( "airdrop_crate_trap_explode" ), location, forward );
	thread play_sound_in_space( "orbital_pkg_trap_detonate", location );
	
	// remove collision before blowing up so it doesn't get in the way of RadiusDamage traces
	if ( IsDefined( crate.friendlyModel ) )
		crate.friendlyModel NotSolid();
	if ( IsDefined( crate.enemyModel ) )
		crate.enemyModel NotSolid();
	
	if ( isDefined( owner ) )
		crate.trapKillCamEnt RadiusDamage( location, 400, 300, 50, owner, "MOD_EXPLOSIVE", "airdrop_trap_explosive_mp" ); 
	else
		crate.trapKillCamEnt RadiusDamage( location, 400, 300, 50, undefined, "MOD_EXPLOSIVE", "airdrop_trap_explosive_mp" );
	
	crate deleteCrate();
}

/*
=============
///ScriptDocBegin
"Name: deleteCrate( <playDestroyVFX> )"
"Summary: deletes a crate"
"Module: Entity"
"CallOn: a crate"
"OptionalArg: <playDestroyVFX>: bool - set to false if this crate is replaced by a booby trap. This will make the trap more sneaky."
"Example: crate deleteCrate();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
deleteCrate( playDestroyVFX, playDeathSound )
{
	if ( !IsDefined( playDestroyVFX ) )
	{
		playDestroyVFX = true;
	}
	
	if ( !IsDefined( playDeathSound ) )
		playDeathSound = false;
	
	if ( isDefined( self.objIdFriendly ) )
		_objective_delete( self.objIdFriendly );

	if ( isDefined( self.objIdEnemy ) )
	{
		_objective_delete( self.objIdEnemy );
	}

	if ( isDefined( self.trapKillcamEnt ) )
		self.trapKillcamEnt delete();

	if ( IsDefined( self.killCamEnt ) )
		self.killCamEnt delete();
	
	if ( IsDefined( self.ownerStringEnt ) )
		self.ownerStringEnt Delete();
	
	if ( IsDefined( self.otherStringEnt ) )
		self.otherStringEnt Delete();
	
	if ( isDefined( self.dropType ) )
	{
		if ( playDestroyVFX )
		{
			PlayFX( getfx( "ocp_death" ), self.origin );
		}
		
		if ( playDeathSound )
		{
			playSoundAtPos( self.origin, "orbital_pkg_self_destruct" );
		}
	}
	
	if( isDefined( self.iconEnts ) )
	{
		foreach( icon_ent in self.iconEnts )
		{
			icon_ent delete();
		}
	}		

	self delete();
}

/**********************************************************
*		 Capture crate functions
***********************************************************/
useHoldThink( player, useTime, useText ) 
{
	if ( IsPlayer( player ) )
  	  player playerLinkTo( self );
	else
		player LinkTo( self );
    player playerLinkedOffsetEnable();
    
    if ( !player isJuggernaut() )
    	player _disableWeapon();
    
    self thread useHoldThinkPlayerReset( player );
    
    self.curProgress = 0;
    self.inUse = true;
    self.useRate = 0;
	
	if ( isDefined( useTime ) )
	{
		if ( player _hasPerk( "specialty_unwrapper" ) && IsDefined( player.specialty_unwrapper_care_bonus ) )
		{
			useTime *= player.specialty_unwrapper_care_bonus;
		}
		self.useTime = useTime;
	}
	else
	{
		if ( player _hasPerk( "specialty_unwrapper" ) && IsDefined( player.specialty_unwrapper_care_bonus ) )
		{
			self.useTime = 3000 * player.specialty_unwrapper_care_bonus;
		}
		else
		{
			self.useTime = 3000;
		}
	}
    
	if ( IsPlayer( player ) )
		player thread personalUseBar( self, useText );
   
    result = useHoldThinkLoop( player );
	assert ( isDefined( result ) );
	    
    if ( !isDefined( self ) )
    	return false;
    
   	self notify( "useHoldThinkLoopDone" );    

    self.inUse = false;
	self.curProgress = 0;

	return ( result );
}

useHoldThinkPlayerReset( player )
{
	player endon( "death" );
	
	self waittill_any( "death", "captured", "useHoldThinkLoopDone" );
	
    if ( isAlive( player ) )
    {
    	if ( !player isJuggernaut() )
        	player _enableWeapon();
    	if ( player isLinked() )
        	player unlink();
    }
}

personalUseBar( object, useText )
{
    self endon( "disconnect" );
    
    if ( isDefined( useText ) )
    	iprintlnbold( "Fixme @agersant " + useText );
	self SetClientOmnvar( "ui_use_bar_text", 1 );
	self SetClientOmnvar( "ui_use_bar_start_time", int( GetTime() ) );

    lastRate = -1;
    while ( isReallyAlive( self ) && isDefined( object ) && object.inUse && !level.gameEnded )
    {
        if ( lastRate != object.useRate )
        {
            if( object.curProgress > object.useTime)
                object.curProgress = object.useTime;
			if ( object.useRate > 0  )
			{
				now = GetTime();
				current = object.curProgress / object.useTime;
				endTime = now + ( 1 - current ) * ( object.useTime / object.useRate );
				self SetClientOmnvar( "ui_use_bar_end_time", int( endTime ) );
			}
			lastRate = object.useRate;
        }
		wait ( 0.05 );
    }

	self SetClientOmnvar( "ui_use_bar_end_time", 0 );
    
}

isHordeLastStand( player )
{
	return IsDefined( level.isHorde ) && level.isHorde && IsDefined( player.lastStand ) && player.lastStand;
}

useHoldThinkLoop( player )
{
	while( !level.gameEnded && isDefined( self ) && isReallyAlive( player ) && !isHordeLastStand( player ) && player useButtonPressed() && self.curProgress < self.useTime )
	{
	        self.curProgress += (50 * self.useRate);
	        
	        if ( isDefined(self.objectiveScaler) )
	        	self.useRate = 1 * self.objectiveScaler;
	        else
	        	self.useRate = 1;
	        
	        if ( self.curProgress >= self.useTime )
	        	return ( isReallyAlive( player ) );
        
        wait 0.05;
	}
	
	return false;
}

isAirdropMarker( weaponName )
{
	switch ( weaponName )
	{
		case "airdrop_marker_mp":
		case "airdrop_mp":
			return true;
		default:
			return false;
	}
}


createUseEnt()
{
	useEnt = spawn( "script_origin", self.origin );
	useEnt.curProgress = 0;
	useEnt.useTime = 0;
	useEnt.useRate = 3000;
	useEnt.inUse = false;

	useEnt thread deleteUseEnt( self );

	return ( useEnt );
}


deleteUseEnt( owner )
{
	self endon ( "death" );
	
	owner waittill ( "death" );
	
	self delete();
}

/*
=============
///ScriptDocBegin
"Name: crateOwnerDoubleTapThink()"
"Summary: polls for a care package owner's double tap"
"Module: Entity"
"CallOn: a care package crate"
"Example: self thread crateOwnerDoubleTapThink();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
crateOwnerDoubleTapThink()
{
	self.packageHoldTimer = 0;
	self.packageSingleTapped = false;
	
	while( !level.gameEnded && isDefined( self ) )
	{
		if ( isReallyAlive( self.owner ) && DistanceSquared( self.origin, self.owner.origin ) < 10000 )
		{
			if ( self.owner useButtonPressed() )
			{
				self.packageHoldTimer++;
			}
			else
			{
				if ( self.packageHoldTimer > 0 )
				{
					if ( self.packageHoldTimer < 5 )
					{
						if ( self.packageSingleTapped == true )
						{
							//The owner has just double tapped the package.
							self notify( "package_double_tap" );
							previous_crateType = self.crateType;
							reroll_attempts = 0;
							while ( self.crateType == previous_crateType && reroll_attempts < 100 )
							{
								reroll_attempts++;		//Try up to 100 rerolls because it wouldn't be very fun if you rerolled the exact same scorestreak.
								self.crateType = getRandomCrateType( self.dropType );
							}
							
							streakRef = getStreakForCrate( self.dropType, self.crateType );
							modules = getModulesForCrate( self.dropType, self.crateType );
							
							hintString = game["strings"][self.dropType + self.crateType + "_hint"];
							
							if( !IsDefined( hintString) )
								hintString = &"PLATFORM_GET_KILLSTREAK";
		
							if ( IsDefined( self.ownerStringEnt ) )
							{
								self.ownerStringEnt setHintString( hintString );
								self.otherStringEnt setHintString( hintString );
								self.ownerStringEnt SetSecondaryHintString( "" );
							}
							else
							{
								self setHintString( hintString );
								self SetSecondaryHintString( "" );
							}
							if ( level.teamBased )
								self maps\mp\_entityheadIcons::setHeadIcon( self.team, maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( streakRef, modules ), (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
							else if ( IsDefined( self.owner ) )
								self maps\mp\_entityheadIcons::setHeadIcon( self.owner, maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( streakRef, modules ), (0,0,CRATE_ICON_Z_OFFSET), 14, 14, undefined, undefined, undefined, undefined, undefined, false );
							
							self.owner PlayLocalSound( "orbital_pkg_reroll" );
							
							return true;
						}
						else
						{
							self.packageSingleTapped = true;
							self thread tapPackageThink();
						}
					}
					self.packageHoldTimer = 0;
				}
			}
		}
        wait 0.05;
	}
}


/*
=============
///ScriptDocBegin
"Name: tapPackageThink()"
"Summary: polls for a double tap of the use button"
"Module: Entity"
"CallOn: a player"
"Example: player thread tapPackageThink();"
"SPMP: MP"
///ScriptDocEnd
=============
*/
tapPackageThink()
{
	level endon( "game_ended" );
	self endon( "death" );
	self endon( "package_double_tap" );
	
	wait( 0.2 );
	
	self.packageSingleTapped = false;
}


/*
=============
///ScriptDocBegin
"Name: boobyTrapCrateThink( <originalCrate> )"
"Summary: handles a newly created booby trapped crate."
"Module: Entity"
"CallOn: a crate"
"MandatoryArg: <originalCrate>: the crate that this trap is replacing"
"Example: crate thread boobyTrapCrateThink( originalCrate );"
"SPMP: MP"
///ScriptDocEnd
=============
*/
boobyTrapCrateThink( originalCrate )
{
	self endon ( "death" );
	
	streakRef = getStreakForCrate( originalCrate.dropType, originalCrate.crateType );
	modules = getModulesForCrate( originalCrate.dropType, originalCrate.crateType );
	
	crateIcon = maps\mp\killstreaks\_killstreaks::getKillstreakCrateIcon( streakRef, modules );
	
	crateHint = undefined;
	if ( isDefined( game["strings"][originalCrate.dropType + originalCrate.crateType + "_hint"] ) )
		crateHint = game["strings"][originalCrate.dropType + originalCrate.crateType + "_hint"];
	else
		crateHint = &"PLATFORM_GET_KILLSTREAK";
	
	crateSetupHintStrings( crateHint );
	crateSetupForUse( "trap", crateIcon );		//TODO: need to use a different hint string depending on possession of specialty_highroller Perk.
	
	self crateTrapSetupKillcam();
		
	self thread crateOtherCaptureThink( originalCrate );
	
	for ( ;; )
	{
		self waittill ( "captured", player );
		
		player thread detonateTrap( self, self.owner );
	}
}

crateTrapSetupKillcam( originalCrate )
{
	result = BulletTrace( self.origin, self.origin + ( 0, 0, 90 ), false, self );
	
	self.trapKillCamEnt = Spawn( "script_model", result[ "position" ] );
	self.trapKillCamEnt SetContents(0);
	self.trapKillCamEnt SetScriptMoverKillCam( "large explosive" );
}

//---REINFORCEMENTS---//

tryUseReinforcementCommon( lifeId, kID, modules )
{
	return maps\mp\killstreaks\_orbital_carepackage::tryUseOrbitalCarePackage( lifeId, "airdrop_reinforcement_common", modules );
}

tryUseReinforcementUncommon( lifeId, kID, modules )
{
	return maps\mp\killstreaks\_orbital_carepackage::tryUseOrbitalCarePackage( lifeId, "airdrop_reinforcement_uncommon", modules );
}

tryUseReinforcementRare( lifeId, kID, modules )
{
	return maps\mp\killstreaks\_orbital_carepackage::tryUseOrbitalCarePackage( lifeId, "airdrop_reinforcement_rare", modules );
}

tryUseReinforcementPractice( lifeId, kID, modules )
{
	return maps\mp\killstreaks\_orbital_carepackage::tryUseOrbitalCarePackage( lifeId, "airdrop_reinforcement_practice", modules );
}

reinforcementCrateKillstreakThink( dropType )
{
	self killstreakCrateThink( dropType );
}

reinforcementCrateSpecialtyThink( dropType )
{
	self endon ( "death" );

	perkRef = getPerkForCrate( dropType, self.crateType );
	secondaryPerkRef = undefined;
	
	if( dropType == "airdrop_reinforcement_rare" )
	{
		secondaryPerkRef = getSecondaryPerkForCrate( dropType );
	}
	
	icon = undefined;
	
	if( isDefined( secondaryPerkRef ) )
	{
		primaryHint = game["strings"][dropType + self.crateType + "_hint"];
		secondaryHint = getSecondaryPerkHintFromPerkRef( secondaryPerkRef );
		
		if( isDefined( primaryHint ) && isDefined( secondaryHint ) )
			self SetReinforcementHintStrings( primaryHint, secondaryHint );
		else
			crateSetupHintStrings( &"MP_PERK_PICKUP_GENERIC_MULTIPLE" );
		
		icon = [];
		icon[0] = getPerkCrateIcon( perkRef );
		if( !isDefined( icon[0] ) )
			icon[0] = "";
		icon[1] = getPerkCrateIcon( secondaryPerkRef );
		if( !isDefined( icon[1] ) )
			icon[1] = "";
	}
	else
	{
		primaryHint = game["strings"][dropType + self.crateType + "_hint"];
		if( isDefined( primaryHint ) )
			self SetReinforcementHintStrings( primaryHint );
		else
			crateSetupHintStrings( &"MP_PERK_PICKUP_GENERIC" );

		icon = getPerkCrateIcon( perkRef );
		if( !isDefined( icon ) )
			icon = "";
	}
	
	//hints already set above
	crateSetupForUse( "all", icon );
	
	if ( IsDefined(self.owner) )
		self thread crateOtherCaptureThink();
	
	self thread crateOwnerCaptureThink();
	
	for ( ;; )
	{
		self waittill ( "captured", player );
		
		if ( isDefined( self.owner ) && player != self.owner )
		{
			if ( !level.teamBased || player.team != self.team )
			{
				player thread maps\mp\_events::hijackerEvent( self.owner );
				
			}
			else
			{
				self.owner thread maps\mp\_events::sharedEvent();
			}
		}
		
		player playLocalSound( "orbital_pkg_use" );
		
		player apply_reinforcement_perk( perkRef );

		perkIdx = int( TableLookupRowNum( "mp/perktable.csv", 1, perkRef ) );

		player SetClientOmnvar( "ui_reinforcement_active_perk_1", perkIdx );
		
		if( isDefined( secondaryPerkRef ) )
		{
			player apply_reinforcement_perk( secondaryPerkRef );

			perkIdx = int( TableLookupRowNum( "mp/perktable.csv", 1, secondaryPerkRef ) );

			player SetClientOmnvar( "ui_reinforcement_active_perk_2", perkIdx );
		}
		else
		{
			// clear second active perk
			player SetClientOmnvar( "ui_reinforcement_active_perk_2", -1 );
		}
		
		self deleteCrate( true );
	}
}

getPerkForCrate( dropType, crateType )
{	
	if ( IsDefined( level.crateTypes[ dropType ][ crateType ].streakRef ) )
		return level.crateTypes[ dropType ][ crateType ].streakRef;
	
	return crateType;
}

getSecondaryPerkForCrate( dropType )
{	
	if ( IsDefined( level.crateTypes[ dropType ] ) && IsDefined( level.crateTypes[ dropType ][ self.crateType ] ) )
	{
		excludeCrateTypes = [];
		foreach( crate in level.crateTypes[ dropType ] )
	    {
			if( !IsSubStr( crate.streakRef, "specialty_" ) )
				excludeCrateTypes[excludeCrateTypes.size] = crate.type;
	    }
		excludeCrateTypes[excludeCrateTypes.size] = self.crateType;
		secondaryCrateType = getRandomCrateType( dropType, excludeCrateTypes );
		if( isDefined( secondaryCrateType ) && isDefined( level.crateTypes[ dropType ][ secondaryCrateType ].streakRef ) )
		{
			return level.crateTypes[ dropType ][ secondaryCrateType ].streakRef;
		}
		
	}
	
	return undefined;
}

getSecondaryPerkHintFromPerkRef( perkName )
{
	crateHint = undefined;
	
	if( isDefined( level.secondaryReinforcementHintText[perkName] ) )
		crateHint = level.secondaryReinforcementHintText[perkName];
	
	return crateHint;
}

getPerkCrateIcon( perkName )
{
	perk_file = "mp/perktable.csv";
	perkName_column = 1;
	imageName_column = 11;
	
	return tableLookup(perk_file, perkName_column, perkName, imageName_column );
}

apply_reinforcement_perk( perkName )
{
	Assert( IsDefined( perkName ) );
	
	if( perkName == "specialty_extended_battery" )				// Grants extra battery energy (currently 50%).
	{
		self givePerk( "specialty_extended_battery", false );
		self givePerk( "specialty_exo_slamboots", false );		// Boost slam applies additional concussion effect.
		
		return;
	}
	
	//######## LOW PROFILE ########
	if( perkName == "specialty_class_lowprofile" )				// Grants immunity to track rounds.
	{
		self givePerk( "specialty_radarimmune", false );			// Immune to UAV while moving.
		self givePerk( "specialty_exoping_immune", false );			// Immune to Exo Ping.
		
		return;
	}
	
	//######## FLAK JACKET ########
	if( perkName == "specialty_class_flakjacket" )
	{
		self givePerk( "specialty_hard_shell", false );				// No slowdown after taking explosive damage.
		self givePerk( "specialty_throwback", false );				// Reset frag grenade fuse on throwback.
		self givePerk( "_specialty_blastshield", false );			// Take less explosive damage.
		
		self.specialty_blastshield_bonus = getIntProperty( "perk_blastShieldScale",	45 ) / 100;
		
		if( IsDefined( level.hardcoreMode ) && level.hardcoreMode )
			self.specialty_blastshield_bonus = getIntProperty( "perk_blastShieldScale_HC", 90 ) / 100;
		
		return;
	}
	
	//######## LIGHTWEIGHT ########
	if( perkName == "specialty_class_lightweight" )
	{
		self givePerk( "specialty_lightweight", false );			// Increases movement speed.
		
		return;
	}
	
	//######## DANGER CLOSE ########
	if( perkName == "specialty_class_dangerclose" )
	{
		self givePerk( "specialty_explosivedamage", false );		// Do extra explosive damage. Canceled by _specialty_blastshield.
		
		return;
	}
	
	//============================================
	// 		 	Tier 2 Perks
	//============================================
	
	//######## BLIND EYE ########
	if( perkName == "specialty_class_blindeye" )
	{
		self givePerk( "specialty_blindeye", false );				// Untargetable to Tracking Drones, Explosive Drones, AI-controlled scorestreaks (e.g. warbird).
		self givePerk( "specialty_plainsight", false);				// Immune to FOF overlay of Player-controlled score streaks
		
		return;
	}
	
	//######## COLDBLOODED ########
	if( perkName == "specialty_class_coldblooded" )
	{
		self givePerk( "specialty_coldblooded", false );			// Immune to Thermal, target enhancer, paint grenade, battlechatter
		self givePerk( "specialty_spygame", false);					// Crosshair doesn't appear red to enemies (and your name doesn't draw overhead )
		self givePerk( "specialty_heartbreaker", false );			// Immune to hearbeat sensor. Name doesn't draw overhead (see autospot in code)?
		
		return;
	}
	
	//######## PERIPHERALS ########
	if( perkName == "specialty_class_peripherals" )
	{
		self givePerk( "specialty_moreminimap", false );			// Increases minimap coverage.
		self givePerk( "specialty_silentkill", false );				// Your kills do not show death icons to enemy players.
		
		return;
	}
	
	//######## FAST HANDS ########
	if( perkName == "specialty_class_fasthands" )
	{
		self givePerk( "specialty_quickswap", false );				// Swap weapons faster.
		self givePerk( "specialty_fastoffhand", false );			// Use equipment faster.
		self givePerk( "specialty_sprintreload", false );			// Enables reloading while sprinting.
		
		return;
	}
	
	//######## GUNG-HO ########
	if( perkName == "specialty_class_dexterity" )
	{
		//self givePerk( "specialty_fastsprintrecovery", false );	// Increases weapon readiness for hip-fire after sprinting. This doesn't help the player at all when paired with sprintfire.
		self givePerk( "specialty_sprintfire", false );				// Enables firing while sprinting and sliding.
		
		return;
	}
	
	
	//============================================
	// 		 	Tier 3 Perks
	//============================================

	//######## HARD WIRED ########
	if( perkName == "specialty_class_hardwired" )
	{
		self givePerk( "specialty_empimmune", false );				// Immune to EMP grenades and the EMP Scorestreak. Code: immune to portable radar and jammer.
		self givePerk( "specialty_stun_resistance", false );		// Immune to missile strike gas (nano swarm). Immune to stun grenade (see setStunResistance()).
		self.stunScaler = 0.1;
		
		return;
	}
	
	//######## TOUGHNESS ########
	if( perkName == "specialty_class_toughness" )
	{
		self setViewKickScale( 0.2 );								// Reducing view kick when shot.
		
		return;
	}
	
	//######## SCAVENGER ########
	if( perkName == "specialty_class_scavenger" )
	{
		self.ammopickup_scalar = 0.2;
		self givePerk( "specialty_scavenger", false );					// Enables scavenger pack drops in code. Must be given for any of the resupply perks to work.
		self givePerk( "specialty_bulletresupply", false );				// Resupplies bullet weapons.
		//self givePerk( "specialty_lethalresupply", false );			// Resupplies grenades (tactical and lethal grenades).
		//self givePerk( "specialty_tacticalresupply", false );			// Resupplies Exo Abilities.
		//self givePerk( "specialty_explosiveammoresupply", false );	// Resupplies rocket launchers.
		self givePerk( "specialty_extraammo", false );					// Spawn with extra ammo.
		
		return;
	}
	
	//######## HARD LINE ########
	if( perkName == "specialty_class_hardline" )
	{
		self givePerk( "specialty_hardline", false );					// Decreases Scorestreak cost by 100 points.
		self maps\mp\killstreaks\_killstreaks::updateStreakCount();
		
		return;
	}
	
	//######## BLAST SUPPRESSOR #########
	// Suppresses minimap ping when performing Exo movement mechanics.
}