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