1120 lines
27 KiB
Plaintext
1120 lines
27 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\_anim;
|
|
#include maps\_specialops;
|
|
#include maps\_hud_util;
|
|
#include maps\_vehicle;
|
|
#include maps\so_rooftop_contingency_code;
|
|
|
|
// Fend off three waves of enemy reinforcements.
|
|
CONST_regular_obj = &"SO_ROOFTOP_CONTINGENCY_OBJ_REGULAR";
|
|
// Fend off four waves of enemy reinforcements.
|
|
CONST_hardened_obj = &"SO_ROOFTOP_CONTINGENCY_OBJ_HARDENED";
|
|
// Fend off five waves of enemy reinforcements.
|
|
CONST_veteran_obj = &"SO_ROOFTOP_CONTINGENCY_OBJ_VETERAN";
|
|
|
|
main()
|
|
{
|
|
level.so_compass_zoom = "far";
|
|
|
|
// Optimization
|
|
SetSavedDvar( "sm_sunShadowScale", 0.5 );
|
|
SetSavedDvar( "r_lightGridEnableTweaks", 1 );
|
|
SetSavedDvar( "r_lightGridIntensity", 1.5 );
|
|
SetSavedDvar( "r_lightGridContrast", 0 );
|
|
|
|
// LevelVars from contigency.gsc
|
|
//level.ai_dont_glow_in_thermal = true;
|
|
level.min_btr_fighting_range = 400;
|
|
level.explosion_dist_sense = 1500;
|
|
level.default_goalradius = 7200;
|
|
level.goodFriendlyDistanceFromPlayerSquared = 250 * 250;
|
|
|
|
PreCacheItem( "remote_missile_snow" );
|
|
level.remote_missile_snow = true;
|
|
|
|
level.cosine[ "60" ] = Cos( 60 );
|
|
level.cosine[ "70" ] = Cos( 70 );
|
|
|
|
// UAV Settings
|
|
// min time for UAV reload
|
|
level.min_time_between_uav_launches = 20 * 1000;
|
|
level.visionThermalDefault = "contingency_thermal_inverted";
|
|
level.VISION_UAV = "contingency_thermal_inverted";
|
|
|
|
// Thermal FX Overrides
|
|
SetThermalBodyMaterial( "thermalbody_snowlevel" );
|
|
level.friendly_thermal_Reflector_Effect = LoadFX( "misc/thermal_tapereflect" );
|
|
|
|
PreCacheItem( "remote_missile_not_player" );
|
|
PreCacheModel( "com_computer_keyboard_obj" );
|
|
PrecacheNightvisionCodeAssets();
|
|
|
|
flag_init( "challenge_success" );
|
|
flag_init( "wave_wiped_out" );
|
|
|
|
flag_init( "waves_start" );
|
|
flag_init( "wave_1_started" );
|
|
flag_init( "wave_2_started" );
|
|
flag_init( "wave_3_started" );
|
|
flag_init( "wave_4_started" );
|
|
flag_init( "wave_5_started" );
|
|
|
|
flag_init( "uav_in_use" );
|
|
flag_init( "wave_spawned" );
|
|
flag_init( "start_countdown" );
|
|
|
|
// Press^3 [{+actionslot 4}] ^7to control the Predator Drone.
|
|
add_hint_string( "use_uav_4", &"HELLFIRE_USE_DRONE", maps\_remotemissile::should_break_use_drone );
|
|
add_hint_string( "use_uav_2", &"HELLFIRE_USE_DRONE_2", maps\_remotemissile::should_break_use_drone );
|
|
|
|
// delete certain non special ops entities
|
|
so_delete_all_by_type( ::type_vehicle_special, ::type_spawners, ::type_spawn_trigger );
|
|
|
|
default_start( ::start_so_rooftop );
|
|
add_start( "start_so_rooftop", ::start_so_rooftop );
|
|
|
|
precache_strings();
|
|
|
|
// init stuff
|
|
maps\_bm21_troops::main( "vehicle_bm21_mobile_cover_snow" );
|
|
maps\_uaz::main( "vehicle_uaz_winter_destructible", "uaz_physics" );
|
|
maps\_uaz::main( "vehicle_uaz_winter_destructible" );
|
|
maps\_ucav::main( "vehicle_ucav" );
|
|
maps\contingency_precache::main();
|
|
maps\createart\contingency_fog::main();
|
|
maps\contingency_fx::main();
|
|
maps\contingency_anim::main_anim();
|
|
|
|
maps\_load::main();
|
|
|
|
maps\_load::set_player_viewhand_model( "viewhands_player_arctic_wind" );
|
|
thread maps\contingency_amb::main();
|
|
maps\createart\contingency_art::main();
|
|
|
|
init_radio();
|
|
|
|
// finite amount of UAV
|
|
// level.remote_detonator_weapon = "remote_missile_detonator_finite";
|
|
level.remote_detonator_weapon = "remote_missile_detonator";
|
|
PreCacheItem( level.remote_detonator_weapon );
|
|
maps\_remotemissile::init();
|
|
maps\_remotemissile::init_radio_dialogue();
|
|
|
|
maps\_compass::setupMiniMap( "compass_map_contingency" );
|
|
|
|
vehicles = GetEntArray( "destructible_vehicle", "targetname" );
|
|
foreach ( vehicle in vehicles )
|
|
{
|
|
vehicle thread destructible_damage_monitor();
|
|
}
|
|
|
|
deadquotes = [];
|
|
deadquotes[ deadquotes.size ] = "@DEADQUOTE_SO_CLAYMORE_POINT_ENEMY";
|
|
deadquotes[ deadquotes.size ] = "@DEADQUOTE_SO_CLAYMORE_ENEMIES_SHOOT";
|
|
so_include_deadquote_array( deadquotes );
|
|
level.so_deadquotes_chance = 0.33;
|
|
|
|
/#
|
|
// thread player_input();
|
|
#/
|
|
}
|
|
|
|
init_radio()
|
|
{
|
|
// Use the weapon caches and set up you claymores if you got any left. Defensive positions, let's go.
|
|
level.scr_radio[ "so_intro" ] = "so_roof_cont_def_pos";
|
|
|
|
// Take control of the predator drone.
|
|
level.scr_radio[ "so_pickup_uav_reminder" ] = "so_roof_cont_mct_control_rig";
|
|
}
|
|
|
|
precache_strings()
|
|
{
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_HOSTILES_COUNT" );
|
|
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_UAZ_COUNT_SINGLE" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_UAZ_COUNT" );
|
|
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_BM21_COUNT" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_BM21_COUNT_SINGLE" );
|
|
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_DRONE_PICKUP" );
|
|
|
|
PrecacheString( CONST_regular_obj );
|
|
PrecacheString( CONST_hardened_obj );
|
|
PrecacheString( CONST_veteran_obj );
|
|
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_WAVE_SECOND_STARTS" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_WAVE_THIRD_STARTS" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_WAVE_FOURTH_STARTS" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_WAVE_FINAL_STARTS" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_WAVE_STARTS" );
|
|
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_CLAYMORE_KILLS" );
|
|
PrecacheString( &"SO_ROOFTOP_CONTINGENCY_HELLFIRE_KILLS" );
|
|
}
|
|
|
|
type_vehicle_special()
|
|
{
|
|
// keep all collmaps
|
|
if ( IsDefined( self.code_classname ) && self.code_classname == "script_vehicle_collmap" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
special_case = !( self transform_vehicle_by_targetname( "base_troop_transport2", "truck_1", "truck_1_guys" ) );
|
|
special_case2 = !( self transform_vehicle_by_targetname( "base_troop_transport1", "truck_2", "truck_2_guys" ) );
|
|
special_case3 = !( self transform_vehicle_by_targetname( "base_truck1", "jeep_1", "jeep_1_guys" ) );
|
|
special_case4 = !( self transform_vehicle_by_targetname( "second_uav", "second_uav", "uav_path" ) );
|
|
|
|
original_case = self type_vehicle();
|
|
|
|
test = 0;
|
|
if ( original_case )
|
|
{
|
|
test = 0;
|
|
}
|
|
|
|
special_result = special_case && special_case2 && special_case3 && special_case4;
|
|
result = special_result && original_case;
|
|
|
|
/#
|
|
if ( !special_result )
|
|
{
|
|
thread so_debug_print( "vehicle[" + self.targetname + "] saved", 5 );
|
|
}
|
|
#/
|
|
|
|
return result;
|
|
}
|
|
|
|
transform_vehicle_by_targetname( vehicle_name, targetname_string, target_string )
|
|
{
|
|
result = IsDefined( self.targetname ) && self.targetname == vehicle_name;
|
|
|
|
if ( result )
|
|
{
|
|
self.targetname = targetname_string;
|
|
self.target = target_string;
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
init_wave( wave_num, count )
|
|
{
|
|
if ( !IsDefined( level.wave_spawn_structs ) )
|
|
{
|
|
level.wave_spawn_structs = [];
|
|
}
|
|
|
|
temp = SpawnStruct();
|
|
temp.hostile_count = count;
|
|
temp.vehicles = [];
|
|
|
|
level.wave_spawn_structs[ wave_num ] = temp;
|
|
}
|
|
|
|
add_wave_vehicle( wave_num, targetname, type, alt_node, delay )
|
|
{
|
|
if ( !IsDefined( level.wave_spawn_structs ) )
|
|
{
|
|
level.wave_spawn_structs = [];
|
|
}
|
|
|
|
if ( !IsDefined( level.wave_spawn_structs[ wave_num ].vehicles ) )
|
|
{
|
|
level.wave_spawn_structs[ wave_num ].vehicles = [];
|
|
}
|
|
|
|
temp = SpawnStruct();
|
|
temp.targetname = targetname;
|
|
temp.ent = GetEnt( targetname, "targetname" );
|
|
temp.type = type;
|
|
|
|
temp.delay = undefined;
|
|
if ( IsDefined( delay ) )
|
|
{
|
|
temp.delay = delay;
|
|
}
|
|
|
|
temp.alt_node = undefined;
|
|
if ( IsDefined( alt_node ) )
|
|
{
|
|
temp.alt_node = alt_node;
|
|
}
|
|
|
|
size = level.wave_spawn_structs[ wave_num ].vehicles.size;
|
|
level.wave_spawn_structs[ wave_num ].vehicles[ size ] = temp;
|
|
}
|
|
|
|
so_setup_regular()
|
|
{
|
|
// Wave 1
|
|
init_wave( 1, 15 );
|
|
|
|
// Wave 2
|
|
init_wave( 2, 17 );
|
|
add_wave_vehicle( 2, "jeep_1", "uaz" );
|
|
|
|
// Wave 3
|
|
init_wave( 3, 19 );
|
|
add_wave_vehicle( 3, "truck_1", "bm21" );
|
|
|
|
|
|
level.challenge_objective = CONST_regular_obj;
|
|
level.new_hostile_accuracy = 1;
|
|
// level.hostile_wave_size = 17;
|
|
// level.hostile_waves = 3;
|
|
level.wiped_out_requirement = 2;
|
|
level.wave_delay = 10;
|
|
level.allowed_uav_ammo = 5;
|
|
level.uav_spawn_delay = 15;
|
|
level.UAV_pickup_respawn = false;
|
|
}
|
|
|
|
so_setup_hardened()
|
|
{
|
|
// Wave 1
|
|
init_wave( 1, 15 );
|
|
|
|
// Wave 2
|
|
init_wave( 2, 16 );
|
|
add_wave_vehicle( 2, "jeep_1", "uaz" );
|
|
|
|
// Wave 3
|
|
init_wave( 3, 17 );
|
|
add_wave_vehicle( 3, "truck_1", "bm21" );
|
|
|
|
// Wave 4
|
|
init_wave( 4, 18 );
|
|
add_wave_vehicle( 4, "jeep_1", "uaz", GetVehicleNode( "jeep_1_guys_alt", "targetname" ) );
|
|
|
|
level.challenge_objective = CONST_hardened_obj;
|
|
level.new_hostile_accuracy = 1;
|
|
level.wiped_out_requirement = 3;
|
|
level.wave_delay = 10;
|
|
level.allowed_uav_ammo = 4;
|
|
level.uav_spawn_delay = 15;
|
|
level.UAV_pickup_respawn = false;
|
|
}
|
|
|
|
so_setup_veteran()
|
|
{
|
|
// Wave 1
|
|
init_wave( 1, 15 );
|
|
|
|
// Wave 2
|
|
init_wave( 2, 16 );
|
|
add_wave_vehicle( 2, "jeep_1", "uaz" );
|
|
|
|
// Wave 3
|
|
init_wave( 3, 17 );
|
|
add_wave_vehicle( 3, "truck_1", "bm21" );
|
|
|
|
// Wave 4
|
|
init_wave( 4, 20 );
|
|
add_wave_vehicle( 4, "jeep_1", "uaz", GetVehicleNode( "jeep_1_guys_alt", "targetname" ) );
|
|
|
|
// Wave 5
|
|
init_wave( 5, 20 );
|
|
add_wave_vehicle( 5, "truck_2", "bm21" );
|
|
add_wave_vehicle( 5, "jeep_1", "uaz", GetVehicleNode( "jeep_1_guys_alt2", "targetname" ) );
|
|
|
|
level.challenge_objective = CONST_veteran_obj;
|
|
level.new_hostile_accuracy = 1;
|
|
level.wiped_out_requirement = 3;
|
|
level.wave_delay = 10;
|
|
level.allowed_uav_ammo = 3;
|
|
level.uav_spawn_delay = 20;
|
|
level.UAV_pickup_respawn = false;
|
|
}
|
|
|
|
so_rooftop_init()
|
|
{
|
|
level.so_uav_picked_up = false;
|
|
level.hostile_count = 0;
|
|
level.wave_spawn_structs = [];
|
|
|
|
Assert( IsDefined( level.gameskill ) );
|
|
switch( level.gameSkill )
|
|
{
|
|
case 0: // Easy
|
|
case 1: so_setup_Regular(); break; // Regular
|
|
case 2: so_setup_hardened();break; // Hardened
|
|
case 3: so_setup_veteran(); break; // Veteran
|
|
}
|
|
|
|
// wave move in delay time multiplier depending on if player is on roof or not
|
|
level.roof_factor = 1;
|
|
|
|
// setup all attack line script origins
|
|
all_attack_lines = GetEntArray( "attack_line", "targetname" );
|
|
foreach ( attack_line in all_attack_lines )
|
|
{
|
|
attack_line.times_used = 0;
|
|
}
|
|
|
|
spawner_setup();
|
|
spawn_functions();
|
|
|
|
level.custom_eog_no_kills = true;
|
|
level.custom_eog_no_partner = true;
|
|
level.eog_summary_callback = ::custom_eog_summary;
|
|
thread enable_escape_warning();
|
|
thread enable_escape_failure();
|
|
thread fade_challenge_out( "challenge_success" );
|
|
|
|
thread player_on_roof_think();
|
|
// thread vehicles_think();
|
|
// thread challenge_complete();
|
|
thread wave_wiped_out();
|
|
thread wave_spawn_think();
|
|
thread uav_pickup_setup();
|
|
thread uav();
|
|
|
|
// Add the players to the remotemissled targets, but as friendly.
|
|
foreach ( player in level.players )
|
|
{
|
|
player.claymore_kills = 0;
|
|
player.hellfire_kills = 0;
|
|
player thread maps\_remotemissile::setup_remote_missile_target();
|
|
player thread threat_priority_thread();
|
|
}
|
|
|
|
// HOLD HERE TILL PLAYERS READY IN ONLINE COOP
|
|
so_wait_for_players_ready();
|
|
|
|
Objective_Add( 1, "current", level.challenge_objective );
|
|
}
|
|
|
|
spawner_setup()
|
|
{
|
|
// Setup the Spawners
|
|
wave_size = get_wave_count();
|
|
for ( i = 1; i < wave_size + 1; i++ )
|
|
{
|
|
new_array = [];
|
|
foreach ( member in GetEntArray( "wave_guys", "script_noteworthy" ) )
|
|
{
|
|
new_array[ new_array.size ] = member;
|
|
if ( new_array.size >= get_wave_ai_count( i ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
level.wave_spawn_structs[ i ].spawners = array_randomize( new_array );
|
|
}
|
|
|
|
// We don't want the failsafe spawners to be included in the Setting up of spawners.
|
|
foreach ( spawner in GetEntArray( "failsafe_spawners", "targetname" ) )
|
|
{
|
|
spawner.script_noteworthy = "wave_guys";
|
|
}
|
|
}
|
|
|
|
spawn_functions()
|
|
{
|
|
level.current_wave = 1;
|
|
|
|
add_global_spawn_function( "axis", ::so_rooftop_ai_postspawn );
|
|
|
|
array_spawn_function_noteworthy( "wave_guys", ::wave_closing_in );
|
|
array_spawn_function_targetname( "truck_1_guys", ::wave_closing_in, "attack_line_med" );
|
|
array_spawn_function_targetname( "truck_2_guys", ::wave_closing_in, "attack_line_med" );
|
|
array_spawn_function_targetname( "jeep_1_guys", ::wave_closing_in, "attack_line_close" );
|
|
array_spawn_function_targetname( "jeep_1_guys_alt", ::wave_closing_in, "attack_line_close" );
|
|
array_spawn_function_targetname( "jeep_1_guys_alt2", ::wave_closing_in, "attack_line_med" );
|
|
|
|
GetEnt( "truck_1", "targetname" ) add_spawn_function( ::setup_base_vehicles );
|
|
GetEnt( "truck_2", "targetname" ) add_spawn_function( ::setup_base_vehicles );
|
|
GetEnt( "jeep_1", "targetname" ) add_spawn_function( ::setup_base_vehicles );
|
|
}
|
|
|
|
// AI -----------------------------------------------------
|
|
|
|
so_rooftop_ai_postspawn()
|
|
{
|
|
level.hostile_count++;
|
|
|
|
self thread hostile_nerf();
|
|
self thread set_wave_id();
|
|
self thread maps\contingency::setup_remote_missile_target_guy();
|
|
self thread death_think();
|
|
}
|
|
|
|
death_think()
|
|
{
|
|
self waittill_any( "death", "pain_death" );
|
|
level.hostile_count--;
|
|
|
|
if ( !IsDefined( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
damage_weapon = undefined;
|
|
attacker = undefined;
|
|
|
|
if ( IsDefined( self.damageweapon ) )
|
|
{
|
|
damage_weapon = self.damageweapon;
|
|
}
|
|
|
|
if ( IsDefined( self.lastattacker ) )
|
|
{
|
|
attacker = self.lastattacker;
|
|
}
|
|
|
|
// Assume destructible
|
|
destructible_killed = false;
|
|
if ( IsDefined( attacker.damageOwner ) )
|
|
{
|
|
if ( IsDefined( attacker.hellfired ) && attacker.hellfired )
|
|
{
|
|
damage_weapon = "remote_missile_snow";
|
|
}
|
|
else if ( IsDefined( attacker.claymored ) && attacker.claymored )
|
|
{
|
|
damage_weapon = "claymore";
|
|
}
|
|
|
|
attacker = attacker.damageOwner;
|
|
|
|
destructible_killed = true;
|
|
}
|
|
|
|
if ( !IsDefined( attacker ) || !IsPlayer( attacker ) || !IsDefined( damage_weapon ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
// To compensate for negative kills we need to make sure we count these guys as standard kills
|
|
// deathFunctions() does not check if the player caused the destructible to blow up and kill the AI.
|
|
if ( destructible_killed )
|
|
{
|
|
attacker.stats[ "kills" ]++;
|
|
}
|
|
|
|
// Claymore check
|
|
if ( damage_weapon == "claymore" )
|
|
{
|
|
attacker.claymore_kills++;
|
|
}
|
|
|
|
// Hellfire check
|
|
if ( damage_weapon == "remote_missile_snow" )
|
|
{
|
|
attacker.hellfire_kills++;
|
|
}
|
|
}
|
|
|
|
destructible_damage_monitor()
|
|
{
|
|
self endon( "exploded" );
|
|
|
|
while ( 1 )
|
|
{
|
|
self waittill( "damage", dmg, attacker, dir, point, mod, model, tagname, partname, dflags, weapon );
|
|
|
|
if ( IsPlayer( attacker ) )
|
|
{
|
|
if ( IsDefined( weapon ) )
|
|
{
|
|
if ( weapon == "remote_missile_snow" )
|
|
{
|
|
self.hellfired = true;
|
|
}
|
|
else if ( weapon == "claymore" )
|
|
{
|
|
self.claymored = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
hostile_nerf()
|
|
{
|
|
self.baseaccuracy = level.new_hostile_accuracy;
|
|
}
|
|
|
|
set_wave_id()
|
|
{
|
|
if ( !isdefined( level.wave_spawn_structs[ level.current_wave ].wave_members ) )
|
|
{
|
|
level.wave_spawn_structs[ level.current_wave ].wave_members = [];
|
|
}
|
|
|
|
members_size = level.wave_spawn_structs[ level.current_wave ].wave_members.size;
|
|
level.wave_spawn_structs[ level.current_wave ].wave_members[ members_size ] = self;
|
|
}
|
|
|
|
getaiarray_by_wave_id()
|
|
{
|
|
Assert( IsDefined( level.current_wave ) );
|
|
Assert( IsDefined( level.wave_spawn_structs ) );
|
|
|
|
members = level.wave_spawn_structs[ level.current_wave1 ].wave_members;
|
|
|
|
Assert( IsDefined( members ) );
|
|
return members;
|
|
}
|
|
|
|
custom_eog_summary()
|
|
{
|
|
foreach ( player in level.players )
|
|
{
|
|
standard_kills = player.stats[ "kills" ];
|
|
standard_kills -= player.hellfire_kills;
|
|
standard_kills -= player.claymore_kills;
|
|
|
|
player add_custom_eog_summary_line( "@SO_ROOFTOP_CONTINGENCY_STANDARD_KILLS", standard_kills );
|
|
player add_custom_eog_summary_line( "@SO_ROOFTOP_CONTINGENCY_HELLFIRE_KILLS", player.hellfire_kills );
|
|
player add_custom_eog_summary_line( "@SO_ROOFTOP_CONTINGENCY_CLAYMORE_KILLS", player.claymore_kills );
|
|
if ( is_coop_online() )
|
|
player maps\_endmission::use_custom_eog_default_kills( get_other_player( player ) );
|
|
}
|
|
}
|
|
|
|
challenge_complete()
|
|
{
|
|
flag_set( "challenge_success" );
|
|
}
|
|
|
|
start_so_rooftop()
|
|
{
|
|
so_rooftop_init();
|
|
|
|
thread fade_challenge_in( undefined, false );
|
|
thread so_intro_dialogue();
|
|
|
|
/#
|
|
test_vehicles();
|
|
#/
|
|
|
|
wait so_standard_wait();
|
|
|
|
enable_challenge_timer( "waves_start", "challenge_success" );
|
|
thread enable_countdown_timer( level.wave_delay );
|
|
flag_set( "start_countdown" );
|
|
|
|
wait( 2 );
|
|
hud_wave_splash( 1, level.wave_delay - 2 );
|
|
flag_set( "waves_start" );
|
|
}
|
|
|
|
so_intro_dialogue()
|
|
{
|
|
wait( 1 );
|
|
radio_dialogue( "so_intro" );
|
|
}
|
|
|
|
// Waves --------------------------------------------------
|
|
|
|
wave_wiped_out()
|
|
{
|
|
level endon( "special_op_terminated" );
|
|
|
|
flag_wait( "waves_start" );
|
|
while ( 1 )
|
|
{
|
|
flag_wait( "wave_spawned" );
|
|
|
|
population = 0;
|
|
ai_wave = GetAIArray( "axis" );
|
|
foreach ( guy in ai_wave )
|
|
{
|
|
if ( IsAlive( guy ) )
|
|
{
|
|
population++;
|
|
}
|
|
}
|
|
|
|
if ( population <= level.wiped_out_requirement )
|
|
{
|
|
// send the remaining guys back to med attack line
|
|
array_thread( ai_wave, ::wave_closing_in, "attack_line_med" );
|
|
//foreach ( guy in ai_wave ) { guy ignore_all_till_goal(); }
|
|
|
|
// Wait for everyone to be dead before starting next wave.
|
|
// enemies = GetAIArray( "bad_guys" );
|
|
while ( level.hostile_count > 0 )
|
|
{
|
|
wait( 0.5 );
|
|
// enemies = GetAIArray( "bad_guys" );
|
|
}
|
|
|
|
//level.wave_spawn_structs[level.current_wave].wave_members = undefined;
|
|
/# so_debug_print( "wave [" + level.current_wave + "] wiped out" ); #/
|
|
|
|
flag_clear( "wave_spawned" );
|
|
flag_set( "wave_wiped_out" );
|
|
|
|
// sounds
|
|
level.player PlaySound( "arcademode_kill_streak_won" );
|
|
}
|
|
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
ignore_all_till_goal()
|
|
{
|
|
self endon( "death" );
|
|
self.pathenemyfightdist = 32;
|
|
self waittill( "goal" );
|
|
self.pathenemyfightdist = 192;
|
|
}
|
|
|
|
wave_spawn_think()
|
|
{
|
|
level endon( "special_op_terminated" );
|
|
|
|
array_thread( level.players, ::hud_hostile_count );
|
|
array_thread( level.players, ::hud_wave_num );
|
|
|
|
flag_wait( "waves_start" );
|
|
for ( i = 1; i < level.wave_spawn_structs.size + 1; i++ )
|
|
{
|
|
flag_clear( "wave_wiped_out" );
|
|
level.current_wave = i;
|
|
|
|
// Spawn in AI
|
|
// We may want to think of a better mechanism of spawning AI in.
|
|
// Rather than all at once, do something of a short period of time
|
|
// If we do, we'll have to address how the Hostiles counter logic is done.
|
|
spawn_failed_count = 0;
|
|
foreach ( spawner in level.wave_spawn_structs[ i ].spawners )
|
|
{
|
|
spawner set_count( 1 );
|
|
guy = spawner spawn_ai();
|
|
|
|
if ( !IsDefined( guy ) )
|
|
{
|
|
spawn_failed_count++;
|
|
so_debug_print( "wave_spawn_think() -- SPAWN FAILED COUNT = " + spawn_failed_count );
|
|
}
|
|
}
|
|
|
|
// If an AI does not spawn, try again until one does.
|
|
failsafe_spawners = GetEntArray( "failsafe_spawners", "targetname" );
|
|
for ( q = 0; q < spawn_failed_count; q++ )
|
|
{
|
|
spawner = failsafe_spawners[ RandomInt( failsafe_spawners.size ) ];
|
|
spawner set_count( 1 );
|
|
guy = spawner spawn_ai();
|
|
|
|
if ( !IsDefined( guy ) )
|
|
{
|
|
q--;
|
|
}
|
|
}
|
|
|
|
so_debug_print( "wave_spawn_think(), Current wave = " + level.current_wave );
|
|
|
|
// Spawn in Vehicles
|
|
vehicles = get_wave_vehicles( level.current_wave );
|
|
foreach ( vehicle in vehicles )
|
|
{
|
|
thread spawn_vehicle_and_go( vehicle );
|
|
}
|
|
|
|
flag_set( "wave_" + ( level.current_wave ) + "_started" );
|
|
level notify( "new_wave_started" );
|
|
|
|
if ( IsDefined( level.so_uav_player ) )
|
|
{
|
|
// Also re-enable if reloading... Just so the player can expect to use it right away.
|
|
level notify( "stop_uav_reload" );
|
|
flag_clear( "uav_reloading" );
|
|
|
|
level.so_uav_player maps\_remotemissile::enable_uav( level.so_uav_picked_up, level.remote_detonator_weapon );
|
|
level.so_uav_player thread maps\_remotemissile::remotemissile_radio_reminder();
|
|
}
|
|
|
|
wait( 1 );// give some time for all AI to spawn into map before monitoring population
|
|
flag_set( "wave_spawned" );
|
|
|
|
/# so_debug_print( "wave [" + ( level.current_wave + 1 ) + "] spawn complete" ); #/
|
|
|
|
flag_wait( "wave_wiped_out" );
|
|
|
|
if ( i == level.wave_spawn_structs.size )
|
|
{
|
|
thread challenge_complete();
|
|
return;
|
|
}
|
|
|
|
if ( IsDefined( level.so_uav_player ) )
|
|
{
|
|
level.so_uav_player maps\_remotemissile::disable_uav( level.so_uav_picked_up, true );
|
|
}
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
player notify( "force_out_of_uav" );
|
|
}
|
|
|
|
hud_new_wave();
|
|
}
|
|
}
|
|
|
|
wave_closing_in( start_with )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !isalive( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self notify( "wave_closing_in_called" );
|
|
self endon( "wave_closing_in_called" );
|
|
|
|
// vehicle riders are to wait till they have unloaded to continue this spawn function
|
|
if ( IsDefined( self.script_noteworthy ) && self.script_noteworthy == "vehicle_guys" )
|
|
{
|
|
self waittill( "jumpedout" );
|
|
}
|
|
|
|
far_delay = 0;
|
|
med_delay = RandomFloatRange( 15, 20 );
|
|
close_delay = RandomFloatRange( 30, 35 );
|
|
player_delay = RandomFloatRange( 15, 25 );
|
|
factor = ( 100 - ( ( level.current_wave - 1 ) * 10 ) ) / 100 * level.roof_factor;
|
|
|
|
if ( self.classname != "actor_enemy_arctic_SNIPER" )
|
|
{
|
|
factor *= 0.75;
|
|
}
|
|
|
|
// shotgun dudes rushes towards player
|
|
if ( self.classname == "actor_enemy_arctic_SHOTGUN" )
|
|
{
|
|
factor *= 0.25;
|
|
}
|
|
|
|
if ( IsDefined( start_with ) && start_with != "attack_line_far" )
|
|
{
|
|
AssertEx( start_with == "attack_line_med" || start_with == "attack_line_close", "wave_closing_in() is misused, " + start_with + " attack line does not exist." );
|
|
|
|
if ( start_with == "attack_line_med" )
|
|
{
|
|
self wave_closing_in_at_line( factor * med_delay, "attack_line_med" );
|
|
}
|
|
|
|
self wave_closing_in_at_line( factor * close_delay, "attack_line_close" );
|
|
}
|
|
else
|
|
{
|
|
self wave_closing_in_at_line( factor * far_delay, "attack_line_far" );
|
|
self wave_closing_in_at_line( factor * med_delay, "attack_line_med" );
|
|
self wave_closing_in_at_line( factor * close_delay, "attack_line_close" );
|
|
}
|
|
|
|
self wave_goto_player( factor * player_delay );
|
|
}
|
|
|
|
threat_priority_thread()
|
|
{
|
|
self endon( "death" );
|
|
|
|
roof_point = getstruct( "so_roof_point", "targetname" );
|
|
|
|
while ( 1 )
|
|
{
|
|
weight = 0;
|
|
|
|
dist = Distance( roof_point.origin, self.origin );
|
|
|
|
// See if the player is away from the roof
|
|
// Then add more weight to him
|
|
if ( dist > 400 )
|
|
{
|
|
weight = dist / 2000;
|
|
}
|
|
|
|
self.so_priority = weight;
|
|
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
get_higher_priority_player( min_weight )
|
|
{
|
|
combined_weight = 1; // Start with 1 to give some more randomness
|
|
|
|
if ( min_weight < 0 )
|
|
{
|
|
combined_weight = 0;
|
|
}
|
|
|
|
player_array = [];
|
|
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( player.so_priority > min_weight )
|
|
{
|
|
player_array[ player_array.size ] = player;
|
|
priority = player.so_priority;
|
|
|
|
dist2d = Distance2D( self.origin, player.origin );
|
|
priority += 1 - ( dist2d / 800 );
|
|
|
|
if ( priority < 0 )
|
|
{
|
|
priority = 0;
|
|
}
|
|
|
|
combined_weight += priority;
|
|
}
|
|
}
|
|
|
|
target_ent = undefined;
|
|
if ( player_array.size > 0 )
|
|
{
|
|
random_weight = RandomFloat( combined_weight );
|
|
curr_weight = 0;
|
|
|
|
foreach( player in player_array )
|
|
{
|
|
curr_weight += player.so_priority;
|
|
|
|
if ( random_weight < curr_weight )
|
|
{
|
|
target_ent = player;
|
|
}
|
|
}
|
|
}
|
|
|
|
return target_ent;
|
|
}
|
|
|
|
wave_goto_player( delay )
|
|
{
|
|
self endon( "death" );
|
|
|
|
wait( delay );
|
|
self thread seek_player();
|
|
}
|
|
|
|
seek_player( target_ent )
|
|
{
|
|
self endon( "death" );
|
|
level endon( "special_op_terminated" );
|
|
|
|
if ( !IsDefined( target_ent ) )
|
|
{
|
|
target_ent = get_higher_priority_player( -1 );
|
|
}
|
|
|
|
self.goalradius = 2000;
|
|
|
|
while ( 1 )
|
|
{
|
|
goalradius = self.goalradius;
|
|
if ( goalradius > 300 )
|
|
{
|
|
goalradius -= RandomIntRange( 200, 600 );
|
|
}
|
|
|
|
if ( goalradius < 300 )
|
|
{
|
|
goalradius = RandomIntRange( 250, 500 );
|
|
}
|
|
|
|
self.goalradius = goalradius;
|
|
|
|
if ( !IsDefined( target_ent ) )
|
|
{
|
|
target_ent = level.player;
|
|
|
|
if ( level.players.size > 1 )
|
|
{
|
|
if ( cointoss() )
|
|
{
|
|
target_ent = level.player2;
|
|
}
|
|
}
|
|
}
|
|
|
|
// Incase the player is downed already, go seek out the other player
|
|
if ( IsDefined( target_ent.coop_downed ) && target_ent.coop_downed )
|
|
{
|
|
count = 0;
|
|
foreach ( player in level.players )
|
|
{
|
|
if ( IsDefined( player.coop_downed ) && player.coop_downed )
|
|
{
|
|
count++;
|
|
}
|
|
}
|
|
|
|
// Get out if both are downed
|
|
if ( count == level.players.size )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if ( level.player == target_ent )
|
|
{
|
|
self.goalradius = 800;
|
|
self thread seek_player( level.player2 );
|
|
return;
|
|
}
|
|
else if ( IsDefined( level.player2 ) && level.player2 == target_ent )
|
|
{
|
|
self.goalradius = 800;
|
|
self thread seek_player( level.player );
|
|
return;
|
|
}
|
|
}
|
|
|
|
self SetGoalEntity( target_ent );
|
|
self waittill( "goal" );
|
|
|
|
wait( RandomFloatRange( 5, 9 ) );
|
|
}
|
|
}
|
|
|
|
player_on_roof_think()
|
|
{
|
|
// if player on roof, challenge is easier
|
|
while ( 1 )
|
|
{
|
|
level waittill( "player_on_roof" );
|
|
|
|
if ( flag( "player_on_roof" ) )
|
|
{
|
|
foreach ( guy in GetAIArray( "axis" ) )
|
|
{
|
|
// guy set_goal_radius( 512 );
|
|
level.roof_factor = 1;
|
|
guy hostile_nerf();
|
|
}
|
|
set_grenade_frequency( 1 );
|
|
/# so_debug_print( "player on roof" ); #/
|
|
}
|
|
else
|
|
{
|
|
foreach ( guy in GetAIArray( "axis" ) )
|
|
{
|
|
// guy set_goal_radius( 220 );
|
|
level.roof_factor = 0.7;// waves move in faster when player on ground
|
|
guy.baseaccuracy = 2;
|
|
}
|
|
set_grenade_frequency( 0.5 );
|
|
/# so_debug_print( "player off roof" ); #/
|
|
}
|
|
|
|
wait( 2 );
|
|
}
|
|
}
|
|
|
|
set_grenade_frequency( fraction )
|
|
{
|
|
if ( !isdefined( fraction ) )
|
|
fraction = 1;
|
|
|
|
maps\_gameskill::add_fractional_data_point( "playerGrenadeBaseTime", 0.25, 40000 * fraction );// original easy
|
|
maps\_gameskill::add_fractional_data_point( "playerGrenadeBaseTime", 0.75, 35000 * fraction );// original normal
|
|
level.difficultySettings[ "playerGrenadeBaseTime" ][ "hardened" ] = 25000 * fraction;
|
|
level.difficultySettings[ "playerGrenadeBaseTime" ][ "veteran" ] = 25000 * fraction;
|
|
|
|
maps\_gameskill::updateGameSkill();
|
|
maps\_gameskill::updateAllDifficulty();
|
|
}
|
|
|
|
wave_closing_in_at_line( delay, attack_line )
|
|
{
|
|
self endon( "death" );
|
|
wait delay;
|
|
|
|
// Get higher priority
|
|
if ( attack_line == "attack_line_far" )
|
|
{
|
|
min_weight = 0.75;
|
|
}
|
|
else if ( attack_line == "attack_line_med" )
|
|
{
|
|
min_weight = 0.5;
|
|
}
|
|
else
|
|
{
|
|
min_weight = 0.25;
|
|
}
|
|
|
|
target_ent = get_higher_priority_player( min_weight );
|
|
|
|
if ( IsDefined( target_ent ) )
|
|
{
|
|
self seek_player( target_ent );
|
|
}
|
|
else
|
|
{
|
|
self set_attack_line( attack_line );
|
|
self waittill( "goal" );
|
|
}
|
|
}
|
|
|
|
set_attack_line( line_position )
|
|
{
|
|
attack_line = GetEntArray( line_position, "script_noteworthy" );
|
|
AssertEx( IsDefined( attack_line ), "There is no " + line_position + " attack line in level." );
|
|
|
|
// use all attack lines evenly
|
|
to_ent = attack_line[ RandomInt( attack_line.size ) ];
|
|
foreach ( ent in attack_line )
|
|
{
|
|
if ( ent.times_used < to_ent.times_used )
|
|
{
|
|
to_ent = ent;
|
|
}
|
|
}
|
|
|
|
self set_goal_radius( to_ent.radius );
|
|
self set_goal_pos( to_ent.origin );
|
|
to_ent.times_used++;
|
|
|
|
/#
|
|
so_debug_print( "AI[" + self GetEntNum() + "] going to [" + line_position + "]" );
|
|
#/
|
|
}
|
|
|
|
test_vehicles()
|
|
{
|
|
// TESTING!!!
|
|
// add_wave_vehicle( 2, "jeep_1", "uaz" );
|
|
// add_wave_vehicle( 3, "truck_1", "bm21" );
|
|
// add_wave_vehicle( 4, "jeep_1", "uaz", GetVehicleNode( "jeep_1_guys_alt", "targetname" ) );
|
|
// add_wave_vehicle( 5, "truck_2", "bm21" );
|
|
// add_wave_vehicle( 5, "jeep_1", "uaz", GetVehicleNode( "jeep_1_guys_alt2", "targetname" ) );
|
|
|
|
// wait( 5 );
|
|
// temp = SpawnStruct();
|
|
// temp.alt_node = undefined;
|
|
|
|
// JEEP 1
|
|
// temp.ent = GetEnt( "jeep_1", "targetname" );
|
|
|
|
// JEEP 1 ALT
|
|
// temp.ent = GetEnt( "jeep_1", "targetname" );
|
|
// temp.alt_node = GetVehicleNode( "jeep_1_guys_alt", "targetname" );
|
|
|
|
// TRUCK 1
|
|
// temp.ent = GetEnt( "truck_1", "targetname" );
|
|
|
|
// TRUCK 2
|
|
// temp.ent = GetEnt( "truck_2", "targetname" );
|
|
|
|
// JEEP 1 ALT2
|
|
// temp.ent = GetEnt( "jeep_1", "targetname" );
|
|
// temp.alt_node = GetVehicleNode( "jeep_1_guys_alt2", "targetname" );
|
|
|
|
// spawn_vehicle_and_go( temp );
|
|
|
|
// level waittill( "never" );
|
|
} |