6242 lines
141 KiB
Plaintext
6242 lines
141 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\_anim;
|
|
#include maps\_hud_util;
|
|
#include maps\favela_escape_anim;
|
|
|
|
#using_animtree( "generic_human" );
|
|
|
|
|
|
// these can be triggered from any trigger, usually makes sense with a spawn trigger though
|
|
triggered_hostile_bursts_setup()
|
|
{
|
|
triggered_hostile_burst_setup_lines();
|
|
|
|
allents = GetEntArray();
|
|
|
|
while( !IsDefined( level.struct_class_names ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
trigs = [];
|
|
foreach( ent in allents )
|
|
{
|
|
if ( !isdefined( ent.code_classname ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( IsSubStr( ent.code_classname, "trigger" ) )
|
|
{
|
|
trigs[ trigs.size ] = ent;
|
|
}
|
|
}
|
|
|
|
foreach( trig in trigs )
|
|
{
|
|
if( !IsDefined( trig.target ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
orgs = GetStructArray( trig.target, "targetname" );
|
|
|
|
foreach( org in orgs )
|
|
{
|
|
if( IsDefined( org.script_noteworthy ) && org.script_noteworthy == "hostile_burst" )
|
|
{
|
|
trig thread triggered_hostile_burst( org );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
triggered_hostile_burst_setup_lines()
|
|
{
|
|
lines = [];
|
|
|
|
// "You're going to pay for what you did to my brother, soldier! You hear me?"
|
|
lines[ lines.size ] = "favesc_pe1_youhearme";
|
|
|
|
// "Eduardo, call the truck! And bring more grenades! We're going to flush them out of this building!"
|
|
lines[ lines.size ] = "favesc_pe2_callthetruck";
|
|
|
|
// "We're going to kill you slowly for what you've done here. Do you hear me?"
|
|
lines[ lines.size ] = "favesc_pe3_killslowly";
|
|
|
|
// "Get on the roof of those buildings and go around them!! They can't stop all of us!!"
|
|
lines[ lines.size ] = "favesc_pe3_cantstop";
|
|
|
|
// "There is nowhere for you to run!!! We will find you wherever you go!!"
|
|
lines[ lines.size ] = "favesc_pe4_wellfindyou";
|
|
|
|
// "Heitor, set up the machine gun in the alley over on the next street, in case they go that way!!!"
|
|
lines[ lines.size ] = "favesc_pe3_mginalley";
|
|
|
|
// "Go after them!! Hurry!! Run them down!!"
|
|
lines[ lines.size ] = "favesc_pe3_afterthem";
|
|
|
|
// "Block the exits from this area! Don't let them escape!!"
|
|
lines[ lines.size ] = "favesc_pe3_blockexits";
|
|
|
|
// "Kill them!!! Kill them all!!! They deserve nothing less!!!"
|
|
lines[ lines.size ] = "favesc_pe4_killthemall";
|
|
|
|
// "There is nowhere for you to run!!! We will find you wherever you go!!"
|
|
lines[ lines.size ] = "favesc_pe4_wellfindyou";
|
|
|
|
// "Chase them!! Chase them!!!"
|
|
lines[ lines.size ] = "favesc_pe4_chasethem";
|
|
|
|
// "Hunt them down like animals!!!"
|
|
lines[ lines.size ] = "favesc_pe4_huntthem";
|
|
|
|
// "I'm going to cut you apart, limb by limb!!!"
|
|
lines[ lines.size ] = "favesc_pe2_limbbylimb";
|
|
|
|
// "People like you killed my family!! Never again!!!"
|
|
lines[ lines.size ] = "favesc_pe4_neveragain";
|
|
|
|
// "The police have no honor, now they send mercenaries to oppress us!!!"
|
|
lines[ lines.size ] = "favesc_pe1_nohonor";
|
|
|
|
// "Don't just throw the grenades! Wait at least two seconds before you throw them!"
|
|
lines[ lines.size ] = "favesc_pe2_wait2seconds";
|
|
|
|
// "If you capture them, keep them alive so we can use the machete!!!"
|
|
lines[ lines.size ] = "favesc_pe1_keepthemalive";
|
|
|
|
// "We need more bandages for the wounded!"
|
|
lines[ lines.size ] = "favesc_pe2_morebandages";
|
|
|
|
// "I will avenge my brother!!"
|
|
lines[ lines.size ] = "favesc_pe1_avengemybrother";
|
|
|
|
// "Get on the roof of those buildings and go around them!! They can't stop all of us!!"
|
|
lines[ lines.size ] = "favesc_pe3_cantstop";
|
|
|
|
// "You're all going to die up here for all the blood you've spilled!!!"
|
|
lines[ lines.size ] = "favesc_pe1_goingtodie";
|
|
|
|
|
|
lines = array_randomize( lines );
|
|
|
|
level.triggeredHostileBursts = lines;
|
|
|
|
level.triggeredHostileBurstIndex = undefined;
|
|
}
|
|
|
|
triggered_hostile_burst( org )
|
|
{
|
|
self waittill( "trigger" );
|
|
|
|
if( !self script_delay() )
|
|
{
|
|
wait( RandomFloatRange( 0.5, 1.25 ) ); // let the enemies spawn
|
|
}
|
|
|
|
burst = undefined;
|
|
if( IsDefined( org.script_parameters ) )
|
|
{
|
|
burst = org.script_parameters;
|
|
}
|
|
|
|
if( !IsDefined( burst ) )
|
|
{
|
|
if( !IsDefined( level.triggeredHostileBurstIndex ) )
|
|
{
|
|
level.triggeredHostileBurstIndex = 0;
|
|
}
|
|
|
|
burst = level.triggeredHostileBursts[ level.triggeredHostileBurstIndex ];
|
|
|
|
level.triggeredHostileBurstIndex++;
|
|
if( level.triggeredHostileBurstIndex >= ( level.triggeredHostileBursts.size - 1 ) )
|
|
{
|
|
level.triggeredHostileBurstIndex = 0;
|
|
}
|
|
}
|
|
|
|
level thread play_sound_in_space( burst, org.origin );
|
|
}
|
|
|
|
|
|
// -------------
|
|
// --- MUSIC ---
|
|
// -------------
|
|
favesc_combat_music()
|
|
{
|
|
thread favesc_combat_music_stop();
|
|
|
|
alias = "favelaescape_combat";
|
|
tracktime = MusicLength( alias );
|
|
|
|
while( !flag( "market_evac_insidepath_start" ) )
|
|
{
|
|
MusicPlayWrapper( alias );
|
|
wait( tracktime );
|
|
music_stop( 1 );
|
|
wait( 3 );
|
|
}
|
|
}
|
|
|
|
favesc_combat_music_stop()
|
|
{
|
|
flag_wait( "market_evac_insidepath_start" );
|
|
music_stop( 7.5 );
|
|
}
|
|
|
|
favesc_waveoff_music()
|
|
{
|
|
music_loop( "favelaescape_waveoff", 72 );
|
|
}
|
|
|
|
favesc_falling_music()
|
|
{
|
|
music_stop( 3 );
|
|
level.player play_sound_on_entity( "favelaescape_fixedfall", "fixedfall_music_done" );
|
|
}
|
|
|
|
favesc_finalrun_music()
|
|
{
|
|
music_stop( 3 );
|
|
wait( 3 );
|
|
MusicPlayWrapper( "favelaescape_finalrun" );
|
|
|
|
flag_wait( "solorun_player_boarded_chopper" );
|
|
music_stop( 10 );
|
|
level.player play_sound_on_entity( "favelaescape_ending" );
|
|
}
|
|
|
|
|
|
// --------------
|
|
// --- WALLAS ---
|
|
// --------------
|
|
/* megaphone processed aliases:
|
|
favesc_pgm_killthemall
|
|
favesc_pgm_wellfindyou
|
|
favesc_pgm_nohonor
|
|
favesc_pgm_huntthem
|
|
favesc_pgm_blockexits
|
|
*/
|
|
radiotower_crowd_walla()
|
|
{
|
|
org = ( 3712, 576, 1211 );
|
|
delaythread( 5, ::play_sound_in_space, "favesc_pgm_wellfindyou", org );
|
|
delaythread( 20, ::play_sound_in_space, "favesc_pgm_huntthem", org );
|
|
play_sound_in_space( "wlla_favela_escape_start", org );
|
|
}
|
|
|
|
vista1_walla()
|
|
{
|
|
org = ( 1972, -1340, 734 );
|
|
delaythread( 5, ::play_sound_in_space, "favesc_pgm_blockexits", org );
|
|
play_sound_in_space( "wlla_favela_escape_vista1", org );
|
|
}
|
|
|
|
vista2_walla()
|
|
{
|
|
flag_wait( "uphill_advance_3" );
|
|
|
|
org = ( -1156, 1796, 1124 );
|
|
delaythread( 5, ::play_sound_in_space, "favesc_pgm_killthemall", org );
|
|
delaythread( 20, ::play_sound_in_space, "favesc_pgm_nohonor", org );
|
|
play_sound_in_space( "wlla_favela_escape_vista2", org );
|
|
}
|
|
|
|
market_evac_escape_walla()
|
|
{
|
|
level.player play_sound_on_entity( "wlla_favela_escape_soccer" );
|
|
}
|
|
|
|
roofrun_walla()
|
|
{
|
|
level.player play_sound_on_entity( "wlla_favela_escape_running1" );
|
|
}
|
|
|
|
bigjump_recovery_rightside_walla()
|
|
{
|
|
org = ( -5588, -1852, 912 );
|
|
delaythread( 5, ::play_sound_in_space, "favesc_pgm_wellfindyou", org );
|
|
play_sound_in_space( "wlla_favela_escape_fallen_right", org );
|
|
}
|
|
|
|
bigjump_recovery_leftside_walla()
|
|
{
|
|
org = ( -5704, -376, 814 );
|
|
delaythread( 10, ::play_sound_in_space, "favesc_pgm_huntthem", org );
|
|
play_sound_in_space( "wlla_favela_escape_fallen_left", org );
|
|
}
|
|
|
|
|
|
// ------------------
|
|
// --- RADIOTOWER ---
|
|
// ------------------
|
|
radiotower_runpath_dialogue()
|
|
{
|
|
waitflag = "runpath_dialogue_continue";
|
|
flag_init( waitflag );
|
|
thread radiotower_runpath_dialogue_triggerwait( waitflag );
|
|
|
|
flag_wait( "introscreen_start_dialogue" );
|
|
|
|
// "Sir, the militia's closin' in! Almost two hundred of 'em, front and back!"
|
|
level.hero1 dialogue( "favesc_gst_closingin" );
|
|
|
|
// "We're gonna have to fight our way to the LZ! Let's go!"
|
|
level.sarge dialogue( "favesc_cmt_fightourway" );
|
|
|
|
// "What about Rojas?"
|
|
level.hero1 dialogue( "favesc_gst_whataboutrojas" );
|
|
|
|
// "Victim of a hostile takeover?"
|
|
level.sarge dialogue( "favesc_cmt_takeover" );
|
|
|
|
// "Works for me."
|
|
level.hero1 dialogue( "favesc_gst_worksforme" );
|
|
|
|
flag_wait( waitflag );
|
|
|
|
// "Nikolai! We're at the top of the favela surrounded by militia! Bring the chopper to the market, do you copy, over!"
|
|
level.sarge dialogue( "favesc_cmt_surrounded" );
|
|
|
|
// "Ok my friend, I am on the way!"
|
|
radio_dialogue( "favesc_nkl_ontheway" );
|
|
|
|
// "Everyone get ready! Lock and load! "
|
|
level.sarge dialogue( "favesc_cmt_lockandload" );
|
|
|
|
// "Let's do this!!"
|
|
level.hero1 dialogue( "favesc_gst_letsdothis" );
|
|
|
|
flag_set( "radiotower_runpath_dialogue_done" );
|
|
}
|
|
|
|
radiotower_runpath_dialogue_triggerwait( flagname )
|
|
{
|
|
trigger_wait_targetname( "trig_intro_playerturnedcorner" );
|
|
flag_set( flagname );
|
|
}
|
|
|
|
// to avoid the feeling of endlessly picking off guys, stop the floodspawners early if we kill enough of them
|
|
radiotower_stop_roof_respawners()
|
|
{
|
|
killsBeforeShutdown = 5;
|
|
|
|
// this spawnfunc will track their deaths
|
|
roofspawners = GetEntArray( "spawner_radiotower_wave1", "targetname" );
|
|
array_thread( roofspawners, ::add_spawn_function, ::radiotower_roofguy_spawnfunc );
|
|
|
|
if( !IsDefined( level.roofGuysKilled ) )
|
|
{
|
|
level.roofGuysKilled = 0;
|
|
}
|
|
|
|
while( level.roofGuysKilled < killsBeforeShutdown )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
trigger_activate_targetname_safe( "trig_killspawner_7" );
|
|
}
|
|
|
|
radiotower_roofguy_spawnfunc()
|
|
{
|
|
self waittill( "death", attacker );
|
|
|
|
if( maps\_spawner::player_saw_kill( self, attacker ) )
|
|
{
|
|
level.roofGuysKilled++;
|
|
}
|
|
}
|
|
|
|
radiotower_runup_scout()
|
|
{
|
|
spawner = GetEnt( "radiotower_path_scout", "targetname" );
|
|
anime = spawner.animation;
|
|
endgoal = GetNode( spawner.target, "targetname" );
|
|
|
|
guy = spawner spawn_ai();
|
|
|
|
guy magic_bullet_shield();
|
|
|
|
guy ignore_everything();
|
|
guy.ignoreme = true;
|
|
|
|
spawner anim_generic_first_frame( guy, anime );
|
|
|
|
trigger_wait_targetname( "trig_radiotower_brushpath_start" );
|
|
|
|
guy stop_magic_bullet_shield();
|
|
guy endon( "death" );
|
|
guy.allowdeath = true;
|
|
|
|
// "Here they come!"
|
|
guy thread play_sound_on_entity( "favesc_pe1_heretheycome" );
|
|
spawner anim_generic( guy, anime );
|
|
|
|
guy.goalradius = 64;
|
|
guy SetGoalNode( endgoal );
|
|
|
|
guy waittill( "goal" );
|
|
|
|
if( IsDefined( guy ) )
|
|
{
|
|
guy clear_ignore_everything();
|
|
guy.ignoreme = false;
|
|
guy.fixednode = false;
|
|
guy.goalradius = 1024;
|
|
guy.combatmode = "ambush";
|
|
}
|
|
}
|
|
|
|
radiotower_runup_friendlies_ignore()
|
|
{
|
|
array_thread( level.friends, ::scr_ignoreall, true );
|
|
|
|
trigger_wait_targetname( "trig_radiotower_brushpath_start" );
|
|
|
|
// lets them look surprised
|
|
wait( 2.25 );
|
|
array_thread( level.friends, ::scr_ignoreall, false );
|
|
}
|
|
|
|
radiotower_friendly_colors()
|
|
{
|
|
trigger_wait_targetname( "trig_radiotower_forcecolor_change_1" );
|
|
level.sarge set_force_color( "b" );
|
|
}
|
|
|
|
radiotower_doorkick_1()
|
|
{
|
|
trigger_wait_targetname( "trig_radiotower_doorkick_1" );
|
|
|
|
tracer = GetStruct( "struct_radiotower_doorkick_1_sighttracer", "targetname" );
|
|
// timer, dot (fov = 80)
|
|
tracer waittill_player_lookat_for_time( 1, 0.7 );
|
|
|
|
spawners = GetEntArray( "spawner_radiotower_doorkick_1", "targetname" );
|
|
door = GetEnt( "sbmodel_radiotower_doorkick_1", "targetname" );
|
|
kickRef = GetStruct( "struct_radiotower_doorkick_1_animref", "targetname" );
|
|
|
|
thread door_kick_housespawn( spawners, door, kickRef );
|
|
|
|
if( flag( "radiotower_runpath_dialogue_done" ) )
|
|
{
|
|
// "Militia comin' out of the shack on the left!"
|
|
level.hero1 delaythread( 1, ::dialogue, "favesc_gst_shackonleft" );
|
|
}
|
|
|
|
// makes sure that dudes will go out the kicked door
|
|
wait( 5 );
|
|
backdoorBlocker = GetEnt( "sbmodel_radiotower_doorkick_1_backdoor_blocker", "targetname" );
|
|
backdoorBlocker Delete();
|
|
}
|
|
|
|
radiotower_curtainpull_1()
|
|
{
|
|
// spawn the curtain model early in this case
|
|
node = GetEnt( "radiotower_curtainpull1_animref", "targetname" );
|
|
curtain_pulldown_spawnmodel( node );
|
|
|
|
// for our special waitfunc to use later
|
|
node.traceSpot = GetStruct( "radiotower_curtainpull1_sighttracer", "targetname" );
|
|
|
|
spawner = GetEnt( "spawner_radiotower_curtainpull_1", "targetname" );
|
|
spawner thread curtain_pulldown( true, ::distant_curtainpull_waitfunc );
|
|
|
|
level endon( "radiotower_exit" );
|
|
|
|
trigger_wait( "trig_radiotower_rooftop_spawn", "script_noteworthy" );
|
|
|
|
spawner spawn_ai();
|
|
|
|
}
|
|
|
|
distant_curtainpull_waitfunc( guy, node )
|
|
{
|
|
guy endon( "death" );
|
|
|
|
tracer = node.traceSpot;
|
|
|
|
// default dot = 0.92
|
|
tracer waittill_player_lookat( 0.95, 1.25 );
|
|
}
|
|
|
|
radiotower_hiding_door_guy_cleanup()
|
|
{
|
|
killtrig = GetEnt( "trig_killspawner_radiotower_hiding_door_guy", "targetname" );
|
|
spawner = undefined;
|
|
door_org = undefined;
|
|
|
|
worldOrigin = ( 4374, 1252, 1060 );
|
|
|
|
ents = GetEntArray( "hiding_door_spawner", "script_noteworthy" );
|
|
spawner = getclosest( worldOrigin, ents );
|
|
|
|
door_clips = level._hiding_door_pushplayer_clips;
|
|
door_clip = getclosest( worldOrigin, door_clips );
|
|
|
|
thread radiotower_hiding_door_guy_cleanup_cancel( spawner, killtrig );
|
|
level endon( "radiotower_hiding_door_guy_cleanup_cancel" );
|
|
|
|
killtrig waittill( "trigger" );
|
|
|
|
door_clip Delete();
|
|
}
|
|
|
|
radiotower_hiding_door_guy_cleanup_cancel( spawner, killtrig )
|
|
{
|
|
killtrig endon( "trigger" );
|
|
|
|
spawner waittill( "trigger" );
|
|
level notify( "radiotower_hiding_door_guy_cleanup_cancel" );
|
|
}
|
|
|
|
radiotower_gate_open( doFx )
|
|
{
|
|
if( !IsDefined( doFx ) )
|
|
{
|
|
doFx = true;
|
|
}
|
|
|
|
gateLeft = GetEnt( "sbmodel_radiotower_gate_left", "targetname" );
|
|
gateRight = GetEnt( "sbmodel_radiotower_gate_right", "targetname" );
|
|
|
|
openTime = 0.75;
|
|
|
|
gates[0] = gateLeft;
|
|
gates[1] = gateRight;
|
|
gates[0] PlaySound( "scn_favela_escape_wood_gate" );
|
|
array_thread( gates, ::sbmodel_rotate, openTime );
|
|
|
|
if( doFx )
|
|
{
|
|
exploder( 1 );
|
|
}
|
|
|
|
gates[0] waittill( "sbmodel_rotatedone" );
|
|
}
|
|
|
|
radiotower_enemy_vehicles_prethink()
|
|
{
|
|
flag_wait( "radiotower_vehicles_start" );
|
|
|
|
thread radiotower_enemy_vehicles();
|
|
}
|
|
|
|
radiotower_enemy_vehicles()
|
|
{
|
|
arr = maps\_vehicle::create_vehicle_from_spawngroup_and_gopath( 0 );
|
|
technical = arr[ 0 ];
|
|
technical thread play_sound_on_entity( "scn_favela_escape_truck_runup" );
|
|
|
|
// open up exploder gates
|
|
exploder( 110 );
|
|
exploder( 120 );
|
|
|
|
technical thread radiotower_technical_setup();
|
|
|
|
// make a badplace where the truck does a donut
|
|
technical thread radiotower_enemy_vehicles_badplaces();
|
|
|
|
gate_node = GetVehicleNode( "node_technical_bust_gate", "script_noteworthy" );
|
|
gate_node waittill( "trigger" );
|
|
|
|
technical delaythread( 0.7, ::play_sound_on_entity, "scn_favela_escape_truck_land" );
|
|
technical delaythread( 1.7, ::play_sound_on_entity, "scn_favela_escape_truck_donut" );
|
|
|
|
flag_set( "radiotower_escape_technical_1_arrival" );
|
|
level thread radiotower_gate_open();
|
|
delaythread( 3, ::radiotower_enemy_vehicle_2 );
|
|
delaythread( 5, ::radiotower_remove_vehicleclip );
|
|
}
|
|
|
|
radiotower_enemy_vehicles_badplaces()
|
|
{
|
|
self waitfor_some_speed();
|
|
|
|
refs = [];
|
|
refs = GetStructArray( "struct_radiotower_vehicle1_road_badplaceRef", "targetname" );
|
|
refs[ refs.size ] = GetStruct( "struct_radiotower_vehicle1_donut_badplaceRef", "targetname" );
|
|
|
|
names = [];
|
|
|
|
foreach( index, ref in refs )
|
|
{
|
|
badplaceName = "vehicle1_badplace_" + index;
|
|
|
|
BadPlace_Cylinder( badplaceName, -1, groundpos( ref.origin ), ref.radius, 128, "axis", "allies" );
|
|
|
|
names[ names.size ] = badplaceName;
|
|
}
|
|
|
|
while( !flag( "radiotower_vehicle1_donut_done" ) && self.health > 0 && self Vehicle_GetSpeed() > 1 )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
foreach( name in names )
|
|
{
|
|
BadPlace_Delete( name );
|
|
}
|
|
}
|
|
|
|
radiotower_enemy_vehicle_2()
|
|
{
|
|
arr = maps\_vehicle::create_vehicle_from_spawngroup_and_gopath( 1 );
|
|
technical = arr[ 0 ];
|
|
|
|
technical thread radiotower_technical_setup();
|
|
|
|
arrivalNode = GetVehicleNode( "vnode_technical2_arrival", "script_noteworthy" );
|
|
arrivalNode waittill( "trigger" );
|
|
flag_set( "radiotower_escape_technical_2_arrival" );
|
|
}
|
|
|
|
radiotower_technical_setup()
|
|
{
|
|
waittillframeend; // let the technical spawn
|
|
|
|
self ent_flag_init( "reached_end_node" );
|
|
self ent_flag_init( "stopped" );
|
|
|
|
self thread technical_waittill_end_node();
|
|
self thread technical_waittill_stopped();
|
|
|
|
// find the gunner
|
|
gunner = self vehicle_get_gunner();
|
|
ASSERT( IsDefined( gunner ) );
|
|
|
|
self ent_flag_init( "godoff" );
|
|
self thread technical_temp_invincibility( gunner );
|
|
|
|
turret = self.mgturret[ 0 ];
|
|
turret thread technical_turret_think( self, gunner );
|
|
}
|
|
|
|
technical_waittill_end_node()
|
|
{
|
|
self endon( "death" );
|
|
self waittill( "reached_end_node" );
|
|
self ent_flag_set( "reached_end_node" );
|
|
}
|
|
|
|
technical_waittill_stopped()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waitfor_some_speed();
|
|
|
|
while( self Vehicle_GetSpeed() > 0 )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self ent_flag_set( "stopped" );
|
|
}
|
|
|
|
vehicle_get_gunner()
|
|
{
|
|
gunner = undefined;
|
|
foreach( guy in self.riders )
|
|
{
|
|
animpos = maps\_vehicle_aianim::anim_pos( self, guy.vehicle_position );
|
|
|
|
if( maps\_vehicle_aianim::guy_should_man_turret( animpos ) )
|
|
{
|
|
gunner = guy;
|
|
break;
|
|
}
|
|
}
|
|
|
|
return gunner;
|
|
}
|
|
|
|
technical_temp_invincibility( gunner )
|
|
{
|
|
self.resetHealth = false;
|
|
|
|
riders = self.riders;
|
|
array_thread( riders, ::magic_bullet_shield );
|
|
foreach( rider in riders )
|
|
{
|
|
rider.health = 10000000;
|
|
rider.threatbias = -2500;
|
|
rider.attackerAccuracy = 0.5;
|
|
rider thread vehicle_rider_reset_health( self, gunner );
|
|
}
|
|
|
|
self thread maps\_vehicle::godon();
|
|
|
|
self waitfor_some_speed();
|
|
|
|
self thread reset_health_at_end_node();
|
|
self thread reset_health_at_low_speed();
|
|
|
|
while( IsDefined( self ) && !self.resetHealth && !self ent_flag( "godoff" ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
if( IsDefined( self ) )
|
|
{
|
|
self thread maps\_vehicle::godoff();
|
|
self vehicle_set_health( 1500 );
|
|
self notify( "technical_health_reset" );
|
|
}
|
|
}
|
|
|
|
vehicle_rider_reset_health( vehicle, gunner )
|
|
{
|
|
self endon( "death" );
|
|
vehicle endon( "death" );
|
|
|
|
vehicle ent_flag_wait( "godoff" );
|
|
self.allowdeath = true;
|
|
|
|
// hack to fix gunner pain anims snapping them back to the turret
|
|
if( IsDefined( gunner ) && self == gunner )
|
|
{
|
|
self.health = 1;
|
|
}
|
|
else
|
|
{
|
|
self.health = 150;
|
|
}
|
|
|
|
self thread stop_magic_bullet_shield_safe();
|
|
}
|
|
|
|
vehicle_set_health( health )
|
|
{
|
|
self.health = health + self.healthbuffer;
|
|
self.currenthealth = self.health;
|
|
}
|
|
|
|
reset_health_at_end_node()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "technical_health_reset" );
|
|
|
|
self waitfor_end_node();
|
|
self.resetHealth = true;
|
|
}
|
|
|
|
reset_health_at_low_speed()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "technical_health_reset" );
|
|
|
|
self waitfor_low_speed();
|
|
self.resetHealth = true;
|
|
}
|
|
|
|
waitfor_end_node()
|
|
{
|
|
self endon( "death" );
|
|
self waittill( "reached_end_node" );
|
|
}
|
|
|
|
waitfor_low_speed()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "technical_health_reset" );
|
|
|
|
while( self Vehicle_GetSpeed() > 2 )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
|
|
waitfor_some_speed()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "technical_health_reset" );
|
|
|
|
while( self Vehicle_GetSpeed() < 1 )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
|
|
technical_turret_think( vehicle, gunner )
|
|
{
|
|
self endon( "death" );
|
|
|
|
// this isn't getting called on all the technicals
|
|
//self thread turret_disable_til_node( vehicle, gunner );
|
|
gunner thread gunner_scripted_death();
|
|
|
|
// gunner has to die, and vehicle has to stop
|
|
gunner waittill( "death" );
|
|
vehicle ent_flag_wait_either( "reached_end_node", "stopped" );
|
|
|
|
// now the gun is usable by the player
|
|
self MakeUsable();
|
|
}
|
|
|
|
turret_disable_til_node( vehicle, gunner )
|
|
{
|
|
self endon( "death" );
|
|
gunner endon( "death" );
|
|
|
|
self TurretFireDisable();
|
|
vehicle waittillmatch( "noteworthy", "vnode_technical_turret_activate" );
|
|
self TurretFireEnable();
|
|
}
|
|
|
|
gunner_scripted_death()
|
|
{
|
|
self endon( "death" );
|
|
level waittill( "kill_technical_gunners" );
|
|
self stop_magic_bullet_shield_safe();
|
|
self Kill();
|
|
}
|
|
|
|
radiotower_remove_vehicleclip()
|
|
{
|
|
clip = GetEnt( "sbmodel_technical_jump_helper", "targetname" );
|
|
clip trigger_off();
|
|
}
|
|
|
|
radiotower_escape_dialogue()
|
|
{
|
|
thread radiotower_enemy_callout_rooftop();
|
|
thread radiotower_enemy_callout_walljumpers_left();
|
|
|
|
// "Tangos at ground level dead ahead!!"
|
|
level.hero1 dialogue( "favesc_gst_deadahead" );
|
|
|
|
// "We've gotta get to the helicopter - head through the gate to the market! Move!"
|
|
level.sarge dialogue( "favesc_cmt_thrugate" );
|
|
|
|
battlechatter_on( "allies" );
|
|
|
|
flag_wait( "radiotower_escape_technical_1_arrival" );
|
|
|
|
battlechatter_off( "allies" );
|
|
|
|
// "Technical comin' in from the south!!"
|
|
level.hero1 dialogue( "favesc_gst_technical" );
|
|
|
|
flag_wait( "radiotower_escape_technical_2_arrival" );
|
|
|
|
// "We got another technical! Take it out!!!"
|
|
level.sarge dialogue( "favesc_cmt_technical" );
|
|
|
|
battlechatter_on( "allies" );
|
|
}
|
|
|
|
radiotower_enemy_callout_rooftop()
|
|
{
|
|
level endon( "radiotower_exit" );
|
|
|
|
trigger_wait( "trig_radiotower_rooftop_spawn", "script_noteworthy" );
|
|
|
|
/*
|
|
startTime = GetTime();
|
|
|
|
flag_waitopen( "scripted_dialogue" );
|
|
|
|
if( seconds( GetTime() - startTime ) < 0.5 )
|
|
{
|
|
wait( 2 );
|
|
}
|
|
*/
|
|
|
|
// "Contaaact!!! Foot-mobiles on the rooftops, closing in fast from the south!!!"
|
|
level.hero1 dialogue( "favesc_gst_onrooftops" );
|
|
}
|
|
|
|
radiotower_enemy_callout_walljumpers_left()
|
|
{
|
|
level endon( "radiotower_exit" );
|
|
|
|
trigger_wait_targetname( "trig_radiotower_walljumper_spawn" );
|
|
|
|
//flag_waitopen( "scripted_dialogue" );
|
|
|
|
// "Tangos moving in low from the southeast!"
|
|
level.sarge dialogue( "favesc_cmt_lowfromse" );
|
|
}
|
|
|
|
radiotower_enemies_retreat()
|
|
{
|
|
// don't retreat until after the second vehicle shows up
|
|
flag_wait( "radiotower_escape_technical_2_arrival" );
|
|
|
|
while( get_alive_enemies().size >= 6 )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
// turn off triggers in case player comes back through here for some reason
|
|
spawntrigs = GetEntArray( "trig_radiotower_cleanup_at_exit", "script_noteworthy" );
|
|
array_thread( spawntrigs, ::trigger_off );
|
|
|
|
// activate killspawner
|
|
trigger_activate_targetname_safe( "trig_killspawner_7" );
|
|
|
|
allenemies = get_alive_enemies();
|
|
enemies = [];
|
|
roofenemies = [];
|
|
foreach( guy in allenemies )
|
|
{
|
|
if( guy.origin[ 0 ] > 3600 )
|
|
{
|
|
if( guy.origin[ 2 ] > 1140 )
|
|
{
|
|
roofenemies[roofenemies.size] = guy;
|
|
}
|
|
else
|
|
{
|
|
enemies[enemies.size] = guy;
|
|
}
|
|
}
|
|
}
|
|
|
|
flag_set( "radiotower_enemies_retreat" );
|
|
|
|
// roof enemies die
|
|
level thread kill_group_over_time( roofenemies, 10 );
|
|
|
|
level notify( "kill_technical_gunners" );
|
|
|
|
// try resetting goalpos to avoid the "cannot set goal volume when a goal entity is set" SRE
|
|
// (this happens to guys who are playerseeking)
|
|
foreach( guy in enemies )
|
|
{
|
|
guy SetGoalPos( guy.origin );
|
|
}
|
|
wait( 0.05 );
|
|
|
|
// guys retreat
|
|
retreatVolume = GetEnt( "goalvolume_52", "targetname" );
|
|
enemies = array_removeundefined( enemies );
|
|
enemies = array_removedead( enemies );
|
|
thread array_call( enemies, ::SetGoalVolumeAuto, retreatVolume );
|
|
|
|
// "Head through that gate!!! Keep pushing to the evac point!!!"
|
|
level.sarge dialogue( "favesc_cmt_thruthatgate" );
|
|
|
|
// "Go! Go! Go!"
|
|
level.hero1 dialogue( "favesc_gst_gogogo" );
|
|
|
|
flag_set( "radiotower_escape_moveup" );
|
|
|
|
// turn off triggers that can send friendlies back after we force them forward
|
|
trigs = GetEntArray( "trig_radiotower_escape_removeAtExit", "targetname" );
|
|
array_thread( trigs, ::trigger_off );
|
|
|
|
// put soap on the right color chain, if the player didn't hit that trigger already
|
|
trigger_activate_targetname_safe( "trig_radiotower_forcecolor_change_1" );
|
|
|
|
// move friendlies up if the player hasn't already
|
|
trig = GetEnt( "trig_script_color_allies_b5", "targetname" );
|
|
if( IsDefined( trig ) )
|
|
{
|
|
trig notify( "trigger" );
|
|
}
|
|
}
|
|
|
|
// ---------------
|
|
// --- STREETS ---
|
|
// ---------------
|
|
street_dialogue()
|
|
{
|
|
thread street_dialogue_playerabove();
|
|
thread street_dialogue_leftalley();
|
|
|
|
flag_wait( "vista1_dialogue_start" );
|
|
|
|
// "Let's go, let's go! We've gotta push through these streets to the market!"
|
|
level.sarge dialogue( "favesc_cmt_pushthrustreets" );
|
|
|
|
flag_wait( "multipath_dialogue_start" );
|
|
|
|
// "Watch for flanking routes!"
|
|
level.sarge dialogue( "favesc_cmt_flankingroutes" );
|
|
|
|
flag_wait( "almostatmarket_dialogue_start" );
|
|
|
|
// "Keep moving!! We're almost at the market!"
|
|
level.sarge dialogue( "favesc_cmt_almostatmarket" );
|
|
}
|
|
|
|
street_dialogue_playerabove()
|
|
{
|
|
level endon( "playerabove_dialogue_cancel" );
|
|
|
|
flag_wait( "playerabove_dialogue_start" );
|
|
|
|
// "Roach! Lay down some fire on the intersection!"
|
|
level.sarge dialogue( "favesc_cmt_laydownfire" );
|
|
}
|
|
|
|
street_dialogue_leftalley()
|
|
{
|
|
flag_wait( "leftalley_dialogue_start" );
|
|
|
|
// "Heads up! Alley on the left!"
|
|
level.sarge thread dialogue( "favesc_cmt_alleyonleft" );
|
|
}
|
|
|
|
vista1_door1_kick()
|
|
{
|
|
trigger_wait_targetname( "trig_vista1_doorkick1" );
|
|
|
|
tracer = GetStruct( "struct_vista1_doorkick1_sighttracer", "targetname" );
|
|
tracer waittill_player_lookat_for_time( 1.5, 0.7 );
|
|
|
|
door = GetEnt( "sbmodel_vista1_door1", "targetname" );
|
|
animref = GetStruct( "struct_vista1_door1_animref", "targetname" );
|
|
spawners = GetEntArray( "spawner_vista1_door1_houseguy", "targetname" );
|
|
|
|
thread door_kick_housespawn( spawners, door, animref );
|
|
}
|
|
|
|
vista1_wavingguy()
|
|
{
|
|
level endon( "multipath_dialogue_start" );
|
|
flag_wait( "street_advance_1" );
|
|
|
|
animref = GetStruct( "struct_vista1_wavingguy_animref", "targetname" );
|
|
spawner = GetEnt( animref.target, "targetname" );
|
|
anime = "favela_run_and_wave";
|
|
|
|
guy = spawner spawn_ai();
|
|
guy.ignoreme = true;
|
|
guy.ignoreall = true;
|
|
guy magic_bullet_shield();
|
|
|
|
animref anim_generic_first_frame( guy, anime );
|
|
|
|
// remove this guy if we don't ever get to him for some reason
|
|
guy thread vista1_wavingguy_cleanup();
|
|
|
|
// wait for anime
|
|
trigger_wait_targetname( "trig_vista1_wavingguy" );
|
|
|
|
// spawn his buddies
|
|
thread trigger_activate_targetname( "trig_vista1_wavingguy_spawngroup" );
|
|
|
|
guy stop_magic_bullet_shield();
|
|
guy.allowdeath = true;
|
|
|
|
guy notify( "wavingguy_activated" );
|
|
|
|
// "Attaaaack!"
|
|
guy thread play_sound_on_entity( "favesc_pe1_attack" );
|
|
|
|
// he waves them on
|
|
animref anim_generic( guy, anime );
|
|
|
|
// when done, waver fights
|
|
if( IsAlive( guy ) )
|
|
{
|
|
guy.ignoreme = false;
|
|
guy.ignoreall = false;
|
|
node = GetNode( guy.target, "targetname" );
|
|
guy SetGoalNode( node );
|
|
}
|
|
}
|
|
|
|
vista1_wavingguy_cleanup()
|
|
{
|
|
self endon( "death" );
|
|
self endon( "wavingguy_activated" );
|
|
|
|
flag_wait( "multipath_dialogue_start" );
|
|
self stop_magic_bullet_shield();
|
|
self Delete();
|
|
}
|
|
|
|
street_roof1_doorkick()
|
|
{
|
|
trigger_wait_targetname_multiple( "trig_street_roof1_doorkick" );
|
|
|
|
spawners = GetEntArray( "spawner_street_roof1_doorkick", "targetname" );
|
|
door = GetEnt( "sbmodel_street_roof1_doorkick", "targetname" );
|
|
kickRef = GetStruct( "struct_street_roof1_doorkick_animref", "targetname" );
|
|
|
|
thread door_kick_housespawn( spawners, door, kickRef );
|
|
}
|
|
|
|
// helps clear out the intersection faster if the player is moving ahead
|
|
street_mid_intersection_clearout()
|
|
{
|
|
flag_wait( "uphill_advance_3" );
|
|
|
|
thread street_mid_intersection_kill_inside_guys();
|
|
|
|
vols = GetEntArray( "volume_enemies_street_mid_intersection", "targetname" );
|
|
ASSERT( vols.size );
|
|
|
|
array_thread( vols, ::street_mid_intersection_clearout_volume );
|
|
}
|
|
|
|
street_mid_intersection_kill_inside_guys()
|
|
{
|
|
guys = get_ai_group_ai( "street_mid_inside_guys" );
|
|
array_thread( guys, ::kill_when_player_not_looking );
|
|
}
|
|
|
|
street_mid_intersection_clearout_volume()
|
|
{
|
|
level endon( "market_evac_start" );
|
|
|
|
clearoutWaitTime = milliseconds( 5 );
|
|
|
|
while( 1 )
|
|
{
|
|
wait( RandomFloatRange( 0.5, 1 ) ); // don't grab the aiarray at the same time for each volume
|
|
|
|
ais = self get_ai_touching_volume( "axis", "human" );
|
|
|
|
foreach( guy in ais )
|
|
{
|
|
if( !IsAlive( guy ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( guy doingLongDeath() )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( IsDefined( guy.volume_clearout ) )
|
|
{
|
|
if( GetTime() > guy.volume_clearout + clearoutWaitTime && !IsDefined( guy.waitingToKill ) )
|
|
{
|
|
guy.waitingToKill = true;
|
|
guy thread kill_when_player_not_looking();
|
|
}
|
|
|
|
continue;
|
|
}
|
|
|
|
guy.volume_clearout = GetTime();
|
|
guy.health = 1;
|
|
guy.attackeraccuracy = 1;
|
|
//guy thread set_ignoreSuppression( true );
|
|
}
|
|
}
|
|
}
|
|
|
|
kill_when_player_not_looking()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( within_fov( level.player.origin, level.player GetPlayerAngles(), self.origin, level.cosine[ "45" ] ) )
|
|
{
|
|
wait( RandomFloatRange( 0.5, 2 ) );
|
|
}
|
|
|
|
self Kill();
|
|
}
|
|
|
|
vista2_technical_prethink()
|
|
{
|
|
trigger_wait_targetname( "trig_vista2_truckstart" );
|
|
thread vista2_technical();
|
|
}
|
|
|
|
vista2_technical()
|
|
{
|
|
arr = maps\_vehicle::create_vehicle_from_spawngroup_and_gopath( 3 );
|
|
technical = arr[ 0 ];
|
|
ASSERT( IsDefined( technical ) );
|
|
|
|
technical thread radiotower_technical_setup();
|
|
}
|
|
|
|
vista2_endhouse_jumpthru()
|
|
{
|
|
trigger_wait_targetname( "trig_vista2_endhouse_clearout" );
|
|
|
|
vol = GetEnt( "vista2_endhouse_goalvolume", "targetname" );
|
|
ais = GetAiArray( "axis" );
|
|
guys = [];
|
|
foreach( guy in ais )
|
|
{
|
|
if( guy IsTouching( vol ) )
|
|
{
|
|
guys[ guys.size ] = guy;
|
|
}
|
|
}
|
|
|
|
if( !guys.size )
|
|
{
|
|
// he's dead already
|
|
return;
|
|
}
|
|
|
|
// we're only set up to handle one guy in the building right now
|
|
ASSERT( guys.size == 1 );
|
|
|
|
houseguy = guys[ 0 ];
|
|
|
|
firespot = GetNode( "node_vista2_endhouse_firespot", "targetname" );
|
|
nadetarget = GetEnt( "org_vista2_endhouse_nadetarget", "targetname" );
|
|
animref = GetStruct( "street_vista2_jumpthru_animref", "targetname" );
|
|
jumperNode = GetNode( "node_vista2_endhouse_windowjumper_target", "targetname" );
|
|
|
|
nader = level.hero1;
|
|
|
|
og_weapon = nader.primaryweapon;
|
|
nader thread forceUseWeapon( "m79", "primary" );
|
|
|
|
og_color = nader get_force_color();
|
|
|
|
nader set_temp_goalradius( 64 );
|
|
nader ignore_everything();
|
|
nader clear_force_color();
|
|
nader AllowedStances( "stand" );
|
|
|
|
nader SetGoalNode( firespot );
|
|
nader waittill( "goal" );
|
|
|
|
if( IsAlive( houseguy ) )
|
|
{
|
|
houseguy magic_bullet_shield();
|
|
|
|
animref = GetStruct( "street_vista2_jumpthru_animref", "targetname" );
|
|
anime = "traverse_window_M_2_dive";
|
|
|
|
houseguy.animname = "generic";
|
|
animation = houseguy getanim( anime );
|
|
org = GetStartOrigin( animref.origin, animref.angles, animation );
|
|
ang = GetStartAngles( animref.origin, animref.angles, animation );
|
|
|
|
houseguy ForceTeleport( org, ang );
|
|
|
|
nader SetEntityTarget( nadetarget );
|
|
//nader delaycall( 0.5, ::Shoot, 1000, nadetarget.origin );
|
|
wait( 1 );
|
|
nader Shoot( 1.0, nadetarget.origin );
|
|
|
|
houseguy delaythread( 1.5, ::stop_magic_bullet_shield );
|
|
houseguy delaythread( 2, ::set_allowdeath, true );
|
|
animref thread anim_generic( houseguy, anime );
|
|
//animref anim_generic_gravity( houseguy, anime );
|
|
|
|
wait( GetAnimLength( animation ) );
|
|
}
|
|
|
|
if( IsAlive( houseguy ) )
|
|
{
|
|
houseguy SetGoalNode( jumperNode );
|
|
}
|
|
|
|
nader ClearEntityTarget();
|
|
nader AllowedStances( "stand", "crouch", "prone" );
|
|
nader set_force_color( og_color );
|
|
nader clear_ignore_everything();
|
|
nader restore_goalradius();
|
|
|
|
nader delaythread( 5, ::forceUseWeapon, og_weapon, "primary" );
|
|
}
|
|
|
|
vista2_firsthalf_enemies_retreat()
|
|
{
|
|
moveToVol = GetEnt( "volume_vista2_retreat", "targetname" );
|
|
|
|
trigger_wait_targetname( "trig_vista2_truckstart" );
|
|
|
|
ais = get_ai_group_ai( "vista2_firsthalf_enemies" );
|
|
foreach( guy in ais )
|
|
{
|
|
guy SetGoalVolumeAuto( moveToVol );
|
|
}
|
|
}
|
|
|
|
vista2_leftbalcony_enemies_magicgrenade()
|
|
{
|
|
level endon( "vista2_leftbalcony_deathflag" );
|
|
flag_wait( "market_advance_1" );
|
|
|
|
spots = [];
|
|
spots[ 0 ] = ( -1064, 1340, 1254 );
|
|
spots[ 1 ] = ( -1156, 1380, 1254 );
|
|
|
|
foreach( spot in spots )
|
|
{
|
|
MagicGrenade( "fraggrenade", spot, groundpos( spot ), RandomFloat( 0.25 ) );
|
|
}
|
|
}
|
|
|
|
|
|
// --------------
|
|
// --- MARKET ---
|
|
// --------------
|
|
market_dialogue()
|
|
{
|
|
thread market_dialogue_chaoticaboves();
|
|
thread market_dialogue_rightshack();
|
|
|
|
flag_wait( "market_dialogue_start" );
|
|
|
|
wait( 2 );
|
|
|
|
// "Squad! Split up and clear the market! Watch your sectors - these guys are everywhere!"
|
|
level.sarge dialogue( "favesc_cmt_splitup" );
|
|
|
|
flag_set( "market_introdialogue_done" );
|
|
}
|
|
|
|
market_dialogue_chaoticaboves()
|
|
{
|
|
trigger_wait_targetname( "trig_market_chaoticaboves_1" );
|
|
|
|
flag_wait( "market_introdialogue_done" );
|
|
|
|
// "Contacts above us at 11 o'clock, firing blind!"
|
|
level.hero1 dialogue( "favesc_gst_firingblind" );
|
|
}
|
|
|
|
market_dialogue_rightshack()
|
|
{
|
|
trigger_wait_targetname( "trig_market_door1" );
|
|
|
|
if( !flag( "market_introdialogue_done" ) )
|
|
{
|
|
flag_wait( "market_introdialogue_done" );
|
|
}
|
|
else
|
|
{
|
|
wait( 2 );
|
|
}
|
|
|
|
// "Tango coming out of the shack on the right!!!"
|
|
level.sarge dialogue( "favesc_cmt_shackonright" );
|
|
}
|
|
|
|
market_kill_extra_redshirts()
|
|
{
|
|
trigger_wait_targetname( "trig_market_door1" );
|
|
|
|
// the hidden reinforcement thread can potentially spawn a new guy later if we're waiting for
|
|
// the player to not be looking at the spawner, so shut that thread down
|
|
level notify( "kill_hidden_reinforcement_waiting" );
|
|
|
|
// only want one after this point
|
|
redshirts = get_nonhero_friends();
|
|
|
|
singleguy = redshirts[ 0 ];
|
|
if( IsDefined( singleguy ) )
|
|
{
|
|
singleguy set_force_color( "p" );
|
|
}
|
|
|
|
if( redshirts.size > 1 )
|
|
{
|
|
redshirts = array_remove( redshirts, singleguy );
|
|
array_thread( redshirts, ::friend_remove );
|
|
array_thread( redshirts, ::disable_replace_on_death );
|
|
array_thread( redshirts, ::scr_set_health, 1 );
|
|
array_thread( redshirts, ::bloody_death_after_min_delay, 10, 10 );
|
|
|
|
// in case the player runs fast ahead, failsafe so these guys don't try to move up on the color chains that don't have enough nodes to accomodate them
|
|
trigger_wait_targetname( "trig_market_redshirts_remove_failsafe" );
|
|
redshirts = array_removedead( redshirts );
|
|
array_thread( redshirts, ::disable_ai_color );
|
|
}
|
|
}
|
|
|
|
market_hero1_change_color()
|
|
{
|
|
trigger_wait_targetname( "trig_market_redshirts_remove_failsafe" );
|
|
// ghost becomes a blue guy
|
|
level.hero1 set_force_color( "b" );
|
|
}
|
|
|
|
market_door1()
|
|
{
|
|
trigger_wait_targetname( "trig_market_door1" );
|
|
|
|
door1_spawners = GetEntArray( "spawner_market_door_1", "targetname" );
|
|
door1 = GetEnt( "sbmodel_market_door_1", "targetname" );
|
|
door1_physicsRef = GetStruct( "struct_physicsref_market_door1", "targetname" );
|
|
door1_animRef = GetStruct( "struct_animref_market_door1_kick", "targetname" );
|
|
|
|
door_kick_housespawn( door1_spawners, door1, door1_animRef, door1_physicsRef );
|
|
}
|
|
|
|
|
|
// ------------------
|
|
// --- MARKET EVAC---
|
|
// ------------------
|
|
market_evac_dialogue()
|
|
{
|
|
flag_wait( "market_evac_insidepath_start" );
|
|
|
|
battlechatter_off( "allies" );
|
|
|
|
// "Nikolai! ETA 20 seconds! Be ready for immediate dustoff!"
|
|
level.sarge dialogue( "favesc_cmt_immediatedustoff" );
|
|
|
|
// "That may not be fast enough! I see more militia closing in on the market!"
|
|
radio_dialogue( "favesc_nkl_notfastenough" );
|
|
|
|
// "Pick up the pace! Let's go!"
|
|
level.sarge dialogue( "favesc_cmt_pickuppace" );
|
|
|
|
flag_wait( "market_evac_chopper_incoming" );
|
|
|
|
// "It's too hot! We will not survive this landing!"
|
|
radio_dialogue( "favesc_nkl_toohot" );
|
|
|
|
delaythread( 2, ::favesc_waveoff_music );
|
|
// "Nikolai, wave off, wave off! We'll meet you at the secondary LZ instead! Go!"
|
|
level.sarge dialogue( "favesc_cmt_waveoff" );
|
|
|
|
// "Very well, I will meet you there! Good luck!"
|
|
radio_dialogue( "favesc_nkl_meetyouthere" );
|
|
}
|
|
|
|
spawn_chopper( spawngroup, followPath )
|
|
{
|
|
if( !IsDefined( followPath ) )
|
|
{
|
|
followPath = true;
|
|
}
|
|
|
|
arr = [];
|
|
|
|
if( followPath )
|
|
{
|
|
arr = maps\_vehicle::create_vehicle_from_spawngroup_and_gopath( spawngroup );
|
|
}
|
|
else
|
|
{
|
|
arr = maps\_vehicle::scripted_spawn( spawngroup );
|
|
}
|
|
chopper = arr[ 0 ];
|
|
ASSERT( IsDefined( chopper ) );
|
|
|
|
chopper.health = 2000000;
|
|
Missile_CreateRepulsorEnt( chopper, 1150, 850 );
|
|
|
|
return chopper;
|
|
}
|
|
|
|
market_evac_chopper()
|
|
{
|
|
chopper = spawn_chopper( 4 );
|
|
level.chopper = chopper;
|
|
|
|
flag_set( "market_chopper_spawned" );
|
|
|
|
//chopper SetYawSpeed( 45, 20, 45, 0 );
|
|
chopper SetMaxPitchRoll( 30, 40 );
|
|
|
|
// wait for player to get near the soccer field entrance
|
|
flag_wait( "market_evac_player_near_soccerfield" );
|
|
|
|
level.chopper Delete();
|
|
|
|
// new chopper path
|
|
chopper = spawn_chopper( 6, false );
|
|
level.chopper = chopper;
|
|
|
|
flag_set( "market_evac_chopper_spawned" );
|
|
|
|
path2start = GetStruct( "struct_market_evac_chopper_path2", "targetname" );
|
|
chopper thread market_evac_chopper_bugout_path( path2start );
|
|
}
|
|
|
|
// jiesang magic
|
|
market_evac_chopper_bugout_path( start )
|
|
{
|
|
self thread play_sound_on_entity( "scn_favela_escape_heli_land" );
|
|
|
|
flag_set( "market_evac_chopper_incoming" );
|
|
|
|
spot2 = GetStruct( start.target, "targetname" );
|
|
spot3 = GetStruct( spot2.target, "targetname" );
|
|
|
|
spot4 = GetStruct( spot3.target, "targetname" );
|
|
spot5 = GetStruct( spot4.target, "targetname" );
|
|
|
|
self Vehicle_SetSpeedImmediate( 30 );
|
|
self Vehicle_SetSpeed( 30, 15, 5 );
|
|
self SetVehGoalPos( spot2.origin, 1 );
|
|
self SetMaxPitchRoll( 30, 25 );
|
|
|
|
self SetNearGoalNotifyDist( 256 );
|
|
self waittill( "near_goal" );
|
|
self SetGoalYaw( spot2.angles[1] );
|
|
|
|
self waittill( "goal" );
|
|
wait( 1 );
|
|
|
|
flag_set( "market_evac_chopper_bugout" );
|
|
|
|
//self waittill( "goal_yaw" );
|
|
|
|
self setVehGoalPos( spot3.origin );
|
|
self Vehicle_SetSpeed( 60, 10 );
|
|
self SetNearGoalNotifyDist( 600 );
|
|
|
|
self waittill( "near_goal" );
|
|
self setVehGoalPos( spot4.origin );
|
|
|
|
flag_set( "market_evac_chopper_leaves_scene" );
|
|
|
|
delaythread( 10, ::market_evac_escape_walla );
|
|
|
|
self waittill( "near_goal" );
|
|
self setVehGoalPos( spot5.origin );
|
|
|
|
self waittill( "goal" );
|
|
|
|
self notify( "death" );
|
|
self Delete();
|
|
|
|
level.chopper = undefined;
|
|
}
|
|
|
|
market_evac_enemy_foreshadowing()
|
|
{
|
|
flag_wait( "market_evac_insidepath_start" );
|
|
|
|
killflag = "market_evac_ambush_start";
|
|
|
|
lines = [];
|
|
// "You're going to pay for what you did to my brother, soldier! You hear me?"
|
|
lines[ lines.size ] = "favesc_pe1_youhearme";
|
|
// "Eduardo, call the truck! And bring more grenades! We're going to flush them out of this building!"
|
|
lines[ lines.size ] = "favesc_pe2_callthetruck";
|
|
// "We're going to kill you slowly for what you've done here. Do you hear me?"
|
|
lines[ lines.size ] = "favesc_pe3_killslowly";
|
|
// "Get on the roof of those buildings and go around them!! They can't stop all of us!!"
|
|
lines[ lines.size ] = "favesc_pe3_cantstop";
|
|
// "They have a helicopter!! Bring more rockets!!! We can shoot it down!!"
|
|
lines[ lines.size ] = "favesc_pe2_morerockets";
|
|
// "There is nowhere for you to run!!! We will find you wherever you go!!"
|
|
lines[ lines.size ] = "favesc_pe4_wellfindyou";
|
|
// "If you capture them, keep them alive so we can use the machete!!!"
|
|
lines[ lines.size ] = "favesc_pe1_keepthemalive";
|
|
|
|
firstWaitMax = 3.5;
|
|
lineWaitMin = 8;
|
|
lineWaitMax = 13;
|
|
|
|
spots = GetStructArray( "struct_market_evac_foreshadow_dialoguespot", "targetname" );
|
|
playOrg = Spawn( "script_origin", ( 0, 0, 0 ) );
|
|
|
|
wait( RandomFloat( firstWaitMax ) );
|
|
|
|
while( !flag( killflag ) )
|
|
{
|
|
lines = array_randomize( lines );
|
|
spots = array_randomize( spots );
|
|
spotIndex = 0;
|
|
|
|
foreach( line in lines )
|
|
{
|
|
if( flag( killflag ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
playOrg.origin = spots[ spotIndex ].origin;
|
|
playOrg PlaySound( line, "sound_done" );
|
|
playOrg waittill( "sound_done" );
|
|
|
|
if( spotIndex == ( spots.size - 1 ) )
|
|
{
|
|
spotIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
spotIndex++;
|
|
}
|
|
|
|
wait( RandomFloatRange( lineWaitMin, lineWaitMax ) );
|
|
}
|
|
}
|
|
|
|
playOrg Delete();
|
|
}
|
|
|
|
market_evac_fakefire_smallarms()
|
|
{
|
|
spots = GetStructArray( "market_evac_ambush_rpg_firespot", "targetname" );
|
|
array_thread( spots, ::fakefire_smallarms_spot, "market_evac_chopper_leaves_scene" );
|
|
}
|
|
|
|
fakefire_smallarms_spot( flagname )
|
|
{
|
|
if( !IsDefined( flagname ) )
|
|
{
|
|
if( IsDefined( self.script_flag ) )
|
|
{
|
|
flagname = self.script_flag;
|
|
}
|
|
}
|
|
|
|
if( flagname == "none" )
|
|
{
|
|
return;
|
|
}
|
|
|
|
ASSERT( IsDefined( flagname ) );
|
|
|
|
spot = self;
|
|
|
|
weapons = level.scriptedweapons;
|
|
|
|
burstShotsMin = 5;
|
|
burstShotsMax = 12;
|
|
|
|
shotWaitMin = 0.1;
|
|
shotWaitMax = 0.15;
|
|
burstWaitMin = 0.5;
|
|
burstWaitMax = 1;
|
|
|
|
start = spot.origin;
|
|
|
|
while( !flag( flagname ) && IsDefined( level.chopper ) )
|
|
{
|
|
weapon = get_random( weapons );
|
|
burstShots = RandomIntRange( 5, 12 );
|
|
|
|
for( i = 0; i < burstShots; i++ )
|
|
{
|
|
if( !IsDefined( level.chopper ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
x = level.chopper.origin[ 0 ] + RandomIntRange( -256, 256 );
|
|
y = level.chopper.origin[ 1 ] + RandomIntRange( -256, 256 );
|
|
z = level.chopper.origin[ 2 ] + RandomIntRange( -256, 0 );
|
|
targetOrigin = ( x, y, z );
|
|
|
|
angles = VectorToAngles( targetOrigin - start );
|
|
forward = AnglesToForward( angles );
|
|
vec = vector_multiply( forward, 12 );
|
|
end = start + vec;
|
|
|
|
// make sure we're not going to hit sarge or the player
|
|
trace = BulletTrace( spot.origin, end, true );
|
|
traceEnt = trace[ "entity" ];
|
|
|
|
if( IsDefined( traceEnt ) )
|
|
{
|
|
if( IsDefined( level.sarge ) )
|
|
{
|
|
if( traceEnt == level.sarge )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if( IsPlayer( traceEnt ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
|
|
MagicBullet( weapon, spot.origin, end );
|
|
|
|
wait( RandomFloatRange( shotWaitMin, shotWaitMax ) );
|
|
}
|
|
|
|
wait( RandomFloatRange( burstWaitMin, burstWaitMax ) );
|
|
}
|
|
}
|
|
|
|
market_evac_fakefire_rpgs()
|
|
{
|
|
allSpots = GetStructArray( "market_evac_ambush_rpg_firespot", "targetname" );
|
|
|
|
// the ones with script_start set should go first
|
|
fireSpots = [];
|
|
foreach( spot in allSpots )
|
|
{
|
|
if( IsDefined( spot.script_start ) )
|
|
{
|
|
fireSpots[ fireSpots.size ] = spot;
|
|
}
|
|
}
|
|
|
|
// bubble sort by script_start so the highest ones go first
|
|
lastIndex = fireSpots.size - 1;
|
|
for( i = 0; i < lastIndex; i++ ) // sort needs to add one to compare so the max is size-1
|
|
{
|
|
for( j = 0; j < lastIndex - i; j++ ) // don't look past the number of previous sorts, since they will be lowest
|
|
{
|
|
if( fireSpots[ j + 1 ].script_start < fireSpots[ j ].script_start )
|
|
{
|
|
temp = fireSpots[ j ];
|
|
fireSpots[ j ] = fireSpots[ j + 1 ];
|
|
fireSpots[ j + 1 ] = temp;
|
|
}
|
|
}
|
|
}
|
|
|
|
// the rest get randomized and added separately
|
|
allSpots = array_remove_array( allSpots, fireSpots );
|
|
allSpots = array_randomize( allSpots );
|
|
foreach( spot in allSpots )
|
|
{
|
|
fireSpots[ fireSpots.size ] = spot;
|
|
}
|
|
|
|
foreach( spot in fireSpots )
|
|
{
|
|
/*
|
|
if( player_can_see( spot.origin ) )
|
|
{
|
|
continue;
|
|
}
|
|
*/
|
|
|
|
target = GetStruct( spot.target, "targetname" );
|
|
MagicBullet( "rpg_straight", spot.origin, target.origin );
|
|
|
|
wait( RandomFloatRange( 0.8, 1.5 ) );
|
|
}
|
|
|
|
while( !flag( "market_evac_chopper_leaves_scene" ) && IsDefined( level.chopper ) )
|
|
{
|
|
fireSpots = array_randomize( fireSpots );
|
|
|
|
foreach( spot in fireSpots )
|
|
{
|
|
if( flag( "market_evac_chopper_leaves_scene" ) || !IsDefined( level.chopper ) )
|
|
{
|
|
break;
|
|
}
|
|
|
|
if( player_can_see( spot.origin ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
|
|
target = level.chopper;
|
|
MagicBullet( "rpg_straight", spot.origin, target.origin );
|
|
|
|
wait( RandomFloatRange( 0.8, 1.5 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
// this is to remove the clip that helps AIs get up on buildings in the roof run area during the market evac event
|
|
market_evac_remove_helperclip()
|
|
{
|
|
clips = level.market_evac_helperClips;
|
|
foreach( clip in clips )
|
|
{
|
|
clip DisconnectPaths();
|
|
clip Delete();
|
|
}
|
|
}
|
|
|
|
market_evac_enemies()
|
|
{
|
|
ambushers = GetEntArray( "ai_market_evac_ambusher", "script_noteworthy" );
|
|
array_thread( ambushers, ::add_spawn_function, ::market_evac_enemy_spawnfunc );
|
|
|
|
wait( 1.5 );
|
|
trigger_activate_targetname( "trig_market_evac_spawn1" );
|
|
|
|
/*
|
|
spawners_rpgs = GetEntArray( "spawner_market_evac_ambush_rpg", "targetname" );
|
|
rpgers = spawn_group( spawners_rpgs );
|
|
*/
|
|
|
|
flag_init( "housespawners_done" );
|
|
|
|
delaythread( 9, ::market_evac_housespawners );
|
|
|
|
flag_wait( "housespawners_done" );
|
|
wait( 0.05 );
|
|
|
|
numGuysAliveBeforeContinuing = 2;
|
|
|
|
while( 1 )
|
|
{
|
|
marketguys = market_evac_get_active_enemies();
|
|
|
|
guysFound = 0;
|
|
|
|
foreach( guy in marketguys )
|
|
{
|
|
if( IsAlive( guy ) && !guy doingLongDeath() )
|
|
{
|
|
guysFound++;
|
|
}
|
|
}
|
|
|
|
if( guysFound <= numGuysAliveBeforeContinuing )
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
}
|
|
|
|
guys = market_evac_get_active_enemies();
|
|
level thread kill_group_over_time( guys, 4 );
|
|
|
|
flag_set( "market_evac_enemies_depleted" );
|
|
}
|
|
|
|
market_evac_get_active_enemies()
|
|
{
|
|
ais = GetAiArray( "axis" );
|
|
ais = array_removedead( ais );
|
|
|
|
marketguys = [];
|
|
foreach( guy in ais )
|
|
{
|
|
if( IsDefined( guy.script_noteworthy ) && guy.script_noteworthy == "ai_market_evac_ambusher" )
|
|
{
|
|
marketguys[ marketguys.size ] = guy;
|
|
}
|
|
}
|
|
|
|
return marketguys;
|
|
}
|
|
|
|
market_evac_enemy_spawnfunc()
|
|
{
|
|
self endon( "death" );
|
|
|
|
// make these guys less accurate so the event is easier
|
|
self.baseaccuracy = self.baseaccuracy * 0.35;
|
|
|
|
if( IsDefined( level.chopper ) )
|
|
{
|
|
self SetEntityTarget( level.chopper, 0.4 );
|
|
}
|
|
|
|
wait( 15 );
|
|
self playerseek();
|
|
}
|
|
|
|
market_evac_housespawners()
|
|
{
|
|
door1_spawners = GetEntArray( "spawner_market_evac_door1", "targetname" );
|
|
door2_spawners = GetEntArray( "spawner_market_evac_door2", "targetname" );
|
|
door3_spawners = GetEntArray( "spawner_market_evac_door3", "targetname" );
|
|
door1 = GetEnt( "sbmodel_market_evac_door1", "targetname" );
|
|
door2 = GetEnt( "sbmodel_market_evac_door2", "targetname" );
|
|
door3 = GetEnt( "sbmodel_market_evac_door3", "targetname" );
|
|
kickRef1 = GetStruct( "struct_animref_market_evac_kick_door1", "targetname" );
|
|
kickRef2 = GetStruct( "struct_animref_market_evac_kick_door2", "targetname" );
|
|
kickRef3 = GetStruct( "struct_animref_market_evac_kick_door3", "targetname" );
|
|
|
|
thread door_kick_housespawn( door3_spawners, door3, kickRef3 );
|
|
delaythread( 2, ::door_kick_housespawn, door1_spawners, door1, kickRef1 );
|
|
delaythread( 4, ::door_kick_housespawn, door2_spawners, door2, kickRef2 );
|
|
|
|
wait( 6 );
|
|
flag_set( "housespawners_done" );
|
|
}
|
|
|
|
market_evac_bugplayer()
|
|
{
|
|
stopflag = "market_evac_player_on_roof";
|
|
level endon( stopflag );
|
|
|
|
lines = [];
|
|
// "Roach! Get up here on the rooftops, let's go!"
|
|
lines[ lines.size ] = "favesc_cmt_getuphere";
|
|
// "Roach! Get over here and climb up to the rooftops!"
|
|
lines[ lines.size ] = "favesc_cmt_climbup";
|
|
// "Roach! You can climb up over here!"
|
|
lines[ lines.size ] = "favesc_cmt_climbuphere";
|
|
|
|
while( !flag( stopflag ) && IsDefined( level.sarge ) )
|
|
{
|
|
lines = array_randomize( lines );
|
|
|
|
foreach( line in lines )
|
|
{
|
|
while( Distance( level.player.origin, level.sarge.origin ) < 256 )
|
|
{
|
|
wait( 1 );
|
|
}
|
|
|
|
if( !flag( stopflag ) )
|
|
{
|
|
level.sarge dialogue( line );
|
|
wait( 20 );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
market_evac_playermantle_watch( zTest )
|
|
{
|
|
while( level.player.origin[ 2 ] < zTest )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
flag_set( "market_evac_player_mantled" );
|
|
}
|
|
|
|
market_evac_playermantle_helper( zTest )
|
|
{
|
|
trig = GetEnt( "trig_market_evac_mantlehelper", "targetname" );
|
|
|
|
NotifyOnCommand( "mantle", "+gostand" );
|
|
NotifyOnCommand( "mantle", "+moveup" );
|
|
|
|
while( 1 )
|
|
{
|
|
if( level.player IsTouching( trig ) && !level.player CanMantle() && level.player.origin[2] < zTest )
|
|
{
|
|
SetSavedDvar( "hud_forceMantleHint", 1 );
|
|
|
|
while( level.player IsTouching( trig ) )
|
|
{
|
|
level.player player_mantle_wait( trig );
|
|
level.player ForceMantle();
|
|
SetSavedDvar( "hud_forceMantleHint", 0 );
|
|
|
|
// wait for mantle to be done
|
|
while( !level.player IsOnGround() )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetSavedDvar( "hud_forceMantleHint", 0 );
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
player_mantle_wait( trig )
|
|
{
|
|
self endon( "left_trigger" );
|
|
|
|
self thread player_left_trigger_notify( trig );
|
|
self waittill( "mantle" );
|
|
}
|
|
|
|
player_left_trigger_notify( trig )
|
|
{
|
|
self endon( "mantle" );
|
|
|
|
while( self IsTouching( trig ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self notify( "left_trigger" );
|
|
}
|
|
|
|
|
|
// ----------------
|
|
// --- ROOF RUN ---
|
|
// ----------------
|
|
roofrun_dialogue()
|
|
{
|
|
// "Let's go, let's go!!"
|
|
level.sarge dialogue( "favesc_cmt_letsgoletsgo" );
|
|
|
|
// "My friend, from up here, it looks like the whole village is trying to kill you!"
|
|
radio_dialogue( "favesc_nkl_wholevillage" );
|
|
|
|
// "Tell me something I don't know! Just get ready to pick us up at the secondary RV!"
|
|
level.sarge dialogue( "favesc_cmt_pickusup" );
|
|
|
|
// DEPRECATED - not enough time to use this one
|
|
// "Ok, I will pick you up soon, keep going!"
|
|
//radio_dialogue( "favesc_nkl_keepgoing" );
|
|
|
|
// "We're running out of rooftop!!!"
|
|
level.hero1 dialogue( "favesc_gst_runoutofroof" );
|
|
// (add after this line so the scr_sound doesn't stomp on the scripted dialogue if the timing works out that way)
|
|
// "Unghh!"
|
|
level.scr_sound[ "freerunner" ][ "favela_escape_bigjump_ghost" ] = "favesc_gst_jumpsfx";
|
|
|
|
// "We can make it! Go go go!"
|
|
level.sarge dialogue( "favesc_cmt_makeitgogo" );
|
|
// "Uraaa!"
|
|
level.scr_sound[ "freerunner" ][ "favela_escape_bigjump_soap" ] = "favesc_cmt_jumpsfx";
|
|
|
|
if( !flag( "player_jump_watcher_stop" ) && !flag( "player_fell_normally" ) )
|
|
{
|
|
thread roofrun_nag_dialogue();
|
|
}
|
|
}
|
|
|
|
roofrun_nag_dialogue()
|
|
{
|
|
// end when jumping or falling off the roof
|
|
level endon( "player_fell_normally" );
|
|
level endon( "player_jump_watcher_stop" );
|
|
|
|
nags = [];
|
|
// "C'mon Roach, we're almost at the chopper!"
|
|
nags[ 0 ] = "favesc_cmt_gettochopper";
|
|
// "Roach, what's the holdup, let's go!"
|
|
nags[ 1 ] = "favesc_cmt_whatsholdup";
|
|
// "We've got to get out of here, Roach, come on!"
|
|
nags[ 2 ] = "favesc_cmt_getoutta";
|
|
|
|
nagIdx = 0;
|
|
minWait = 10000;
|
|
nextTime = -1;
|
|
while( 1 )
|
|
{
|
|
wait( 0.1 );
|
|
|
|
lowSpeed = false;
|
|
timeForNewLine = false;
|
|
|
|
vel = level.player GetVelocity();
|
|
// figure out the length of the vector to get the speed (distance from world center = length)
|
|
speed = Distance( ( vel[ 0 ], vel[ 1 ], 0 ), ( 0, 0, 0 ) ); // don't care about Z velocity
|
|
|
|
if( speed < 125 )
|
|
{
|
|
lowSpeed = true;
|
|
}
|
|
|
|
if( GetTime() >= nextTime )
|
|
{
|
|
timeForNewLine = true;
|
|
}
|
|
|
|
if( !lowSpeed || !timeForNewLine )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
level.sarge dialogue( nags[ nagIdx ] );
|
|
nextTime = GetTime() + minWait;
|
|
|
|
nagIdx++;
|
|
if( nagIdx >= nags.size )
|
|
{
|
|
nagIdx = 0;
|
|
}
|
|
}
|
|
}
|
|
|
|
roofrun_waitfor_finish()
|
|
{
|
|
while( level.runnersDone < level.friends.size )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
flag_set( "roofrun_done" );
|
|
}
|
|
|
|
roofrun_player_bigjump()
|
|
{
|
|
player = level.player;
|
|
|
|
jumpstart_trig = GetEnt( "trig_roofrun_player_bigjump_start", "targetname" );
|
|
edgeref = GetStruct( "struct_player_bigjump_edge_reference", "targetname" );
|
|
groundref = GetStruct( "struct_player_recovery_animref", "targetname" );
|
|
|
|
jumpForward = AnglesToForward( edgeref.angles );
|
|
thread player_jump_watcher();
|
|
|
|
// takes care of a player who missed the cool animated jump and just fell
|
|
thread player_normalfall_watcher();
|
|
level endon( "player_fell_normally" );
|
|
|
|
while( 1 )
|
|
{
|
|
breakout = false;
|
|
|
|
while( level.player IsTouching( jumpstart_trig ) )
|
|
{
|
|
flag_wait( "player_jumping" );
|
|
if( player_leaps( jumpstart_trig, jumpForward, 0.915, true ) ) // 0.925
|
|
{
|
|
breakout = true;
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
if( breakout )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
flag_set( "player_jump_watcher_stop" );
|
|
|
|
thread favesc_falling_music();
|
|
|
|
falltrig = GetEnt( "trig_roofrun_playerjump_falltrig", "targetname" );
|
|
falltrig Delete();
|
|
|
|
flag_set( "roofrun_player_bigjump_start" );
|
|
|
|
player TakeAllWeapons();
|
|
|
|
animname = "player_bigjump";
|
|
jump_scene = "jump";
|
|
anime_jump = level.scr_anim[ animname ][ jump_scene ];
|
|
|
|
// figure out our start point
|
|
// move the edge reference to the player location, this math only works because we are moving it in a cardinal direction
|
|
startpoint = ( edgeref.origin[ 0 ], player.origin[ 1 ], edgeref.origin[ 2 ] );
|
|
startangles = edgeref.angles;
|
|
animstartorigin = GetStartOrigin( startpoint, startangles, anime_jump );
|
|
animstartangles = GetStartAngles( startpoint, startangles, anime_jump );
|
|
|
|
// spawn our animref spot
|
|
animref = Spawn( "script_origin", startpoint );
|
|
animref.angles = startangles;
|
|
|
|
// spawn & set up the rig
|
|
player_rig = spawn_anim_model( animname, animstartorigin );
|
|
player_rig.angles = animstartangles;
|
|
player_rig Hide();
|
|
|
|
// don't show the rig until after the player has blended into the animation
|
|
thread bigjump_player_blend_to_anim( player_rig );
|
|
player_rig delaycall( 1.0, ::Show );
|
|
|
|
// set up the part that falls away
|
|
thread bigjump_roof_anim( player_rig, groundref );
|
|
|
|
animref thread anim_single_solo( player_rig, jump_scene );
|
|
thread roofrun_player_bigjump_rumble();
|
|
|
|
// soap tries to catch player
|
|
player_rig waittillmatch( "single anim", "start_soap" );
|
|
|
|
level.player FreezeControls( true );
|
|
|
|
level.sarge thread scr_animplaybackrate( 1 );
|
|
|
|
// if Soap isn't ahead of the player, don't include him in the animation
|
|
if( flag( "roofrun_sarge_bigjumped" ) )
|
|
{
|
|
thread bigjump_slowmo_controls();
|
|
|
|
flag_set( "bigjump_sargeplayer_interact_start" );
|
|
|
|
if( IsDefined( level.sarge.animlooporg ) )
|
|
{
|
|
level.sarge.animlooporg notify( "stop_loop" );
|
|
}
|
|
|
|
animref thread anim_single_solo( level.sarge, "favela_escape_bigjump_soap_reach" );
|
|
}
|
|
|
|
// level progression
|
|
thread roofrun_player_recovery( player_rig, groundref );
|
|
|
|
if( flag( "roofrun_sarge_bigjumped" ) )
|
|
{
|
|
level.sarge waittillmatch( "single anim", "end" );
|
|
wait( 1 );
|
|
}
|
|
else
|
|
{
|
|
wait( 5 );
|
|
}
|
|
|
|
animref Delete();
|
|
}
|
|
|
|
roofrun_player_bigjump_rumble()
|
|
{
|
|
wait( 1.25 );
|
|
level.player PlayRumbleOnEntity( "artillery_rumble" );
|
|
wait( 0.25 );
|
|
endTime = GetTime() + milliseconds( 0.4 );
|
|
while( GetTime() < endTime )
|
|
{
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
wait( 0.115 );
|
|
}
|
|
|
|
wait( 0.1 );
|
|
|
|
level.player PlayRumbleOnEntity( "artillery_rumble" );
|
|
}
|
|
|
|
bigjump_slowmo_controls()
|
|
{
|
|
wait( 0.5 );
|
|
slowmo_start();
|
|
slowmo_setspeed_slow( 0.3 ); // 0.1 console
|
|
slowmo_setlerptime_in( 0.15 ); // 0.25 console
|
|
slowmo_lerp_in();
|
|
|
|
wait( 0.6 ); // 0.7 console
|
|
|
|
slowmo_setlerptime_out( 0.15 ); // 0.2 console
|
|
slowmo_lerp_out();
|
|
slowmo_end();
|
|
}
|
|
|
|
player_normalfall_watcher()
|
|
{
|
|
level endon( "player_jump_watcher_stop" );
|
|
|
|
trig = GetEnt( "trig_roofrun_playerjump_falltrig", "targetname" );
|
|
trig waittill( "trigger" );
|
|
trig Delete();
|
|
|
|
flag_set( "player_fell_normally" );
|
|
|
|
// alternate level progression
|
|
thread roofrun_player_recovery();
|
|
}
|
|
|
|
roofrun_player_recovery( player_rig, groundref )
|
|
{
|
|
// either we got here after the animated jump...
|
|
if( IsDefined( player_rig ) )
|
|
{
|
|
player_rig waittillmatch( "single anim", "blackout" );
|
|
}
|
|
// ...or we fell
|
|
else
|
|
{
|
|
level.player EnableInvulnerability(); // don't die from the fall
|
|
|
|
while( !level.player IsOnGround() )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
music_stop();
|
|
|
|
level.player FreezeControls( true );
|
|
|
|
level.player DisableInvulnerability();
|
|
}
|
|
|
|
// rumble for hitting the ground
|
|
level.player PlayRumbleLoopOnEntity( "artillery_rumble" );
|
|
level.player delaycall( 1, ::StopRumble, "artillery_rumble" );
|
|
|
|
blacktime = 6.5;
|
|
thread bigjump_angrymob( blacktime );
|
|
thread bigjump_dialogue( blacktime );
|
|
thread bigjump_heartbeat( blacktime );
|
|
|
|
shockfile = "favela_escape_player_recovery";
|
|
level.player Shellshock( shockfile, blacktime + 0.1 );
|
|
|
|
if( !IsDefined( groundref ) )
|
|
{
|
|
groundref = GetStruct( "struct_player_recovery_animref", "targetname" );
|
|
}
|
|
|
|
SetSavedDvar( "objectiveHide", 1 );
|
|
|
|
level thread favesc_finalrun_music();
|
|
black_overlay = maps\_hud_util::create_client_overlay( "black", 0, level.player );
|
|
black_overlay.alpha = 1;
|
|
level.player allowcrouch( false );
|
|
level.player AllowProne( false );
|
|
level.player setstance( "stand" );
|
|
|
|
|
|
flag_set( "player_recovery_blackscreen" );
|
|
|
|
level.player TakeAllWeapons();
|
|
|
|
animname = "player_bigjump";
|
|
recover_scene = "recover";
|
|
recover_animation = level.scr_anim[ animname ][ recover_scene ];
|
|
animstartorigin = GetStartOrigin( groundref.origin, groundref.angles, recover_animation );
|
|
animstartangles = GetStartAngles( groundref.origin, groundref.angles, recover_animation );
|
|
|
|
// if the player fell, we need to set up the rig for the first time
|
|
if( !IsDefined( player_rig ) )
|
|
{
|
|
player_rig = spawn_anim_model( "player_bigjump", animstartorigin );
|
|
player_rig.angles = animstartangles;
|
|
level.player PlayerLinkToBlend( player_rig, "tag_player", 0.05 );
|
|
}
|
|
|
|
// stop friends from looping anims before deleting them
|
|
foreach( guy in level.friends )
|
|
{
|
|
guy notify( "stop_loop" );
|
|
|
|
if( IsDefined( guy.animlooporg ) )
|
|
{
|
|
guy.animlooporg notify( "stop_loop" );
|
|
}
|
|
}
|
|
delaythread( 0.05, ::delete_all_friends );
|
|
|
|
// delete chopper
|
|
chopper = get_vehicle( "veh_chopper_roofrun", "targetname" );
|
|
if( IsDefined( chopper ) )
|
|
{
|
|
chopper Delete();
|
|
}
|
|
|
|
thread vision_set_fog_changes( "favela_escape_playerfall_recovery", 1 );
|
|
wait( blacktime );
|
|
|
|
flag_set( "player_bigjump_done" );
|
|
|
|
groundref thread anim_single_solo( player_rig, recover_scene );
|
|
|
|
animtime = GetAnimLength( level.scr_anim[ "player_bigjump" ][ "recover" ] );
|
|
thread solorun_recovery_objective_update_flagset( animtime );
|
|
thread player_bigjump_recovery_vfx( animtime, shockfile );
|
|
thread recovery_fov_change();
|
|
|
|
wait( 0.1 );
|
|
black_overlay FadeOverTime( 1 );
|
|
black_overlay.alpha = 0;
|
|
|
|
delaythread( ( animtime - 2 ), ::bigjump_save );
|
|
|
|
// wait for recovery anim end
|
|
player_rig waittillmatch( "single anim", "end" );
|
|
level.player allowcrouch( true );
|
|
level.player AllowProne( true );
|
|
|
|
thread vision_set_fog_changes( "favela_escape", 1.5 );
|
|
|
|
flag_set( "player_recovery_done" );
|
|
|
|
black_overlay Destroy();
|
|
|
|
level.player Unlink();
|
|
level.player FreezeControls( false );
|
|
|
|
player_rig Delete();
|
|
}
|
|
|
|
solorun_recovery_objective_update_flagset( animTime )
|
|
{
|
|
waitTime = animTime * 0.75;
|
|
wait( waitTime );
|
|
flag_set( "solorun_objective_display" );
|
|
SetSavedDvar( "objectiveHide", 0 );
|
|
}
|
|
|
|
recovery_fov_change()
|
|
{
|
|
setsaveddvar( "cg_fov", 30 );
|
|
wait( 5 );
|
|
lerp_fov_overtime( 2, 40 );
|
|
//setsaveddvar( "cg_fov", 40 );
|
|
wait( 2.2 );
|
|
//wait( 9.2 );
|
|
lerp_fov_overtime( 2, 65 );
|
|
}
|
|
|
|
|
|
bigjump_save()
|
|
{
|
|
// we can force save since we are in a known state of goodness and safeness
|
|
SaveGame( "bigjump_recovery", &"AUTOSAVE_AUTOSAVE" );
|
|
}
|
|
|
|
roofrun_modulate_playerspeed( targetEnt )
|
|
{
|
|
// these are the distance ranges from the targetEnt that will trigger speed changes
|
|
distMin = 175;
|
|
distMax = 300;
|
|
distCatchup = 425;
|
|
|
|
// allowed run speed range
|
|
runSpeedMin = 80;
|
|
runSpeedMax = 90;
|
|
runSpeedCatchup = 95;
|
|
|
|
// allowed sprint multiplier range
|
|
sprintMultMin = 1.2;
|
|
sprintMultMax = 1.4;
|
|
sprintMultCatchup = 1.5;
|
|
|
|
interval = 0.5;
|
|
|
|
playerFailDist = 850;
|
|
playerFailDmg = 75;
|
|
|
|
while( !flag( "roofrun_done" ) )
|
|
{
|
|
distFromTarget = get_dist_to_plane_nearest_player( targetEnt );
|
|
|
|
println( distFromTarget );
|
|
|
|
/* do damage if the player is too far back
|
|
if( distFromTarget > playerFailDist )
|
|
{
|
|
forward = AnglesToForward( level.player GetPlayerAngles() );
|
|
backward = vector_multiply( forward, -1 );
|
|
dmgOrigin = level.player.origin + ( 0, 0, 50 );
|
|
dmgOrigin += vector_multiply( backward, 5000 );
|
|
|
|
level.player DoDamage( playerFailDmg, dmgOrigin );
|
|
}
|
|
*/
|
|
|
|
// init to something
|
|
targetRunSpeed = runSpeedMax;
|
|
targetSprintMult = sprintMultMax;
|
|
|
|
// if past max distance, give a speed boost above established maximums
|
|
if( distFromTarget > distCatchup )
|
|
{
|
|
targetRunSpeed = runSpeedCatchup;
|
|
targetSprintMult = sprintMultCatchup;
|
|
println( "CATCHUP" );
|
|
}
|
|
// otherwise scale speed based on how far away the player is
|
|
else
|
|
{
|
|
fraction = get_fraction( distFromTarget, distMin, distMax );
|
|
|
|
runRange = runSpeedMax - runSpeedMin; // figure out the range of values that we will use to modify the max value
|
|
runRangeFrac = runRange * ( 1 - fraction ); // figure out where in the range the fraction puts us
|
|
targetRunSpeed = runSpeedMax - runRangeFrac; // modify the max speed by the fractional range value
|
|
|
|
sprintRange = sprintMultMax - sprintMultMin;
|
|
sprintRangeFrac = sprintRange * ( 1 - fraction );
|
|
targetSprintMult = sprintMultMax - sprintRangeFrac;
|
|
|
|
if( distFromTarget >= distMax )
|
|
{
|
|
println( "MAX" );
|
|
}
|
|
else if( distFromTarget <= distMin )
|
|
{
|
|
println( "MIN" );
|
|
}
|
|
}
|
|
|
|
player_speed_percent( targetRunSpeed, interval );
|
|
player_sprint_multiplier_blend( targetSprintMult, interval );
|
|
|
|
wait( interval );
|
|
}
|
|
}
|
|
|
|
get_dist_to_plane_nearest_player( ent )
|
|
{
|
|
P = level.player.origin;
|
|
A = ent.origin + ( AnglesToRight( ent.angles ) * -5000 );
|
|
B = ent.origin + ( AnglesToRight( ent.angles ) * 5000 );
|
|
|
|
nearestOrigin = PointOnSegmentNearestToPoint( A, B, P );
|
|
return Distance( P, nearestOrigin );
|
|
}
|
|
|
|
get_fraction( value, min, max )
|
|
{
|
|
if( value >= max )
|
|
{
|
|
return 1.0;
|
|
}
|
|
|
|
if( value <= min )
|
|
{
|
|
return 0.0;
|
|
}
|
|
|
|
|
|
fraction = ( value - min ) / ( max - min );
|
|
fraction = cap_value( fraction, 0.0, 1.0 );
|
|
return fraction;
|
|
}
|
|
|
|
player_sprint_multiplier_blend( goal, time )
|
|
{
|
|
curr = GetDvarFloat( "player_sprintSpeedScale" );
|
|
|
|
if ( IsDefined( time ) )
|
|
{
|
|
range = goal - curr;
|
|
interval = .05;
|
|
numcycles = time / interval;
|
|
fraction = range / numcycles;
|
|
|
|
while ( abs( goal - curr ) > abs( fraction * 1.1 ) )
|
|
{
|
|
curr += fraction;
|
|
SetSavedDvar( "player_sprintSpeedScale", curr );
|
|
wait interval;
|
|
}
|
|
}
|
|
|
|
SetSavedDvar( "player_sprintSpeedScale", goal );
|
|
}
|
|
|
|
roofrun_sarge( skipToBigJump )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self ent_flag_wait( "roofrun_start" );
|
|
|
|
if( !IsDefined( skipToBigJump ) )
|
|
{
|
|
skipToBigJump = false;
|
|
}
|
|
|
|
self set_generic_run_anim( "freerunnerA_run" );
|
|
|
|
if( !skipToBigJump )
|
|
{
|
|
animref = GetStruct( "roofrun_jump1", "targetname" );
|
|
anime = "freerunnerA_right";
|
|
animref anim_reach_solo( self, anime );
|
|
animref anim_single_run_solo( self, anime );
|
|
|
|
self roofrun_friendly_setup();
|
|
|
|
// wait for player if necessary
|
|
struct = GetStruct( "roofrun_sarge_waitforplayer", "targetname" );
|
|
self SetGoalPos( struct.origin );
|
|
self waittill( "goal" );
|
|
}
|
|
else
|
|
{
|
|
// we've already teleported him, just set him up
|
|
self roofrun_friendly_setup();
|
|
}
|
|
|
|
// a trigger sets this flag
|
|
flag_wait( "player_near_bigjump" );
|
|
|
|
// big jump
|
|
animref = GetStruct( "roofrun_bigjump3", "targetname" );
|
|
temp = Spawn( "script_origin", animref.origin );
|
|
temp.angles = animref.angles;
|
|
animref = temp;
|
|
|
|
|
|
self.anim_bigjump_org = animref;
|
|
anime = "favela_escape_bigjump_soap";
|
|
animref anim_reach_solo( self, anime );
|
|
self PushPlayer( false );
|
|
|
|
if( !flag( "roofrun_player_bigjump_start" ) )
|
|
{
|
|
flag_set( "roofrun_sarge_bigjumped" );
|
|
|
|
animref anim_single_solo( self, anime );
|
|
self SetGoalPos( self.origin );
|
|
|
|
if( !flag( "bigjump_sargeplayer_interact_start" ) )
|
|
{
|
|
anime = "favela_escape_bigjump_soap_loop";
|
|
self.animlooporg = animref;
|
|
animref thread anim_loop_solo( self, anime, "stop_loop" );
|
|
}
|
|
}
|
|
|
|
level.runnersDone++;
|
|
}
|
|
|
|
roofrun_hero1( skipToBigJump )
|
|
{
|
|
self endon( "death" );
|
|
|
|
animref = GetStruct( "roofrun_jump2", "targetname" );
|
|
anime = "roofrun_laundry_2";
|
|
sheet = GetEnt( "smodel_roofrun_sheet_right", "targetname" );
|
|
self thread sheet_roofrun_animate( animref, sheet, anime );
|
|
|
|
self ent_flag_wait( "roofrun_start" );
|
|
|
|
if( !IsDefined( skipToBigJump ) )
|
|
{
|
|
skipToBigJump = false;
|
|
}
|
|
|
|
self set_generic_run_anim( "freerunnerB_run" );
|
|
|
|
if( !skipToBigJump )
|
|
{
|
|
anime = "freerunnerB_mid";
|
|
animref anim_reach_solo( self, anime );
|
|
animref anim_single_run_solo( self, anime );
|
|
}
|
|
|
|
self roofrun_friendly_setup();
|
|
|
|
// big jump
|
|
animref = GetStruct( "roofrun_bigjump1", "targetname" );
|
|
anime = "favela_escape_bigjump_ghost";
|
|
if( !skipToBigJump )
|
|
{
|
|
animref anim_reach_solo( self, anime );
|
|
}
|
|
|
|
self PushPlayer( false );
|
|
animref anim_single_run_solo( self, anime );
|
|
|
|
self.animlooporg = animref;
|
|
anime = "favela_escape_bigjump_ghost_loop";
|
|
animref thread anim_loop_solo( self, anime, "stop_loop" );
|
|
|
|
level.runnersDone++;
|
|
}
|
|
|
|
roofrun_redshirt( skipToBigJump )
|
|
{
|
|
self endon( "death" );
|
|
|
|
animref = GetStruct( "roofrun_jump2", "targetname" );
|
|
anime = "roofrun_laundry_1";
|
|
sheet = GetEnt( "smodel_roofrun_sheet_left", "targetname" );
|
|
|
|
self thread sheet_roofrun_animate( animref, sheet, anime );
|
|
|
|
self ent_flag_wait( "roofrun_start" );
|
|
|
|
if( !IsDefined( skipToBigJump ) )
|
|
{
|
|
skipToBigJump = false;
|
|
}
|
|
|
|
self set_generic_run_anim( "freerunnerA_run" );
|
|
|
|
if( !skipToBigJump )
|
|
{
|
|
anime = "freerunnerA_left";
|
|
animref anim_reach_solo( self, anime );
|
|
animref anim_single_run_solo( self, anime );
|
|
}
|
|
|
|
self roofrun_friendly_setup();
|
|
|
|
// (he automatically runs across the little bridge to reach to the big jump)
|
|
// big jump
|
|
animref = GetStruct( "roofrun_bigjump2", "targetname" );
|
|
anime = "favela_escape_bigjump_faust";
|
|
if( !skipToBigJump )
|
|
{
|
|
animref anim_reach_solo( self, anime );
|
|
}
|
|
self PushPlayer( false );
|
|
animref anim_single_run_solo( self, anime );
|
|
|
|
self.animlooporg = animref;
|
|
anime = "favela_escape_bigjump_faust_loop";
|
|
animref thread anim_loop_solo( self, anime, "stop_loop" );
|
|
|
|
level.runnersDone++;
|
|
}
|
|
|
|
// self = the guy running and pushing the sheet out of the way
|
|
sheet_roofrun_animate( animref, og_sheet, anime )
|
|
{
|
|
sheet = spawn_anim_model( "laundry", og_sheet.origin );
|
|
sheet.angles = og_sheet.angles;
|
|
og_sheet Delete();
|
|
|
|
animref thread anim_first_frame_solo( sheet, anime );
|
|
|
|
self waittillmatch( "single anim", "start_laundry" );
|
|
animref anim_single_solo( sheet, anime );
|
|
}
|
|
|
|
roofrun_friendly_generic()
|
|
{
|
|
self roofrun_friendly_setup();
|
|
|
|
self waittill( "roofrun_reset" );
|
|
self restore_goalradius();
|
|
|
|
self SetGoalPos( self.origin );
|
|
|
|
self roofrun_friendly_cleanup();
|
|
}
|
|
|
|
roofrun_friendly_setup()
|
|
{
|
|
self PushPlayer( true );
|
|
self disable_cqbwalk();
|
|
self.dontavoidplayer = true;
|
|
self.disablearrivals = true;
|
|
self.disableexits = true;
|
|
self.usechokepoints = false;
|
|
|
|
self ignore_everything();
|
|
self set_temp_goalradius( 32 );
|
|
|
|
self scr_moveplaybackrate( 1 );
|
|
|
|
if( !IsDefined( self.roofrunStoredVals ) )
|
|
{
|
|
self.og_walkDistFacingMotion = self.walkDistFacingMotion;
|
|
}
|
|
self.walkDistFacingMotion = 0;
|
|
|
|
if( !IsDefined( self.roofrunStoredVals ) )
|
|
{
|
|
self.og_maxsightdistsqrd = self.maxsightdistsqrd;
|
|
}
|
|
self.maxsightdistsqrd = 0;
|
|
|
|
if( !IsDefined( self.roofrunStoredVals ) )
|
|
{
|
|
self.og_pathRandomPercent = self.pathRandomPercent;
|
|
}
|
|
self.pathRandomPercent = 0;
|
|
|
|
if( !IsDefined( self.roofrunStoredVals ) )
|
|
{
|
|
self.og_animname = self.animname;
|
|
}
|
|
self.animname = "freerunner";
|
|
|
|
self.roofrunStoredVals = true;
|
|
}
|
|
|
|
roofrun_friendly_cleanup()
|
|
{
|
|
self notify( "roofrun_friendly_cleanup" );
|
|
|
|
self.moveplaybackrate = 1;
|
|
self.animplaybackrate = 1;
|
|
self.moveTransitionRate = 1;
|
|
|
|
self PushPlayer( false );
|
|
self.dontavoidplayer = false;
|
|
self.disablearrivals = false;
|
|
self.disableexits = false;
|
|
self.usechokepoints = true;
|
|
|
|
self.walkDistFacingMotion = self.og_walkDistFacingMotion;
|
|
|
|
self clear_ignore_everything();
|
|
self restore_goalradius();
|
|
|
|
self.maxsightdistsqrd = self.og_maxsightdistsqrd;
|
|
self.pathRandomPercent = self.og_pathRandomPercent;
|
|
|
|
self.animname = self.og_animname;
|
|
}
|
|
|
|
player_leaps( trig, jump_forward, goodDot, checkIsOnGround )
|
|
{
|
|
if( !IsDefined( goodDot ) )
|
|
{
|
|
goodDot = 0.965;
|
|
}
|
|
|
|
if( !IsDefined( checkIsOnGround ) )
|
|
{
|
|
checkIsOnGround = true;
|
|
}
|
|
|
|
if( !level.player IsTouching( trig ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( level.player GetStance() != "stand" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( checkIsOnGround && level.player IsOnGround() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// gotta jump straight
|
|
player_angles = level.player GetPlayerAngles();
|
|
player_angles = ( 0, player_angles[ 1 ], 0 );
|
|
player_forward = anglestoforward( player_angles );
|
|
dot = vectordot( player_forward, jump_forward );
|
|
if ( dot < goodDot )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vel = level.player GetVelocity();
|
|
// figure out the length of the vector to get the speed (distance from world center = length)
|
|
velocity = Distance( ( vel[ 0 ], vel[ 1 ], 0 ), ( 0, 0, 0 ) ); // don't care about Z velocity
|
|
if ( velocity < 162 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
//level.player setVelocity( ( vel[ 0 ] * 1.5, vel[ 1 ] * 1.5, vel[ 2 ] ) );
|
|
return true;
|
|
}
|
|
|
|
bigjump_roof_anim( player_rig, animref )
|
|
{
|
|
roof = bigjump_roof_setup();
|
|
|
|
roof_rig = spawn_anim_model( "roof_rig" );
|
|
roof_rig Hide();
|
|
scene = "breakaway";
|
|
roofanim = level.scr_anim[ roof_rig.animname ][ scene ];
|
|
roof_rig.origin = GetStartOrigin( animref.origin, animref.angles, roofanim );
|
|
roof_rig.angles = GetStartAngles( animref.origin, animref.angles, roofanim );
|
|
roof LinkTo( roof_rig, "J_Roof_01" );
|
|
|
|
player_rig waittillmatch( "single anim", "start_roof_collapse" );
|
|
animref anim_single_solo( roof_rig, scene );
|
|
}
|
|
|
|
bigjump_roof_setup()
|
|
{
|
|
ents = GetEntArray( "roof_fall", "targetname" );
|
|
// find the sbmodel
|
|
sbmodel = undefined;
|
|
|
|
foreach( ent in ents )
|
|
{
|
|
if( ent.code_classname == "script_brushmodel" )
|
|
{
|
|
sbmodel = ent;
|
|
break;
|
|
}
|
|
}
|
|
|
|
ASSERT( IsDefined( sbmodel ) );
|
|
|
|
ents = array_remove( ents, sbmodel );
|
|
|
|
// link everything else to the sbmodel
|
|
foreach( ent in ents )
|
|
{
|
|
ent LinkTo( sbmodel );
|
|
}
|
|
|
|
return sbmodel;
|
|
}
|
|
|
|
bigjump_player_blend_to_anim( player_rig )
|
|
{
|
|
wait( 0.3 );
|
|
// Print3d( level.player.origin, "x", (1,1,1), 1, 2, 500 );
|
|
level.player PlayerLinkToBlend( player_rig, "tag_player", 0.6, 0.2, 0.4 );
|
|
}
|
|
|
|
player_bigjump_recovery_vfx( animtime, shockfile )
|
|
{
|
|
shocktime = animtime * 0.75;
|
|
endtime = GetTime() + milliseconds( shocktime );
|
|
|
|
level.player Shellshock( shockfile, shocktime + 1.5 );
|
|
|
|
//blurHigh = 0.6;
|
|
//blurLow = 0.2;
|
|
blurHigh = 4.5;
|
|
blurLow = 2;
|
|
blurTransTime = 0.5;
|
|
waitMin = 0.75;
|
|
waitMax = 1.25;
|
|
while( GetTime() < endtime )
|
|
{
|
|
SetBlur( blurHigh, blurTransTime );
|
|
wait( RandomFloatRange( waitMin, waitMax ) );
|
|
SetBlur( blurLow, blurTransTime );
|
|
wait( RandomFloatRange( waitMin, waitMax ) );
|
|
}
|
|
|
|
SetBlur( 0, .5 );
|
|
|
|
/*-----------------------
|
|
LOOK AT HANDS, BG BLURS
|
|
-------------------------
|
|
dof_see_hands = [];
|
|
dof_see_hands[ "nearStart" ] = 0;
|
|
dof_see_hands[ "nearEnd" ] = 0;
|
|
dof_see_hands[ "nearBlur" ] = 6;
|
|
dof_see_hands[ "farStart" ] = 30;
|
|
dof_see_hands[ "farEnd" ] = 70;
|
|
dof_see_hands[ "farBlur" ] = 2.5;
|
|
thread blend_dof( dof_start, dof_see_hands, 3 );
|
|
SetBlur( 0, 3 );
|
|
|
|
|
|
flag_wait( "notetrack_player_lowerhands" );
|
|
/*-----------------------
|
|
DROP HANDS, BG COMES INTO FOCUS
|
|
-------------------------
|
|
dof_see_dudes = [];
|
|
dof_see_dudes[ "nearStart" ] = 4.7;
|
|
dof_see_dudes[ "nearEnd" ] = 56;
|
|
dof_see_dudes[ "nearBlur" ] = 6;
|
|
dof_see_dudes[ "farStart" ] = 1000;
|
|
dof_see_dudes[ "farEnd" ] = 7000;
|
|
dof_see_dudes[ "farBlur" ] = 0;
|
|
//thread blend_dof( dof_see_hands, dof_see_dudes, 5 );
|
|
thread blend_dof( dof_see_hands, dof_start, 6 );
|
|
*/
|
|
}
|
|
|
|
bigjump_heartbeat( waitTime )
|
|
{
|
|
//wait( waitTime );
|
|
|
|
beatTime = 1.5;
|
|
|
|
endTime = GetTime() + milliseconds( waitTime );
|
|
while( GetTime() < endTime )
|
|
{
|
|
play_sound_in_space( "breathing_heartbeat", level.player.origin );
|
|
|
|
if( GetTime() < endTime )
|
|
{
|
|
wait( beatTime );
|
|
}
|
|
}
|
|
|
|
beatTime = 1;
|
|
|
|
play_sound_in_space( "breathing_hurt_start", level.player.origin, true );
|
|
wait( beatTime );
|
|
|
|
tracker = 0;
|
|
|
|
while( !flag( "player_recovery_done" ) )
|
|
{
|
|
alias = "breathing_hurt";
|
|
|
|
// just do a regular heartbeat on odd numbered beats
|
|
if( tracker % 2 != 0 )
|
|
{
|
|
alias = "breathing_heartbeat";
|
|
}
|
|
|
|
play_sound_in_space( alias, level.player.origin, true );
|
|
|
|
tracker++;
|
|
wait( beatTime );
|
|
}
|
|
|
|
play_sound_in_space( "breathing_better", level.player.origin, true );
|
|
}
|
|
|
|
bigjump_dialogue( waitTime )
|
|
{
|
|
wait( 5.5 );
|
|
|
|
// "Roach!!! Roach!!! Wake up!!!"
|
|
radio_dialogue( "favesc_cmt_wakeup" );
|
|
|
|
wait( 0.5 );
|
|
|
|
// "Roach! We can see them from the chopper! They're coming for you, dozens of 'em!!!"
|
|
radio_dialogue( "favesc_gst_comingforyou" );
|
|
|
|
wait( 0.5 );
|
|
|
|
// "Roach! There's too many of them! Get the hell out of there and find a way to the rooftops! Move!"
|
|
thread radio_dialogue( "favesc_cmt_toomany" );
|
|
}
|
|
|
|
bigjump_angrymob( waitTime )
|
|
{
|
|
if( IsDefined( waitTime ) )
|
|
{
|
|
wait( waitTime );
|
|
}
|
|
|
|
// wallas
|
|
thread bigjump_recovery_rightside_walla();
|
|
thread bigjump_recovery_leftside_walla();
|
|
|
|
// -- ground mob on right --
|
|
thread bigjump_angrymob_right_ground();
|
|
|
|
// -- rooftop guys on left --
|
|
delaythread( 3.5, ::bigjump_angrymob_left_roof );
|
|
|
|
// -- ground mob on left --
|
|
delaythread( 7.75, ::bigjump_angrymob_left_ground );
|
|
}
|
|
|
|
bigjump_angrymob_left_roof()
|
|
{
|
|
wait( 1 );
|
|
|
|
animref_center = GetStruct( "struct_mob_roof_2", "targetname" );
|
|
animref_left = GetStruct( "struct_mob_roof_1", "targetname" );
|
|
animref_right = GetStruct( "struct_mob_roof_3", "targetname" );
|
|
roofspawners = GetEntArray( "spawner_mob_left_roof", "targetname" );
|
|
|
|
roofguys = spawn_group( roofspawners, true, false );
|
|
ASSERT( roofguys.size == 4 );
|
|
|
|
roofguys[ 0 ].animref = animref_center;
|
|
roofguys[ 0 ].anime = "favela_escape_rooftop_mob1";
|
|
roofguys[ 1 ].animref = animref_center;
|
|
roofguys[ 1 ].anime = "favela_escape_rooftop_mob2";
|
|
roofguys[ 2 ].animref = animref_right;
|
|
roofguys[ 2 ].anime = "favela_escape_rooftop_mob4";
|
|
roofguys[ 3 ].animref = animref_left;
|
|
roofguys[ 3 ].anime = "favela_escape_rooftop_mob3";
|
|
|
|
array_thread( roofguys, ::bigjump_angrymob_roofguy );
|
|
}
|
|
|
|
bigjump_angrymob_left_ground()
|
|
{
|
|
animref = GetEnt( "animref_mob_left", "targetname" );
|
|
spawners = GetEntArray( "spawner_mob_left", "targetname" );
|
|
|
|
anims = [];
|
|
anims[ anims.size ] = "mob_left_A";
|
|
anims[ anims.size ] = "mob_left_B";
|
|
anims[ anims.size ] = "mob_left_C";
|
|
anims[ anims.size ] = "mob_left_D";
|
|
|
|
guys = spawn_group( spawners, true, false );
|
|
ASSERT( guys.size == anims.size );
|
|
|
|
foreach( index, guy in guys )
|
|
{
|
|
guy.ignoreall = true;
|
|
guy.ignoreme = true;
|
|
|
|
scene = anims[ index ];
|
|
anime = level.scr_anim[ "generic" ][ scene ];
|
|
|
|
animref thread anim_generic_gravity( guy, scene );
|
|
guy thread angrymob_animdone_think( anime, animref );
|
|
}
|
|
}
|
|
|
|
bigjump_angrymob_right_ground()
|
|
{
|
|
animref = GetEnt( "animref_mob_right", "targetname" );
|
|
spawners = GetEntArray( "spawner_mob_right", "targetname" );
|
|
|
|
anims = [];
|
|
anims[ anims.size ] = "mob2_arc_A";
|
|
anims[ anims.size ] = "mob2_arc_B";
|
|
anims[ anims.size ] = "mob3_arc_C";
|
|
anims[ anims.size ] = "mob2_arc_D";
|
|
anims[ anims.size ] = "mob2_arc_E";
|
|
anims[ anims.size ] = "mob2_arc_F";
|
|
anims[ anims.size ] = "mob2_arc_G";
|
|
anims[ anims.size ] = "mob2_arc_H";
|
|
|
|
guys = spawn_group( spawners, true, false );
|
|
ASSERT( guys.size == anims.size );
|
|
|
|
foreach( index, guy in guys )
|
|
{
|
|
guy.ignoreall = true;
|
|
guy.ignoreme = true;
|
|
|
|
scene = anims[ index ];
|
|
anime = level.scr_anim[ "generic" ][ scene ];
|
|
|
|
animref thread anim_generic_gravity( guy, scene );
|
|
guy thread angrymob_animdone_think( anime, animref );
|
|
}
|
|
}
|
|
|
|
bigjump_angrymob_roofguy()
|
|
{
|
|
self.ignoreall = true;
|
|
self.ignoreme = true;
|
|
|
|
self.animref anim_generic( self, self.anime );
|
|
|
|
self SetGoalPos( self.origin );
|
|
}
|
|
|
|
angrymob_animdone_think( anime, animref )
|
|
{
|
|
animtime = GetAnimLength( anime );
|
|
endTime = GetTime() + milliseconds( animtime );
|
|
level waittill_any_timeout( animtime, "solorun_mob_start_shooting" );
|
|
|
|
if( GetTime() < endTime )
|
|
{
|
|
self StopAnimScripted();
|
|
}
|
|
|
|
if( !flag( "solorun_mob_start_shooting" ) )
|
|
{
|
|
self set_generic_run_anim( "intro_casual_walk", true );
|
|
self.angrymob_newrunanim = true;
|
|
}
|
|
|
|
self.goalradius = 512;
|
|
self SetGoalEntity( level.player );
|
|
|
|
flag_wait( "solorun_mob_start_shooting" );
|
|
|
|
/*
|
|
if( IsDefined( animref ) )
|
|
{
|
|
animref anim_stopanimscripted();
|
|
}
|
|
*/
|
|
self notify( "stop_animmode" );
|
|
//self anim_stopanimscripted();
|
|
|
|
if( IsDefined( self.angrymob_newrunanim ) )
|
|
{
|
|
self clear_run_anim();
|
|
}
|
|
|
|
self.ignoreall = false;
|
|
}
|
|
|
|
|
|
// ----------------
|
|
// --- SOLO RUN ---
|
|
// ----------------
|
|
solorun_chaser_spawnfunc()
|
|
{
|
|
if( !IsDefined( level.chasers ) )
|
|
{
|
|
level.chasers = [];
|
|
}
|
|
|
|
level.chasers[ level.chasers.size ] = self;
|
|
}
|
|
|
|
solorun_chasers_remove()
|
|
{
|
|
flag_wait( "solorun_player_off_balcony" );
|
|
|
|
if( !IsDefined( level.chasers ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
level.chasers = array_removedead( level.chasers );
|
|
level.chasers = array_removeundefined( level.chasers );
|
|
|
|
array_thread( level.chasers, ::solorun_chaser_remove );
|
|
}
|
|
|
|
dont_shoot_player_in_back()
|
|
{
|
|
self endon( "death" );
|
|
level.player endon( "death" );
|
|
|
|
// don't fire at the player while he's not looking
|
|
while( 1 )
|
|
{
|
|
playerangles = level.player GetPlayerAngles();
|
|
player_forward = AnglesToForward( playerangles );
|
|
vec = VectorNormalize( self.origin - level.player GetEye() );
|
|
anglesFromPlayer = VectorToAngles( vec );
|
|
forward_to_self = AnglesToForward( anglesFromPlayer );
|
|
|
|
dot = vectordot( player_forward, forward_to_self );
|
|
|
|
// in view of the player
|
|
if( dot > 0.75 || flag( "solorun_player_progression_stalled" ) )
|
|
{
|
|
if( IsDefined( self.dontEverShoot ) )
|
|
{
|
|
self.dontEverShoot = undefined;
|
|
}
|
|
}
|
|
// not in view
|
|
else
|
|
{
|
|
if( !IsDefined( self.dontEverShoot ) )
|
|
{
|
|
self.dontEverShoot = true;
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
solorun_chaser_remove()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( player_can_see_ai( self ) )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
self Delete();
|
|
}
|
|
|
|
solorun_timer_prethink()
|
|
{
|
|
dvar = GetDvarInt( "timer_off" );
|
|
if( IsDefined( dvar ) && dvar > 0 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
flag_wait( "solorun_timer_start" );
|
|
timerTime = 30;
|
|
timerLoc = &"FAVELA_ESCAPE_CHOPPER_TIMER";
|
|
thread solorun_timer( timerTime, timerLoc, true );
|
|
|
|
thread solorun_timer_extend_when_close( timerTime, timerLoc );
|
|
|
|
flag_wait( "chopperjump_player_jump" );
|
|
level notify( "kill_timer" );
|
|
level.timer destroy();
|
|
}
|
|
|
|
solorun_timer_extend_when_close( timerTime, timerLoc )
|
|
{
|
|
if( level.gameskill == 3 )
|
|
{
|
|
// player gets no help on veteran
|
|
return;
|
|
}
|
|
|
|
flag_wait( "trig_solorun_player_on_slide" );
|
|
|
|
// if timer is less than 10 seconds, reset to 10 seconds
|
|
extraTime = 10.5;
|
|
elapsedTime = seconds( GetTime() - level.start_time );
|
|
if( timerTime - elapsedTime < extraTime )
|
|
{
|
|
thread solorun_timer( extraTime, timerLoc, true );
|
|
}
|
|
|
|
thread autosave_by_name( "chopperjump" );
|
|
}
|
|
|
|
solorun_timer( iSeconds, sLabel, bUseTick )
|
|
{
|
|
if ( getdvar( "notimer" ) == "1" )
|
|
return;
|
|
|
|
if ( !isdefined( bUseTick ) )
|
|
bUseTick = false;
|
|
// destroy any previous timer just in case
|
|
killTimer();
|
|
level endon( "kill_timer" );
|
|
|
|
// -- timer setup --
|
|
level.hudTimerIndex = 20;
|
|
level.timer = maps\_hud_util::get_countdown_hud( -250 );
|
|
level.timer SetPulseFX( 30, 900000, 700 );// something, decay start, decay duration
|
|
level.timer.label = sLabel;
|
|
level.timer settenthstimer( iSeconds );
|
|
level.start_time = gettime();
|
|
|
|
// -- timer expired --
|
|
if ( bUseTick == true )
|
|
thread timer_tick();
|
|
wait( iSeconds );
|
|
|
|
flag_set( "timer_expired" );
|
|
level.timer destroy();
|
|
// Mission failed. The objective was not completed in time.
|
|
level thread solorun_timer_expired();
|
|
}
|
|
|
|
timer_tick()
|
|
{
|
|
level endon( "stop_timer_tick" );
|
|
level endon( "kill_timer" );
|
|
while ( true )
|
|
{
|
|
wait( 1 );
|
|
level.player thread play_sound_on_entity( "countdown_beep" );
|
|
level notify( "timer_tick" );
|
|
}
|
|
}
|
|
|
|
solorun_timer_expired()
|
|
{
|
|
deadquote = &"FAVELA_ESCAPE_CHOPPER_TIMER_EXPIRED";
|
|
level.player endon( "death" );
|
|
level endon( "kill_timer" );
|
|
level notify( "mission failed" );
|
|
level.player FreezeControls( true );
|
|
|
|
setDvar( "ui_deadquote", deadquote );
|
|
maps\_utility::missionFailedWrapper();
|
|
level notify( "kill_timer" );
|
|
}
|
|
|
|
killTimer()
|
|
{
|
|
level notify( "kill_timer" );
|
|
|
|
if ( IsDefined( level.timer ) )
|
|
{
|
|
level.timer Destroy();
|
|
}
|
|
}
|
|
|
|
solorun_start_playerfail( timeout )
|
|
{
|
|
level endon( "solorun_player_off_balcony" );
|
|
|
|
// "Run for it!!! Get to the rooftops!!"
|
|
thread radio_dialogue( "favesc_cmt_runforit" );
|
|
|
|
xTest = -6074;
|
|
zTest = 900;
|
|
trig = GetEnt( "trig_solorun_start_playersafezone", "targetname" );
|
|
maxhealth = level.player.maxhealth;
|
|
|
|
thread solorun_start_playerfail_timeout( timeout );
|
|
thread solorun_player_leaves_trigger( trig );
|
|
|
|
while( 1 )
|
|
{
|
|
if( solorun_start_playerfail_should_damage( xTest, trig, "solorun_mob_start_shooting" ) )
|
|
{
|
|
dmgspot = level.player.origin;
|
|
|
|
axis = GetAiArray( "axis" );
|
|
if( axis.size )
|
|
{
|
|
guy = get_random( axis );
|
|
dmgspot = guy.origin;
|
|
}
|
|
level.player DoDamage( maxhealth * 0.25, dmgspot );
|
|
|
|
if( !flag( "solorun_mob_start_shooting" ) )
|
|
{
|
|
flag_set( "solorun_mob_start_shooting" );
|
|
}
|
|
}
|
|
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
solorun_start_playerfail_should_damage( xTest, trig, flagname )
|
|
{
|
|
if( level.player.origin[ 0 ] < xTest && !level.player IsTouching( trig ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
if( level.player IsTouching( trig ) && flag( flagname ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
solorun_start_playerfail_timeout( timeout )
|
|
{
|
|
level endon( "solorun_mob_start_shooting" );
|
|
wait( timeout );
|
|
flag_set( "solorun_mob_start_shooting" );
|
|
}
|
|
|
|
solorun_player_leaves_trigger( trig )
|
|
{
|
|
level endon( "solorun_mob_start_shooting" );
|
|
|
|
while( 1 )
|
|
{
|
|
trig waittill( "trigger", other );
|
|
if( IsPlayer( other ) )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
while( level.player IsTouching( trig ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
flag_set( "solorun_mob_start_shooting" );
|
|
}
|
|
|
|
solorun_civilian_doorshut()
|
|
{
|
|
trig = GetEnt( "trig_solorun_civilian_doorshut", "targetname" );
|
|
spawner = GetEnt( trig.target, "targetname" );
|
|
animref = GetStruct( "struct_solorun_civilian_doorshut_animref", "targetname" );
|
|
|
|
door = spawn_anim_model( "civ_door" );
|
|
guy = spawner spawn_ai();
|
|
guy.animname = "default_civilian";
|
|
guy.ignoreme = true;
|
|
guy ignore_everything();
|
|
guy thread magic_bullet_shield();
|
|
|
|
ents[ 0 ] = door;
|
|
ents[ 1 ] = guy;
|
|
|
|
animref thread anim_loop( ents, "run_and_slam_idle", "stop_loop" );
|
|
|
|
trig waittill( "trigger" );
|
|
|
|
door notify( "stop_loop" );
|
|
guy notify( "stop_loop" );
|
|
|
|
animref anim_single( ents, "run_and_slam" );
|
|
|
|
guy SetGoalPos( guy.origin );
|
|
|
|
// idle at end
|
|
guy thread anim_loop_solo( guy, "run_and_slam_endidle", "stop_loop" );
|
|
|
|
flag_wait( "solorun_player_at_balcony" );
|
|
guy stop_magic_bullet_shield();
|
|
guy notify( "stop_loop" );
|
|
wait( 0.05 );
|
|
guy Delete();
|
|
}
|
|
|
|
rooftop_slide_exploder()
|
|
{
|
|
flag_wait( "trig_solorun_player_on_slide" );
|
|
exploder( "roof_slide" );
|
|
}
|
|
|
|
rooftop_slide_glassbreak()
|
|
{
|
|
trigger_wait_targetname( "trig_end_glass_break" );
|
|
level notify( "glass_break", level.player );
|
|
|
|
level.player PlayRumbleOnEntity( "artillery_rumble" );
|
|
thread play_sound_in_space( "scn_favela_escape_player_window", level.player.origin );
|
|
}
|
|
|
|
rooftop_slide_deleteaxis()
|
|
{
|
|
trigger_wait_targetname( "trig_end_glass_break" );
|
|
axis = GetAiArray( "axis" );
|
|
array_call( axis, ::Delete );
|
|
}
|
|
|
|
solorun_player_difficulty_adjustment()
|
|
{
|
|
movingVelocity = 170;
|
|
easySettings = false;
|
|
|
|
earlySuperHardSettings = false;
|
|
|
|
safeZoneTrig = GetEnt( "trig_solorun_start_playersafezone", "targetname" );
|
|
|
|
while( 1 )
|
|
{
|
|
// first make sure he's not running around outside the solorun start area
|
|
if( flag( "solorun_player_outside_first_house" ) && !level.player IsTouching( safeZoneTrig ) && !earlySuperHardSettings )
|
|
{
|
|
earlySuperHardSettings = true;
|
|
|
|
level.player.noPlayerInvul = true;
|
|
set_custom_gameskill_func( ::solorun_superhard_gameskill_settings );
|
|
}
|
|
else if( ( !flag( "solorun_player_outside_first_house" ) || level.player IsTouching( safeZoneTrig ) ) && earlySuperHardSettings )
|
|
{
|
|
// this resets settings that don't get set to new values when the player is away from the starting area
|
|
earlySuperHardSettings = false;
|
|
|
|
level.player.noPlayerInvul = undefined;
|
|
set_custom_gameskill_func( ::solorun_reset_gameskill_settings );
|
|
}
|
|
|
|
vel = level.player GetVelocity();
|
|
velocity = Distance( ( vel[ 0 ], vel[ 1 ], 0 ), ( 0, 0, 0 ) ); // don't care about Z velocity
|
|
|
|
// easier when player is moving
|
|
if( velocity >= movingVelocity && !easySettings )
|
|
{
|
|
easySettings = true;
|
|
|
|
set_custom_gameskill_func( ::solorun_fastmoving_gameskill_settings );
|
|
}
|
|
else if( velocity < movingVelocity && easySettings )
|
|
{
|
|
easySettings = false;
|
|
|
|
set_custom_gameskill_func( ::solorun_slowmoving_gameskill_settings );
|
|
}
|
|
|
|
wait( 0.5 );
|
|
}
|
|
}
|
|
|
|
solorun_superhard_gameskill_settings()
|
|
{
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "easy" ] = 100;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "normal" ] = 100;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "hardened" ] = 100;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "veteran" ] = 100;
|
|
|
|
level.difficultySettings[ "longRegenTime" ][ "easy" ] = 20000;
|
|
level.difficultySettings[ "longRegenTime" ][ "normal" ] = 20000;
|
|
level.difficultySettings[ "longRegenTime" ][ "hardened" ] = 20000;
|
|
level.difficultySettings[ "longRegenTime" ][ "veteran" ] = 20000;
|
|
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "easy" ] = 0;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "normal" ] = 0;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "hardened" ] = 0;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "veteran" ] = 0;
|
|
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "easy" ] = 50000;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "normal" ] = 50000;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "hardened" ] = 50000;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "veteran" ] = 50000;
|
|
|
|
level.difficultySettings[ "invulTime_preShield" ][ "easy" ] = 0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "normal" ] = 0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "hardened" ] = 0;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "veteran" ] = 0;
|
|
|
|
level.difficultySettings[ "invulTime_onShield" ][ "easy" ] = 0;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "normal" ] = 0;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "hardened" ] = 0;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "veteran" ] = 0;
|
|
|
|
level.difficultySettings[ "invulTime_postShield" ][ "easy" ] = 0;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "normal" ] = 0;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "hardened" ] = 0;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "veteran" ] = 0;
|
|
}
|
|
|
|
solorun_reset_gameskill_settings()
|
|
{
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "easy" ] = 4000;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "normal" ] = 2500;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "hardened" ] = 600;
|
|
level.difficultySettings[ "player_deathInvulnerableTime" ][ "veteran" ] = 100;
|
|
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "easy" ] = 0.9;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "normal" ] = 1.0;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "hardened" ] = 1.15;
|
|
level.difficultySettings[ "base_enemy_accuracy" ][ "veteran" ] = 1.15;
|
|
|
|
level.difficultySettings[ "invulTime_preShield" ][ "easy" ] = 0.6;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "normal" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "hardened" ] = 0.3;
|
|
level.difficultySettings[ "invulTime_preShield" ][ "veteran" ] = 0.0;
|
|
|
|
level.difficultySettings[ "invulTime_onShield" ][ "easy" ] = 1.6;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "normal" ] = 1.0;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "hardened" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_onShield" ][ "veteran" ] = 0.25;
|
|
|
|
level.difficultySettings[ "invulTime_postShield" ][ "easy" ] = 0.5;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "normal" ] = 0.4;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "hardened" ] = 0.3;
|
|
level.difficultySettings[ "invulTime_postShield" ][ "veteran" ] = 0.0;
|
|
}
|
|
|
|
solorun_fastmoving_gameskill_settings()
|
|
{
|
|
// the amount of health you have in this difficulty
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "easy" ] = 900; // 475
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "normal" ] = 550; // 275
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "hardened" ] = 330; // 165
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "veteran" ] = 230; // 115
|
|
|
|
// If you go to red flashing, the amount of time before your health regens
|
|
level.difficultySettings[ "longRegenTime" ][ "easy" ] = 1000; // 5000
|
|
level.difficultySettings[ "longRegenTime" ][ "normal" ] = 1000; // 5000
|
|
level.difficultySettings[ "longRegenTime" ][ "hardened" ] = 1000; // 3200
|
|
level.difficultySettings[ "longRegenTime" ][ "veteran" ] = 1000; // 3200
|
|
}
|
|
|
|
solorun_slowmoving_gameskill_settings()
|
|
{
|
|
// defaults
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "easy" ] = 475;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "normal" ] = 275;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "hardened" ] = 165;
|
|
level.difficultySettings[ "playerDifficultyHealth" ][ "veteran" ] = 115;
|
|
|
|
level.difficultySettings[ "longRegenTime" ][ "easy" ] = 5000;
|
|
level.difficultySettings[ "longRegenTime" ][ "normal" ] = 5000;
|
|
level.difficultySettings[ "longRegenTime" ][ "hardened" ] = 3200;
|
|
level.difficultySettings[ "longRegenTime" ][ "veteran" ] = 3200;
|
|
}
|
|
|
|
solorun_playerhurt_replacehint()
|
|
{
|
|
wait( 1 );
|
|
|
|
// "You are Hurt, Run For Your Life!"
|
|
level.strings[ "take_cover" ].text = &"FAVELA_ESCAPE_SOLORUN_KEEP_MOVING";
|
|
}
|
|
|
|
solorun_player_progression_tracker()
|
|
{
|
|
level endon( "trig_solorun_player_on_slide" );
|
|
|
|
trigs = GetEntArray( "trig_solorun_roof_progression", "targetname" );
|
|
ASSERT( trigs.size );
|
|
|
|
level.solorun_progression_hilo = 0;
|
|
thread solorun_player_progression_hilo_watcher();
|
|
|
|
lastTrigNum = 1;
|
|
maxCap = 1;
|
|
|
|
while( 1 )
|
|
{
|
|
foreach( trig in trigs )
|
|
{
|
|
if( level.player IsTouching( trig ) )
|
|
{
|
|
if( trig.script_count > lastTrigNum )
|
|
{
|
|
if( level.solorun_progression_hilo >= maxCap )
|
|
{
|
|
level.solorun_progression_hilo = 0;
|
|
}
|
|
|
|
level.solorun_progression_hilo++;
|
|
}
|
|
else if( trig.script_count < lastTrigNum )
|
|
{
|
|
level.solorun_progression_hilo--;
|
|
}
|
|
|
|
while( level.player IsTouching( trig ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
lastTrigNum = trig.script_count;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
solorun_player_progression_hilo_watcher()
|
|
{
|
|
level endon( "trig_solorun_player_on_slide" );
|
|
|
|
flagstr = "solorun_player_progression_stalled";
|
|
og_hilo = 0;
|
|
|
|
while( 1 )
|
|
{
|
|
while( level.solorun_progression_hilo == og_hilo )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
if( level.solorun_progression_hilo < 1 )
|
|
{
|
|
// going backwards
|
|
if( !flag( flagstr ) )
|
|
{
|
|
flag_set( flagstr );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( flag( flagstr ) )
|
|
{
|
|
flag_clear( flagstr );
|
|
}
|
|
}
|
|
|
|
og_hilo = level.solorun_progression_hilo;
|
|
}
|
|
}
|
|
|
|
solorun_sprint_tracker()
|
|
{
|
|
// no hints on veteran
|
|
if( level.gameskill > 2 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
flagstr = "solorun_player_sprinted";
|
|
flag_init( flagstr );
|
|
level thread set_flag_on_sprint( flagstr );
|
|
|
|
//level.player ent_flag_wait( "player_has_red_flashing_overlay" );
|
|
|
|
while( level.player.health > ( level.player.maxhealth * 0.9 ) && !flag( flagstr ) )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
if( !flag( flagstr ) )
|
|
{
|
|
// hint
|
|
level thread sprint_hint( flagstr );
|
|
}
|
|
}
|
|
|
|
sprint_hint( flagstr )
|
|
{
|
|
hintstr = &"FAVELA_ESCAPE_HINT_SPRINT_PC_ALT"; // "Press ^3[{+breath_sprint}]^7 while moving forward to sprint."
|
|
if( !level.console )
|
|
{
|
|
change = false;
|
|
|
|
// we want the sprint command to be the one that gets displayed on PC
|
|
if ( is_command_bound( "+sprint" ) )
|
|
{
|
|
change = true;
|
|
}
|
|
// if neither are bound, show the PC-specific hint
|
|
else if( !is_command_bound( "+breath_sprint" ) )
|
|
{
|
|
change = true;
|
|
}
|
|
|
|
if( change )
|
|
{
|
|
hintstr = &"FAVELA_ESCAPE_HINT_SPRINT_PC"; //"Press ^3[{+sprint}]^7 while moving forward to sprint."
|
|
}
|
|
}
|
|
|
|
fontsize = 1.6;
|
|
if( !level.console )
|
|
{
|
|
fontsize = 1.2;
|
|
}
|
|
hintElem = CreateFontString( "default", fontsize );
|
|
hintElem.hidewheninmenu = true;
|
|
hintElem setPoint( "TOP", undefined, 0, 110 );
|
|
hintElem.sort = 0.5;
|
|
hintElem.alpha = 0;
|
|
hintElem SetText( hintstr );
|
|
|
|
fadeTime = 0.25;
|
|
hintElem FadeOverTime( fadeTime );
|
|
hintElem.alpha = 1;
|
|
|
|
//endTime = GetTime() + 5000;
|
|
//while( !flag( flagstr ) && GetTime() < endTime && level.player.health > 0 )
|
|
while( !flag( flagstr ) && level.player.health > 0 )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
hintElem FadeOverTime( fadeTime );
|
|
hintElem.alpha = 0;
|
|
wait( fadeTime );
|
|
hintElem Destroy();
|
|
}
|
|
|
|
set_flag_on_sprint( flagstr )
|
|
{
|
|
NotifyOnCommand( flagstr, "+breath_sprint" );
|
|
NotifyOnCommand( flagstr, "+sprint" );
|
|
|
|
level.player waittill( flagstr );
|
|
flag_set( flagstr );
|
|
}
|
|
|
|
player_bullet_whizbys()
|
|
{
|
|
thread player_bullet_whizby_sounds();
|
|
|
|
dmgtrigs = GetEntArray( "solorun_dmgtrig", "targetname" );
|
|
array_thread( dmgtrigs, ::player_bullet_whizby_dmgtrigs );
|
|
|
|
trigs = GetEntArray( "trig_solorun_squibs", "targetname" );
|
|
array_thread( trigs, ::player_bullet_whizby_trig );
|
|
}
|
|
|
|
// to cover having "bursts" of squibs, play bullet sounds randomly in the background
|
|
player_bullet_whizby_sounds()
|
|
{
|
|
level endon( "solorun_player_at_balcony" );
|
|
|
|
shotWaitMin = 0.07;
|
|
shotWaitMax = 0.1;
|
|
|
|
shotsMin = 5;
|
|
shotsMax = 9;
|
|
|
|
burstWaitMin = 1;
|
|
burstWaitMax = 2;
|
|
|
|
weapons = level.scriptedweapons;
|
|
sounds = level.scriptedweaponsounds;
|
|
|
|
while( 1 )
|
|
{
|
|
weapon = get_random( weapons );
|
|
numShots = RandomIntRange( shotsMin, shotsMax );
|
|
|
|
for( i = 0; i < numShots; i++ )
|
|
{
|
|
playereye = level.player GetEye();
|
|
|
|
thread play_sound_in_space( sounds[ weapon ], playereye );
|
|
|
|
if( cointoss() )
|
|
{
|
|
thread play_sound_in_space( "whizby", playereye );
|
|
}
|
|
|
|
wait( RandomFloatRange( shotWaitMin, shotWaitMax ) );
|
|
}
|
|
|
|
wait( RandomFloatRange( burstWaitMin, burstWaitMax ) );
|
|
}
|
|
}
|
|
|
|
player_bullet_whizby_dmgtrigs()
|
|
{
|
|
self waittill( "trigger" );
|
|
RadiusDamage( self.origin, 32, 1000, 1000 );
|
|
}
|
|
|
|
player_bullet_whizby_trig()
|
|
{
|
|
ASSERT( IsDefined( self.target ) );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", other );
|
|
|
|
if( IsAlive( other ) && IsDefined( other.team ) && other.team == "axis" )
|
|
{
|
|
break;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
|
|
weapons = level.scriptedweapons;
|
|
|
|
weapon = get_random( weapons );
|
|
|
|
fx = getfx( "squib_plaster" );
|
|
|
|
shotWaitMin = 0.07;
|
|
shotWaitMax = 0.1;
|
|
|
|
// either target a bunch of spots (for random squibs),
|
|
// or just one spot that targets a line of spots (for ordered squibs)
|
|
squibs = GetStructArray( self.target, "targetname" );
|
|
if( squibs.size == 1 )
|
|
{
|
|
squibs = get_targeted_line_array( squibs[ 0 ] );
|
|
}
|
|
// for now, they're all random since I think that's looking better and less scripty
|
|
//else
|
|
//{
|
|
squibs = array_randomize( squibs );
|
|
//}
|
|
|
|
squibs[ 0 ] script_delay();
|
|
|
|
foreach( squib in squibs )
|
|
{
|
|
// aim in the direction the squib is pointing
|
|
ASSERT( IsDefined( squib.angles ) );
|
|
forward = AnglesToForward( squib.angles );
|
|
forwardscaled = vector_multiply( forward, 1024 );
|
|
targetspot = squib.origin + forwardscaled;
|
|
|
|
MagicBullet( weapon, squib.origin, targetspot );
|
|
|
|
if( cointoss() )
|
|
{
|
|
thread play_sound_in_space( "whizby", squib.origin );
|
|
}
|
|
|
|
wait( RandomFloatRange( shotWaitMin, shotWaitMax ) );
|
|
}
|
|
}
|
|
|
|
get_targeted_line_array( start )
|
|
{
|
|
arr = [];
|
|
arr[ 0 ] = start;
|
|
point = start;
|
|
|
|
while( IsDefined( point.target ) )
|
|
{
|
|
nextpoint = GetStruct( point.target, "targetname" );
|
|
if( !IsDefined( nextpoint ) )
|
|
{
|
|
nextpoint = GetEnt( point.target, "targetname" );
|
|
}
|
|
if( !IsDefined( nextpoint ) )
|
|
{
|
|
nextpoint = GetNode( point.target, "targetname" );
|
|
}
|
|
|
|
if( IsDefined( nextpoint ) )
|
|
{
|
|
arr[ arr.size ] = nextpoint;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
|
|
point = nextpoint;
|
|
}
|
|
|
|
return arr;
|
|
}
|
|
|
|
player_bullet_whizby_location_trig()
|
|
{
|
|
spots = GetStructArray( self.target, "targetname" );
|
|
ASSERT( spots.size );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", other );
|
|
|
|
if( IsPlayer( other ) && !flag( "whizby_location_updating" ) )
|
|
{
|
|
flag_set( "whizby_location_updating" );
|
|
|
|
level.whizbyStarts = spots;
|
|
while( other IsTouching( self ) )
|
|
{
|
|
wait( 0.1 );
|
|
}
|
|
|
|
flag_clear( "whizby_location_updating" );
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
solorun_rooftop_squibs()
|
|
{
|
|
level.player endon( "death" );
|
|
level endon( "trig_solorun_player_on_slide" );
|
|
|
|
flag_wait( "solorun_player_off_balcony" );
|
|
|
|
startOffsetMin = 128;
|
|
startOffsetMax = 256;
|
|
|
|
endOffsetMin = 32;
|
|
endOffsetMax = 64;
|
|
|
|
shotWaitMin = 0.08;
|
|
shotWaitMax = 0.11;
|
|
|
|
burstWaitMin = 0.5;
|
|
burstWaitMax = 1;
|
|
|
|
shotsMin = 5;
|
|
shotsMax = 9;
|
|
|
|
weapons = level.scriptedweapons;
|
|
|
|
while( 1 )
|
|
{
|
|
weapon = get_random( weapons );
|
|
numShots = RandomIntRange( shotsMin, shotsMax );
|
|
|
|
axis = GetAiArray( "axis" );
|
|
enemy = undefined;
|
|
foreach( guy in axis )
|
|
{
|
|
if( guy CanSee( level.player ) && !player_can_see_ai( guy ) )
|
|
{
|
|
if( !IsDefined( enemy ) || ( Distance( guy.origin, level.player.origin ) < Distance( enemy.origin, level.player.origin ) ) )
|
|
{
|
|
enemy = guy;
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !IsDefined( enemy ) )
|
|
{
|
|
wait( 1 );
|
|
continue;
|
|
}
|
|
|
|
for( i = 0; i < numShots; i++ )
|
|
{
|
|
if( !IsAlive( enemy ) || player_can_see_ai( enemy ) )
|
|
{
|
|
wait( 0.05 );
|
|
continue;
|
|
}
|
|
|
|
startPos = enemy GetEye() + ( 0, 0, 32 );
|
|
|
|
playereye = level.player GetEye();
|
|
playerangles = level.player.angles;
|
|
forward = AnglesToForward( playerangles );
|
|
refPoint = playereye + ( forward * 256 );
|
|
startZ = refPoint[ 2 ] + 256;
|
|
|
|
groundRefPoint = groundpos( refPoint );
|
|
endX = solorun_rooftop_squib_offset( groundRefPoint[ 0 ], endOffsetMin, endOffsetMax );
|
|
endY = solorun_rooftop_squib_offset( groundRefPoint[ 1 ], endOffsetMin, endOffsetMax );
|
|
endZ = startZ;
|
|
endPos = groundpos( ( endX, endY, endZ ) );
|
|
|
|
// make sure we're not going to hit the player
|
|
trace = BulletTrace( startPos, endPos, true );
|
|
traceEnt = trace[ "entity" ];
|
|
|
|
if( IsDefined( traceEnt ) )
|
|
{
|
|
if( IsPlayer( traceEnt ) )
|
|
{
|
|
continue;
|
|
}
|
|
}
|
|
|
|
MagicBullet( weapon, startPos, endPos );
|
|
|
|
wait( RandomFloatRange( shotWaitMin, shotWaitMax ) );
|
|
}
|
|
|
|
wait( RandomFloatRange( burstWaitMin, burstWaitMax ) );
|
|
}
|
|
|
|
}
|
|
|
|
solorun_rooftop_squib_offset( coord, offsetMin, offsetMax )
|
|
{
|
|
rand = RandomIntRange( offsetMin, offsetMax );
|
|
|
|
if( cointoss() )
|
|
{
|
|
rand *= -1;
|
|
}
|
|
|
|
newcoord = coord + rand;
|
|
return newcoord;
|
|
}
|
|
|
|
solorun_chopper_audio()
|
|
{
|
|
trigger_wait_targetname( "trig_balcony_chopper_spawn" );
|
|
|
|
chopper = undefined;
|
|
while( !IsDefined( chopper ) )
|
|
{
|
|
wait( 0.05 );
|
|
chopper = get_vehicle( "solorun_balcony_chopper", "targetname" );
|
|
}
|
|
|
|
chopper thread play_sound_on_entity( "scn_favela_escape_heli_flyover" );
|
|
}
|
|
|
|
solorun_rooftop_chopper_fakefire()
|
|
{
|
|
trigs = GetEntArray( "solorun_chopper_fakefire_trig", "targetname" );
|
|
|
|
array_thread( trigs, ::solorun_rooftop_chopper_fakefire_trig );
|
|
}
|
|
|
|
solorun_rooftop_chopper_fakefire_trig()
|
|
{
|
|
spots = GetStructArray( self.target, "targetname" );
|
|
|
|
self waittill( "trigger" );
|
|
|
|
array_thread( spots, ::fakefire_smallarms_spot );
|
|
array_thread( spots, ::solorun_rooftop_chopper_fakefire_spot );
|
|
}
|
|
|
|
solorun_rooftop_chopper_fakefire_spot()
|
|
{
|
|
if( !self script_delay() )
|
|
{
|
|
wait( RandomFloatRange( 0.5, 1.2 ) );
|
|
}
|
|
|
|
target = GetStruct( self.target, "targetname" );
|
|
|
|
MagicBullet( "rpg_straight_short_life", self.origin, target.origin );
|
|
}
|
|
|
|
solorun_chopper_spawnfunc()
|
|
{
|
|
self.health = 2000000;
|
|
Missile_CreateRepulsorEnt( self, 1150, 1200 );
|
|
|
|
level.chopper = self;
|
|
}
|
|
|
|
solorun_dialogue( bugplayer )
|
|
{
|
|
balconyflag = "solorun_player_at_balcony";
|
|
|
|
if( !IsDefined( bugplayer ) || bugplayer )
|
|
{
|
|
thread solorun_dialogue_bugplayer_inside( balconyflag );
|
|
}
|
|
|
|
flag_wait( balconyflag );
|
|
// "Roach! I see you! Jump down to the rooftops and meet us south of your position! Go!"
|
|
radio_dialogue( "favesc_cmt_meetussouth" );
|
|
|
|
// "Gas is very low! I must leave in thirty seconds, ok?"
|
|
radio_dialogue( "favesc_nkl_verylow" );
|
|
|
|
flag_set( "solorun_timer_start" );
|
|
|
|
// "Roach! We're running on fumes here! You got thirty seconds! Run!"
|
|
radio_dialogue( "favesc_cmt_onfumes" );
|
|
|
|
|
|
// DEPRECATED maybe do this one if you're in the trigger for a while?
|
|
//flag_wait( "solorun_dialogue_3" );
|
|
// "Head to the right!"
|
|
//radio_dialogue( "favesc_cmt_headtoright" );
|
|
|
|
|
|
flag_wait( "solorun_dialogue_4" );
|
|
// "Left!!! Turn left and jump down!"
|
|
radio_dialogue( "favesc_cmt_leftturnleft" );
|
|
|
|
|
|
flag_wait( "solorun_dialogue_5" );
|
|
// "Come on!!!!"
|
|
radio_dialogue( "favesc_cmt_comeon" );
|
|
}
|
|
|
|
solorun_dialogue_bugplayer_inside( ender )
|
|
{
|
|
level endon( ender );
|
|
level.player endon( "death" );
|
|
|
|
wait( 5 );
|
|
//flag_wait( "solorun_dialogue_1" );
|
|
// "Roach, we're circling the area but I can't see you! You've got to get to the rooftops!"
|
|
radio_dialogue( "favesc_cmt_circlingarea" );
|
|
|
|
wait( 15 );
|
|
//flag_wait( "solorun_dialogue_2" );
|
|
// "Roach, we're running low on fuel! Where the hell are you?!"
|
|
radio_dialogue( "favesc_cmt_lowonfuel" );
|
|
}
|
|
|
|
solorun_chopperjump_killtrig()
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( 1 )
|
|
{
|
|
self waittill( "trigger", other );
|
|
|
|
if( IsPlayer( other ) )
|
|
{
|
|
SetDvar( "ui_deadquote", "@FAVELA_ESCAPE_DEADQUOTE_FAILED_CHOPPER_JUMP" );
|
|
maps\_utility::missionFailedWrapper();
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
solorun_chopperjump( waitBeforeJump )
|
|
{
|
|
if( !IsDefined( waitBeforeJump ) || waitBeforeJump )
|
|
{
|
|
flag_wait( "trig_solorun_player_on_slide" );
|
|
}
|
|
|
|
slidetrig = GetEnt( "trig_slide_chopperjump_ledge", "targetname" );
|
|
killtrig = GetEnt( "killtrig_chopperjump", "script_noteworthy" );
|
|
killtrig thread solorun_chopperjump_killtrig();
|
|
|
|
animref = GetStruct( "solorun_chopperjump_animref", "targetname" );
|
|
|
|
animname_player = "player";
|
|
animname_chopper = "chopper";
|
|
animname_ladder = "ladder";
|
|
animname_doorguy = "chopper_door_guy";
|
|
doorguyTag = "tag_detach";
|
|
propsTag = "tag_body";
|
|
fly_in_scene = "chopperjump_in";
|
|
loop_scene = "chopperjump_loop";
|
|
jump_scene = "chopperjump_jump";
|
|
fly_out_scene = "chopperjump_flyaway";
|
|
anime_jump = level.scr_anim[ animname_player ][ jump_scene ];
|
|
|
|
// spawn props
|
|
player_rig = spawn_anim_model( animname_player, animref.origin );
|
|
player_rig Hide();
|
|
|
|
chopper = spawn_anim_model( animname_chopper, animref.origin );
|
|
chopper.angles = animref.angles;
|
|
maps\_vehicle::build_drive( chopper getanim( "rotors" ), undefined, 0 );
|
|
chopper thread maps\_vehicle::animate_drive_idle();
|
|
chopper thread solorun_chopper_sfx();
|
|
level.chopper = chopper;
|
|
|
|
spawner = GetEnt( "chopperjump_sarge", "targetname" );
|
|
sarge = spawner spawn_ai();
|
|
sarge.animname = animname_doorguy;
|
|
level.sarge = sarge;
|
|
sarge LinkTo( chopper, doorguyTag );
|
|
|
|
thread chopperjump_dialogue();
|
|
|
|
ladder = spawn_anim_model( animname_ladder, ( 0, 0, 0 ) );
|
|
level.chopperladder = ladder;
|
|
|
|
player_rig LinkTo( chopper, propsTag );
|
|
ladder LinkTo( chopper, propsTag );
|
|
|
|
player_and_ladder = [];
|
|
player_and_ladder[ 0 ] = player_rig;
|
|
player_and_ladder[ 1 ] = ladder;
|
|
|
|
// otherwise sunshadows pop in and out as world geo goes in and out of the view
|
|
EnableForcedSunShadows();
|
|
|
|
// flying in anim
|
|
thread solorun_chopperjump_chopper_fly_in_and_idle( animref, chopper, ladder, sarge, fly_in_scene, propsTag, loop_scene, doorguyTag );
|
|
|
|
// catch the jump
|
|
jumpstart_vol = GetEnt( "trig_player_chopperjump", "script_noteworthy" );
|
|
jumpForward = AnglesToForward( ( 0, 90, 0 ) );
|
|
thread player_jump_watcher();
|
|
|
|
altLookSpots = GetStructArray( "struct_chopperjump_alt_lookspot", "targetname" );
|
|
ASSERT( altLookSpots.size );
|
|
|
|
while( 1 )
|
|
{
|
|
breakout = false;
|
|
|
|
while( level.player IsTouching( jumpstart_vol ) )
|
|
{
|
|
flag_wait( "player_jumping" );
|
|
if( player_leaps_to_chopper( jumpstart_vol, ladder, altLookSpots, true ) )
|
|
{
|
|
breakout = true;
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
if( breakout )
|
|
{
|
|
break;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
|
|
flag_set( "chopperjump_player_jump" );
|
|
|
|
thread vision_set_fog_changes( "favela_escape_chopperjump", 3 );
|
|
|
|
killtrig Delete();
|
|
slidetrig Delete();
|
|
|
|
chopper notify( "stop_loop" );
|
|
|
|
animlength = GetAnimLength( player_rig getanim( jump_scene ) );
|
|
|
|
level.player DisableWeapons();
|
|
level.player AllowJump( false );
|
|
level.player AllowCrouch( false );
|
|
level.player AllowProne( false );
|
|
|
|
// jump anim
|
|
chopper thread anim_single( player_and_ladder, jump_scene, propsTag );
|
|
// don't show the rig until after the player has blended into the animation
|
|
thread chopperjump_player_blend_to_anim( player_rig );
|
|
player_rig delaycall( 0.9, ::Show );
|
|
delaythread( 1, ::solorun_chopperjump_rumble );
|
|
|
|
level.chopperFlyawayDelay = animlength * 0.2;
|
|
flag_set( "solorun_player_boarded_chopper" );
|
|
|
|
animref delaythread( 0.05, ::anim_single_solo, chopper, fly_out_scene );
|
|
chopper delaythread( 0.05, ::anim_single_solo, sarge, fly_out_scene, doorguyTag );
|
|
}
|
|
|
|
solorun_chopperjump_rumble()
|
|
{
|
|
level.player PlayRumbleOnEntity( "artillery_rumble" );
|
|
wait( 0.5 );
|
|
while( !flag( "level_faded_to_black" ) )
|
|
{
|
|
level.player PlayRumbleOnEntity( "damage_light" );
|
|
wait( 0.115 );
|
|
}
|
|
}
|
|
|
|
player_leaps_to_chopper( volume, ladder, altLookSpots, checkIsOnGround )
|
|
{
|
|
if( !IsDefined( checkIsOnGround ) )
|
|
{
|
|
checkIsOnGround = true;
|
|
}
|
|
|
|
if( !volume IsTouching( level.player ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if ( level.player GetStance() != "stand" )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( checkIsOnGround && level.player IsOnGround() )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
// need to be looking at the ladder or at one of our alternate look spots while jumping for it
|
|
lookingGood = true;
|
|
|
|
refPoint = ladder.origin + ( 0, 0, -85 ); // uses the same offset as the objective indicator
|
|
fov = GetDvarInt( "cg_fov" );
|
|
if( !level.player WorldPointInReticle_Circle( refPoint, fov, 120 ) )
|
|
{
|
|
lookingGood = false;
|
|
}
|
|
|
|
if( !lookingGood )
|
|
{
|
|
foundOne = false;
|
|
foreach( spot in altLookSpots )
|
|
{
|
|
//print3d( spot.origin, "*", ( 1, 1, 1 ), 1, 1, 60 );
|
|
|
|
if( level.player WorldPointInReticle_Circle( spot.origin, fov, 165 ) )
|
|
{
|
|
foundOne = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( foundOne )
|
|
{
|
|
lookingGood = true;
|
|
}
|
|
}
|
|
|
|
if( !lookingGood )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
vel = level.player GetVelocity();
|
|
// figure out the length of the vector to get the speed (distance from world center = length)
|
|
velocity = Distance( ( vel[ 0 ], vel[ 1 ], 0 ), ( 0, 0, 0 ) ); // don't care about Z velocity
|
|
if ( velocity < 145 )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
solorun_chopperjump_chopper_fly_in_and_idle( animref, chopper, ladder, sarge, fly_in_scene, propsTag, loop_scene, doorguyTag )
|
|
{
|
|
animref thread anim_single_solo( chopper, fly_in_scene );
|
|
chopper thread anim_single_solo( sarge, fly_in_scene, doorguyTag );
|
|
chopper anim_single_solo( ladder, fly_in_scene, propsTag );
|
|
|
|
// idle anim
|
|
if( !flag( "chopperjump_player_jump" ) )
|
|
{
|
|
packets = [];
|
|
packet_chopper = [];
|
|
packet_chopper[ "guy" ] = chopper;
|
|
packet_chopper[ "entity" ] = animref;
|
|
packet_chopper[ "tag" ] = undefined;
|
|
packets[ packets.size ] = packet_chopper;
|
|
|
|
packet_ladder = [];
|
|
packet_ladder[ "guy" ] = ladder;
|
|
packet_ladder[ "entity" ] = chopper;
|
|
packet_ladder[ "tag" ] = propsTag;
|
|
packets[ packets.size ] = packet_ladder;
|
|
|
|
packet_sarge = [];
|
|
packet_sarge[ "guy" ] = sarge;
|
|
packet_sarge[ "entity" ] = chopper;
|
|
packet_sarge[ "tag" ] = doorguyTag;
|
|
packets[ packets.size ] = packet_sarge;
|
|
|
|
chopper thread anim_loop_packet( packets, loop_scene );
|
|
}
|
|
}
|
|
|
|
solorun_chopper_sfx()
|
|
{
|
|
idleAlias = "pavelow_idle_high";
|
|
movingAlias = "pavelow_engine_high";
|
|
|
|
self thread play_loop_sound_on_entity( idleAlias );
|
|
|
|
flag_wait( "chopperjump_player_jump" );
|
|
wait( level.chopperFlyawayDelay );
|
|
|
|
self thread play_loop_sound_on_entity( movingAlias );
|
|
wait( 0.25 );
|
|
self notify( "stop sound" + idleAlias );
|
|
}
|
|
|
|
player_jump_watcher()
|
|
{
|
|
level endon( "player_jump_watcher_stop" );
|
|
|
|
jumpflag = "player_jumping";
|
|
if( !flag_exist( jumpflag ) )
|
|
{
|
|
flag_init( jumpflag );
|
|
}
|
|
else
|
|
{
|
|
flag_clear( jumpflag );
|
|
}
|
|
|
|
NotifyOnCommand( "playerjump", "+gostand" );
|
|
NotifyOnCommand( "playerjump", "+moveup" );
|
|
|
|
while( 1 )
|
|
{
|
|
level.player waittill( "playerjump" );
|
|
wait( 0.1 ); // jumps don't happen immediately
|
|
|
|
if( !level.player IsOnGround() )
|
|
{
|
|
flag_set( jumpflag );
|
|
println( "jumping" );
|
|
}
|
|
|
|
while( !level.player IsOnGround() )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
flag_clear( jumpflag );
|
|
println( "not jumping" );
|
|
}
|
|
}
|
|
|
|
chopperjump_player_blend_to_anim( player_rig )
|
|
{
|
|
wait( 0.3 );
|
|
level.player notify( "stop_sliding" ); // defensive. deleting the slide trigger when we catch the jump should work, but just in case it starts in some super edge case, I don't want StopSlide() to ever try to unlink him
|
|
level.player PlayerLinkToBlend( player_rig, "tag_player", 0.6, 0.2, 0.4 );
|
|
}
|
|
|
|
chopperjump_dialogue()
|
|
{
|
|
trigger_wait_targetname( "chopperjump_dialogue_jumpforit" );
|
|
|
|
// "Jump for it!!!"
|
|
level.sarge thread dialogue_queue( "favesc_cmt_jump" );
|
|
|
|
flag_wait( "chopperjump_player_jump" );
|
|
wait( 2 );
|
|
|
|
// "Nikolai! We got him! Get us outta here!"
|
|
radio_dialogue( "favesc_cmt_gothim" );
|
|
|
|
wait( 1 );
|
|
|
|
// "Where to, Captain?"
|
|
radio_dialogue( "favesc_nkl_whereto" );
|
|
|
|
wait( 0.4 );
|
|
|
|
// "Just get us to the sub..."
|
|
radio_dialogue( "favesc_cmt_tothesub" );
|
|
}
|
|
|
|
|
|
// -----------------
|
|
// -- AI STUFF --
|
|
// -----------------
|
|
get_single_redshirt()
|
|
{
|
|
guys = get_nonhero_friends();
|
|
ASSERT( guys.size == 1 );
|
|
redshirt = guys[ 0 ];
|
|
return redshirt;
|
|
}
|
|
|
|
setup_color_friendly_spawners()
|
|
{
|
|
spawners = GetEntArray( "color_friendly_spawner", "targetname" );
|
|
ASSERT( spawners.size );
|
|
|
|
level._color_friendly_spawners = spawners;
|
|
}
|
|
|
|
enemy_cleanup()
|
|
{
|
|
cleanuptrigs = GetEntArray( "enemy_cleanup_trigger", "targetname" );
|
|
array_thread( cleanuptrigs, ::enemy_cleanup_trigger_think );
|
|
}
|
|
|
|
enemy_cleanup_trigger_think()
|
|
{
|
|
assertstr = "Enemy cleanup trigger at origin " + self.origin + " needs to target a volume.";
|
|
ASSERTEX( IsDefined( self.target ), assertstr );
|
|
|
|
killVolumes = GetEntArray( self.target, "targetname" );
|
|
ASSERTEX( killVolumes.size, assertstr );
|
|
|
|
self waittill( "trigger" );
|
|
guys = GetAiArray( "axis" );
|
|
|
|
foreach( guy in guys )
|
|
{
|
|
foreach( vol in killVolumes )
|
|
{
|
|
foundHim = false;
|
|
if( guy IsTouching( vol ) )
|
|
{
|
|
foundHim = true;
|
|
guy Delete();
|
|
break;
|
|
}
|
|
|
|
if( foundHim )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
array_delete( killVolumes );
|
|
self Delete();
|
|
}
|
|
|
|
try_spawn_loop( numTries, waitTime )
|
|
{
|
|
if( !IsDefined( numTries ) )
|
|
{
|
|
numTries = 10;
|
|
}
|
|
|
|
if( !IsDefined( waitTime ) )
|
|
{
|
|
waitTime = 0.05;
|
|
}
|
|
|
|
for( i = 0; i < numTries; i++ )
|
|
{
|
|
if( IsDefined( self.forcespawn ) && self.forcespawn > 0 )
|
|
{
|
|
guy = self StalingradSpawn();
|
|
}
|
|
else
|
|
{
|
|
guy = self DoSpawn();
|
|
}
|
|
|
|
if( !spawn_failed( guy ) )
|
|
{
|
|
return guy;
|
|
}
|
|
|
|
wait( waitTime );
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
spawn_group_staggered( aSpawners, doSafe )
|
|
{
|
|
spawn_group( aSpawners, doSafe, true );
|
|
}
|
|
|
|
spawn_group( aSpawners, doSafe, doStaggered )
|
|
{
|
|
ASSERTEX( ( aSpawners.size > 0 ), "The array passed to array_spawn function is empty" );
|
|
|
|
if( !IsDefined( doSafe ) )
|
|
{
|
|
doSafe = false;
|
|
}
|
|
|
|
if( !IsDefined( doStaggered ) )
|
|
{
|
|
doStaggered = false;
|
|
}
|
|
|
|
aSpawners = array_randomize( aSpawners );
|
|
|
|
spawnedGuys = [];
|
|
foreach( index, spawner in aSpawners )
|
|
{
|
|
guy = spawner spawn_ai();
|
|
spawnedGuys[ spawnedGuys.size ] = guy;
|
|
|
|
if( doStaggered )
|
|
{
|
|
if( index != ( aSpawners.size - 1 ) )
|
|
{
|
|
wait( randomfloatrange( .25, 1 ) );
|
|
}
|
|
}
|
|
}
|
|
|
|
if( doSafe )
|
|
{
|
|
//check to ensure all the guys were spawned
|
|
ASSERTEX( ( aSpawners.size == spawnedGuys.size ), "Not all guys were spawned successfully from array_spawn" );
|
|
}
|
|
|
|
return spawnedGuys;
|
|
}
|
|
|
|
ai_unlimited_rocket_ammo()
|
|
{
|
|
self endon( "death" );
|
|
|
|
ASSERT( IsDefined( self.a.rockets ) );
|
|
|
|
while( 1 )
|
|
{
|
|
if( self.a.rockets < 3 )
|
|
{
|
|
self.a.rockets = 3;
|
|
}
|
|
|
|
wait( 1 );
|
|
}
|
|
}
|
|
|
|
bloody_pain( damage, attacker, direction_vec, point, type, modelName, tagName )
|
|
{
|
|
if( damage <= 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
angles = VectorToAngles( direction_vec );
|
|
forward = AnglesToForward( angles );
|
|
up = AnglesToUp( angles );
|
|
|
|
anglesReversed = VectorToAngles( direction_vec ) + ( 0, 180, 0 );
|
|
backward = AnglesToForward( anglesReversed );
|
|
|
|
PlayFX( getfx( "headshot" ), point, forward, up );
|
|
PlayFX( getfx( "bodyshot" ), point, backward, up );
|
|
}
|
|
|
|
// ---- door kicker guys ----
|
|
// spawns AIs in a house and has one of them kick the door open
|
|
door_kick_housespawn( spawners, door, animRef, physicsRef )
|
|
{
|
|
spawners = array_randomize( spawners );
|
|
|
|
kickerSpawner = undefined;
|
|
|
|
// if one of them has script_parameters set to "kicker" then he should kick the door
|
|
foreach( spawner in spawners )
|
|
{
|
|
if( IsDefined( spawner.script_parameters ) && spawner.script_parameters == "kicker" )
|
|
{
|
|
kickerSpawner = spawner;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !IsDefined( kickerSpawner ) )
|
|
{
|
|
kickerSpawner = spawners[ 0 ];
|
|
}
|
|
|
|
spawners = array_remove( spawners, kickerSpawner );
|
|
kicker = kickerSpawner spawn_ai( true );
|
|
kicker magic_bullet_shield();
|
|
|
|
// spawn the other guys after the kicker has started his anim
|
|
if( spawners.size )
|
|
{
|
|
delaythread( 0.15, ::spawn_group, spawners );
|
|
}
|
|
|
|
kickAnime = "door_kick_in";
|
|
kickNotetrack = "kick";
|
|
if( IsDefined( animRef.script_noteworthy ) )
|
|
{
|
|
switch( animRef.script_noteworthy )
|
|
{
|
|
case "wave":
|
|
kickAnime = "doorburst_wave";
|
|
kickNotetrack = "door_kick";
|
|
break;
|
|
|
|
case "search":
|
|
kickAnime = "doorburst_search";
|
|
kickNotetrack = "door_kick";
|
|
break;
|
|
|
|
case "fall":
|
|
kickAnime = "doorburst_fall";
|
|
kickNotetrack = "door_kick";
|
|
break;
|
|
}
|
|
}
|
|
|
|
animRef thread anim_generic( kicker, kickAnime );
|
|
kicker waittillmatch( "single anim", kickNotetrack );
|
|
thread play_sound_in_space( "door_wood_double_kick", door.origin );
|
|
door thread sbmodel_rotate( 0.25, true );
|
|
|
|
// optionally push some stuff out of the way when the door opens
|
|
if( IsDefined( physicsRef ) )
|
|
{
|
|
PhysicsExplosionCylinder( physicsRef.origin, physicsRef.radius, ( physicsRef.radius / 2 ), 1.0 );
|
|
}
|
|
|
|
kicker stop_magic_bullet_shield();
|
|
kicker.allowdeath = true;
|
|
|
|
kicker waittillmatch( "single anim", "end" );
|
|
|
|
if( IsAlive( kicker ) )
|
|
{
|
|
if( IsDefined( kickerSpawner.script_playerseek ) && kickerSpawner.script_playerseek > 0 )
|
|
{
|
|
kicker playerseek();
|
|
}
|
|
else
|
|
{
|
|
if( IsDefined( kickerSpawner.target ) )
|
|
{
|
|
node = GetNode( kickerSpawner.target, "targetname" );
|
|
if( IsDefined( node ) )
|
|
{
|
|
kicker set_temp_goalradius( 96 );
|
|
kicker SetGoalNode( node );
|
|
|
|
kicker waittill_notify_or_timeout( "goal", 5 );
|
|
|
|
if( IsAlive( kicker ) )
|
|
{
|
|
kicker restore_goalradius();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// ---- chaotic above shooter guys ----
|
|
chaotic_above_shooter()
|
|
{
|
|
self endon( "death" );
|
|
|
|
animref = GetStruct( self.target, "targetname" );
|
|
|
|
// favela_chaotic_above_through, favela_chaotic_above_through_uzi, favela_chaotic_above_through_back
|
|
anime = "favela_chaotic_above_through";
|
|
if( IsDefined( animref.script_noteworthy ) )
|
|
{
|
|
anime = animref.script_noteworthy;
|
|
}
|
|
|
|
animref anim_generic_reach( self, anime );
|
|
|
|
self.allowdeath = true;
|
|
animref anim_generic( self, anime );
|
|
}
|
|
|
|
// ---- window smash guys ----
|
|
window_smash_stop_inside()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self window_smash( "window_smash_stop_inside" );
|
|
|
|
node = GetNode( self.target, "targetname" );
|
|
|
|
if( IsDefined( self.script_playerseek ) && self.script_playerseek )
|
|
{
|
|
self playerseek();
|
|
}
|
|
else if( IsDefined( node ) )
|
|
{
|
|
self SetGoalNode( node );
|
|
}
|
|
}
|
|
|
|
window_smash( smashAnime )
|
|
{
|
|
self endon( "death" );
|
|
|
|
errorstr = "window smash guy at origin " + self.origin + " needs to be targeting a script_struct that he can use as his animref.";
|
|
ASSERTEX( IsDefined( self.target ), errorstr );
|
|
animref = GetStruct( self.target, "targetname" );
|
|
ASSERTEX( IsDefined( animref ), errorstr );
|
|
|
|
animref anim_generic_reach( self, smashAnime );
|
|
animref anim_generic( self, smashAnime );
|
|
}
|
|
|
|
// ---- curtain pulldown guys ----
|
|
curtain_pulldown( bWaitForPlayer, specialWaitFunc )
|
|
{
|
|
if ( !isdefined( bWaitForPlayer ) )
|
|
bWaitForPlayer = false;
|
|
|
|
assert( isdefined( self.target ) );
|
|
node = self curtain_pulldown_getnode();
|
|
assert( isdefined( node ) );
|
|
|
|
curtain = curtain_pulldown_spawnmodel( node );
|
|
|
|
self waittill( "spawned", guy );
|
|
if ( spawn_failed( guy ) )
|
|
return;
|
|
|
|
guy endon( "death" );
|
|
|
|
guy.animname = "curtain_pull";
|
|
|
|
guy set_ignoreme( true );
|
|
guy.usechokepoints = false;
|
|
|
|
wait 0.05;
|
|
|
|
guy_and_curtain[ 0 ] = guy;
|
|
guy_and_curtain[ 1 ] = curtain;
|
|
|
|
node anim_reach_solo( guy, "pulldown" );
|
|
|
|
if ( bWaitForPlayer )
|
|
{
|
|
node anim_first_frame_solo( guy, "pulldown" );
|
|
|
|
if( IsDefined( specialWaitFunc ) )
|
|
{
|
|
[[specialWaitFunc]]( guy, node );
|
|
}
|
|
else
|
|
{
|
|
waittill_player_lookat( 0.9, undefined, true, 5.0 );
|
|
}
|
|
}
|
|
|
|
guy.allowdeath = true;
|
|
node anim_single( guy_and_curtain, "pulldown" );
|
|
|
|
guy endon( "death" );
|
|
|
|
guy set_ignoreme( false );
|
|
guy.goalradius = 1000;
|
|
guy setGoalPos( guy.origin );
|
|
guy.usechokepoints = true;
|
|
}
|
|
|
|
// broke out into a separate function so we can spawn the model early
|
|
curtain_pulldown_spawnmodel( node )
|
|
{
|
|
if( IsDefined( node.curtain ) )
|
|
{
|
|
return node.curtain;
|
|
}
|
|
|
|
curtain = spawn_anim_model( "curtain" );
|
|
|
|
node thread anim_first_frame_solo( curtain, "pulldown" );
|
|
node.curtain = curtain;
|
|
|
|
return node.curtain;
|
|
}
|
|
|
|
curtain_pulldown_getnode()
|
|
{
|
|
nodes = getentarray( self.target, "targetname" );
|
|
foreach( node in nodes )
|
|
{
|
|
if ( node.classname == "script_origin" )
|
|
return node;
|
|
}
|
|
assertMsg( "curtain pulldown guy doesn't target a script_origin" );
|
|
}
|
|
|
|
dialogue( msg )
|
|
{
|
|
if( !IsDefined( level.scripted_dialogue_struct ) )
|
|
{
|
|
level.scripted_dialogue_struct = SpawnStruct();
|
|
}
|
|
|
|
level.scripted_dialogue_struct function_stack( ::dialogue_stack, self, msg );
|
|
}
|
|
|
|
dialogue_stack( guy, msg )
|
|
{
|
|
guy dialogue_queue( msg );
|
|
}
|
|
|
|
dialogue_print( line, timeout )
|
|
{
|
|
if( !IsDefined( timeout ) )
|
|
{
|
|
timeout = 3;
|
|
}
|
|
|
|
hintfade = 0.5;
|
|
|
|
level endon( "clearing_hints" );
|
|
|
|
if ( isDefined( level.tempHint ) )
|
|
level.tempHint destroyElem();
|
|
|
|
level.tempHint = createFontString( "default", 1.5 );
|
|
level.tempHint setPoint( "BOTTOM", undefined, 0, -40 );
|
|
level.tempHint.color = ( 1, 1, 1 );
|
|
level.tempHint setText( line );
|
|
level.tempHint.alpha = 0;
|
|
level.tempHint fadeOverTime( 0.5 );
|
|
level.tempHint.alpha = 1;
|
|
level.tempHint.sort = 1;
|
|
wait( 0.5 );
|
|
level.tempHint endon( "death" );
|
|
|
|
wait( timeout );
|
|
|
|
level.tempHint fadeOverTime( hintfade );
|
|
level.tempHint.alpha = 0;
|
|
wait( hintfade );
|
|
|
|
level.tempHint destroyElem();
|
|
}
|
|
|
|
playsound_from_random_spot( soundalias, moveSpots )
|
|
{
|
|
// in case we're just passing a single spot
|
|
if( !IsArray( moveSpots ) )
|
|
{
|
|
newArr = [];
|
|
newArr[ 0 ] = moveSpots;
|
|
moveSpots = newArr;
|
|
}
|
|
|
|
org = Spawn( "script_origin", get_random( moveSpots ).origin );
|
|
org PlaySound( soundAlias, "sound_done" );
|
|
org waittill( "sound_done" );
|
|
org Delete();
|
|
}
|
|
|
|
delete_at_path_end()
|
|
{
|
|
self waittill( "reached_path_end" );
|
|
|
|
if( IsAlive( self ) )
|
|
{
|
|
self Kill();
|
|
}
|
|
|
|
wait( 0.1 );
|
|
|
|
if( IsDefined( self ) )
|
|
{
|
|
self Delete();
|
|
}
|
|
}
|
|
|
|
ignore_til_pathend_or_damage()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self ignore_everything();
|
|
self waittill_either( "reached_path_end", "damage" );
|
|
self clear_ignore_everything();
|
|
}
|
|
|
|
ignore_til_pathend()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self ignore_everything();
|
|
self waittill( "reached_path_end" );
|
|
self clear_ignore_everything();
|
|
}
|
|
|
|
ignore_and_delete_at_path_end()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self ignore_everything();
|
|
|
|
while( 1 )
|
|
{
|
|
msg = self waittill_any_return( "reached_path_end", "damage" );
|
|
|
|
if( msg == "damage" )
|
|
{
|
|
self clear_ignore_everything();
|
|
}
|
|
else if( msg == "reached_path_end" )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
wait( 0.1 );
|
|
if( IsDefined( self ) )
|
|
{
|
|
self Delete();
|
|
}
|
|
}
|
|
|
|
playerseek_at_path_end()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self waittill( "reached_path_end" );
|
|
self playerseek();
|
|
}
|
|
|
|
playerseek()
|
|
{
|
|
self SetGoalEntity( level.player );
|
|
}
|
|
|
|
scr_animplaybackrate( rate )
|
|
{
|
|
self.animplaybackrate = rate;
|
|
}
|
|
|
|
scr_usechokepoints( bool )
|
|
{
|
|
self.useChokePoints = bool;
|
|
}
|
|
|
|
scr_disable_dontshootwhilemoving()
|
|
{
|
|
self.dontshootwhilemoving = undefined;
|
|
}
|
|
|
|
scr_moveplaybackrate( rate )
|
|
{
|
|
self.moveplaybackrate = rate;
|
|
}
|
|
|
|
scr_accuracy( acc )
|
|
{
|
|
self.baseAccuracy = acc;
|
|
}
|
|
|
|
scr_setgoalvolume( ent )
|
|
{
|
|
self SetGoalVolume( ent );
|
|
}
|
|
|
|
scr_cleargoalvolume()
|
|
{
|
|
self ClearGoalVolume();
|
|
}
|
|
|
|
scr_set_health( newhealth )
|
|
{
|
|
self.health = newhealth;
|
|
}
|
|
|
|
scr_ignoreall( bool )
|
|
{
|
|
self.ignoreall = bool;
|
|
}
|
|
|
|
scr_ignoreme( bool )
|
|
{
|
|
self.ignoreme = bool;
|
|
}
|
|
|
|
scr_walkDistFacingMotion( dist )
|
|
{
|
|
self.walkDistFacingMotion = dist;
|
|
}
|
|
|
|
set_temp_goalradius( newRadius )
|
|
{
|
|
if( !IsDefined( self.og_goalradius ) )
|
|
{
|
|
self.og_goalradius = self.goalradius;
|
|
}
|
|
|
|
self.goalradius = newRadius;
|
|
}
|
|
|
|
restore_goalradius()
|
|
{
|
|
if( IsDefined( self.og_goalradius ) )
|
|
{
|
|
self.goalradius = self.og_goalradius;
|
|
}
|
|
}
|
|
|
|
set_threatbias_group( groupname )
|
|
{
|
|
self.og_threatbiasgroup = self GetThreatBiasGroup();
|
|
self SetThreatBiasGroup( groupname );
|
|
}
|
|
|
|
reset_threatbias_group()
|
|
{
|
|
ASSERT( IsDefined( self.og_threatbiasgroup ) );
|
|
self SetThreatBiasGroup( self.og_threatbiasgroup );
|
|
}
|
|
|
|
magic_bullet_shield_safe()
|
|
{
|
|
if( !IsDefined( self.magic_bullet_shield ) || !self.magic_bullet_shield )
|
|
{
|
|
self thread magic_bullet_shield();
|
|
}
|
|
}
|
|
|
|
stop_magic_bullet_shield_safe()
|
|
{
|
|
if( IsDefined( self.magic_bullet_shield ) && self.magic_bullet_shield )
|
|
{
|
|
self thread stop_magic_bullet_shield();
|
|
}
|
|
}
|
|
|
|
ignore_everything()
|
|
{
|
|
self.ignoreall = true;
|
|
self.grenadeawareness = 0;
|
|
self.ignoreexplosionevents = true;
|
|
self.ignorerandombulletdamage = true;
|
|
self.ignoresuppression = true;
|
|
self.fixednode = false;
|
|
self.disableBulletWhizbyReaction = true;
|
|
self disable_pain();
|
|
|
|
self.og_newEnemyReactionDistSq = self.newEnemyReactionDistSq;
|
|
self.newEnemyReactionDistSq = 0;
|
|
}
|
|
|
|
clear_ignore_everything()
|
|
{
|
|
self.ignoreall = false;
|
|
self.grenadeawareness = 1;
|
|
self.ignoreexplosionevents = false;
|
|
self.ignorerandombulletdamage = false;
|
|
self.ignoresuppression = false;
|
|
self.fixednode = true;
|
|
self.disableBulletWhizbyReaction = false;
|
|
self enable_pain();
|
|
|
|
if( IsDefined( self.og_newEnemyReactionDistSq ) )
|
|
{
|
|
self.newEnemyReactionDistSq = self.og_newEnemyReactionDistSq;
|
|
}
|
|
}
|
|
|
|
be_less_scared()
|
|
{
|
|
self.grenadeawareness = 0;
|
|
self.ignoreexplosionevents = true;
|
|
self.ignorerandombulletdamage = true;
|
|
self.ignoresuppression = true;
|
|
//self.fixednode = false;
|
|
self.disableBulletWhizbyReaction = true;
|
|
self disable_pain();
|
|
|
|
self PushPlayer( true );
|
|
}
|
|
|
|
group_clear_atScriptedGoal( arr )
|
|
{
|
|
foreach( guy in arr )
|
|
{
|
|
guy.atScriptedGoal = false;
|
|
}
|
|
}
|
|
|
|
goto_scripted_goalnode( node )
|
|
{
|
|
self ignore_everything();
|
|
|
|
self set_temp_goalradius( 32 );
|
|
self SetGoalNode( node );
|
|
self waittill( "goal" );
|
|
self.atScriptedGoal = true;
|
|
|
|
self clear_ignore_everything();
|
|
}
|
|
|
|
group_waitfor_scriptedgoal( arr )
|
|
{
|
|
while( 1 )
|
|
{
|
|
foundOne = false;
|
|
|
|
foreach( guy in arr )
|
|
{
|
|
if( IsAlive( guy ) && !guy.atScriptedGoal )
|
|
{
|
|
foundOne = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( !foundOne )
|
|
{
|
|
break;
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
|
|
level notify( "group_at_scriptedgoal" );
|
|
}
|
|
|
|
get_alive_enemies()
|
|
{
|
|
enemies = GetAiArray( "axis" );
|
|
return array_removeDead( enemies );
|
|
}
|
|
|
|
get_nonhero_friends()
|
|
{
|
|
nonheroes = [];
|
|
|
|
foreach( guy in level.friends )
|
|
{
|
|
if( !IsDefined( guy.isHero ) )
|
|
{
|
|
nonheroes[ nonheroes.size ] = guy;
|
|
}
|
|
}
|
|
|
|
return nonheroes;
|
|
}
|
|
|
|
remove_nonhero_friends( exceptThisMany )
|
|
{
|
|
flag_wait( "friends_setup" );
|
|
|
|
nonheroes = get_nonhero_friends();
|
|
|
|
if( IsDefined( exceptThisMany ) )
|
|
{
|
|
nonheroes = array_remove( nonheroes, nonheroes[0] );
|
|
}
|
|
|
|
battlechatter_off( "allies" );
|
|
|
|
foreach( guy in nonheroes )
|
|
{
|
|
level.friends = array_remove( level.friends, guy );
|
|
|
|
guy disable_replace_on_death();
|
|
guy stop_magic_bullet_shield_safe();
|
|
guy Kill();
|
|
}
|
|
|
|
battlechatter_on( "allies" );
|
|
}
|
|
|
|
// color reinforcement system startup thread
|
|
favela_escape_friendly_startup_thread()
|
|
{
|
|
self scr_accuracy( level.friendly_baseaccuracy );
|
|
self friend_add();
|
|
}
|
|
|
|
// adds a guy to level.friends
|
|
friend_add()
|
|
{
|
|
if( !IsDefined( level.friends ) )
|
|
{
|
|
level.friends = [];
|
|
}
|
|
|
|
ASSERT( !is_in_array( level.friends, self ), "Trying to add a guy to level.friends who's already in there." );
|
|
|
|
level.friends = array_add( level.friends, self );
|
|
self thread remove_from_friends_on_death();
|
|
}
|
|
|
|
// removes a guy from level.friends
|
|
friend_remove()
|
|
{
|
|
ASSERT( IsDefined( level.friends ) );
|
|
level.friends = array_remove( level.friends, self );
|
|
}
|
|
|
|
remove_from_friends_on_death()
|
|
{
|
|
self waittill( "death" );
|
|
self friend_remove();
|
|
}
|
|
|
|
delete_all_friends()
|
|
{
|
|
if( IsDefined( level.scripted_dialogue_struct ) )
|
|
{
|
|
// clear the dialogue function stack so it's not waiting to run a function that depends on one of these guys being alive
|
|
level.scripted_dialogue_struct function_stack_clear();
|
|
}
|
|
|
|
array_notify( level.friends, "death" );
|
|
|
|
nonheroes = get_nonhero_friends();
|
|
foreach( guy in nonheroes )
|
|
{
|
|
guy disable_replace_on_death();
|
|
guy stop_magic_bullet_shield_safe();
|
|
guy Delete();
|
|
}
|
|
|
|
level.sarge disable_replace_on_death();
|
|
level.hero1 disable_replace_on_death();
|
|
level.sarge stop_magic_bullet_shield();
|
|
level.hero1 stop_magic_bullet_shield();
|
|
level.sarge Delete();
|
|
level.hero1 Delete();
|
|
}
|
|
|
|
warp_friends_and_player( str )
|
|
{
|
|
level.friends = array_removeundefined( level.friends );
|
|
level.friends = array_removedead( level.friends );
|
|
|
|
friendSpots = GetStructArray( str, "targetname" );
|
|
playerSpot = GetStruct( str + "_player", "targetname" );
|
|
|
|
ASSERT( friendSpots.size >= level.friends.size );
|
|
ASSERT( IsDefined( playerSpot ) );
|
|
|
|
foreach( index, guy in level.friends )
|
|
{
|
|
origin = friendSpots[ index ].origin;
|
|
angles = friendSpots[ index ].angles;
|
|
guy thread teleport_to_origin( origin, angles );
|
|
}
|
|
|
|
level.player teleport_to_origin( playerSpot.origin, playerSpot.angles );
|
|
}
|
|
|
|
teleport_to_node( node )
|
|
{
|
|
self teleport_to_origin( node.origin, node.angles );
|
|
}
|
|
|
|
teleport_to_origin( origin, angles )
|
|
{
|
|
if( !IsDefined( angles ) )
|
|
{
|
|
angles = ( 0, 0, 0 );
|
|
}
|
|
|
|
if( !IsPlayer( self ) )
|
|
{
|
|
self ForceTeleport( groundpos( origin ), angles );
|
|
self SetGoalPos( self.origin );
|
|
}
|
|
else
|
|
{
|
|
org = level.player spawn_tag_origin();
|
|
level.player PlayerLinkTo( org, "tag_origin", 1 );
|
|
org MoveTo( origin, 0.05 );
|
|
org RotateTo( angles, 0.05 );
|
|
wait( 0.1 );
|
|
level.player Unlink();
|
|
org Delete();
|
|
}
|
|
}
|
|
|
|
kill_group_over_time( guys, time )
|
|
{
|
|
if( guys.size < 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
timePerKill = time / guys.size;
|
|
|
|
foreach( index, guy in guys )
|
|
{
|
|
guy thread bloody_death( 0 );
|
|
|
|
if( index != guys.size - 1 )
|
|
{
|
|
wait( timePerKill );
|
|
}
|
|
}
|
|
}
|
|
|
|
bloody_death_after_min_delay( mindelay, randomdelay )
|
|
{
|
|
wait( mindelay );
|
|
self bloody_death( randomdelay );
|
|
}
|
|
|
|
// fake death
|
|
bloody_death( delay )
|
|
{
|
|
self endon( "death" );
|
|
|
|
if( !IsSentient( self ) || !IsAlive( self ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
if( IsDefined( self.bloody_death ) && self.bloody_death )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self.bloody_death = true;
|
|
|
|
if( IsDefined( delay ) )
|
|
{
|
|
wait( RandomFloat( delay ) );
|
|
}
|
|
|
|
tags = [];
|
|
tags[0] = "j_hip_le";
|
|
tags[1] = "j_hip_ri";
|
|
tags[2] = "j_head";
|
|
tags[3] = "j_spine4";
|
|
tags[4] = "j_elbow_le";
|
|
tags[5] = "j_elbow_ri";
|
|
tags[6] = "j_clavicle_le";
|
|
tags[7] = "j_clavicle_ri";
|
|
|
|
for( i = 0; i < 3 + RandomInt( 5 ); i++ )
|
|
{
|
|
random = RandomIntRange( 0, tags.size );
|
|
//vec = self GetTagOrigin( tags[random] );
|
|
self thread bloody_death_fx( tags[random], undefined );
|
|
wait( RandomFloat( 0.1 ) );
|
|
}
|
|
|
|
self DoDamage( self.health + 50, self.origin );
|
|
}
|
|
|
|
bloody_death_fx( tag, fxName )
|
|
{
|
|
if( !IsDefined( fxName ) )
|
|
{
|
|
fxName = level._effect["flesh_hit"];
|
|
}
|
|
|
|
PlayFxOnTag( fxName, self, tag );
|
|
}
|
|
|
|
|
|
// -------------------------------
|
|
// -- FRIENDLY COLOR MANAGEMENT --
|
|
// -------------------------------
|
|
// kind of a fancy version of the linear friendly color management technique, in that it
|
|
// supports multiple colorCode/volume combos per trigger
|
|
// - the basic concept is that we don't want friendlies to move forward until
|
|
// a volume associated with their colorCode is clear of enemies
|
|
color_flags_advance( baseName, numTotalFlags, startFlagNum )
|
|
{
|
|
thread color_flags_advance_cleanup();
|
|
level endon( "color_flags_advance_stop" );
|
|
|
|
team = "allies";
|
|
|
|
// maybe we want to start from the middle of the chain, like for a start point
|
|
if( !IsDefined( startFlagNum ) )
|
|
{
|
|
startFlagNum = 1;
|
|
}
|
|
|
|
for ( i = startFlagNum; i <= numTotalFlags; i++ )
|
|
{
|
|
// final name looks like "[baseName]_1" or whatever
|
|
name = baseName + "_" + i;
|
|
flagtrig = GetEnt( name, "targetname" );
|
|
colortrig = GetEnt( flagtrig.target, "targetname" );
|
|
|
|
flag_wait( name );
|
|
|
|
// get all the color codes on this trigger
|
|
array = colortrig maps\_colors::get_colorcodes_from_trigger( colortrig.script_color_allies, team );
|
|
colorCodes = array[ "colorCodes" ];
|
|
colorCodesByColorIndex = array[ "colorCodesByColorIndex" ];
|
|
colors = array[ "colors" ];
|
|
|
|
// figure out the test volume for each color code
|
|
// note: only works for one volume per color code! I think this is how the color system works anyway, only one volume is supported afaik.
|
|
infos = [];
|
|
foreach( colorCode in colorCodes )
|
|
{
|
|
volume = level.arrays_of_colorCoded_volumes[ team ][ colorCode ];
|
|
if( IsDefined( volume ) )
|
|
{
|
|
// can only support one volume per colorCode at the moment
|
|
ASSERTEX( !color_flags_dupe_info( infos, colorCode ), "More than one volume found for colorCode " + colorCode + ", currently we only support one volume per colorCode." );
|
|
|
|
info = spawnstruct();
|
|
|
|
// set up arrays for us to manually pass to activate_color_trigger_internal
|
|
info.colorCodes[ 0 ] = colorCode;
|
|
color = GetSubStr( colorCode, 0, 1 );
|
|
info.colors[ 0 ] = color;
|
|
info.colorCodesByColorIndex[ color ] = colorCodesByColorIndex[ color ];
|
|
info.colortrig = colortrig;
|
|
info.name = name;
|
|
info.volume = volume;
|
|
|
|
infos[ infos.size ] = info;
|
|
}
|
|
}
|
|
|
|
array_thread( infos, ::color_flags_advance_queue_add );
|
|
}
|
|
}
|
|
|
|
color_flags_advance_cleanup()
|
|
{
|
|
level waittill( "color_flags_advance_stop" );
|
|
|
|
level.color_flags_advance_queue = undefined;
|
|
}
|
|
|
|
color_flags_advance_queue_manager( color )
|
|
{
|
|
level endon( "color_flags_advance_stop" );
|
|
|
|
while( 1 )
|
|
{
|
|
// if we have any volumes of this color to wait for, do them in order
|
|
if( level.color_flags_advance_queue[ color ].size )
|
|
{
|
|
// process the first volume in the stack, if the stack has any entries
|
|
volume = level.color_flags_advance_queue[ color ][ 0 ];
|
|
volume color_flag_volume_advance_wait();
|
|
}
|
|
// otherwise, wait for an update and try again
|
|
else
|
|
{
|
|
level waittill( "color_flag_advance_queue_updated" );
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
// self = the info spawnstruct to add
|
|
color_flags_advance_queue_add()
|
|
{
|
|
color = self.colors[ 0 ];
|
|
|
|
if( !IsDefined( level.color_flags_advance_queue ) )
|
|
{
|
|
level.color_flags_advance_queue = [];
|
|
}
|
|
|
|
// see if we need to set up the key for this color
|
|
keys = GetArrayKeys( level.color_flags_advance_queue );
|
|
foundOne = false;
|
|
foreach( key in keys )
|
|
{
|
|
if( key == color )
|
|
{
|
|
foundOne = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// set it up if necessary
|
|
if( !foundOne )
|
|
{
|
|
level.color_flags_advance_queue[ color ] = [];
|
|
|
|
// kick off a queue manager for this color
|
|
thread color_flags_advance_queue_manager( color );
|
|
}
|
|
|
|
// add the volume to the end of the array
|
|
level.color_flags_advance_queue[ color ][ level.color_flags_advance_queue[ color ].size ] = self;
|
|
|
|
level notify( "color_flag_advance_queue_updated" );
|
|
}
|
|
|
|
// self = the info spawnstruct to remove
|
|
color_flags_advance_queue_remove()
|
|
{
|
|
color = self.colors[ 0 ];
|
|
|
|
ASSERTEX( self == level.color_flags_advance_queue[ color ][ 0 ], "Tried to remove a volume from the color_flags_advance queue for color " + color + ", but that volume wasn't at the top of the stack. This is unexpected." );
|
|
|
|
level.color_flags_advance_queue[ color ] = array_remove( level.color_flags_advance_queue[ color ], self );
|
|
}
|
|
|
|
// self = the info spawnstruct
|
|
color_flag_volume_advance_wait()
|
|
{
|
|
level endon( "color_flags_advance_stop" );
|
|
|
|
self.volume waittill_volume_dead_or_dying();
|
|
|
|
self.colorTrig thread maps\_colors::activate_color_trigger_internal( self.colorCodes, self.colors, "allies", self.colorCodesByColorIndex );
|
|
|
|
self color_flags_advance_queue_remove();
|
|
}
|
|
|
|
color_flags_dupe_info( infos, colorCode )
|
|
{
|
|
if( !infos.size )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
foreach( info in infos )
|
|
{
|
|
if( info.colorCodes[ 0 ] == colorCode )
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
|
|
// ----------------------
|
|
// --- AIRLINER STUFF ---
|
|
// ----------------------
|
|
airliner_flyby_trigs()
|
|
{
|
|
ASSERT( IsDefined( level.airliner ) );
|
|
airliner_hide();
|
|
|
|
lockflag = "airliner_flyby";
|
|
flag_init( lockflag );
|
|
|
|
trigs = GetEntArray( "trig_airliner_flyby", "targetname" );
|
|
array_thread( trigs, ::airliner_flyby, lockflag );
|
|
}
|
|
|
|
airliner_setup()
|
|
{
|
|
// gets all the ents in the prefab
|
|
ents = GetEntArray( "sbmodel_airliner_flyby", "targetname" );
|
|
level.airlinerParts = ents;
|
|
|
|
org = undefined;
|
|
light_wingtip_left = undefined;
|
|
light_belly = undefined;
|
|
light_tail = undefined;
|
|
lights_wingtip_right = [];
|
|
engine_exhausts = [];
|
|
|
|
// find the script_origins and sort them
|
|
foreach( ent in ents )
|
|
{
|
|
if( ent.code_classname == "script_origin" )
|
|
{
|
|
ASSERT( IsDefined( ent.script_noteworthy ) );
|
|
|
|
switch( ent.script_noteworthy )
|
|
{
|
|
case "origin_marker":
|
|
org = ent;
|
|
break;
|
|
|
|
case "light_wingtip_left":
|
|
light_wingtip_left = ent;
|
|
break;
|
|
|
|
case "light_belly":
|
|
light_belly = ent;
|
|
break;
|
|
|
|
case "light_tail":
|
|
light_tail = ent;
|
|
break;
|
|
|
|
case "light_wingtip_right":
|
|
lights_wingtip_right[ lights_wingtip_right.size ] = ent;
|
|
break;
|
|
|
|
case "engine_exhaust":
|
|
engine_exhausts[ engine_exhausts.size ] = ent;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
ASSERT( IsDefined( org ) );
|
|
otherents = array_remove( ents, org );
|
|
|
|
// turn on lights & exhaust
|
|
light_wingtip_left = airliner_fx( light_wingtip_left, "airliner_wingtip_left" );
|
|
light_belly = airliner_fx( light_belly, "airliner_belly" );
|
|
light_tail = airliner_fx( light_tail, "airliner_tail" );
|
|
lights_wingtip_right = airliner_fx_group( lights_wingtip_right, "airliner_wingtip_right" );
|
|
engine_exhausts = airliner_fx_group( engine_exhausts, "airliner_exhaust" );
|
|
|
|
otherents[ otherents.size ] = light_wingtip_left;
|
|
otherents[ otherents.size ] = light_belly;
|
|
otherents[ otherents.size ] = light_tail;
|
|
otherents = array_combine( otherents, lights_wingtip_right );
|
|
otherents = array_combine( otherents, engine_exhausts );
|
|
|
|
// link everything to the main script_origin
|
|
foreach( ent in otherents )
|
|
{
|
|
ent LinkTo( org );
|
|
}
|
|
|
|
org.og_angles = org.angles;
|
|
|
|
return org;
|
|
}
|
|
|
|
airliner_fx( spot, fxid )
|
|
{
|
|
if( IsDefined( spot ) )
|
|
{
|
|
newspot = spot spawn_tag_origin();
|
|
spot = newspot;
|
|
|
|
PlayFxOnTag( getfx( fxid ), spot, "tag_origin" );
|
|
|
|
return spot;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
airliner_fx_group( group, fxid )
|
|
{
|
|
if( group.size )
|
|
{
|
|
newspots = [];
|
|
|
|
foreach( spot in group )
|
|
{
|
|
newspot = spot spawn_tag_origin();
|
|
newspots[ newspots.size ] = newspot;
|
|
}
|
|
|
|
group = newspots;
|
|
|
|
foreach( spot in group )
|
|
{
|
|
PlayFxOnTag( getfx( fxid ), spot, "tag_origin" );
|
|
}
|
|
|
|
return group;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
airliner_hide()
|
|
{
|
|
array_call( level.airlinerParts, ::Hide );
|
|
}
|
|
|
|
airliner_show()
|
|
{
|
|
array_call( level.airlinerParts, ::Show );
|
|
}
|
|
|
|
airliner_flyby( lockflag )
|
|
{
|
|
pathStart = GetStruct( self.target, "targetname" );
|
|
pathEnd = GetStruct( pathStart.target, "targetname" );
|
|
ASSERT( IsDefined( pathStart ), IsDefined( pathEnd ) );
|
|
|
|
self waittill( "trigger" );
|
|
|
|
level notify( "airliner_flyby" );
|
|
|
|
speed = 1500;
|
|
if( IsDefined( pathStart.speed ) )
|
|
{
|
|
speed = pathStart.speed;
|
|
}
|
|
|
|
flag_waitopen( lockflag );
|
|
flag_set( lockflag );
|
|
|
|
// move jet to path origin and unhide
|
|
level.airliner.origin = pathStart.origin;
|
|
|
|
if( IsDefined( pathStart.angles ) )
|
|
{
|
|
level.airliner.angles = pathStart.angles;
|
|
}
|
|
else
|
|
{
|
|
level.airliner.angles = level.airliner.og_angles;
|
|
}
|
|
|
|
wait( 0.05 ); // if we don't wait a bit while moving the plane, we'll see it move through the world
|
|
airliner_show();
|
|
|
|
level.airliner thread airliner_flyby_audio( pathStart.origin, pathEnd.origin, self );
|
|
|
|
// move along path
|
|
dist = Distance( pathStart.origin, pathEnd.origin );
|
|
time = dist / speed;
|
|
level.airliner MoveTo( pathEnd.origin, time );
|
|
level.airliner waittill( "movedone" );
|
|
|
|
// hide at path end
|
|
airliner_hide();
|
|
|
|
flag_clear( lockflag );
|
|
|
|
self Delete();
|
|
}
|
|
|
|
airliner_flyby_audio( start, end, trig )
|
|
{
|
|
if( IsDefined( trig.script_sound ) )
|
|
{
|
|
self play_sound_on_entity( trig.script_sound );
|
|
return;
|
|
}
|
|
|
|
loop = "veh_airliner_dist_loop"; // main looping sound
|
|
boom = "veh_airliner_boom_low"; // layers on top when the plane goes near the player
|
|
|
|
boomspot = PointOnSegmentNearestToPoint( start, end, level.player.origin );
|
|
boomDistSqd = 350 * 350;
|
|
|
|
self thread play_loop_sound_on_entity( loop );
|
|
|
|
while( DistanceSquared( self.origin, boomspot ) < boomDistSqd )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
wait( 0.5 ); // delay before the "boom"
|
|
|
|
self thread play_sound_in_space( boom );
|
|
self waittill( "movedone" );
|
|
self stop_sound( loop );
|
|
}
|
|
|
|
stop_sound( alias )
|
|
{
|
|
self notify( "stop sound" + alias );
|
|
}
|
|
|
|
|
|
// -----------------
|
|
// -- UTIL STUFF --
|
|
// -----------------
|
|
sbmodel_rotate( rotateTime, makeNotSolid )
|
|
{
|
|
if( !IsDefined( makeNotSolid ) )
|
|
{
|
|
makeNotSolid = false;
|
|
}
|
|
|
|
linker = GetEnt( self.target, "targetname" );
|
|
ASSERTEX( IsDefined( linker ), "sbmodel_rotate(): sbmodel at origin " + self.origin + " doesn't have a linker entity targeted. Did you make it a script_struct instead of a script_origin by mistake?" );
|
|
|
|
self LinkTo( linker );
|
|
|
|
self ConnectPaths();
|
|
|
|
ASSERTEX( IsDefined( linker.script_angles ), "sbmodel rotate linker script_origin at origin " + linker.origin + " needs script_angles set." );
|
|
|
|
linker.og_angles = linker.angles;
|
|
|
|
linker RotateTo( linker.script_angles, rotateTime );
|
|
linker waittill( "rotatedone" );
|
|
|
|
self DisconnectPaths();
|
|
|
|
self Unlink();
|
|
|
|
if( makeNotSolid )
|
|
{
|
|
self NotSolid();
|
|
self thread make_solid_again_when_player_isnt_touching();
|
|
}
|
|
|
|
self notify( "sbmodel_rotatedone" );
|
|
}
|
|
|
|
make_solid_again_when_player_isnt_touching()
|
|
{
|
|
while( level.player IsTouching( self ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
|
|
self Solid();
|
|
}
|
|
|
|
sbmodel_rotate_back( rotateTime )
|
|
{
|
|
linker = GetEnt( self.target, "targetname" );
|
|
ASSERTEX( IsDefined( linker.og_angles ) );
|
|
|
|
self LinkTo( linker );
|
|
|
|
self ConnectPaths();
|
|
|
|
ASSERTEX( IsDefined( linker.script_angles ), "sbmodel rotate linker script_origin at origin " + linker.origin + " needs script_angles set." );
|
|
|
|
linker RotateTo( linker.og_angles, rotateTime );
|
|
linker waittill( "rotatedone" );
|
|
|
|
self DisconnectPaths();
|
|
|
|
self Unlink();
|
|
|
|
self notify( "sbmodel_rotatedone" );
|
|
}
|
|
|
|
minigun_squib_line( lineTime, fireInterval, weaponType )
|
|
{
|
|
turret = self.scriptedTurret;
|
|
|
|
lineStart = GetStruct( "hind_fakefire_impactLine_start", "targetname" );
|
|
lineEnd = GetStruct( lineStart.target, "targetname" );
|
|
|
|
numSquibs = lineTime / fireInterval;
|
|
|
|
distance = Distance2D( lineStart.origin, lineEnd.origin );
|
|
movementPerSquib = distance / numSquibs;
|
|
|
|
targetOrigin = lineStart.origin;
|
|
|
|
// get info about the direction of the line
|
|
vec = VectorNormalize( lineEnd.origin - lineStart.origin );
|
|
angles = VectorToAngles( vec );
|
|
forward = AnglesToForward( angles );
|
|
|
|
// how random our squib placement will be
|
|
offsetMin = -25;
|
|
offsetMax = 25;
|
|
|
|
axis = GetAiArray( "axis" ); // if lineTime is super long this could be unreliable
|
|
killradius = 64;
|
|
|
|
startTime = GetTime();
|
|
|
|
for( i = 0; i < numSquibs; i++ )
|
|
{
|
|
truePos = groundpos( targetOrigin );
|
|
//Print3D( truePos, "*", ( 1, 1, 1 ), 0.8, 0.5, 90 );
|
|
|
|
offsetX = RandomFloatRange( offsetMin, offsetMax );
|
|
offsetY = RandomFloatRange( offsetMin, offsetMax );
|
|
adjustedPos = ( truePos[ 0 ] + offsetX, truePos[ 1 ] + offsetY, truePos[ 2 ] );
|
|
|
|
ASSERTEX( IsDefined( turret ), "minigun_squib_line(): the turret deleted after " + seconds( GetTime() - startTime ) + " seconds!" );
|
|
MagicBullet( weaponType, turret GetTagOrigin( "tag_flash" ), adjustedPos );
|
|
|
|
foreach( guy in axis )
|
|
{
|
|
if( !IsAlive( guy ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if( Distance2D( guy.origin, truePos ) < 64 )
|
|
{
|
|
guy Kill();
|
|
}
|
|
}
|
|
|
|
wait( fireInterval );
|
|
|
|
// move the target origin up
|
|
targetOrigin += vector_multiply( forward, movementPerSquib );
|
|
}
|
|
}
|
|
|
|
player_can_see( origin )
|
|
{
|
|
if( !level.player animscripts\battlechatter::pointInFov( origin ) )
|
|
{
|
|
return false;
|
|
}
|
|
|
|
if( SightTracePassed( level.player GetEye(), origin, false, level.player ) )
|
|
{
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
deletetrigs()
|
|
{
|
|
trigs = GetEntArray( "delete", "script_noteworthy" );
|
|
array_thread( trigs, ::delete_after_touch );
|
|
}
|
|
|
|
delete_after_touch()
|
|
{
|
|
self waittill( "trigger" );
|
|
wait( 0.05 );
|
|
|
|
if( IsDefined( self ) )
|
|
{
|
|
self Delete();
|
|
}
|
|
}
|
|
|
|
// spins the minigun up to the full rate needed to fire
|
|
minigun_spinup()
|
|
{
|
|
if( self GetBarrelSpinRate() == 1 )
|
|
{
|
|
return;
|
|
}
|
|
|
|
self StartBarrelSpin();
|
|
|
|
while( self GetBarrelSpinRate() < 1 )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
keep_objective_on_entity( objective_number, ent )
|
|
{
|
|
ent endon( "death" );
|
|
level endon( "objective_complete" + objective_number );
|
|
|
|
for ( ;; )
|
|
{
|
|
objective_position( objective_number, ent.origin );
|
|
wait 0.05;
|
|
}
|
|
}
|
|
|
|
// makes sure self is in the trigger for a certain amount of time before continuing
|
|
trigger_wait_fuse( trig, fuseTime )
|
|
{
|
|
self endon( "death" );
|
|
|
|
while( IsDefined( self ) && IsDefined( trig ) )
|
|
{
|
|
trig waittill( "trigger", other );
|
|
|
|
if( self == other )
|
|
{
|
|
endTime = GetTime() + milliseconds( fuseTime );
|
|
|
|
while( GetTime() < endTime )
|
|
{
|
|
wait( 0.1 );
|
|
|
|
if( !self IsTouching( trig ) )
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if( GetTime() >= endTime )
|
|
{
|
|
trig notify( "trigger_fuse", self );
|
|
return;
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
// like trigger_wait_targetname, except it works for multiple triggers named with the same targetname
|
|
trigger_wait_targetname_multiple( trigTN )
|
|
{
|
|
trigs = GetEntArray( trigTN, "targetname" );
|
|
if ( !trigs.size )
|
|
{
|
|
AssertMsg( "no triggers found with targetname: " + trigTN );
|
|
return;
|
|
}
|
|
|
|
other = undefined;
|
|
|
|
if( trigs.size > 1 )
|
|
{
|
|
array_thread( trigs, ::trigger_wait_multiple_think, trigTN );
|
|
level waittill( trigTN, other );
|
|
}
|
|
else
|
|
{
|
|
trigs[ 0 ] waittill( "trigger", other );
|
|
}
|
|
|
|
return other;
|
|
}
|
|
|
|
trigger_wait_multiple_think( trigTN )
|
|
{
|
|
self endon( trigTN );
|
|
|
|
self waittill( "trigger", other );
|
|
level notify( trigTN, other );
|
|
}
|
|
|
|
// gets a trigger by targetname and triggers it, not doing anything if the trigger is undefined.
|
|
// - use for artificially activating triggers that the player might have already activated
|
|
// normally, like killspawners, etc.
|
|
trigger_activate_targetname_safe( trigTN )
|
|
{
|
|
trig = GetEnt( trigTN, "targetname" );
|
|
if( IsDefined( trig ) )
|
|
{
|
|
trig notify( "trigger" );
|
|
}
|
|
}
|
|
|
|
trigger_activate_targetname( trigTN )
|
|
{
|
|
trig = GetEnt( trigTN, "targetname" );
|
|
ASSERT( IsDefined( trig ) );
|
|
|
|
trig notify( "trigger" );
|
|
}
|
|
|
|
// waits until flag is set, then threads the function
|
|
flag_wait_thread( flag, process )
|
|
{
|
|
flag_wait( flag );
|
|
self thread [[ process ]]();
|
|
}
|
|
|
|
waittill_defined( ent )
|
|
{
|
|
while( !IsDefined( ent ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
waittill_undefined( ent )
|
|
{
|
|
while( IsDefined( ent ) )
|
|
{
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
milliseconds( seconds )
|
|
{
|
|
return seconds * 1000;
|
|
}
|
|
|
|
seconds( milliseconds )
|
|
{
|
|
return milliseconds / 1000;
|
|
}
|
|
|
|
fade_to_black( fadeTime, isForeground )
|
|
{
|
|
level.black_overlay = maps\_hud_util::create_client_overlay( "black", 0, level.player );
|
|
level.black_overlay FadeOverTime( fadeTime );
|
|
level.black_overlay.alpha = 1;
|
|
|
|
if( IsDefined( isForeground ) )
|
|
{
|
|
level.black_overlay.foreground = isForeground;
|
|
}
|
|
|
|
wait( fadeTime );
|
|
}
|
|
|
|
fade_in_from_black( fadeTime )
|
|
{
|
|
ASSERT( IsDefined( level.black_overlay ) );
|
|
|
|
level.black_overlay FadeOverTime( fadeTime );
|
|
level.black_overlay.alpha = 0;
|
|
wait( fadeTime );
|
|
|
|
level.black_overlay Destroy();
|
|
}
|
|
|
|
remove_all_flood_spawners()
|
|
{
|
|
trigs = getentarray( "flood_spawner", "targetname" );
|
|
foreach( trig in trigs )
|
|
{
|
|
trig Delete();
|
|
}
|
|
}
|
|
|
|
get_random( arr )
|
|
{
|
|
ASSERT( arr.size > 0 );
|
|
|
|
if( arr.size == 1 )
|
|
{
|
|
return arr[ 0 ];
|
|
}
|
|
|
|
return arr[ RandomInt( arr.size - 1 ) ];
|
|
}
|
|
|
|
player_dots()
|
|
{
|
|
for ( ;; )
|
|
{
|
|
Print3d( level.player.origin, ".", (1,1,1), 1, 1, 500 );
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
|
|
// bootleg client-side flashlight. originally this was for testing hunted lights but
|
|
// ended up being interesting enough to keep around for later
|
|
player_fake_flashlight()
|
|
{
|
|
// for testing hunted light off the player
|
|
SetSavedDvar( "r_spotlightStartRadius", 36 );
|
|
SetSavedDvar( "r_spotlightEndRadius", 325 );
|
|
SetSavedDvar( "r_spotlightBrightness", 0.9 );
|
|
fxspot = spawn_tag_origin();
|
|
fxspot.origin = level.player GetEye();
|
|
fxspot.angles = level.player GetPlayerAngles();
|
|
fxspot thread flashlight_updater();
|
|
fx = getfx( "flashlight" );
|
|
PlayFxOnTag( fx, fxspot, "tag_origin" );
|
|
|
|
level.player.fakeflashlight = fxspot;
|
|
}
|
|
|
|
flashlight_updater()
|
|
{
|
|
while( 1 )
|
|
{
|
|
playerAngles = level.player GetPlayerAngles();
|
|
playerEye = level.player GetEye();
|
|
|
|
if( self.angles != playerAngles )
|
|
{
|
|
self.angles = playerAngles;
|
|
}
|
|
|
|
if( self.origin != playerEye )
|
|
{
|
|
self.origin = playerEye;
|
|
}
|
|
wait( 0.05 );
|
|
}
|
|
} |