boiii-scripts/mp/killstreaks/_supplydrop.gsc
2023-04-13 17:30:38 +02:00

3464 lines
113 KiB
Plaintext

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