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