#using scripts\codescripts\struct;

#using scripts\shared\callbacks_shared;
#using scripts\shared\challenges_shared;
#using scripts\shared\clientfield_shared;
#using scripts\shared\entityheadicons_shared;
#using scripts\shared\gameobjects_shared;
#using scripts\shared\hostmigration_shared;
#using scripts\shared\hud_util_shared;
#using scripts\shared\killstreaks_shared;
#using scripts\shared\popups_shared;
#using scripts\shared\scoreevents_shared;
#using scripts\shared\sound_shared;
#using scripts\shared\util_shared;
#using scripts\shared\weapons\_hacker_tool;
#using scripts\shared\weapons\_smokegrenade;
#using scripts\shared\weapons\_tacticalinsertion;
#using scripts\shared\weapons\_weapons;
#using scripts\shared\weapons\_heatseekingmissile;
#using scripts\shared\weapons\_weaponobjects;
#using scripts\shared\vehicleriders_shared;
#using scripts\shared\flag_shared;
#using scripts\shared\flagsys_shared;

#using scripts\mp\_challenges;
#using scripts\mp\_util;
#using scripts\mp\gametypes\_battlechatter;
#using scripts\mp\gametypes\_hostmigration;
#using scripts\mp\killstreaks\_ai_tank;
#using scripts\mp\killstreaks\_airsupport;
#using scripts\mp\killstreaks\_emp;
#using scripts\mp\killstreaks\_helicopter;
#using scripts\mp\killstreaks\_killstreak_bundles;
#using scripts\mp\killstreaks\_killstreak_detect;
#using scripts\mp\killstreaks\_killstreak_hacking;
#using scripts\mp\killstreaks\_killstreak_weapons;
#using scripts\mp\killstreaks\_killstreakrules;
#using scripts\mp\killstreaks\_killstreaks;
#using scripts\mp\killstreaks\_supplydrop;
#using scripts\mp\killstreaks\_combat_robot;

  
    	   	                                                                                                                                         	                                	                                                                                                                                                                                                                                                                                                                                                                    	        	     	              	    	   	               	                                              	                                                           	                               	                     	                                                                                                           	                                                                 	                                                              	                                                                                                                                             	                            	                                     	                                       	                                                                                                              	   	                  	       	                                                    	                   	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                            	                                                                                                 	                                    	                                                                                        	           	                        	                                            	                                             	                                                   	                                                             	                                                         	                                                                    	                                                                                                                                                                                                                                	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                  	    	         	                                                              	                                                          	                                   	                                   	                                                    	                                       	                                           	     	                                                                                                                                                                                                                                                      	                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	                                                               	        	                                                                                                                              	                                                                                                                     	                               	                                                      	                          
                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                                         	   	                                                                                                                                                                                                                                  	                                                                                                                                                                                                                                                                                                                                                                                                                                                   
                                                                                                   	  	                                                                    	                     	                 	                                                                         	                       	                                                      	                                                      	                                                       	                                                                                                  	                                                                                                                 	                                                   	                	                                          	                                      	       	  	             	                                                     	                                                                             	                         	                                                                	                                                             	                                                             	                                                                                      	                                                         	                                                         	                                                               	                                                               	                                                   	                                                                	                                                     	                                                	                                                	                                                        	                                                                                                  	                                                                             	                                            	                                                                                    	                                          	                     	                                              	                                        	                 	   















#precache( "material", "compass_supply_drop_black" );
#precache( "material", "compass_supply_drop_green" );
#precache( "material", "compass_supply_drop_red" );
#precache( "material", "compass_supply_drop_white" );
#precache( "material", "waypoint_recon_artillery_strike" );
#precache( "material", "t7_hud_ks_wpn_turret_drop" );
#precache( "material", "t7_hud_ks_rolling_thunder_drop" );
#precache( "material", "t7_hud_ks_drone_amws_drop" );


// TODO: this is a placeholder head icon for when a supply drop is hacked and a booby trap is made
#precache( "material","headicon_dead");
#precache( "string", "KILLSTREAK_CAPTURING_CRATE" );
#precache( "string", "KILLSTREAK_HACKING_CRATE" );
#precache( "string", "KILLSTREAK_SUPPLY_DROP_DISARM_HINT" );
#precache( "triggerstring", "KILLSTREAK_SUPPLY_DROP_DISARM_HINT" );
#precache( "string", "KILLSTREAK_SUPPLY_DROP_DISARMING_CRATE" );
#precache( "string", "KILLSTREAK_SUPPLY_DROP_HACKED" );

#precache( "triggerstring", "KILLSTREAK_AI_TANK_CRATE" );
#precache( "triggerstring", "KILLSTREAK_MINIGUN_CRATE" );
#precache( "triggerstring", "PLATFORM_MINIGUN_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_M32_CRATE" );
#precache( "triggerstring", "PLATFORM_M32_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_AMMO_CRATE" );
#precache( "triggerstring", "PLATFORM_AMMO_CRATE_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_RADAR_CRATE" );
#precache( "triggerstring", "PLATFORM_RADAR_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_RCBOMB_CRATE" );
#precache( "triggerstring", "PLATFORM_RCBOMB_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_MISSILE_DRONE_CRATE" );
#precache( "triggerstring", "PLATFORM_MISSILE_DRONE_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_COUNTERU2_CRATE" );
#precache( "triggerstring", "PLATFORM_COUNTERU2_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_REMOTE_MISSILE_CRATE" );
#precache( "triggerstring", "PLATFORM_REMOTE_MISSILE_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_PLANE_MORTAR_CRATE");
#precache( "triggerstring", "PLATFORM_PLANE_MORTAR_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_AUTO_TURRET_CRATE" );
#precache( "triggerstring", "PLATFORM_AUTO_TURRET_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_MICROWAVE_TURRET_CRATE" );
#precache( "triggerstring", "PLATFORM_MICROWAVE_TURRET_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_MINIGUN_CRATE" );
#precache( "triggerstring", "PLATFORM_MINIGUN_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_M32_CRATE" );
#precache( "triggerstring", "PLATFORM_M32_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_HELICOPTER_GUARD_CRATE" );
#precache( "triggerstring", "PLATFORM_HELICOPTER_GUARD_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_SATELLITE_CRATE" );
#precache( "triggerstring", "PLATFORM_SATELLITE_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_QRDRONE_CRATE" );
#precache( "triggerstring", "PLATFORM_QRDRONE_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_AI_TANK_CRATE" );
#precache( "triggerstring", "PLATFORM_AI_TANK_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_HELICOPTER_CRATE" );
#precache( "triggerstring", "PLATFORM_HELICOPTER_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_EMP_CRATE" );
#precache( "triggerstring", "PLATFORM_EMP_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_RAPS_CRATE" );
#precache( "triggerstring", "PLATFORM_RAPS_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_DART_CRATE" );
#precache( "triggerstring", "PLATFORM_DART_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_SENTINEL_CRATE" );
#precache( "triggerstring", "PLATFORM_SENTINEL_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_COMBAT_ROBOT_CRATE" );
#precache( "triggerstring", "PLATFORM_COMBAT_ROBOT_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_REMOTE_MORTAR_CRATE" );
#precache( "triggerstring", "PLATFORM_REMOTE_MORTAR_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_HELICOPTER_GUNNER_CRATE" );
#precache( "triggerstring", "PLATFORM_HELICOPTER_GUNNER_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_DOGS_CRATE" );
#precache( "triggerstring", "PLATFORM_DOGS_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_MISSILE_SWARM_CRATE" );
#precache( "triggerstring", "PLATFORM_MISSILE_SWARM_GAMBLER" );
#precache( "triggerstring", "KILLSTREAK_EARNED_SUPPLY_DROP" );
#precache( "triggerstring", "KILLSTREAK_DRONE_STRIKE_CRATE" );
#precache( "triggerstring", "PLATFORM_DRONE_STRIKE_GAMBLER" );
#precache( "triggerstring", "PLATFORM_AI_TANK_CRATE_GAMBLER" ); 

#precache( "string", "KILLSTREAK_AIRSPACE_FULL" );
#precache( "string", "KILLSTREAK_SUPPLY_DROP_INBOUND" );
#precache( "string", "FriendlyBlue" );
#precache( "string", "EnemyOrange" );
#precache( "eventstring", "mpl_killstreak_supply" );
#precache( "fx", "killstreaks/fx_supply_drop_smoke" );
#precache( "fx", "explosions/fx_exp_grenade_default" );

#using_animtree ( "mp_vehicles" );

#namespace supplydrop;

function init()
{
	level.crateModelFriendly = "wpn_t7_care_package_world";
	level.crateModelEnemy = "wpn_t7_care_package_world";	
	level.crateModelTank = "wpn_t7_drop_box";
	level.crateModelBoobyTrapped = "wpn_t7_care_package_world";
	level.vtolDropHelicopterVehicleInfo = "vtol_supplydrop_mp";
	
	level.crateOwnerUseTime = 500;
	level.crateNonOwnerUseTime = GetGametypeSetting("crateCaptureTime") * 1000;
	level.crate_headicon_offset = (0, 0, 15);
	level.supplyDropDisarmCrate = &"KILLSTREAK_SUPPLY_DROP_DISARM_HINT";
	level.disarmingCrate = &"KILLSTREAK_SUPPLY_DROP_DISARMING_CRATE";
	
	level.supplydropCarePackageIdleAnim = %o_drone_supply_care_idle;
	level.supplydropCarePackageDropAnim = %o_drone_supply_care_drop;
	level.supplydropAiTankIdleAnim = %o_drone_supply_agr_idle;
	level.supplydropAiTankDropAnim = %o_drone_supply_agr_drop;
		
	clientfield::register( "helicopter", "supplydrop_care_package_state", 1, 1, "int" );
	clientfield::register( "helicopter", "supplydrop_ai_tank_state", 1, 1, "int" );

	clientfield::register( "vehicle", "supplydrop_care_package_state", 1, 1, "int" );
	clientfield::register( "vehicle", "supplydrop_ai_tank_state", 1, 1, "int" );
	
	clientfield::register( "scriptmover", "supplydrop_thrusters_state", 1, 1, "int" );	
	clientfield::register( "scriptmover", "aitank_thrusters_state", 1, 1, "int" );	
	
	clientfield::register( "toplayer", "marker_state", 1, 2, "int" );
	
	level._supply_drop_smoke_fx = "killstreaks/fx_supply_drop_smoke";
	level._supply_drop_explosion_fx = "explosions/fx_exp_grenade_default";

	killstreaks::register( "supply_drop", "supplydrop_marker", "killstreak_supply_drop", "supply_drop_used",&useKillstreakSupplyDrop, undefined, true );
	killstreaks::register_strings("supply_drop", &"KILLSTREAK_EARNED_SUPPLY_DROP", &"KILLSTREAK_AIRSPACE_FULL", &"KILLSTREAK_SUPPLY_DROP_INBOUND", undefined, &"KILLSTREAK_SUPPLY_DROP_HACKED" );
	killstreaks::register_dialog("supply_drop", "mpl_killstreak_supply", "supplyDropDialogBundle", "supplyDropPilotDialogBundle", "friendlySupplyDrop", "enemySupplyDrop", "enemySupplyDropMultiple", "friendlySupplyDropHacked", "enemySupplyDropHacked", "requestSupplyDrop", "threatSupplyDrop" );
	killstreaks::register_alt_weapon("supply_drop", "mp40_blinged" );
	killstreaks::register_alt_weapon("supply_drop", "supplydrop" );
	killstreaks::allow_assists("supply_drop", true);

	killstreaks::devgui_scorestreak_command( "supply_drop", "Random", "set scr_supply_drop_gui random; set scr_supply_drop_give 1");
	killstreaks::devgui_scorestreak_command( "supply_drop", "Random Ally Crate", "set scr_supply_drop_gui random; set scr_givetestsupplydrop 1");
	killstreaks::devgui_scorestreak_command( "supply_drop", "Random Enemy Crate", "set scr_supply_drop_gui random; set scr_givetestsupplydrop 2");
	
	killstreak_bundles::register_killstreak_bundle( "supply_drop_ai_tank" ); // only registering for damage processing only, to limit scope of change
	killstreak_bundles::register_killstreak_bundle( "supply_drop_combat_robot" ); // only registering for damage processing only, to limit scope of change

	level.crateTypes = [];
	level.categoryTypeWeight = [];

	// percentage of drop explanation:
	//		add all of the numbers up: 15 + 2 + 3 + etc. = 80 for example
	//		now if you want to know the percentage of the minigun_mp drop, you'd do (minigun_mp number / total) or 2/80 = 2.5% chance of dropping
	// right now this is at a perfect 1000, so the percentages are easy to understand
	//registerCrateType( "supplydrop", "ammo", "ammo", 0, &"KILLSTREAK_AMMO_CRATE", &"PLATFORM_AMMO_CRATE_GAMBLER",&giveCrateAmmo );
	registerCrateType( "supplydrop", "killstreak", "uav", 125, &"KILLSTREAK_RADAR_CRATE", &"PLATFORM_RADAR_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "rcbomb", 105, &"KILLSTREAK_RCBOMB_CRATE", &"PLATFORM_RCBOMB_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "counteruav", 115, &"KILLSTREAK_COUNTERU2_CRATE", &"PLATFORM_COUNTERU2_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "remote_missile", 90, &"KILLSTREAK_REMOTE_MISSILE_CRATE", &"PLATFORM_REMOTE_MISSILE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "planemortar", 80, &"KILLSTREAK_PLANE_MORTAR_CRATE", &"PLATFORM_PLANE_MORTAR_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "autoturret", 90, &"KILLSTREAK_AUTO_TURRET_CRATE", &"PLATFORM_AUTO_TURRET_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "microwave_turret", 120, &"KILLSTREAK_MICROWAVE_TURRET_CRATE", &"PLATFORM_MICROWAVE_TURRET_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "satellite", 20, &"KILLSTREAK_SATELLITE_CRATE", &"PLATFORM_SATELLITE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "drone_strike", 75, &"KILLSTREAK_DRONE_STRIKE_CRATE", &"PLATFORM_DRONE_STRIKE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "helicopter_comlink", 30, &"KILLSTREAK_HELICOPTER_CRATE", &"PLATFORM_HELICOPTER_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "emp", 5, &"KILLSTREAK_EMP_CRATE", &"PLATFORM_EMP_GAMBLER", &giveCrateKillstreak );
//	registerCrateType( "supplydrop", "killstreak", "helicopter_player_gunner", 2, &"KILLSTREAK_HELICOPTER_GUNNER_CRATE", &"PLATFORM_HELICOPTER_GUNNER_GAMBLER",&giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "raps", 20, &"KILLSTREAK_RAPS_CRATE", &"PLATFORM_RAPS_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "dart", 75, &"KILLSTREAK_DART_CRATE", &"PLATFORM_DART_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "sentinel", 20, &"KILLSTREAK_SENTINEL_CRATE", &"PLATFORM_SENTINEL_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "combat_robot", 5, &"KILLSTREAK_COMBAT_ROBOT_CRATE", &"PLATFORM_COMBAT_ROBOT_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "supplydrop", "killstreak", "ai_tank_drop", 25, &"KILLSTREAK_AI_TANK_CRATE", &"PLATFORM_AI_TANK_CRATE_GAMBLER", &giveCrateKillstreak );
	
	registerCrateType( "inventory_supplydrop", "killstreak", "uav", 125, &"KILLSTREAK_RADAR_CRATE", &"PLATFORM_RADAR_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "counteruav", 115, &"KILLSTREAK_COUNTERU2_CRATE", &"PLATFORM_COUNTERU2_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "rcbomb", 105, &"KILLSTREAK_RCBOMB_CRATE", &"PLATFORM_RCBOMB_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "remote_missile", 90, &"KILLSTREAK_REMOTE_MISSILE_CRATE", &"PLATFORM_REMOTE_MISSILE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "planemortar", 80, &"KILLSTREAK_PLANE_MORTAR_CRATE", &"PLATFORM_PLANE_MORTAR_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "autoturret", 90, &"KILLSTREAK_AUTO_TURRET_CRATE", &"PLATFORM_AUTO_TURRET_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "microwave_turret", 120, &"KILLSTREAK_MICROWAVE_TURRET_CRATE", &"PLATFORM_MICROWAVE_TURRET_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "satellite", 20, &"KILLSTREAK_SATELLITE_CRATE", &"PLATFORM_SATELLITE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "helicopter_comlink", 30, &"KILLSTREAK_HELICOPTER_CRATE", &"PLATFORM_HELICOPTER_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "emp", 5, &"KILLSTREAK_EMP_CRATE", &"PLATFORM_EMP_GAMBLER", &giveCrateKillstreak );
//	registerCrateType( "inventory_supplydrop", "killstreak", "helicopter_player_gunner", 2, &"KILLSTREAK_HELICOPTER_GUNNER_CRATE", &"PLATFORM_HELICOPTER_GUNNER_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "raps", 20, &"KILLSTREAK_RAPS_CRATE", &"PLATFORM_RAPS_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "dart", 75, &"KILLSTREAK_DART_CRATE", &"PLATFORM_DART_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "sentinel", 20, &"KILLSTREAK_SENTINEL_CRATE", &"PLATFORM_SENTINEL_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "combat_robot", 5, &"KILLSTREAK_COMBAT_ROBOT_CRATE", &"PLATFORM_COMBAT_ROBOT_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "ai_tank_drop", 25, &"KILLSTREAK_AI_TANK_CRATE", &"PLATFORM_AI_TANK_CRATE_GAMBLER", &giveCrateKillstreak );
	registerCrateType( "inventory_supplydrop", "killstreak", "drone_strike", 75, &"KILLSTREAK_DRONE_STRIKE_CRATE", &"PLATFORM_DRONE_STRIKE_GAMBLER", &giveCrateKillstreak );

	registerCrateType( "inventory_ai_tank_drop", "killstreak", "ai_tank_drop", 75, &"KILLSTREAK_AI_TANK_CRATE", undefined, undefined, &ai_tank::crateLand );
	registerCrateType( "ai_tank_drop", "killstreak", "ai_tank_drop", 75, &"KILLSTREAK_AI_TANK_CRATE", undefined, undefined, &ai_tank::crateLand );
	
	// for the gambler perk, have its own crate types with a greater chance to get good stuff
	// right now this is at a perfect 1000, so the percentages are easy to understand
	//registerCrateType( "gambler", "ammo", "ammo", 0, &"KILLSTREAK_AMMO_CRATE", undefined, &giveCrateAmmo );
	registerCrateType( "gambler", "killstreak", "uav", 95, &"KILLSTREAK_RADAR_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "counteruav", 85, &"KILLSTREAK_COUNTERU2_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "rcbomb", 75, &"KILLSTREAK_RCBOMB_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "microwave_turret", 110, &"KILLSTREAK_MICROWAVE_TURRET_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "remote_missile", 100, &"KILLSTREAK_REMOTE_MISSILE_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "planemortar", 80, &"KILLSTREAK_PLANE_MORTAR_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "autoturret", 100, &"KILLSTREAK_AUTO_TURRET_CRATE", undefined, &giveCrateKillstreak );
//	registerCrateType( "gambler", "killstreak", "helicopter_guard", 0, &"KILLSTREAK_HELICOPTER_GUARD_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "satellite", 30, &"KILLSTREAK_SATELLITE_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "ai_tank_drop", 40, &"KILLSTREAK_AI_TANK_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "helicopter_comlink", 45, &"KILLSTREAK_HELICOPTER_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "emp", 10, &"KILLSTREAK_EMP_CRATE", undefined, &giveCrateKillstreak );
//	registerCrateType( "gambler", "killstreak", "helicopter_player_gunner", 8, &"KILLSTREAK_HELICOPTER_GUNNER_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "raps", 35, &"KILLSTREAK_RAPS_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "dart", 75, &"KILLSTREAK_DART_CRATE", undefined, &giveCrateKillstreak );		
	registerCrateType( "gambler", "killstreak", "sentinel", 35, &"KILLSTREAK_SENTINEL_CRATE", undefined, &giveCrateKillstreak );		
	registerCrateType( "gambler", "killstreak", "combat_robot", 10, &"KILLSTREAK_COMBAT_ROBOT_CRATE", undefined, &giveCrateKillstreak );
	registerCrateType( "gambler", "killstreak", "drone_strike", 75, &"KILLSTREAK_DRONE_STRIKE_CRATE", undefined, &giveCrateKillstreak );			
	
	level.crateCategoryWeights = [];
	level.crateCategoryTypeWeights = [];
	
	foreach( categoryKey, category in level.crateTypes )
	{
		finalizeCrateCategory( categoryKey );
	}

	/#
		level thread supply_drop_dev_gui();
	#/
}

function finalizeCrateCategory( category )
{
	level.crateCategoryWeights[category] = 0;

	crateTypeKeys = getarraykeys( level.crateTypes[category] );
	
	// must leave this as a for loop not a foreach loop
	// it must match the loop in getRandomCrateType
	for ( crateType = 0; crateType < crateTypeKeys.size; crateType++ )
	{
		typeKey = crateTypeKeys[crateType];
		level.crateTypes[category][typeKey].previousWeight = level.crateCategoryWeights[category];
		level.crateCategoryWeights[category] += level.crateTypes[category][typeKey].weight;
		level.crateTypes[category][typeKey].weight = level.crateCategoryWeights[category];
	}
}

function advancedFinalizeCrateCategory( category )
{
	level.crateCategoryTypeWeights[category] = 0;
	crateTypeKeys = getarraykeys( level.categoryTypeWeight[category] );
	
	// must leave this as a for loop not a foreach loop
	// it must match the loop in getRandomCrateType
	for ( crateType = 0; crateType < crateTypeKeys.size; crateType++ )
	{
		typeKey = crateTypeKeys[crateType];
		level.crateCategoryTypeWeights[category] += level.categoryTypeWeight[category][typeKey].weight;
		level.categoryTypeWeight[category][typeKey].weight = level.crateCategoryTypeWeights[category];
	}
	
	finalizeCrateCategory( category );
}

function setCategoryTypeWeight( category, type, weight )
{
	if ( !isdefined(level.categoryTypeWeight[category]) )
	{
		level.categoryTypeWeight[category] = [];
	}
	
	level.categoryTypeWeight[category][type] = SpawnStruct();
	
	level.categoryTypeWeight[category][type].weight = weight;
	
	count = 0;
	totalWeight = 0;
	startIndex = undefined;
	finalIndex = undefined;
	
	crateNameKeys = getarraykeys( level.crateTypes[category] );
	
	// must leave this as a for loop not a foreach loop
	// it must match the loop in getRandomCrateType
	for ( crateName = 0; crateName < crateNameKeys.size; crateName++ )
	{
		nameKey = crateNameKeys[crateName];

		if ( level.crateTypes[category][nameKey].type == type )
		{
			count++;
			totalWeight = totalWeight + level.crateTypes[category][nameKey].weight;
			
			if ( !isdefined( startIndex ) )
			{
				startIndex = crateName;
			}
			
			if ( isdefined( finalIndex ) && (( finalIndex + 1 ) != crateName ) )
			{
				/#util::error("Crate type declaration must be contiguous");#/
				callback::abort_level();
				
				return;
			}
			
			finalIndex = crateName;
		} 
	}
	
	level.categoryTypeWeight[category][type].totalCrateWeight = totalWeight; 
	level.categoryTypeWeight[category][type].crateCount = count;
	level.categoryTypeWeight[category][type].startIndex = startIndex;
	level.categoryTypeWeight[category][type].finalIndex = finalIndex;
}

function registerCrateType( category, type, name, weight, hint, hint_gambler, giveFunction, landFunctionOverride )
{
/#
	// do not register a crate for any scorestreak that does not exist
	//if ( !killstreaks::is_registered(name) )
	//{
	//	return;
	//}
#/
		
	itemName = level.killstreaks[name].menuName;
		
	if( IsItemRestricted( itemName ) )
		return;
		
	if ( !isdefined(level.crateTypes[category]) )
	{
		level.crateTypes[category] = [];
	}
	
	crateType = SpawnStruct();
	crateType.type = type;
	crateType.name = name;
	crateType.weight = weight;
	crateType.hint = hint;
	crateType.hint_gambler = hint_gambler;
	crateType.giveFunction = giveFunction;
	crateWeapon = killstreaks::get_killstreak_weapon( name );
	if( isdefined(crateWeapon) )
	{
		crateType.objective = GetCrateHeadObjective( crateWeapon );
	}
	if ( isdefined( landFunctionOverride ) )
	{
		crateType.landFunctionOverride = landFunctionOverride;
	}
	
	level.crateTypes[category][name] = crateType;
	
	game["strings"][name + "_hint"] = hint;
	
/#	
	level thread add_devgui_command( category, name );
#/
}

function add_devgui_command( category, name )
{
/#
	level endon( "game_ended" );
	
	wait 0.1; // make sure all killstreaks are registered first
	wait ( RandomIntRange( 2, 10 ) * .05 );
	if ( category == "inventory_supplydrop" && killstreaks::is_registered(name) )
	{
		killstreaks::devgui_scorestreak_command( name, "Care Package", "set scr_supply_drop_gui "+ name +"; set scr_supply_drop_give 1");
	}
#/
}

function getRandomCrateType( category, gambler_crate_name )
{
	if( !isdefined(level.crateTypes) || !isdefined(level.crateTypes[category]) ) 
		return;
	
	Assert( isdefined(level.crateTypes) );
	Assert( isdefined(level.crateTypes[category]) );
	Assert( isdefined(level.crateCategoryWeights[category]) );

	typeKey = undefined;
	crateTypeStart = 0;	
	randomWeightEnd = RandomIntRange( 1, level.crateCategoryWeights[category] + 1 );
	find_another = false;

	crateNameKeys = getarraykeys( level.crateTypes[category] );

	if ( isdefined( level.categoryTypeWeight[category] ) )
	{
		randomWeightEnd = RandomInt(level.crateCategoryTypeWeights[category] ) + 1;
		crateTypeKeys = getarraykeys( level.categoryTypeWeight[category] );
		
		for ( crateType = 0; crateType < crateTypeKeys.size; crateType++ )
		{
			typeKey = crateTypeKeys[crateType];
			
			if ( level.categoryTypeWeight[category][typeKey].weight < randomWeightEnd )
				continue;
				
			crateTypeStart = level.categoryTypeWeight[category][typeKey].startIndex;
			randomWeightEnd = RandomInt( level.categoryTypeWeight[category][typeKey].totalCrateWeight) + 1;
			randomWeightEnd += level.crateTypes[category][crateNameKeys[crateTypeStart]].previousWeight;
			break;
		}
	}
	
	for ( crateType = crateTypeStart; crateType < crateNameKeys.size; crateType++ )
	{
		typeKey = crateNameKeys[crateType];
		
		if ( level.crateTypes[category][typeKey].weight < randomWeightEnd )
			continue;
		
		// if we have the gambler perk then make sure we aren't getting the same thing again
		if( isdefined( gambler_crate_name ) && level.crateTypes[category][typeKey].name == gambler_crate_name )
		{
			find_another = true;
		}

		// go find another crate
		if( find_another )
		{
			if( crateType < crateNameKeys.size - 1 )
			{
				crateType++;
			}
			else if( crateType > 0 )
			{
				crateType--;
			}
			typeKey = crateNameKeys[crateType];
		}

		break;
	}

	/#
	if( isdefined(level.dev_gui_supply_drop) && level.dev_gui_supply_drop != "random" && level.dev_gui_supply_drop != "" )
	{
		typeKey = level.dev_gui_supply_drop;
	}
	#/

	return level.crateTypes[category][typeKey];
}

function giveCrateItem( crate )
{
	if ( !IsAlive( self ) || !isdefined( crate.crateType ) )
		return;
		
	Assert( isdefined(crate.crateType.giveFunction), "no give function defined for " + crate.crateType.name );
	
	return [[crate.crateType.giveFunction]]( "inventory_" + crate.crateType.name );
}

function giveCrateKillstreakWaiter( event, removeCrate, extraEndon )
{
	self endon( "give_crate_killstreak_done" );
	if ( isdefined( extraEndon ) )
	{
		self endon( extraEndon );
	}
	self waittill( event );
	self notify( "give_crate_killstreak_done", removeCrate );
}

function giveCrateKillstreak( killstreak )
{
	self killstreaks::give( killstreak );
}

function giveSpecializedCrateWeapon( weapon )
{
	switch ( weapon.name )
	{
	case "minigun":
		level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_MINIGUN_INBOUND", self );
		level weapons::add_limited_weapon( weapon, self, 3 );
		break;
	case "m32":
		level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_M32_INBOUND", self );
		level weapons::add_limited_weapon( weapon, self, 3 );
		break;
	case "m202_flash":
		level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_M202_FLASH_INBOUND", self );
		level weapons::add_limited_weapon( weapon, self, 3 );
		break;
	case "m220_tow":
		level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_M220_TOW_INBOUND", self );
		level weapons::add_limited_weapon( weapon, self, 3 );
		break;
	case "mp40_blinged":
		level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_MP40_INBOUND", self );
		level weapons::add_limited_weapon( weapon, self, 3 );
		break;

	default:
		break;
	}
	
}

function giveCrateWeapon( weapon_name )
{
	weapon = GetWeapon(weapon_name);
	if ( weapon == level.weaponNone )
		return;
		
	currentWeapon = self GetCurrentWeapon();

	if ( currentWeapon == weapon || self HasWeapon( weapon ) ) 
	{
		self GiveMaxAmmo( weapon );
		return true;
	}
	
	// if the player is holding anything other than primary or secondary weapons, 
	// take away the last primary or secondary weapon the player was holding before giving the crate weapon. 
	if ( currentWeapon.isSupplyDropWeapon || isdefined( level.grenade_array[currentWeapon] )|| isdefined( level.inventory_array[currentWeapon] ) ) 
	{
		self TakeWeapon( self.lastdroppableweapon );
		self GiveWeapon( weapon );
		self switchToWeapon( weapon );
		return true;
	}
	
	self AddWeaponStat( weapon, "used", 1 );

	giveSpecializedCrateWeapon( weapon );	
	
	self GiveWeapon( weapon );
	self switchToWeapon( weapon );

	self waittill( "weapon_change", newWeapon );
	
	self killstreak_weapons::useKillstreakWeaponFromCrate( weapon );
	
	return true;
}

function useSupplyDropMarker( package_contents_id, context )
{
	player = self;
	//self endon("death"); // never endon death in for this thread
	self endon("disconnect");
	self endon("spawned_player");
	
	supplyDropWeapon = level.weaponNone;
	currentWeapon = self GetCurrentWeapon();
	prevWeapon = currentWeapon;
	if ( currentWeapon.isSupplyDropWeapon )
	{
		supplyDropWeapon = currentWeapon;
	}
	
	if( supplyDropWeapon.isGrenadeWeapon )
		trigger_event = "grenade_fire";
	else
		trigger_event = "weapon_fired";	
	
	self thread supplyDropWatcher( package_contents_id, trigger_event, supplyDropWeapon, context );

	self.supplyGrenadeDeathDrop = false;
	
	while( true )
	{
		player AllowMelee( false );
		notifyString = self util::waittill_any_return( "weapon_change", trigger_event, "disconnect", "spawned_player" );
		player AllowMelee( true );

		if ( !isdefined( notifyString ) || ( notifyString != trigger_event ) ) 
		{
			cleanup( context, player );
			return false; 
		}
		
		
		if( isdefined( player.markerPosition ) )
		{
			break;
		}
	}
	
	self notify ( "trigger_weapon_shutdown" );
	
	// for some reason we never had the supply drop weapon
	if ( supplyDropWeapon == level.weaponNone )
	{
		cleanup( context, player );
		return false;
	}
	
	if ( isdefined( self ) )
	{
		// don't take the supplyDropWeapon until the throwing (firing) state is completed
		notifyString = self util::waittill_any_return( "weapon_change", "death", "disconnect", "spawned_player" );
		
		self TakeWeapon( supplyDropWeapon );
	
		// if we no longer have the supply drop weapon in our inventory then 
		// it must have been successful
		if ( self HasWeapon( supplyDropWeapon ) || self GetAmmoCount( supplyDropWeapon ) )
		{
			cleanup( context, player );
			return false;
		}
	}
	
	return true;
}

function isSupplyDropGrenadeAllowed( killstreak )
{
	if ( !self killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
	{
		self killstreaks::switch_to_last_non_killstreak_weapon();
	
		return false;
	}

	return true;
}

function AddDropLocation( killstreak_id, location )
{
	level.droplocations[killstreak_id] = location;
}

function DelDropLocation( killstreak_id )
{
	level.droplocations[killstreak_id] = undefined;
}

function IsLocationGood( location, context )
{
	//check no zones	
	foreach( dropLocation in level.dropLocations )
	{
		if( Distance2DSquared( dropLocation, location ) < 60 * 60 )
			return false;
	}
	
	if ( context.perform_physics_trace === true )
	{
		mask = (1 << 0);
		if( isdefined( context.tracemask ) ) 
			mask = context.tracemask;
		
		radius = context.radius;
		//trace = physicstrace( location + ( 0,0, 5000 ), location + ( 0, 0, 10 ), ( -radius, -radius, 0 ), ( radius, radius, radius ), undefined, mask );
		trace = physicstrace( location + ( 0,0, 5000 ), location + ( 0, 0, 10 ), ( -radius, -radius, 0 ), ( radius, radius, 2 * radius ), undefined, mask );
	
		///#Box( location, (-radius, -radius, 10 ), ( radius, radius, 5000 ), 0, ( 0, 0.7, 0 ), 0.6, false, 1 );#/
	
		if( trace["fraction"] < 1 )
		{
			///#sphere( location, radius, ( 1, 0, 0 ), 1, true, 10, 1 );#/
			return false;
		}
		else
		{
			///#sphere( location, radius, ( 0, 0, 1 ), 1, true, 10, 1 );#/
		}
	}
	
	// check for a valid start node	
	closestPoint = GetClosestPointOnNavMesh( location, max( context.max_dist_from_location, 24 ), context.dist_from_boundary );

	isValidPoint = isdefined( closestPoint );
	
	// make sure the selected point is roughly on the same floor
	if ( isValidPoint && context.check_same_floor === true && Abs( location[2] - closestPoint[2] ) > 96 )
		isValidPoint = false;
	
	if ( isValidPoint && Distance2DSquared( location, closestPoint ) > ( (context.max_dist_from_location) * (context.max_dist_from_location) ) )
		isValidPoint = false;


/# 
	if ( GetDVarInt( "scr_supply_drop_valid_location_debug", 0 ) )
	{
		if ( !isValidPoint )
		{
			// debug draw closest valid location on nav mesh (red)
			otherClosestPoint = GetClosestPointOnNavMesh( location, GetDVarFloat( "scr_supply_drop_valid_location_radius_debug", 96 ), context.dist_from_boundary );
			if ( isdefined( otherClosestPoint ) )
			{
				sphere( otherClosestPoint, context.max_dist_from_location, ( 1, 0, 0 ), 0.8, false, 20, 1 );
			}
		}
		else
		{
			// debug draw valid location on nav mesh (green)
			sphere( closestPoint, context.max_dist_from_location, ( 0, 1, 0 ), 0.8, false, 20, 1 );
			util::drawcylinder( closestPoint, context.radius, 8000, 1.0/60.0, undefined, ( 0, 0.9, 0 ), 0.7 );
		}
	}
#/

	return isValidPoint;
}
	
function useKillstreakSupplyDrop( killstreak )
{
	player = self;
	
	if ( !player isSupplyDropGrenadeAllowed( killstreak ) )
		return false;
	
	context = SpawnStruct();
	context.radius = level.killstreakCoreBundle.ksAirdropSupplydropRadius;
	context.dist_from_boundary = 12;
	context.max_dist_from_location = 4;
	context.perform_physics_trace = true;
	context.isLocationGood = &IsLocationGood;
	context.objective = &"airdrop_supplydrop";	
	context.validLocationSound = level.killstreakCoreBundle.ksValidCarepackageLocationSound;
	context.tracemask = (1 << 0) | (1 << 2);
	context.dropTag = "tag_attach";
	context.dropTagOffset = ( -32, 0, 23 );
	context.killstreakType = killstreak;

	result = player useSupplyDropMarker( undefined, context );
	
	player notify( "supply_drop_marker_done" );

	if ( !isdefined( result ) || !result )
		return false;

	return result;
}

function use_killstreak_death_machine( killstreak )
{
	if ( !self killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
		return false;

	weapon = GetWeapon( "minigun" );
	currentWeapon = self GetCurrentWeapon();

	// if the player is holding anything other than primary or secondary weapons, 
	// take away the last primary or secondary weapon the player was holding before giving the crate weapon. 
	if ( currentWeapon.isSupplyDropWeapon || isdefined( level.grenade_array[currentWeapon] ) || isdefined( level.inventory_array[currentWeapon] ) ) 
	{
		self TakeWeapon( self.lastdroppableweapon );
		self GiveWeapon( weapon );
		self SwitchToWeapon( weapon );

		//This will make it so the player cannot pick up weapons while using this weapon for the first time.
		self setBlockWeaponPickup( weapon, true );
		return true;
	}

	level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_MINIGUN_INBOUND", self );
	level weapons::add_limited_weapon( weapon, self, 3 );

	self TakeWeapon( currentWeapon );
	self GiveWeapon( weapon );
	self SwitchToWeapon( weapon );

	//This will make it so the player cannot pick up weapons while using this weapon for the first time.
	self setBlockWeaponPickup( weapon, true );
	return true;
}

function use_killstreak_grim_reaper( killstreak )
{
	if ( !self killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
		return false;

	weapon = GetWeapon( "m202_flash" );
	currentWeapon = self GetCurrentWeapon();

	// if the player is holding anything other than primary or secondary weapons, 
	// take away the last primary or secondary weapon the player was holding before giving the crate weapon. 
	if ( currentWeapon.isSupplyDropWeapon || isdefined( level.grenade_array[currentWeapon] ) || isdefined( level.inventory_array[currentWeapon] ) ) 
	{
		self TakeWeapon( self.lastdroppableweapon );
		self GiveWeapon( weapon );
		self SwitchToWeapon( weapon );

		//This will make it so the player cannot pick up weapons while using this weapon for the first time.
		self setBlockWeaponPickup( weapon, true );
		return true;
	}

	level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_M202_FLASH_INBOUND", self );
	level weapons::add_limited_weapon( weapon, self, 3 );

	self TakeWeapon( currentWeapon );
	self GiveWeapon( weapon );
	self SwitchToWeapon( weapon );
	
	//This will make it so the player cannot pick up weapons while using this weapon for the first time.
	self setBlockWeaponPickup( weapon, true );
	return true;
}

function use_killstreak_tv_guided_missile( killstreak )
{
	if ( !killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
	{
		self iPrintLnBold( level.killstreaks[ killstreak].notAvailableText );

		return false;
	}

	weapon = GetWeapon( "m220_tow" );
	currentWeapon = self GetCurrentWeapon();

	// if the player is holding anything other than primary or secondary weapons, 
	// take away the last primary or secondary weapon the player was holding before giving the crate weapon. 
	if ( currentWeapon.isSupplyDropWeapon || isdefined( level.grenade_array[currentWeapon] ) || isdefined( level.inventory_array[currentWeapon] ) ) 
	{
		self TakeWeapon( self.lastdroppableweapon );
		self GiveWeapon( weapon );
		self SwitchToWeapon( weapon );

		//This will make it so the player cannot pick up weapons while using this weapon for the first time.
		self setBlockWeaponPickup( weapon, true );
		return true;
	}

	level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_M220_TOW_INBOUND", self );
	level weapons::add_limited_weapon( weapon, self, 3 );

	self TakeWeapon( currentWeapon );
	self GiveWeapon( weapon );
	self SwitchToWeapon( weapon );
	
	//This will make it so the player cannot pick up weapons while using this weapon for the first time.
	self setBlockWeaponPickup( weapon, true );
	return true;
}

function use_killstreak_mp40( killstreak )
{
	if ( !killstreakrules::isKillstreakAllowed( killstreak, self.team ) )
	{
		self iPrintLnBold( level.killstreaks[killstreak].notAvailableText );

		return false;
	}

	weapon = GetWeapon( "mp40_blinged" );
	currentWeapon = self GetCurrentWeapon();

	// if the player is holding anything other than primary or secondary weapons, 
	// take away the last primary or secondary weapon the player was holding before giving the crate weapon. 
	if ( currentWeapon.isSupplyDropWeapon || isdefined( level.grenade_array[currentWeapon] ) || isdefined( level.inventory_array[currentWeapon] ) ) 
	{
		self TakeWeapon( self.lastdroppableweapon );
		self GiveWeapon( weapon );
		self SwitchToWeapon( weapon );
		
		//This will make it so the player cannot pick up weapons while using this weapon for the first time.
		self setBlockWeaponPickup( weapon, true );
		return true;
	}

	level thread popups::DisplayTeamMessageToAll( &"KILLSTREAK_MP40_INBOUND", self );
	level weapons::add_limited_weapon( weapon, self, 3 );

	self TakeWeapon( currentWeapon );
	self GiveWeapon( weapon );
	self SwitchToWeapon( weapon );
	
	//This will make it so the player cannot pick up weapons while using this weapon for the first time.
	self setBlockWeaponPickup( weapon, true );
	return true;
}

function cleanUpWatcherOnDeath( team, killstreak_id )
{
	player = self;
	self endon( "disconnect" );
	self endon( "supplyDropWatcher" );
	self endon( "trigger_weapon_shutdown" );
	self endon( "spawned_player" );
	self endon( "weapon_change" );
	
	self util::waittill_any( "death", "joined_team", "joined_spectators" );
	
	killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );
	self notify( "cleanup_marker" );
}

function cleanup( context, player )
{
	if( isdefined( context ) && isdefined( context.marker ) )
	{
		context.marker delete();
		context.marker = undefined;
		if( isdefined( context.markerFXHandle ) )
		{
			context.markerFXHandle delete();
			context.markerFXHandle = undefined;
		}
		
		if ( isdefined( player ) )
		{
			player clientfield::set_to_player( "marker_state", 0 ); // off
		}
		
		DelDropLocation( context.killstreak_id );
	}	
}

function MarkerUpdateThread( context )
{
	player = self;
	player endon( "supplyDropWatcher" );
	player endon( "spawned_player" );
	player endon( "disconnect" );
	player endon( "weapon_change" );
	player endon( "death" );	
	
	markerModel = spawn( "script_model", ( 0, 0, 0 ) );
	context.marker = markerModel;
	
	player thread MarkerCleanupThread( context );	
	
	while( true )
	{		
		if( player flagsys::get( "marking_done" ) )
			break; // we dont delete the marker yet, just stop moving it around.
		
		minRange = level.killstreakCoreBundle.ksMinAirdropTargetRange;
		maxRange = level.killstreakCoreBundle.ksMaxAirdropTargetRange;
		
		forwardVector = VectorScale( AnglesToForward( player GetPlayerAngles() ), maxRange );
		//results = BulletTrace( player GetEye(), player GetEye() + forwardVector, false, player );
		
		mask = (1 << 0);
		if( isdefined( context.tracemask ) ) 
			mask = context.tracemask;
		
		radius = 2;
		results = physicstrace( player GetEye(), player GetEye() + forwardVector, ( -radius, -radius, 0 ), ( radius, radius, 2 * radius ), player, mask );
		
		
		markerModel.origin = results["position"];
		
		tooClose = DistanceSquared( markerModel.origin, player.origin ) < minRange * minRange;
		
		if( ( results["normal"][2] > 0.7 ) && !tooClose && isdefined( context.isLocationGood ) && [[context.isLocationGood]]( markerModel.origin, context ) )
		{
			player.markerPosition = markerModel.origin;
			player clientfield::set_to_player( "marker_state", 1 ); // good
		}
		else
		{
			player.markerPosition = undefined;
			player clientfield::set_to_player( "marker_state", 2 ); // bad
		}
		
		{wait(.05);};
	}
}

function supplyDropWatcher( package_contents_id, trigger_event, supplyDropWeapon, context )
{
	player = self;
	self notify( "supplyDropWatcher" );
	
	self endon( "supplyDropWatcher" );
	self endon( "spawned_player" );
	self endon( "disconnect" );
	self endon( "weapon_change" );
	
	team = self.team;

	killstreak_id = killstreakrules::killstreakStart( "supply_drop", team, false, false );
	if ( killstreak_id == -1 )
		return;
	
	context.killstreak_id = killstreak_id;
	
	player flagsys::clear( "marking_done" );

	if( !supplyDropWeapon.isGrenadeWeapon )
		self thread MarkerUpdateThread( context );
	
	self thread checkForEmp();
	
	self thread checkWeaponChange( team, killstreak_id );

	self thread cleanUpWatcherOnDeath( team, killstreak_id ); 
	
	while( true )
	{
		self waittill( trigger_event, weapon_instance, weapon );
		
		isSupplyDropWeapon = true;
		if( trigger_event == "grenade_fire" )
			isSupplyDropWeapon = weapon.isSupplyDropWeapon;
			
		if ( isdefined( self ) && isSupplyDropWeapon )
		{
			if( isdefined( context ) )
			{
				if( !isdefined( player.markerPosition ) || !supplydrop::islocationgood( player.markerPosition, context ) )
				{
					if( isdefined( level.killstreakCoreBundle.ksInvalidLocationSound ) )
						player playsoundtoplayer( level.killstreakCoreBundle.ksInvalidLocationSound, player );
					
					if( isdefined( level.killstreakCoreBundle.ksInvalidLocationString ) )
						player iPrintLnBold( Istring( level.killstreakCoreBundle.ksInvalidLocationString ) );
					
					continue;
				}
				
				if( isdefined( context.validLocationSound ) )
					player playsoundtoplayer( context.validLocationSound, player );
				
				self thread heliDeliverCrate( player.markerPosition, weapon_instance, self, team, killstreak_id, package_contents_id, context );
			}
			else
			{
				self thread doSupplyDrop( weapon_instance, weapon, self, killstreak_id, package_contents_id );
				weapon_instance thread do_supply_drop_detonation( weapon, self );
				weapon_instance thread supplyDropGrenadeTimeout( team, killstreak_id, weapon );
			}
			self killstreaks::switch_to_last_non_killstreak_weapon();
		}
		else
		{
			killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );
			self notify( "cleanup_marker" );
		}
		
		break;
	}
	
	player flagsys::set( "marking_done" );
	player clientfield::set_to_player( "marker_state", 0 );
}

function checkForEmp()
{
	self endon( "supplyDropWatcher" );
	self endon( "spawned_player" );
	self endon( "disconnect" );
	self endon( "weapon_change" );
	self endon( "death" );
	self endon( "trigger_weapon_shutdown" );
	
	self waittill( "emp_jammed" );
	
	self killstreaks::switch_to_last_non_killstreak_weapon();
}

function supplyDropGrenadeTimeout( team, killstreak_id, weapon )
{
	self endon( "death" );
	self endon("stationary");
	
	GRENADE_LIFETIME = 10;

	//If the grenade hasn't stopped moving after a certain time delete it.
	wait( GRENADE_LIFETIME );
	
	if( !isdefined( self ) )
		return;

	self notify( "grenade_timeout" );
	
	killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );

	if ( weapon.name == "ai_tank_drop" )
	{
		killstreakrules::killstreakStop( "ai_tank_drop", team, killstreak_id );
		self notify( "cleanup_marker" );
	}
	else if ( weapon.name == "inventory_ai_tank_drop" )
	{
		killstreakrules::killstreakStop( "inventory_ai_tank_drop", team, killstreak_id );
		self notify( "cleanup_marker" );
	}	
	else if ( weapon.name == "combat_robot_drop" )
	{
		killstreakrules::killstreakStop( "combat_robot_drop", team, killstreak_id );
		self notify( "cleanup_marker" );
	}
	else if ( weapon.name == "inventory_combat_robot_drop" )
	{
		killstreakrules::killstreakStop( "inventory_combat_robot_drop", team, killstreak_id );
		self notify( "cleanup_marker" );
	}	

	self delete();
}

function checkWeaponChange( team, killstreak_id )
{
	self endon( "supplyDropWatcher" );
	self endon( "spawned_player" );
	self endon( "disconnect" );
	self endon( "trigger_weapon_shutdown" );
	self endon( "death" );
	
	self waittill( "weapon_change" );
	killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );	
	self notify( "cleanup_marker" );
}

function supplyDropGrenadePullWatcher( killstreak_id )
{
	self endon( "disconnect" );
	self endon( "weapon_change" );

	self waittill ( "grenade_pullback", weapon );

	self util::_disableUsability();

	self thread watchForGrenadePutDown();
	
	self waittill ( "death" );
	
	killstreak = "supply_drop";
	self.supplyGrenadeDeathDrop = true;

	if( weapon.isSupplyDropWeapon )
	{
		killstreak = killstreaks::get_killstreak_for_weapon( weapon );
	}

	if ( !( isdefined( self.usingKillstreakFromInventory ) && self.usingKillstreakFromInventory ) ) 
	{
		self killstreaks::change_killstreak_quantity( weapon, -1 );
	}
	else
	{
		killstreaks::remove_used_killstreak( killstreak, killstreak_id );
	}
}

function watchForGrenadePutDown()
{
	self notify( "watchForGrenadePutDown" );
	self endon( "watchForGrenadePutDown" );
	self endon( "death" );
	self endon( "disconnect" );

	self util::waittill_any( "grenade_fire", "weapon_change" );
	
	self notify ( "trigger_weapon_shutdown" );
	
	self util::_enableUsability();
}

function playerChangeWeaponWaiter()
{
	self endon( "supply_drop_marker_done" );

	self endon( "disconnect" );
	self endon( "spawned_player" );

	currentWeapon = self GetCurrentWeapon();
	
	while ( currentWeapon.isSupplyDropWeapon )
	{
		self waittill( "weapon_change", currentWeapon );
	}

	// if the killstreak ended because of a weapon change
	// give a frame to allow the weapon_change to trigger in other scripts
	waittillframeend;

	self notify( "supply_drop_marker_done" );
}

function getIconForCrate()
{
	icon = undefined;
	
	switch ( self.crateType.type )
	{
		case "killstreak":
			{
				if( isDefined(self.crateType.objective) )
				{
					return self.crateType.objective;
				}
				else if (self.crateType.name == "inventory_ai_tank_drop" )
				{
					icon = "t7_hud_ks_drone_amws";
				}
				else
				{
					killstreak = killstreaks::get_menu_name( self.crateType.name );
					icon = level.killStreakIcons[killstreak];
				}
			}
			break;
		
		case "weapon":
			{
				switch( self.crateType.name )
				{
				case "minigun":
					icon = "hud_ks_minigun";
					break;
				case "m32":
					icon = "hud_ks_m32";
					break;
				case "m202_flash":
					icon = "hud_ks_m202";
					break;
				case "m220_tow":
					icon = "hud_ks_tv_guided_missile";
					break;
				case "mp40_drop":
					icon = "hud_mp40";
						break;
				default:
					icon = "waypoint_recon_artillery_strike";
					break;
				}
			}
			break;
		
		case "ammo":
			{
				icon = "hud_ammo_refill";
			}
			break;

		default:
				return undefined;
			break;
	}
	
	return icon + "_drop";
}

function crateActivate( hacker )
{
	self MakeUsable();
	self SetCursorHint("HINT_NOICON");
	
	if( !isdefined( self.crateType ) )
		return;

	self setHintString( self.crateType.hint );	
	if ( isdefined( self.crateType.hint_gambler ) )
	{
		self setHintStringForPerk( "specialty_showenemyequipment", self.crateType.hint_gambler );
	}

	crateObjID = gameobjects::get_next_obj_id();	
	objective_add( crateObjID, "invisible", self.origin );
	//blue/friendly
	objective_icon( crateObjID, "compass_supply_drop_white" );
	objective_setcolor( crateObjID, &"FriendlyBlue" );
	objective_state( crateObjID, "active" );
	self.friendlyObjID = crateObjID;
	self.enemyObjID = [];
	
	icon = self getIconForCrate();
	
	if (isdefined( hacker ))
	{
		// hacked crate stops appearing as enemy equipment
		self clientfield::set( "enemyequip", 0 );
	}
	
	if ( level.teambased )
	{
		objective_team( crateObjID, self.team );

		foreach( team in level.teams )
		{
			if ( self.team == team )
				continue;
				
			crateObjID = gameobjects::get_next_obj_id();	
			objective_add( crateObjID, "invisible", self.origin );
			if( isdefined( self.hacker ) )
			{
				//black/hacked
				objective_icon( crateObjID, "compass_supply_drop_black" );
			}
			else
			{
				//orange/enemy
				objective_icon( crateObjID, "compass_supply_drop_white" );
				objective_setcolor( crateObjID, &"EnemyOrange" );
			}
			objective_team( crateObjID, team );
			objective_state( crateObjID, "active" );
			self.enemyObjID[self.enemyObjID.size] = crateObjID;	
		}
	}
	else
	{
		if ( !self.visibleToAll )
		{
			Objective_SetInvisibleToAll( crateObjID );
	
			enemyCrateObjID = gameobjects::get_next_obj_id();	
			objective_add( enemyCrateObjID, "invisible", self.origin );
			objective_icon( enemyCrateObjID, "compass_supply_drop_white" );
			objective_setcolor( enemyCrateObjID, &"EnemyOrange" );
			objective_state( enemyCrateObjID, "active" );
			
			if ( isplayer( self.owner ) )
			{
				Objective_SetInvisibleToPlayer( enemyCrateObjID, self.owner );
			}
			
			self.enemyObjID[self.enemyObjID.size] = enemyCrateObjID;
		}
		
		if ( isplayer( self.owner ) )
		{
			Objective_SetVisibleToPlayer( crateObjID, self.owner );
		}
		
		if( isdefined( self.hacker ) )
		{
			Objective_SetInvisibleToPlayer( crateObjID, self.hacker );
			
			crateObjID = gameobjects::get_next_obj_id();	
			objective_add( crateObjID, "invisible", self.origin );
			//black/hacked
			objective_icon( crateObjID, "compass_supply_drop_black" );
			objective_state( crateObjID, "active" );
			Objective_SetInvisibleToAll( crateObjID );
			Objective_SetVisibleToPlayer( crateObjID, self.hacker );
			self.hackerObjID = crateObjID;
		}
	}
	
	if( !self.visibleToAll && isdefined( icon ) )
	{
		self entityheadIcons::setEntityHeadIcon( self.team, self, level.crate_headicon_offset, icon, true );	
		if( self.entityHeadObjectives.size > 0 )
		{
			objectiveID = self.entityHeadObjectives[self.entityHeadObjectives.size - 1];
			if( isdefined( objectiveID ) )
			{
				Objective_SetInvisibleToAll( objectiveID );
				Objective_SetVisibleToPlayer( objectiveID, self.owner );
			}
		}
	}
	
	if ( isdefined( self.owner ) && IsPlayer(self.owner) && self.owner util::is_bot() )
	{
		self.owner notify( "bot_crate_landed", self );
	}

	if ( isdefined( self.owner ) )
	{
		self.owner notify( "crate_landed", self );
		
		setRicochetProtectionEndTime( "supply_drop", self.killstreak_id, self.owner );
	}
}

function setRicochetProtectionEndTime( killstreak, killstreak_id, owner )
{
	ksBundle = level.killstreakBundle[ killstreak ];
	if ( isdefined( ksBundle ) && isdefined( ksBundle.ksRicochetPostLandDuration ) && ksBundle.ksRicochetPostLandDuration > 0  )
	{
		endtime = GetTime() + ( ksBundle.ksRicochetPostLandDuration * 1000 );
		killstreaks::set_ricochet_protection_endtime( killstreak_id, owner, endtime );
	}
}

function crateDeactivate( )
{
	self makeunusable();

	if ( isdefined(self.friendlyObjID) )
	{
		Objective_Delete( self.friendlyObjID );
		gameobjects::release_obj_id(self.friendlyObjID);
		self.friendlyObjID = undefined;
	}
	
	if ( isdefined(self.enemyObjID) )
	{
		foreach( objId in self.enemyObjID )
		{
			Objective_Delete( objId );
			gameobjects::release_obj_id(objId);
		}
		self.enemyObjID = [];
	}
	
	if ( isdefined(self.hackerObjID) )
	{
		Objective_Delete( self.hackerObjID );
		gameobjects::release_obj_id(self.hackerObjID);
		self.hackerObjID = undefined;
	}
}


function ownerTeamChangeWatcher()
{
	self notify( "ownerTeamChangeWatcher_singleton" );
	self endon ("ownerTeamChangeWatcher_singleton");
	
	self endon("death");

	if ( !level.teamBased || !isdefined( self.owner ) )
		return;

	self.owner waittill("joined_team");

	self.owner = undefined;
}

function dropAllToGround( origin, radius, stickyObjectRadius )
{
	PhysicsExplosionSphere( origin, radius, radius, 0 );
	{wait(.05);};
	weapons::drop_all_to_ground( origin, radius );

	supplydrop::dropCratesToGround( origin, radius );
	level notify( "drop_objects_to_ground", origin, stickyObjectRadius );
}

function dropEverythingTouchingCrate( origin )
{
	// a sphere with a radius of 44 covers the current supply drop exactly
	dropAllToGround( origin, 70, 70 );
}

function dropAllToGroundAfterCrateDelete( crate, crate_origin )
{
	crate waittill("death");
	wait( 0.1 );
	
	crate dropEverythingTouchingCrate( crate_origin );
}

function dropCratesToGround( origin, radius )
{
	crate_ents = GetEntArray( "care_package", "script_noteworthy" );
	radius_sq = radius * radius;
	for ( i = 0 ; i < crate_ents.size ; i++ )
	{
		if ( DistanceSquared( origin, crate_ents[i].origin ) < radius_sq )
		{
			crate_ents[i] thread dropCrateToGround();
		}
	}
}

function dropCrateToGround()
{
	self endon("death");
	
	if ( isdefined( self.droppingToGround ) )
		return;
		
	self.droppingToGround = true;
	
	// we need to recursively have this crate trigger a drop to ground as well
	dropEverythingTouchingCrate( self.origin );
	
	self crateDeactivate();
	self thread crateDropToGroundKill();
	self crateRedoPhysics();
	self crateActivate();
		
	self.droppingToGround = undefined;
}

function ConfigureTeamPost( owner )
{
	crate = self;
	
	crate thread ownerTeamChangeWatcher();	
}

function crateSpawn( killstreak, killstreakId, owner, team, drop_origin, drop_angle )
{
	crate = spawn( "script_model", drop_origin, 1 );
	crate killstreaks::configure_team( killstreak, killstreakId, owner, undefined, undefined, &ConfigureTeamPost );
	
	crate.angles = drop_angle;
	crate.visibleToAll = false;
	crate.script_noteworthy = "care_package";
	crate clientfield::set( "enemyequip", 1 );
	
	if ( killstreak == "ai_tank_drop" || killstreak == "inventory_ai_tank_drop" )
	{
		crate setModel( level.crateModelTank );
		crate setEnemyModel( level.crateModelTank );
	}
	else
	{
		crate setModel( level.crateModelFriendly );
		crate setEnemyModel( level.crateModelEnemy );
	}
	
	// Care Packages will cut the navmesh causing AI's to walk around them.
	crate DisconnectPaths();
	
	switch( killstreak )
	{
	case "turret_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "autoturret" ];
		break;
	case "tow_turret_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "auto_tow" ];
		break;
	case "m220_tow_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "m220_tow" ];
		break;
	case "ai_tank_drop":
	case "inventory_ai_tank_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "ai_tank_drop" ];
		break;	
	case "minigun_drop":
	case "inventory_minigun_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "minigun" ];
		break;	
	case "m32_drop":
	case "inventory_m32_drop":
		crate.crateType = level.crateTypes[ killstreak ][ "m32" ];
		break;	
	default:
		crate.crateType = getRandomCrateType( "supplydrop" );
		break;
	}
	
	return crate;
}

function crateDelete( drop_all_to_ground )
{
	if( !isdefined( self ) )
		return;
	
	killstreaks::remove_ricochet_protection( self.killstreak_id, self.originalowner );

	if( !isdefined( drop_all_to_ground ) )
	{
		drop_all_to_ground = true;
	}
	
	if ( isdefined(self.friendlyObjID) )
	{
		Objective_Delete( self.friendlyObjID );
		gameobjects::release_obj_id(self.friendlyObjID);
		self.friendlyObjID = undefined;
	}
	
	if ( isdefined(self.enemyObjID) )
	{
		foreach( objId in self.enemyObjID )
		{
			Objective_Delete( objId );
			gameobjects::release_obj_id(objId);
		}
		self.enemyObjID = undefined;
	}
	
	if ( isdefined(self.hackerObjID) )
	{
		Objective_Delete( self.hackerObjID );
		gameobjects::release_obj_id(self.hackerObjID);
		self.hackerObjID = undefined;
	}
	
	if( drop_all_to_ground )
	{
		level thread dropAllToGroundAfterCrateDelete( self, self.origin );
	}
	
	if ( isdefined ( self.killcament ) )
	{
		self.killcament thread util::deleteAfterTime( 5 );	
	}

	self Delete();
}

function stationaryCrateOverride()
{
	self endon("death");
	self endon("stationary");
	
	wait( 3 ); // give some time for the physics to settle
	
	// if not turn it off and fire the notify
	
	self.angles = self.angles;
	self.origin = self.origin; // this should turn off the physics
	
	self notify( "stationary" );
}

function timeoutCrateWaiter()
{
	self endon("death");
	self endon("stationary");
	
	// if the crate has not stopped moving for some time just get rid of it
	wait( 20 );
	
	self crateDelete( true );
}

function cratePhysics()
{
	//forcePointVariance = 200.0;
	//vertVelocityMin = -100.0;
	//vertVelocityMax = 100.0;
	
	//forcePointX = RandomFloatRange( 0-forcePointVariance, forcePointVariance );
	//forcePointY = RandomFloatRange( 0-forcePointVariance, forcePointVariance );
	//forcePoint = ( forcePointX, forcePointY, 0 );
	//initialVelocityZ = RandomFloatRange( vertVelocityMin, vertVelocityMax );
	//forcePoint += self.origin;
	
	forcePoint = self.origin;
	params = level.killstreakBundle["supply_drop"];
	if(!isdefined(params.ksLandingVelocity))params.ksLandingVelocity=100;
	initialVelocity = ( 0, 0, -params.ksLandingVelocity / 40 );

	self PhysicsLaunch( forcePoint, initialVelocity );
	
	self thread timeoutCrateWaiter();
	self thread stationaryCrateOverride();

	self thread update_crate_velocity();
	self thread play_impact_sound();

	self waittill("stationary");
}

function get_height( e_ignore )
{
	if(!isdefined(e_ignore))e_ignore=self;
	
	const height_diff = 10;
	trace = GroundTrace( self.origin + (0,0,height_diff), self.origin + ( 0, 0, -10000 ), false, e_ignore, false );
	
	/#
	recordLine( self.origin + (0,0,height_diff), trace[ "position" ], ( 1, .5, 0 ), "Animscript", self );
	#/
	
	return Distance( self.origin, trace[ "position" ] );
}

function crateControlledDrop( killstreak, v_target_location )
{
	crate = self;
	
	supplydrop = true;
	if( killstreak == "ai_tank_drop" ) 
		supplydrop = false;
	
	if( supplydrop ) 
		params = level.killstreakBundle["supply_drop"];
	else
		params = level.killstreakBundle["ai_tank_drop"];
	
	if(!isdefined(params.ksThrustersOffHeight))params.ksThrustersOffHeight=100;
	if(!isdefined(params.ksTotalDropTime))params.ksTotalDropTime=4;
	if(!isdefined(params.ksAccelTimePercentage))params.ksAccelTimePercentage=0.65;
	
	accelTime = params.ksTotalDropTime * params.ksAccelTimePercentage;
	decelTime = params.ksTotalDropTime - accelTime;
	
	target = ( v_target_location[0], v_target_location[1], v_target_location[2] + params.ksThrustersOffHeight );
	
	hostmigration::waitTillHostMigrationDone();
	crate moveto( target, params.ksTotalDropTime, accelTime, decelTime ) ;

	crate thread WatchForCrateKill( v_target_location[2] + (isdefined(params.ksStartCrateKillHeightFromGround)?params.ksStartCrateKillHeightFromGround:200) );
	
	wait( accelTime - 0.05 );

	if( supplydrop )
		crate clientfield::set( "supplydrop_thrusters_state", 1 );
	else
		crate clientfield::set( "aitank_thrusters_state", 1 );
	
	crate waittill( "movedone" );
	
	hostmigration::waitTillHostMigrationDone();
	
	if( supplydrop )
		crate clientfield::set( "supplydrop_thrusters_state", 0 );
	else
		crate clientfield::set( "aitank_thrusters_state", 0 );

	crate cratePhysics();
}

function play_impact_sound() //self == crate
{
	self endon( "entityshutdown" );
	self endon( "stationary" );
	self endon( "death" );

	wait( 0.5 ); //this wait is to delay the fall speed check

	while( abs( self.velocity[2] ) > 5 ) //this is not 0 since the crate will sometimes rock a bit before it stops moving
	{
		wait( 0.1 );
	}

	self PlaySound( "phy_impact_supply" );
}

function update_crate_velocity() //self == crate
{
	self endon( "entityshutdown" );
	self endon( "stationary" );

	self.velocity = ( 0,0,0 );
	self.old_origin = self.origin;

	while( isdefined( self ) )
	{
		self.velocity = ( self.origin - self.old_origin );
		self.old_origin = self.origin;

		{wait(.05);};
	}
}


function crateRedoPhysics()
{
	forcePoint = self.origin;
	
	initialVelocity = ( 0, 0, 0 );

	self PhysicsLaunch(forcePoint,initialVelocity);
	
	self thread timeoutCrateWaiter();
	self thread stationaryCrateOverride();
	
	self waittill("stationary");
}

function do_supply_drop_detonation( weapon, owner ) // self == weapon_instance
{
	self notify( "supplyDropWatcher" );

	self endon( "supplyDropWatcher" );
	self endon( "spawned_player" );
	self endon( "disconnect" );
	self endon( "death" );
	self endon ( "grenade_timeout" );

	// control the explosion events to circumvent the code cleanup
	self util::waitTillNotMoving();
	self.angles = ( 0, self.angles[1], 90 );
	fuse_time = weapon.fuseTime / 1000; // fuse time comes back in milliseconds
	wait( fuse_time );
	
	if ( !isdefined( owner ) || !owner EMP::EnemyEMPActive() )
	{
		thread smokegrenade::playSmokeSound( self.origin, 6, level.sound_smoke_start, level.sound_smoke_stop, level.sound_smoke_loop );
		PlayFXOnTag( level._supply_drop_smoke_fx, self, "tag_fx" );
		proj_explosion_sound = weapon.projExplosionSound;
		sound::play_in_space( proj_explosion_sound, self.origin );
	}
	
	// need to clean up the canisters
	wait( 3 );
	self delete();
}

function doSupplyDrop( weapon_instance, weapon, owner, killstreak_id, package_contents_id, context )
{
	weapon endon ( "explode" );
	weapon endon ( "grenade_timeout" );
	self endon( "disconnect" );
	team = owner.team;
	weapon_instance thread watchExplode( weapon, owner, killstreak_id, package_contents_id );
	weapon_instance util::waitTillNotMoving();
	weapon_instance notify( "stoppedMoving" );
	
	self thread heliDeliverCrate( weapon_instance.origin, weapon, owner, team, killstreak_id, package_contents_id, context );
}

function watchExplode( weapon, owner, killstreak_id, package_contents_id )
{
	self endon( "stoppedMoving" );
	team = owner.team;
	self waittill( "explode", position ); 
	
	owner thread heliDeliverCrate( position, weapon, owner, team, killstreak_id, package_contents_id );
}

function crateTimeOutThreader()
{
	crate = self;
	crateTimeOut( 90 );
	crate thread deleteOnOwnerLeave();
}
function crateTimeOut( time )
{
	crate = self;
	self thread killstreaks::WaitForTimeout( "inventory_supply_drop", 90 * 1000, &crateDelete, "death" );
}

function deleteOnOwnerleave()
{
	crate = self;
	crate endon( "death" );
	crate.owner util::waittill_any( "joined_team", "joined_spectators", "disconnect" );
	crate crateDelete( true );
}

function WaitAndDelete( time )
{
	self endon( "death" );
	wait( time );
	self delete();
}

function dropCrate( origin, angle, killstreak, owner, team, killcamEnt, killstreak_id, package_contents_id, crate_, context )
{
	angle = ( angle[0] * 0.5, angle[1] * 0.5, angle[2] * 0.5 );
	
	if ( isdefined( crate_ ) )
	{
		origin = crate_.origin;
		angle = crate_.angles;
		crate_ thread WaitAndDelete( 0.1 );
	}
	crate = crateSpawn( killstreak, killstreak_id, owner, team, origin, angle );
	killCamEnt unlink();
	killCamEnt linkto( crate );
	crate.killcamEnt = killcamEnt;
	crate.killstreak_id = killstreak_id;
	crate.package_contents_id = package_contents_id;
	killCamEnt thread util::deleteAfterTime( 15 );
	killCamEnt thread unlinkOnRotation( crate );
	
	crate endon("death");
	
	crate crateTimeOutThreader();

	trace = GroundTrace( crate.origin + ( 0, 0, -100 ), crate.origin + ( 0, 0, -10000 ), false, crate, false );
	v_target_location = trace["position"];	

/#
	if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
	{
		util::drawcylinder( v_target_location, context.radius, 8000, 99999999, "stop_heli_drop_valid_location_dropped_cylinder", ( 0, 0, 0.9 ), 0.8 );
	}	
#/
		
	crate crateControlledDrop(killstreak, v_target_location );	
	
	crate thread hacker_tool::registerWithHackerTool( level.carePackageHackerToolRadius, level.carePackageHackerToolTimeMs );
	
	cleanup( context, owner ) ;
	
	if ( isdefined( crate.crateType ) && isdefined( crate.crateType.landFunctionOverride ) )
	{
		[[crate.crateType.landFunctionOverride]]( crate, killstreak, owner, team, context );
	}
	else
	{
		crate crateActivate();
	
		crate thread crateUseThink();
		crate thread crateUseThinkOwner();
		
		if( isdefined( crate.crateType ) && isdefined( crate.crateType.hint_gambler )) 
		{
			crate thread crateGamblerThink();
		}

		default_land_function( crate, killstreak, owner, team );
	}
	

}

function unlinkOnRotation( crate )
{
	self endon( "delete" );
	crate endon( "death" );
	crate endon( "entityshutdown" );
	crate endon( "stationary" );
	
	waitBeforeRotationCheck = GetDvarFloat( "scr_supplydrop_killcam_rot_wait", 0.5 );
	wait( waitBeforeRotationCheck ); //this wait is to delay the fall speed check

	minCos = GetDvarFloat( "scr_supplydrop_killcam_max_rot", 0.999 );
		
	cosine = 1;

	currentDirection = VectorNormalize( AnglesToForward( crate.angles ) );

	while( cosine > minCos  ) 
	{
		oldDirection = currentDirection;
		{wait(.05);};
		currentDirection = VectorNormalize( AnglesToForward( crate.angles ) );
		cosine = vectordot( oldDirection, currentDirection );
	}
	self unlink();
}


function default_land_function( crate, category, owner, team ) 
{
	while ( 1 )
	{
		crate waittill("captured", player, remote_hack );

		player challenges::capturedCrate( owner );
		deleteCrate = player giveCrateItem( crate );
		if ( isdefined( deleteCrate ) && !deleteCrate )
		{
			continue;
		}
		
		playerHasEngineerPerk = player HasPerk( "specialty_showenemyequipment" );
		
		// added functionality to specialty_showenemyequipment to create a booby trapped supply crate once this is captured
		if( ( playerHasEngineerPerk || remote_hack==true ) &&
			owner != player &&
			((level.teambased && team != player.team) || !level.teambased) )
		{
			// spawn an explosive crate right before we delete the other
			spawn_explosive_crate( crate.origin, crate.angles, category, owner, team, player, playerHasEngineerPerk );
			crate MakeUnusable();
			util::wait_network_frame(); // to avoid crate blinking
			crate crateDelete( false );
		}
		else
		{
			crate crateDelete( true );
		}
		return;
	}
}

function spawn_explosive_crate( origin, angle, killstreak, owner, team, hacker, playerHasEngineerPerk ) // self == crate
{
	// No killstreakId needed since there's currently no dialog to attach to an exploding crate
	crate = crateSpawn( killstreak, undefined, owner, team, origin, angle );
	crate SetOwner( owner );
	crate SetTeam( team );

	if ( level.teambased )
	{
		crate setEnemyModel( level.crateModelBoobyTrapped );
		crate MakeUsable( team );
	}
	else
	{
		crate setEnemyModel( level.crateModelEnemy );
	}
	
	crate.hacker = hacker;
	crate.visibleToAll = false;
	crate crateActivate( hacker );
	crate setHintStringForPerk( "specialty_showenemyequipment", level.supplyDropDisarmCrate );
	crate thread crateUseThink();
	crate thread crateUseThinkOwner();
	crate thread watch_explosive_crate();
	crate crateTimeOutThreader();
	crate.playerHasEngineerPerk = playerHasEngineerPerk;
}

function watch_explosive_crate() // self == crate
{
	killCamEnt = spawn( "script_model", self.origin + (0,0,60) );
	self.killcament = killcament;
	self waittill( "captured", player, remote_hack );
	
	// give warning and then explode if the capturer didnt have hacker perk
	if ( !player HasPerk( "specialty_showenemyequipment" ) && !remote_hack )
	{
		self thread entityheadIcons::setEntityHeadIcon( player.team, player, level.crate_headicon_offset, "headicon_dead", true );	
		self loop_sound( "wpn_semtex_alert", 0.15 );
	
		if( !isdefined( self.hacker ) )
		{
			self.hacker = self;
		}
		self RadiusDamage( self.origin, 256, 300, 75, self.hacker, "MOD_EXPLOSIVE", GetWeapon( "supplydrop" ) );
		PlayFX( level._supply_drop_explosion_fx, self.origin );
		PlaySoundAtPosition( "wpn_grenade_explode", self.origin );
	}
	else
	{
		PlaySoundAtPosition ( "mpl_turret_alert", self.origin );
		scoreevents::processScoreEvent( "disarm_hacked_care_package", player );
		player challenges::disarmedHackedCarepackage();
	}
	wait ( 0.1 );
	self crateDelete();
	killcament thread util::deleteAfterTime( 5 );	
}

function loop_sound( alias, interval ) // self == crate
{
	self endon( "death" );
	while( 1 )
	{
		PlaySoundAtPosition( alias, self.origin );

		wait interval;
		interval = (interval / 1.2);

		if (interval < .08)
		{
			break;
		}	
	}
}

function WatchForCrateKill( start_kill_watch_z_threshold )
{
	crate = self;
	crate endon( "death" );
	crate endon( "stationary" );

	while ( crate.origin[2] > start_kill_watch_z_threshold )
	{
		{wait(.05);};
	}
	
	stationaryThreshold = 2;
	killThreshold = 15;
	maxFramesTillStationary = 20;
	numFramesStationary = 0;	
	
	while( true )
	{	
		vel = 0;
		if( isdefined( self.velocity ) )
			vel = abs( self.velocity[2] );
		
		if( vel > killThreshold )
		{
			crate is_touching_crate();
			crate is_clone_touching_crate();
		}
		
		if( vel < stationaryThreshold )
			numFramesStationary++;
		else
			numFramesStationary = 0;

		if( numFramesStationary >= maxFramesTillStationary )
			break;
		
		{wait(.05);};
	}
}

function crateKill() // self == crate
{
	self endon( "death" );

	// kill anyone under it
	stationaryThreshold = 2;
	killThreshold = 15;
	maxFramesTillStationary = 20;
	numFramesStationary = 0;
	while( true )
	{	
		vel = 0;
		if ( isdefined( self.velocity ) )
			vel = abs( self.velocity[2] );
		
		if ( vel > killThreshold )
		{
			self is_touching_crate();
			self is_clone_touching_crate();
		}

		if ( vel < stationaryThreshold )
			numFramesStationary++;
		else
			numFramesStationary = 0;

		if ( numFramesStationary >= maxFramesTillStationary )
			break;
		
		wait 0.01;
	}
}

function crateDropToGroundKill()
{
	self endon( "death" );
	self endon( "stationary" );

	for ( ;; )
	{
		players = GetPlayers();
		doTrace = false;

		for ( i = 0; i < players.size; i++ )
		{
			if ( players[i].sessionstate != "playing" )
				continue;

			if ( players[i].team == "spectator" )
				continue;

			//Check if any equipment gets landed on
			self is_equipment_touching_crate( players[i] );

			if ( !IsAlive( players[i] ) )
				continue;
			
			flattenedSelfOrigin = (self.origin[0], self.origin[1], 0 );
			flattenedPlayerOrigin = (players[i].origin[0], players[i].origin[1], 0 );
			
			if ( DistanceSquared( flattenedSelfOrigin, flattenedPlayerOrigin ) > 64 * 64 )
				continue;

			doTrace = true;
			break;
		}

		// do the trace
		if ( doTrace )
		{
			start = self.origin;
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( 1.0, 0.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( -1.0, 0.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( 0.0, -1.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( 0.0, 1.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( 1.0, 1.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( -1.0, 1.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( 1.0, -1.0, 0.0 );
			crateDropToGroundTrace( start );

			start = self GetPointInBounds( -1.0, -1.0, 0.0 );
			crateDropToGroundTrace( start );

			wait( 0.2 );
		}
		else
		{
			wait( 0.5 );
		}
	}
}

function crateDropToGroundTrace( start )
{
	end = start + ( 0, 0, -8000 );

	trace = BulletTrace( start, end, true, self, true, true );

	if ( isdefined( trace[ "entity" ] ) && IsPlayer( trace[ "entity" ] ) && IsAlive( trace[ "entity" ] ) )
	{
		player = trace[ "entity" ];

		if ( player.sessionstate != "playing" )
			return;

		if ( player.team == "spectator" )
			return;

		if ( DistanceSquared( start, trace[ "position" ] ) < 12 * 12 || self IsTouching( player ) )
		{
			player DoDamage( player.health + 1, player.origin, self.owner, self, "none", "MOD_HIT_BY_OBJECT", 0, GetWeapon( "supplydrop" ) );
			player playsound ( "mpl_supply_crush" );
			player playsound ( "phy_impact_supply" );			
		}
	}
}

function is_touching_crate() // self == crate
{
	if ( !isdefined( self ) )
		return;

	crate = self;
	extraBoundary = ( 10, 10, 10 );
	players = GetPlayers();
		
	//crate_bottom_point = self GetPointInBounds( 0.0, 0.0, -1.0 );
	crate_bottom_point = self.origin;
	
	// /# sphere( crate_bottom_point, 10, ( 1.0, 0, 0 ), 1.0, true, 20, 120 ); #/
		
	foreach( player in level.players )
	{		
		if( isdefined( player ) && IsAlive( player ) )
		{
			stance = player GetStance();
			stance_z_offset = ( ( stance == "stand" ) ? 40 : ( ( stance == "crouch" ) ? 18 : 6 ) );
			player_test_point = player.origin + ( 0, 0, stance_z_offset );
			
			// /# sphere( player_test_point, 10, ( 0, 0, 1.0 ), 1.0, true, 20, 120 ); #/
				
			if (  ( player_test_point[2] < crate_bottom_point[2] ) && self IsTouching( player, extraBoundary ) )
			{
				attacker = ( isdefined( self.owner ) ? self.owner : self );
				
				player DoDamage( player.health + 1, player.origin, attacker, self, "none", "MOD_HIT_BY_OBJECT", 0, GetWeapon( "supplydrop" ) );
				player playsound ("mpl_supply_crush");
				player playsound ("phy_impact_supply");
			}
		}

		self is_equipment_touching_crate( player );
	}
	
	vehicles = GetEntArray( "script_vehicle", "classname" );
	foreach( vehicle in vehicles )
	{
		if( IsVehicle( vehicle ) )
		{
			if( isdefined( vehicle.archetype ) && ( vehicle.archetype == "wasp" ) )
			{
				if( crate IsTouching( vehicle, ( 2, 2, 2 ) ) )
				{
					vehicle notify( "sentinel_shutdown" );
				}
			}
		}
	}
}

function is_clone_touching_crate() // self == crate
{
	if ( !isdefined( self ) )
		return;
	
	extraBoundary = ( 10, 10, 10 );
	actors = GetActorArray();
	for( i = 0; i < actors.size; i++ )
	{
		if( isdefined( actors[i] ) && isdefined( actors[i].isAiClone ) && IsAlive( actors[i] ) && ( actors[i].origin[2] < self.origin[2] ) && self IsTouching( actors[i], extraBoundary ) )
		{
			attacker = ( isdefined( self.owner ) ? self.owner : self );
			
			actors[i] DoDamage( actors[i].health + 1, actors[i].origin, attacker, self, "none", "MOD_HIT_BY_OBJECT", 0, GetWeapon( "supplydrop" ) );
			actors[i] playsound ("mpl_supply_crush");
			actors[i] playsound ("phy_impact_supply");			
		}
	}
}

function is_equipment_touching_crate( player ) // self == crate, player is passed to access their equipment
{
	extraBoundary = ( 10, 10, 10 );
	if( isdefined( player ) && isdefined( player.weaponObjectWatcherArray ) )
	{
		for( watcher = 0; watcher < player.weaponObjectWatcherArray.size; watcher++ )
		{ 
			objectWatcher = player.weaponObjectWatcherArray[watcher];
			objectArray = objectWatcher.objectArray;
			
			if( isdefined( objectArray ) )
			{
				for( weaponObject = 0; weaponObject < objectArray.size; weaponObject++ )
				{
					if( isdefined(objectArray[weaponObject]) && self IsTouching( objectArray[weaponObject], extraBoundary ) )
					{
						if( isdefined(objectWatcher.onDetonateCallback) )
						{
							objectWatcher thread weaponobjects::waitAndDetonate( objectArray[weaponObject], 0 );
						}
						else
						{
							weaponobjects::removeWeaponObject( objectWatcher, objectArray[weaponObject] );
							
							// TODO-T8: this doesn't actually delete the weapon object; make a call to weaponobjects::deleteWeaponObjectInstance here after it is implemented

						}
					}
				}
			}
		}
	}

	//Check for tactical insertion
	extraBoundary = ( 15, 15, 15 );
	if( isdefined( player ) && isdefined( player.tacticalInsertion ) && self IsTouching( player.tacticalInsertion, extraBoundary ) )
	{
		player.tacticalInsertion thread tacticalinsertion::fizzle();
	}
}

function spawnUseEnt()
{
	useEnt = spawn( "script_origin", self.origin );
	useEnt.curProgress = 0;
	useEnt.inUse = false;
	useEnt.useRate = 0;
	useEnt.useTime = 0;
	useEnt.owner = self;
	
	useEnt thread useEntOwnerDeathWaiter( self );

	return useEnt;
}

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

// taken from _gameobject maybe we can just use the _gameobject code
function crateUseThink() // self == crate
{
	while( isdefined(self) )
	{
		self waittill("trigger", player );
	
		if ( !isdefined( self ) )
			break;
		
		if ( !isAlive( player ) )
			continue;
			
		if ( !player isOnGround() )
			continue;
		
		if ( isdefined( self.owner ) && self.owner == player )
			continue;
			
		useEnt = self spawnUseEnt();
		result = false;
		
		// if the crate has been hacked then we'll need to know later on the useEnt
		if( isdefined( self.hacker ) )
		{
			useEnt.hacker = self.hacker;
		}
		
		self.useEnt = useEnt;

		result = useEnt useHoldThink( player, level.crateNonOwnerUseTime );
		
		if ( isdefined( useEnt ) )
		{
			useEnt Delete();
		}
		
		if ( result && isdefined( self ) )
		{
			scoreevents::giveCrateCaptureMedal( self, player );
			self notify("captured", player, false );
		}
	}
}

function crateUseThinkOwner() // self == crate
{
	self endon("joined_team");

	while( isdefined(self) )
	{
		self waittill("trigger", player );
		
		if ( !isdefined( self ) )
			break;

		if ( !isAlive( player ) )
			continue;
			
		//if ( !player isOnGround() )
			//continue;
		
		if ( !isdefined( self.owner ) )
			continue;

		if ( self.owner != player )
			continue;
			
		result = self useHoldThink( player, level.crateOwnerUseTime );

		if ( result && isdefined( self ) && isdefined( player ) )
		{
			self notify("captured", player, false );
		}
	}
}

function useHoldThink( player, useTime ) // self == a script origin (useEnt) or the crate
{
	player notify ( "use_hold" );
	player util::freeze_player_controls( true );

	player util::_disableWeapon();

	self.curProgress = 0;
	self.inUse = true;
	self.useRate = 0;
	self.useTime = useTime;
	
	player thread personalUseBar( self );

	result = useHoldThinkLoop( player );
	
	if ( isdefined( player ) )
	{
		player notify( "done_using" );
	}
	
	if ( isdefined( player ) )
	{
		
		if ( IsAlive(player) )
		{
			player util::_enableWeapon();
	
			player util::freeze_player_controls( false );
		}
	}
	
	if ( isdefined( self ) )
	{
		self.inUse = false;
	}

	// result may be undefined if useholdthinkloop hits an endon
	if ( isdefined( result ) && result )
		return true;
	
	return false;
}

function continueHoldThinkLoop( player )
{
	if ( !isdefined(self ) )
		return false;
		
	if ( self.curProgress >= self.useTime )
		return false;
		
	if ( !IsAlive( player ) )
		return false;

	if ( player.throwingGrenade )
		return false;

	if ( !(player useButtonPressed()) )
		return false;

	if ( player meleeButtonPressed() )
		return false;
		
	if ( player IsInVehicle() )
		return false;
	
	if ( player IsWeaponViewOnlyLinked() )
		return false;

	if ( player IsRemoteControlling() )
		return false;

	return true;
}

function useHoldThinkLoop( player )
{
	level endon ( "game_ended" );
	self endon("disabled");
	self.owner endon( "crate_use_interrupt" );
	
	timedOut = 0;
	
	while( self continueHoldThinkLoop( player ) )
	{
		timedOut += 0.05;

		self.curProgress += (50 * self.useRate);
		self.useRate = 1;

		if ( self.curProgress >= self.useTime )
		{
			self.inUse = false;
						
			wait .05;
			
			return isAlive( player );
		}
		
		{wait(.05);};
		hostmigration::waitTillHostMigrationDone();
	}
	
	return false;
}

function crateGamblerThink()
{
	self endon( "death" );

	for ( ;; )
	{

		self waittill( "trigger_use_doubletap", player );

			
		if ( !player HasPerk( "specialty_showenemyequipment" ))
		{
			continue;
		}
		if( isdefined( self.useEnt ) && self.useEnt.inUse )
		{
			// TODO: get a fail sound for this
			if(  IsDefined( self.owner ) && self.owner != player )
				continue;
		}
		
		player playlocalsound ("uin_gamble_perk");
		
		self.crateType = getRandomCrateType( "gambler", self.crateType.name );
		self crateReactivate();
		self setHintStringForPerk( "specialty_showenemyequipment", self.crateType.hint );

		self notify( "crate_use_interrupt" );
		level notify( "use_interrupt", self );
		
		return;
	}
}

function crateReactivate()
{
	self setHintString( self.crateType.hint );

	icon = self getIconForCrate();
	
	self thread entityheadIcons::setEntityHeadIcon( self.team, self, level.crate_headicon_offset, icon, true );
}

function personalUseBar( object ) // self == player, object == a script origin (useEnt) or the crate
{
	self endon("disconnect");
	
	captureCrateState = 0;
	
	if( self HasPerk( "specialty_showenemyequipment" ) && 
		object.owner != self && 
		!isdefined( object.hacker ) &&
		( ( level.teambased && object.owner.team != self.team ) || !level.teambased ) )
	{
		captureCrateState = 2;
		self PlayLocalSound ( "evt_hacker_hacking" );
	}
	else if( self HasPerk( "specialty_showenemyequipment" ) &&
		isdefined( object.hacker ) &&
		(object.owner == self || 
		( level.teambased && object.owner.team == self.team ) ) )
	{
		captureCrateState = 3;
		self PlayLocalSound ( "evt_hacker_hacking" );
	}
	else
	{
		captureCrateState = 1;
		self.is_capturing_own_supply_drop = ( object.owner === self ) && ( !isdefined( object.originalOwner ) || object.originalOwner == self );
	}

	lastRate = -1;
	while ( isAlive( self ) && isdefined(object) && object.inUse && !level.gameEnded )
	{
		if ( lastRate != object.useRate )
		{
			if( object.curProgress > object.useTime)
				object.curProgress = object.useTime;

			if ( !object.useRate )
			{
				self clientfield::set_player_uimodel( "hudItems.captureCrateTotalTime", 0 );
				self clientfield::set_player_uimodel( "hudItems.captureCrateState", 0 );
			}
			else
			{
				barFrac = object.curProgress / object.useTime;
				rateOfChange = object.useRate / object.useTime;
				captureCrateTotalTime = 0;
				if ( rateOfChange > 0 )
				{
					captureCrateTotalTime = ( ( 1 - barFrac ) / rateOfChange );
				}
				
				self clientfield::set_player_uimodel( "hudItems.captureCrateTotalTime", Int( captureCrateTotalTime ) );
				self clientfield::set_player_uimodel( "hudItems.captureCrateState", captureCrateState );
			}
		}	
		lastRate = object.useRate;
		{wait(.05);};
	}
	
	self.is_capturing_own_supply_drop = false;
	self clientfield::set_player_uimodel( "hudItems.captureCrateTotalTime", 0 );
	self clientfield::set_player_uimodel( "hudItems.captureCrateState", 0 );
}

function spawn_helicopter( owner, team, origin, angles, model, targetname, killstreak_id, context )
{
	chopper = spawnHelicopter( owner, origin, angles, model, targetname );
	if ( !isdefined( chopper ) )
	{
		if ( isplayer( owner ) )
		{
			killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );
			self notify( "cleanup_marker" );
		}
		return undefined;
	}

	chopper killstreaks::configure_team( "supply_drop", killstreak_id, owner );
//	chopper killstreak_hacking::enable_hacking( SUPPLY_DROP_NAME );
//	if ( isdefined ( context.killstreakRef ) )
//	{
//		chopper killstreak_hacking::override_hacked_killstreak_reference( context.killstreakRef );
//	}
	
	chopper.maxhealth = level.heli_maxhealth;		// max health
	//chopper.health = 999999;								// we check against maxhealth in the damage monitor to see if this gets destroyed, so we don't want this to die prematurely			
	chopper.rocketDamageOneShot = chopper.maxhealth + 1;	// Make it so the heatseeker blows it up in one hit for now
	chopper.damageTaken = 0;
	
	hardpointTypeForDamage = "supply_drop";
	if ( context.killstreakref === "inventory_ai_tank_drop" || context.killstreakref === "ai_tank_drop" )
	{
		hardpointTypeForDamage = "supply_drop_ai_tank";
	}
	else if ( context.killstreakref === "inventory_combat_robot" || context.killstreakref === "combat_robot" )
	{
		hardpointTypeForDamage = "supply_drop_combat_robot";
	}
		
	chopper thread helicopter::heli_damage_monitor( hardpointTypeForDamage );
	chopper thread heatseekingmissile::MissileTarget_ProximityDetonateIncomingMissile("crashing", "death");
	chopper.spawnTime = GetTime();
	chopper clientfield::set( "enemyvehicle", 1 );
	
	supplydropSpeed = GetDvarInt( "scr_supplydropSpeedStarting", 250 ); // 250);
	supplydropAccel = GetDvarInt( "scr_supplydropAccelStarting", 100 ); //175);
	chopper SetSpeed( supplydropSpeed, supplydropAccel );	

	/# chopper util::debug_slow_heli_speed(); #/

	maxPitch = GetDvarInt( "scr_supplydropMaxPitch", 25);
	maxRoll = GetDvarInt( "scr_supplydropMaxRoll", 45 ); // 85);
	chopper SetMaxPitchRoll( 0, maxRoll );	
	
	chopper SetDrawInfrared( true );
	
	Target_Set(chopper, ( 0, 0, -25 ));
	
	if ( isplayer( owner ) )
	{
		chopper thread refCountDecChopper(team, killstreak_id);
	}
	chopper thread heliDestroyed();

	return chopper;
}

function getDropHeight(origin)
{
	return airsupport::getMinimumFlyHeight();
}

function getDropDirection()
{
	return (0, RandomInt(360), 0);
}

function getNextDropDirection( drop_direction, degrees )
{
	drop_direction = (0, drop_direction[1] + degrees, 0 );

	if( drop_direction[1] >= 360 )
		drop_direction = (0, drop_direction[1] - 360, 0 );

	return drop_direction;
}

function getHeliStart( drop_origin, drop_direction )
{
	dist = -1 * GetDvarInt( "scr_supplydropIncomingDistance", 15000 ); // 15000);
	pathRandomness = 100;
	direction = drop_direction + (0, RandomIntRange( -2, 3 ), 0);
	
	start_origin = drop_origin + ( AnglesToForward( direction ) * dist );
	start_origin += ( (randomfloat(2) - 1)*pathRandomness, (randomfloat(2) - 1)*pathRandomness, 0 );
/#
	if ( GetDvarInt( "scr_noflyzones_debug", 0 ) ) 
	{
		if ( level.noFlyZones.size )
		{
			index = RandomIntRange( 0, level.noFlyZones.size );
			delta = drop_origin - level.noFlyZones[index].origin;
			delta = ( delta[0] + RandomInt(10), delta[1] + RandomInt(10), 0 );
			delta = VectorNormalize( delta );
			start_origin = drop_origin + ( delta * dist );
		}
	}
#/
	return start_origin;
}

function getHeliEnd( drop_origin, drop_direction )
{
	pathRandomness = 150;
	dist = -1 * GetDvarInt( "scr_supplydropOutgoingDistance", 15000);
	
	// have the heli do a sharp turn when leaving
	if ( RandomIntRange(0,2) == 0 )
		turn = RandomIntRange( 60,121);
	else
		turn = -1 * RandomIntRange( 60,121);
		
	direction = drop_direction + (0, turn, 0);
	
	end_origin = drop_origin + ( AnglesToForward( direction ) * dist );
	end_origin += ( (randomfloat(2) - 1)*pathRandomness  , (randomfloat(2) - 1)*pathRandomness  , 0 );

	return end_origin;
}

function addOffsetOntoPoint( point, direction, offset )
{
	angles = VectorToAngles( (direction[0], direction[1], 0) );
	
	offset_world = RotatePoint( offset, angles );
	
	return (point + offset_world);			
}

function supplyDropHeliStartPath_v2_setup( goal, goal_offset )
{
	goalPath = SpawnStruct();

	goalPath.start = helicopter::getValidRandomStartNode( goal ).origin;
	
	return goalPath;
}

function supplyDropHeliStartPath_v2_part2_local( goal, goalPath, goal_local_offset )
{
	direction = ( goal - goalPath.start );

	goalPath.path = [];
	goalPath.path[0] = addOffsetOntoPoint( goal, direction, goal_local_offset );
}

function supplyDropHeliStartPath_v2_part2( goal, goalPath, goal_world_offset )
{
	goalPath.path = [];
	goalPath.path[0] = goal + goal_world_offset;
}

function supplyDropHeliStartPath(goal, goal_offset)
{
	total_tries = 12;
	tries = 0;
	
	goalPath = SpawnStruct();
	drop_direction = getDropDirection();
	
	
	while ( tries < total_tries )
	{
		goalPath.start = getHeliStart( goal, drop_direction );
		
		goalPath.path = airsupport::getHeliPath( goalPath.start, goal );
		
		startNoFlyZones = airsupport::insideNoFlyZones( goalPath.start, false );
		
		if ( IsDefined( goalPath.path ) && startNoFlyZones.size == 0 )
		{
			if ( goalPath.path.size > 1 )
			{
				direction = ( goalPath.path[goalPath.path.size - 1] - goalPath.path[goalPath.path.size - 2] );
			}
			else
			{
				direction = ( goalPath.path[goalPath.path.size - 1] - goalPath.start );
			}
			goalPath.path[goalPath.path.size - 1] = addOffsetOntoPoint(goalPath.path[goalPath.path.size - 1], direction, goal_offset);
/#
			sphere( goalPath.path[goalPath.path.size - 1], 10, (0,0,1), 1, true, 10, 1000 );
#/
			return goalPath;
		}
		
		//Couldn't find a path that didn't cross a no fly zone picking random directions, so try the last tried direction plus 30 degrees
		drop_direction = getNextDropDirection( drop_direction, 30 );
		
		tries++;
	}

	//Couldn't find a valid direction, so just bring it in even if it will fly through something
	drop_direction = getDropDirection();
	goalPath.start = getHeliStart( goal, drop_direction );
	
	direction = ( goal - goalPath.start );
	goalPath.path = [];
	goalPath.path[0] = addOffsetOntoPoint( goal, direction, goal_offset );
	
	return goalPath;
}

function supplyDropHeliEndPath_v2( start )
{
	goalPath = SpawnStruct();

	goalPath.start = start;
	
	goal = helicopter::getValidRandomLeaveNode( start ).origin;
	
	goalPath.path = [];
	goalPath.path[0] = goal;
	
	return goalPath;
}

function supplyDropHeliEndPath(origin, drop_direction)
{
	total_tries = 5;
	tries = 0;
	
	goalPath = SpawnStruct();
	
	while ( tries < total_tries )
	{
		goal = getHeliEnd( origin, drop_direction );
		
		goalPath.path = airsupport::getHeliPath( origin, goal );
		
		if ( isdefined( goalPath.path ) )
		{
			return goalPath;
		}
		
		tries++;
	}
	
	// could not locate a clear path try the leave nodes
	leave_nodes = getentarray( "heli_leave", "targetname" ); 		
	foreach ( node in leave_nodes )
	{
		goalPath.path = airsupport::getHeliPath( origin, node.origin );
		
		if ( isdefined( goalPath.path ) )
		{
			return goalPath;
		}
	}
	
	// points where the helicopter leaves to
	goalPath.path = [];
	goalPath.path[0] = getHeliEnd( origin, drop_direction );
	
	return goalPath;
}

function incCrateKillstreakUsageStat(weapon, killstreak_id)
{
	if ( weapon == level.weaponNone )
		return;

	switch ( weapon.name )
	{
	case "turret_drop":
		self killstreaks::play_killstreak_start_dialog( "turret_drop", self.pers["team"], killstreak_id );
		break;
	case "tow_turret_drop":
		self killstreaks::play_killstreak_start_dialog( "tow_turret_drop", self.pers["team"], killstreak_id );
		break;
	case "supplydrop_marker":
	case "inventory_supplydrop_marker":
		self killstreaks::play_killstreak_start_dialog( "supply_drop", self.pers["team"], killstreak_id );
		level thread popups::DisplayKillstreakTeamMessageToAll( "supply_drop", self );
		self challenges::calledInCarePackage();
		self AddWeaponStat( GetWeapon( "supplydrop" ), "used", 1 );
		break;
	case "ai_tank_drop":
	case "inventory_ai_tank_drop":
		self killstreaks::play_killstreak_start_dialog( "ai_tank_drop", self.pers["team"], killstreak_id );
		level thread popups::DisplayKillstreakTeamMessageToAll( "ai_tank_drop", self );
		self AddWeaponStat( GetWeapon( "ai_tank_drop" ), "used", 1 );
		break;
	case "inventory_minigun_drop":
	case "minigun_drop":
		self killstreaks::play_killstreak_start_dialog( "minigun", self.pers["team"], killstreak_id );
		break;
	case "m32_drop":
	case "inventory_m32_drop":
		self killstreaks::play_killstreak_start_dialog( "m32", self.pers["team"], killstreak_id );
		break;
	case "combat_robot_drop":
		level thread popups::DisplayKillstreakTeamMessageToAll( "combat_robot", self );
		break;
		
	}
}

function MarkerCleanupThread( context )
{
	player = self;
	player util::waittill_any( "death", "disconnect", "joined_team", "joined_spectators", "cleanup_marker" );
	cleanup( context, player );
}

// gets the world drop point from which a payload is dropped from (also the payload's origin while attached to the chopper)
function GetChopperDropPoint( context ) // self = chopper
{
	chopper = self;
	return ( isdefined( context.dropTag ) ? chopper GetTagOrigin( context.dropTag ) + RotatePoint( (isdefined(context.dropTagOffset)?context.dropTagOffset:(0,0,0)), chopper.angles ) : chopper.origin );
}

function heliDeliverCrate( origin, weapon, owner, team, killstreak_id, package_contents_id, context )
{
	if ( owner EMP::EnemyEMPActive() && !owner hasperk("specialty_immuneemp") )
	{
		killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );
		self notify( "cleanup_marker" );
		return;
	}

/#	
	if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
	{
		level notify( "stop_heli_drop_valid_location_marked_cylinder" );
		level notify( "stop_heli_drop_valid_location_arrived_at_goal_cylinder" );
		level notify( "stop_heli_drop_valid_location_dropped_cylinder" );
		util::drawcylinder( origin, context.radius, 8000, 99999999, "stop_heli_drop_valid_location_marked_cylinder", ( 0.4, 0, 0.4 ), 0.8 );
	}
#/
	
	if ( !isdefined( context.marker ) )
		return;

	context.markerFXHandle = SpawnFx( level.killstreakCoreBundle.fxMarkedLocation, context.marker.origin + ( 0, 0, 5 ), ( 0, 0, 1 ), ( 1, 0, 0 ) );
	context.markerFXHandle.team = owner.team;
	TriggerFX( context.markerFXHandle );
	AddDropLocation( killstreak_id, context.marker.origin );
	killstreakBundle = ( isdefined( context.killstreakType ) ? level.killstreakbundle[ context.killstreakType ] : undefined );
	ricochetDistance = ( isdefined( killstreakBundle ) ? killstreakBundle.ksRicochetDistance : undefined );
	killstreaks::add_ricochet_protection( killstreak_id, owner, context.marker.origin, ricochetDistance );

	context.marker.team = owner.team;	
	context.marker entityheadicons::destroyEntityHeadIcons();
	
	// the offset for the icon can be controlled from the objective in APE
	context.marker entityheadicons::setEntityHeadIcon( owner.pers["team"], owner, undefined, context.objective );

	if( isdefined( weapon ) )
		incCrateKillstreakUsageStat( weapon, killstreak_id );

	rear_hatch_offset_local = GetDvarInt( "scr_supplydropOffset", 0);

	drop_origin = origin;
	drop_height = getDropHeight(drop_origin);
	drop_height += level.zOffsetCounter * 350;
	level.zOffsetCounter++;
	if( level.zOffsetCounter >= 5 )
		level.zOffsetCounter = 0;

	heli_drop_goal = ( drop_origin[0], drop_origin[1], drop_height ); //  + rear_hatch_offset_world;
	
/#
	sphere( heli_drop_goal, 10, (0,1,0), 1, true, 10, 1000 );
#/

	goalPath = undefined;
	
	if ( IsDefined( context.dropOffset ) )
	{
		goalPath = supplyDropHeliStartPath_v2_setup(heli_drop_goal, context.dropOffset );
		supplyDropHeliStartPath_v2_part2_local(heli_drop_goal, goalPath, context.dropOffset );
	}
	else
	{
		goalPath = supplyDropHeliStartPath_v2_setup(heli_drop_goal, (rear_hatch_offset_local, 0, 0 ));
		goal_path_setup_needs_finishing = true;
	}

	drop_direction = VectorToAngles( (heli_drop_goal[0], heli_drop_goal[1], 0) - (goalPath.start[0], goalPath.start[1], 0));
	
	if( isdefined( context.vehiclename ) )
		helicopterVehicleInfo = context.vehiclename;
	else
		helicopterVehicleInfo = level.vtolDropHelicopterVehicleInfo;
	
	chopper = spawn_helicopter(	owner, 
	                           	team, 
	                           	goalPath.start, 
	                           	drop_direction,
	                           	helicopterVehicleInfo, 	                           	
	                           	"",
	                           	killstreak_id, context );
	
	if ( goal_path_setup_needs_finishing === true )
	{
		goal_world_offset = chopper.origin - chopper GetChopperDropPoint( context );
		supplyDropHeliStartPath_v2_part2( heli_drop_goal, goalPath, goal_world_offset );
		goal_path_setup_needs_finishing = false;
	}

	// disable drop location wait until we have a more proper design-approved solution
	waitForOnlyOneDropLocation = false;

	while( level.dropLocations.size > 1 && waitForOnlyOneDropLocation) // wait for the older ongoing drops to finish
	{
		// remove old drop locations
		ArrayRemoveValue( level.dropLocations, undefined );

		wait_for_drop = false;
		foreach( id, dropLocation in level.dropLocations )	
		{ // check if older drop is still ongoing 
			if( id < killstreak_id )
			{
				wait_for_drop = true;
				break;
			}
		}
		if( wait_for_drop )
			wait 0.5;
		else
			break;
	}
	
	chopper.killstreakWeaponName = weapon.name;
	
	if( isdefined( context ) && isdefined( context.hasFlares ) ) 
	{
		chopper.numFlares = 3;
		chopper.flareOffset = ( 0, 0 ,0 );
		chopper thread helicopter::create_flare_ent( (0, 0, -50 ) );
	}	
	else
	{
		chopper.numFlares = 0;
	}
	
	killCamEnt = spawn( "script_model", chopper.origin + (0,0,800) );
	killCamEnt.angles = (100, chopper.angles[1], chopper.angles[2]);
	killCamEnt.startTime = gettime();
	killCamEnt linkTo( chopper );
	
	//Wait until the chopper is within the map bounds or within a certain distance of it's target before the SAM turret can target it
	if ( isplayer( owner ) )
	{
		Target_SetTurretAquire( self, false );
		chopper thread SAMTurretWatcher( drop_origin );
	}
	
	if ( !isdefined( chopper ) )
		return;

	if( isdefined( context ) && isdefined( context.prolog ) ) // we need callbacks for this
	{
		chopper [[context.prolog]]( context );
	}
	else
	{
		chopper thread heliDropCrate( level.killstreakWeapons[weapon], owner, rear_hatch_offset_local, killCamEnt, killstreak_id, package_contents_id, context );
		//chopper thread heliDropCrate( weapon.name, owner, rear_hatch_offset_local, killCamEnt, killstreak_id, package_contents_id, context );
	}
	
	chopper endon("death");
	
	chopper thread airsupport::followPath( goalPath.path, "drop_goal", true);

	chopper thread speedRegulator(heli_drop_goal);

	chopper waittill( "drop_goal" );
	
	if( isdefined( context ) && isdefined( context.epilog ) )
	{
		chopper [[context.epilog]]( context );
	}
	
/#
	PrintLn("Chopper Incoming Time: " + ( GetTime() - chopper.spawnTime ) );
#/

	// wait 0.1;
	
/#
	if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
	{		
		if ( isdefined( context.dropOffset ) )
		{
			chopper_drop_point = chopper.origin - RotatePoint( context.dropOffset, chopper.angles );
		}
		else
		{
			chopper_drop_point = chopper GetChopperDropPoint( context );
		}
		
		trace = GroundTrace( chopper_drop_point + ( 0, 0, -100 ), chopper_drop_point + ( 0, 0, -10000 ), false, undefined, false );
		debug_drop_location = trace["position"];
		
		util::drawcylinder( debug_drop_location, context.radius, 8000, 99999999, "stop_heli_drop_valid_location_arrived_at_goal_cylinder", ( 1.0, 0.6, 0.0 ), 0.9 );

		IPrintLn( "Goal notified at 2D distance: " + Distance2D( chopper_drop_point, heli_drop_goal ) );
	}
#/

	on_target = false;
	last_distance_from_goal_squared = ( (9999999.0) * (9999999.0) );
	continue_waiting = true;
	remaining_tries = 30; // fail-safe, about one and a half seconds
	while ( continue_waiting && remaining_tries > 0 )
	{
		if ( isdefined( context.dropOffset ) )
		{
			chopper_drop_point = chopper.origin - RotatePoint( context.dropOffset, chopper.angles );
		}
		else
		{
			chopper_drop_point = chopper GetChopperDropPoint( context );
		}

		current_distance_from_goal_squared = Distance2DSquared( chopper_drop_point, heli_drop_goal );
		continue_waiting = ( ( current_distance_from_goal_squared < last_distance_from_goal_squared ) && ( current_distance_from_goal_squared > ( (3.7) * (3.7) ) ) );
		last_distance_from_goal_squared = current_distance_from_goal_squared;

		/#
			if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
			{
		    	sphere( chopper_drop_point, 8, ( 1, 0, 0 ), 0.9, false, 20, 1 );
			}
		#/
	    
		if ( continue_waiting )
		{
			/#
				if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
				{
					IPrintLn( "--- 2D distance: " + Distance2D( chopper_drop_point, heli_drop_goal ) );
				}
			#/
				
	    	{wait(.05);};
		}
		
		remaining_tries--;
	}
	
/#
	if ( GetDvarInt( "scr_supply_drop_valid_location_debug", 0 ) )
	{
		IPrintLn( "Crate Dropped at 2D distance: " + Distance2D( chopper_drop_point, heli_drop_goal ) );
	}
#/

	chopper notify("drop_crate", chopper.origin, chopper.angles, chopper.owner);
	chopper.dropTime = GetTime();
	chopper playsound ("veh_supply_drop");
	
	wait ( 0.7 );
	
	if ( isdefined( level.killstreakWeapons[weapon] ) )
	{
		chopper killstreaks::play_pilot_dialog_on_owner( "waveStartFinal", level.killstreakWeapons[weapon], chopper.killstreak_id );
    }
	    
	supplydropSpeed = GetDvarInt( "scr_supplydropSpeedLeaving", 250 ); 
	supplydropAccel = GetDvarInt( "scr_supplydropAccelLeaving", 60 );
	chopper setspeed( supplydropSpeed, supplydropAccel );	

	/# chopper util::debug_slow_heli_speed(); #/

	goalPath = supplyDropHeliEndPath_v2( chopper.origin );
	
	chopper airsupport::followPath( goalPath.path, undefined, false );
/#
	PrintLn("Chopper Outgoing Time: " + ( GetTime() - chopper.dropTime ) );
#/
	chopper notify( "leaving" );
	chopper Delete();
	
}

function SAMTurretWatcher( destination )
{
	self endon( "leaving" );
	self endon( "helicopter_gone" );
	self endon( "death" );

	SAM_TURRET_AQUIRE_DIST = 1500;

	while(1)
	{
		if( Distance( destination, self.origin ) < SAM_TURRET_AQUIRE_DIST )
			break;

		if( self.origin[0] > level.spawnMins[0] && self.origin[0] < level.spawnMaxs[0] &&
			self.origin[1] > level.spawnMins[1] && self.origin[1] < level.spawnMaxs[1] )
			break;

		wait( 0.1 );
	}

	Target_SetTurretAquire( self, true );
}

function speedRegulator( goal )
{
	self endon("drop_goal");
	self endon("death");
	
	wait (3);

	supplydropSpeed = GetDvarInt( "scr_supplydropSpeed", 400);
	supplydropAccel = GetDvarInt( "scr_supplydropAccel", 60);
	self SetYawSpeed( 100, 60, 60 );
	self SetSpeed( supplydropSpeed, supplydropAccel );	

	/# self util::debug_slow_heli_speed(); #/

	wait (1);
	maxPitch = GetDvarInt( "scr_supplydropMaxPitch", 25);
	maxRoll = GetDvarInt( "scr_supplydropMaxRoll", 35 ); // 85);
	self SetMaxPitchRoll( maxPitch, maxRoll );	
}

function heliDropCrate( killstreak, originalOwner, offset, killCamEnt, killstreak_id, package_contents_id, context )
{
	helicopter = self;
	originalOwner endon ( "disconnect" );
	
	crate = crateSpawn( killstreak, killstreak_id, originalOwner, self.team, self.origin, self.angles );

    if ( killstreak == "inventory_supply_drop" || killstreak == "supply_drop" )
    {
    	crate LinkTo( helicopter, (isdefined(context.dropTag)?context.dropTag:"tag_origin"), (isdefined(context.dropTagOffset)?context.dropTagOffset:(0,0,0)) );
    	helicopter clientfield::set( "supplydrop_care_package_state", 1 );
    }
    else if ( killstreak == "inventory_ai_tank_drop" || killstreak == "ai_tank_drop" || killstreak == "ai_tank_marker" )
    {
    	crate LinkTo( helicopter, (isdefined(context.dropTag)?context.dropTag:"tag_origin"), (isdefined(context.dropTagOffset)?context.dropTagOffset:(0,0,0)) );
    	helicopter clientfield::set( "supplydrop_ai_tank_state", 1 );
    }

	team = self.team;
	
	helicopter waittill("drop_crate", origin, angles, chopperOwner );
	
	if ( isdefined( chopperOwner ) )
	{
		owner = chopperOwner;
		
		if ( owner != originalOwner ) // chopper has been hacked
		{
			crate killstreaks::configure_team( killstreak, owner );
			killstreaks::remove_ricochet_protection( killstreak_id, owner );
		}
	}
	else
	{
		owner = originalOwner;
	}
	
	if ( isdefined( self ) )
	{
		team = self.team;
	
		if ( killstreak == "inventory_supply_drop" || killstreak == "supply_drop" )
	    {
	    	helicopter clientfield::set( "supplydrop_care_package_state", 0 );
	    }
	    else if ( killstreak == "inventory_ai_tank_drop" || killstreak == "ai_tank_drop" )
	    {
	    	helicopter clientfield::set( "supplydrop_ai_tank_state", 0 );
	    }
	    
	    enemy = helicopter.owner battlechatter::get_closest_player_enemy( helicopter.origin, true );
		enemyRadius = battlechatter::mpdialog_value( "supplyDropRadius", 0 );
		
		if ( isdefined( enemy ) && Distance2DSquared( origin, enemy.origin ) < enemyRadius * enemyRadius )
		{
			enemy battlechatter::play_killstreak_threat( killstreak );
		}
	}
	
	if( team == owner.team ) // dont drop if the team changed //
	{
		//ideally we can not respawn a new crate, but unlink the old crate then zero out the velocity
		rear_hatch_offset_height = GetDvarInt( "scr_supplydropOffsetHeight", 200);
		rear_hatch_offset_world = RotatePoint( ( offset, 0, 0), angles );
		drop_origin = origin - (0,0,rear_hatch_offset_height) - rear_hatch_offset_world;
		thread dropCrate(drop_origin, angles, killstreak, owner, team, killCamEnt, killstreak_id, package_contents_id, crate, context );
	}
}

function heliDestroyed()
{
	self endon( "leaving" );
	self endon( "helicopter_gone" );
	self endon( "death" );
	
	while( true )
	{
		if( self.damageTaken > self.maxhealth )
			break;

		{wait(.05);};
	}

	if (! isdefined(self) )
		return;

	
	self SetSpeed( 25, 5 );
	self thread lbSpin( RandomIntRange(180, 220) );
	
	wait( RandomFloatRange( .5, 1.5 ) );
	
	self notify( "drop_crate", self.origin, self.angles, self.owner );
	
	lbExplode();
}

// crash explosion
function lbExplode()
{
	forward = ( self.origin + ( 0, 0, 1 ) ) - self.origin;
	playfx ( level.chopper_fx["explode"]["death"], self.origin, forward );
	
	// play heli explosion sound
	self playSound( level.heli_sound["crash"] );
	self notify ( "explode" );

	if ( isdefined( self.delete_after_destruction_wait_time ) )
	{
		self Hide();
		self WaitAndDelete( self.delete_after_destruction_wait_time  );
	}
	else
	{
		self delete();
	}
}


function lbSpin( speed )
{
	self endon( "explode" );
	
	// tail explosion that caused the spinning
	playfxontag( level.chopper_fx["explode"]["large"], self, "tail_rotor_jnt" );
	playfxontag( level.chopper_fx["fire"]["trail"]["large"], self, "tail_rotor_jnt" );
	
	self setyawspeed( speed, speed, speed );
	while ( isdefined( self ) )
	{
		self settargetyaw( self.angles[1]+(speed*0.9) );
		wait ( 1 );
	}
}

function refCountDecChopper( team, killstreak_id )
{
	self waittill("death");
	killstreakrules::killstreakStop( "supply_drop", team, killstreak_id );
	self notify( "cleanup_marker" );
}

/#
//Adds functionality so we can drop specific supply drops when we want for development purposes
function supply_drop_dev_gui()
{	
	//Init my dvar
	SetDvar("scr_supply_drop_gui", "");

	while(1)
	{
		wait(0.5);

		//Grab my dvar every frame
		devgui_string = GetDvarString( "scr_supply_drop_gui");

		level.dev_gui_supply_drop = devgui_string;
	}
}
#/
