cod5-sdk/raw/maps/ber2_event1.gsc
2008-11-20 00:00:00 +00:00

5312 lines
133 KiB
Text
Raw Blame History

//
// file: ber2_event1.gsc
// description: event 1 script for berlin2
// scripter: slayback
//
#include maps\_utility;
#include common_scripts\utility;
#include maps\_anim;
#include maps\ber2;
#include maps\ber2_util;
#include maps\ber2_anim;
#include maps\_music;
#include maps\_busing;
#using_animtree( "generic_human" );
// -- STARTS --
// start at the very beginning of event 1
event1_start()
{
warp_players_underworld();
warp_friendlies( "struct_event1_start_friends", "targetname" );
warp_players( "struct_event1_start", "targetname" );
set_color_chain( "trig_script_color_allies_b35" );
level thread event1_intro_reznov_dialogue();
level thread event1_intro_execution_vignette();
level thread event1_setup();
}
// start just before the first dropdown (beginning of e1 combat)
event1_start_action()
{
warp_players_underworld();
warp_friendlies( "struct_event1_startaction_friends", "targetname" );
warp_players( "struct_event1_startaction", "targetname" );
set_color_chain( "trig_script_color_allies_b2" );
level thread event1_setup();
}
// start on the stairs leading down to the second apartment
event1_start_apt2()
{
warp_players_underworld();
warp_friendlies( "struct_e1_apt2_friends", "targetname" );
warp_players( "struct_e1_apt2", "targetname" );
set_color_chain( "trig_script_color_allies_b5" );
level thread event1_setup( false );
}
// start at the bank atrium after the first two sets of apartments
event1_start_atrium()
{
warp_players_underworld();
warp_friendlies( "struct_event1_before_atrium_friends", "targetname" );
warp_players( "struct_event1_before_atrium", "targetname" );
set_color_chain( "trig_script_color_allies_b9" );
level thread event1_setup( false );
}
// start before the loading dock area
event1_start_loadingdock()
{
GetEnt( "trig_script_color_allies_b13", "targetname" ) Delete();
warp_players_underworld();
warp_friendlies( "struct_start_loadingdock_friends", "targetname" );
warp_players( "struct_start_loadingdock", "targetname" );
set_color_chain( "trig_script_color_allies_b14" );
level thread event1_setup( false );
}
// start outside the first set of buildings, on the street
event1_start_outside()
{
GetEnt( "trig_script_color_allies_b18", "targetname" ) Delete();
warp_players_underworld();
warp_friendlies( "struct_event1_outside_friends", "targetname" );
warp_players( "struct_event1_outside", "targetname" );
set_color_chain( "trig_script_color_allies_b19" );
level thread event1_setup( false );
}
// start outside at the schoolcircle
event1_start_street_regroup()
{
warp_players_underworld();
warp_friendlies( "struct_event1_street_regroup_friends", "targetname" );
guys = [];
guys = level.friends;
spawners = GetEntArray( "spawner_outside_russian_1", "targetname" );
nodes = GetNodeArray( "node_street_regroup", "targetname" );
ASSERTEX( IsDefined( nodes ), nodes.size > 0, "Couldn't find nodes" );
for( i = 0; i < 6; i++ )
{
guy = spawners[i] StalingradSpawn();
if( spawn_failed( guy ) )
{
ASSERTMSG( "Key redshirt failed to spawn for the schoolcircle start spot setup." );
return;
}
guy Teleport( nodes[i].origin, guy.angles );
guys[guys.size] = guy;
}
// let aigroup threads catch the spawn
wait( 0.05 );
for( i = 0; i < guys.size; i++ )
{
guys[i] thread magic_bullet_shield_safe();
guys[i] SetGoalPos( guys[i].origin );
}
warp_players( "struct_event1_street_regroup", "targetname" );
level thread event1_setup( false );
building_collapse_street_executions();
}
// -- END STARTS --
event1_setup( doSneak )
{
if( !IsDefined( doSneak ) )
{
doSneak = true;
}
set_objective( 0 );
//TUEY Set Music State to "INTRO"
setmusicstate("INTRO");
thread event1_action();
// ambient threads
thread event1_fallingdebris_triggers_setup();
thread event1_fallingsign();
thread event1_random_arty_shake();
thread event1_fakefire();
thread event1_katyusha();
thread event1_ambient_streetbattle_drones();
// gameplay threads
if( doSneak )
{
thread event1_aisneak_apt1();
}
thread event1_atrium_dialogue();
thread event1_smoky_hallway();
// vignette threads
thread event1_knees_execution( "trig_e1_knees_execution" );
thread loadingdock_dialogue();
// street action
thread street_action();
}
event1_ambient_streetbattle_drones()
{
trigger_wait( "trig_script_color_allies_b16", "targetname" );
level notify( "kill_e1_ambient_street_battle_drones" );
kill_drones( "drone", "targetname", 0, 1 );
}
event1_intro_reznov_dialogue()
{
level waittill( "controls_active" );
// "Dimitri, are you ready?"
level.sarge playsound_generic_facial( "Ber2_IGD_400A_REZN" );
}
// --- intro execution vignette ---
event1_intro_execution_vignette()
{
flag_wait( "all_players_connected" );
if( is_german_build() )
{
level thread intro_execution_runpast_colorchain();
flag_set( "intro_execution_done" );
// friendlies wait for a bit before wanting to move up
wait( 1.5 );
if( !flag( "intro_execution_friendlies_moveup" ) )
{
//set_color_chain( "trig_script_color_allies_b41" );
flag_set( "intro_execution_friendlies_moveup" );
}
return;
}
level thread intro_execution_runpast_colorchain();
level thread friendlies_intro_patrolwalk();
level thread intro_execution_friends_dialogue();
// battlechatter gets turned back on after sneaky action
thread battlechatter_off( "allies" );
thread battlechatter_off( "axis" );
animSpot = getstruct_safe( "struct_intro_execution_animref", "targetname" );
victimSpawner = getent_safe( animSpot.target, "targetname" );
redshirtSpawners = GetEntArray( victimSpawner.target, "targetname" );
ASSERTEX( redshirtSpawners.size == 3, "Couldn't find enough redshirt spawners for the intro execution vignette." );
victim = victimSpawner spawn_ai();
if ( spawn_failed( victim ) )
{
ASSERTMSG( "Intro execution vignette victim failed to spawn." );
return;
}
victim.ignoreme = true;
victim.anim_disableLongDeath = true;
victim.pathenemyfightdist = 0;
victim.pathenemylookahead = 0;
victim.nodeathragdoll = true;
victim.grenadeammo = 0;
victim.dropweapon = 0;
victim.animname = "introIGC_victim";
victim.targetname = "introIGC_victim";
victim animscripts\shared::DropAIWeapon();
victim remove_gear();
// give him a poppable helmet
if( IsDefined( victim.hatModel ) )
{
if( victim.hatModel != "char_ger_wermachtwet_helm1" )
{
victim Detach( victim.hatModel, "" );
victim.hatModel = "char_ger_wermachtwet_helm1";
victim Attach( victim.hatModel, "" );
}
}
else
{
victim.hatModel = "char_ger_wermachtwet_helm1";
victim Attach( victim.hatModel, "" );
}
guys = [];
for( i = 0; i < redshirtSpawners.size; i++ )
{
guy = redshirtSpawners[i] spawn_ai();
if ( spawn_failed( victim ) )
{
ASSERTMSG( "Intro execution vignette redshirt failed to spawn from spawner at " + redshirtSpawners[i].origin );
return;
}
guy thread magic_bullet_shield_safe();
guy.ignoreme = true;
guys[guys.size] = guy;
}
// clean up spawners
thread delete_group( redshirtSpawners, 5 );
victimSpawner thread scr_delete( 5 );
for( i = 1; i < guys.size; i++ )
{
ASSERTEX( is_active_ai( guys[i] ), "One of the intro IGC friendlies can't be found." );
}
guys[0].animname = "introIGC_guy2";
guys[1].animname = "introIGC_guy3";
guys[2].animname = "introIGC_guy4";
victim.executioner = guys[1];
// animate the victim with his deathanim
victim.deathanim = level.scr_anim["introIGC_victim"]["intro_igc"];
newOrigin = GetStartOrigin( animSpot.origin, animSpot.angles, victim.deathanim );
newAngles = GetStartAngles( animSpot.origin, animSpot.angles, victim.deathanim );
victim Teleport( newOrigin, newAngles );
// animate everyone in one-frame idles so we're ready to start on a dime
animSpot thread anim_loop_solo( victim, "idle", undefined, "stop_idle_loop" );
animSpot thread anim_loop( guys, "idle", undefined, "stop_idle_loop" );
wait( 0.05 );
// now wait for anim to start
trigger_wait( "trig_intro_executionvignette_start", "targetname" );
animSpot notify( "stop_idle_loop" );
array_thread( guys, ::anim_stopanimscripted );
victim anim_stopanimscripted();
// take guy2 out because his anim ends earlier
allGuys = guys;
guy2 = guys[0];
guys = array_remove( guys, guy2 );
level thread event1_introIGC_headshotFX( victim );
victim thread event1_introIGC_victimDialogue();
victim DoDamage( victim.health + 5, (0,0,0) );
victim thread introIGC_victim_playerkill( allGuys );
//animate redshirts
guy2 thread intro_execution_guy_cleanup( 1 );
guys[0] thread intro_execution_guy_cleanup( 2 );
guys[1] thread intro_execution_guy_cleanup( 3 );
animSpot thread anim_single_solo( guy2, "intro_igc" );
animSpot thread anim_single( guys, "intro_igc" );
guys[0] waittillmatch( "single anim", "end" );
flag_set( "intro_execution_done" );
// friendlies wait for a bit before wanting to move up
wait( 1.5 );
if( !flag( "intro_execution_friendlies_moveup" ) )
{
set_color_chain( "trig_script_color_allies_b41" );
flag_set( "intro_execution_friendlies_moveup" );
}
}
introIGC_victim_playerkill( redshirts )
{
level endon( "intro_execution_done" );
while( 1 )
{
self waittill( "damage", amount, attacker );
if( IsPlayer( attacker ) )
{
break;
}
}
level notify( "intro_execution_interrupted" );
arcademode_assignpoints( "arcademode_score_assist", attacker );
self anim_stopanimscripted();
self startragdoll();
for( i = 0; i < redshirts.size; i++ )
{
redshirts[i] anim_stopanimscripted();
redshirts[i] SetGoalPos( self.origin );
redshirts[i] notify( "single anim", "end" );
}
}
event1_introIGC_victimDialogue()
{
level endon( "intro_execution_interrupted" );
self waittillmatch( "deathanim", "dialog" );
self PlaySound( "Ber2_IGD_000A_GER1" );
self waittillmatch( "deathanim", "dialog" );
self PlaySound( "Ber2_IGD_002A_GER1" );
}
// just waits to see if we hit the color chain trigger that moves friendlies past the execution scene
intro_execution_runpast_colorchain()
{
level endon( "intro_execution_friendlies_moveup" );
trigger_wait( "trig_script_color_allies_b41", "targetname" );
flag_set( "intro_execution_friendlies_moveup" );
}
friendlies_intro_patrolwalk()
{
// introscreen doesn't show up on checkpoint restart
doingIntroscreen = GetDvarInt( "introscreen" );
if( IsDefined( doingIntroscreen ) && doingIntroscreen )
{
level waittill( "controls_active" );
}
trig = getent_safe( "trig_script_color_allies_b0", "targetname" );
//trig notify( "trigger" );
trig waittill( "trigger" );
wait( 1.5 );
friends = get_friends( false );
array_thread( friends, ::scr_intro_patrolwalk );
flag_wait( "intro_execution_friendlies_moveup" );
friends = get_friends( false )
array_thread( friends, ::scr_intro_patrolwalk_reset );
}
intro_execution_friends_dialogue()
{
flag_wait_all( "intro_execution_friendlies_moveup", "intro_execution_done" );
if( !flag( "aisneak_dialogue_thread_started" ) )
{
level.hero1 say_dialogue( "hero1", "notwar_murder", true );
}
if( !flag( "aisneak_dialogue_thread_started" ) )
{
wait( 1 );
}
if( !flag( "aisneak_dialogue_thread_started" ) )
{
sarge_giveorder( "how_you_end_a_war", true );
}
flag_set( "intro_execution_dialogue_done" );
}
scr_intro_patrolwalk()
{
self.disableArrivals = true;
self.disableExits = true;
self thread set_generic_run_anim( "patrol_walk", true );
}
scr_intro_patrolwalk_reset()
{
self.disableArrivals = false;
self.disableExits = false;
self thread clear_run_anim();
}
intro_execution_guy_cleanup( extraWait )
{
self endon( "death" );
self waittillmatch( "single anim", "end" );
if( IsDefined( extraWait ) )
{
wait( extraWait );
}
self.goalradius = 24;
self SetGoalNode( getnode_safe( self.target, "targetname" ) );
self waittill( "goal" );
self thread stop_magic_bullet_shield_safe();
wait( 8 );
while( is_active_ai( self ) )
{
playerCanSee = false;
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( SightTracePassed( players[i] GetEye(), self GetEye(), false, players[i] ) )
{
playerCanSee = true;
break;
}
}
if( !playerCanSee )
{
self bloody_death( true );
}
wait( 5 );
}
}
event1_introIGC_gunshotFX( executioner )
{
level endon( "intro_execution_interrupted" );
PlayFxOnTag( level._effect["rifleflash"], executioner, "tag_flash" );
wait( 0.2 );
PlayFxOnTag( level._effect["rifle_shelleject"], executioner, "tag_brass" );
}
event1_introIGC_headshotFX( victim )
{
level endon( "intro_execution_interrupted" );
executioner = victim.executioner;
victim waittillmatch( "deathanim", "head_shot" );
victim thread animscripts\death::helmetPop();
if( is_mature() )
{
forward = AnglesToForward( ( executioner GetTagAngles( "tag_flash" ) ) );
PlayFX( level._effect["headshot"], victim GetTagOrigin( "J_Brow_LE" ), forward );
victim PlaySound( "bullet_large_flesh" );
wait( 0.3 );
PlayFxOnTag( level._effect["bloodspurt"], victim, "J_Brow_LE" );
wait( 1.6 );
PlayFxOnTag( level._effect["bloodspurt"], victim, "J_Brow_LE" );
}
}
// --- end intro IGC ---
event1_action()
{
// kick off ambient stuff on the street
level thread event1_rooftop_rockets();
// send control to event 2 script when we hit a trigger
trigger_wait( "trigger_objective1_reached", "targetname" );
level thread maps\ber2_event2::event2_setup();
}
event1_random_arty_shake()
{
wait( 0.1 );
ender = "subway_gate_opened";
level endon( ender );
level.pauseRandomShake = false;
minWait = 30;
maxWait = 60;
soundSpot = Spawn( "script_origin", ( 2023, 137, -104 ) );
soundSpot thread event1_random_arty_shake_cleanup( ender );
while( 1 )
{
wait( RandomIntRange( minWait, maxWait ) );
if( !level.pauseRandomShake )
{
players = get_players();
soundSpot PlaySound( "bomb_far" );
//Kevin: Notify to play arty hitting building
level notify("ber2_earthquake");
for( i = 0; i < players.size; i++ )
{
players[i] thread generic_rumble_loop( 5.4 );
Earthquake( RandomFloatRange( .20, .25 ), 6, players[i].origin, 500 );
}
}
}
}
event1_random_arty_shake_cleanup( ender )
{
level waittill( ender );
wait( 1 );
if( IsDefined( self ) )
{
self Delete();
}
}
event1_fakefire()
{
if( IsDefined( level.clientscripts ) && level.clientscripts )
{
flag_set( "event1_fakefire_start" );
clientNotify( "e1fs" );
}
else
{
firePoints = GetStructArray( "struct_e1_fakefire", "targetname" );
ASSERTEX( IsDefined( firePoints ) && firePoints.size > 0, "Can't find fakefire points." );
array_thread( firePoints, maps\ber2_fx::ambient_fakefire, "subway_gate_closed", true );
}
}
event1_katyusha()
{
level waittill ( "spawnvehiclegroup5" );
wait( 0.1 );
kat = getent_safe( "e1_ambient_katyusha", "targetname" );
//kat transmittargetname(); // transmit target name to client, so we can get ent there...
kat veh_stop_at_node( "node_katyusha_stop1", "script_noteworthy", 10, 10 );
wait( 1.5 );
// shoot rockets until we want to move on
if( !flag( "street_charge_moveup1" ) )
{
//clientNotify( "katStart" ); // start clientside katyusha barrage
while( !flag( "street_charge_moveup1" ) )
{
// rocket barrage
//rocket_amount, targets, attack_range, dest_z_height
targets = GetStructArray( "struct_e1_katyusha_target", "targetname" );
kat maps\_katyusha::rocket_barrage( 10, targets, 600, 1800 );
if( !flag( "street_charge_moveup1" ) )
{
wait( RandomIntRange( 5, 9 ) );
}
}
}
//clientNotify("katStop"); // kill clientside katyusha barrage
// GTFO
kat notify( "stop_rocket_barrage" );
kat.rollingdeath = 1;
kat ResumeSpeed( 11, 3, 3 );
// wait for death node
deathnode = getvehiclenode_safe( "node_katyusha_rocketdeath", "script_noteworthy" );
deathnode waittill( "trigger" );
// rocket death
//rocketStart1 = ( 2854.3, 1112.2, 472 );
//rocketStart2 = ( 2791.7, 1282.9, 472 );
//rocketStart3 = ( 2724.7, 1473.8, 472 );
rocketStart1 = ( 2328, 3654, 1020 );
rocketStart2 = ( 2328, 3654, 844 );
rocketStart3 = ( 2344, 3846, 684 );
while( !OkTospawn() )
{
wait( 0.1 );
}
MagicBullet( "panzerschrek", rocketStart2, kat.origin );
wait( 1.2 );
MagicBullet( "panzerschrek", rocketStart1, kat.origin );
wait( 0.85 );
MagicBullet( "panzerschrek", rocketStart2, kat.origin );
wait( 1.2 );
// kill tank
if( kat.health )
{
RadiusDamage( kat.origin, 10, kat.health + 1, kat.health + 1 );
}
}
tank_idle_fire_turret( aimSpot, doRandomOffset )
{
self endon( "death" );
self endon( "stop_tank_idle_fire" );
if( !IsDefined( aimSpot ) )
{
aimSpot = ( 1481, 175, -92 );
}
og_aimSpot = aimSpot;
while( 1 )
{
if( IsDefined( doRandomOffset ) && doRandomOffset )
{
aimSpot = og_aimSpot + ( RandomFloatRange( -100, 100 ), RandomFloatRange( -100, 100 ), RandomFloatRange( -100, 100 ) );
}
self thread tank_fire_at_origin( aimSpot );
wait( RandomIntRange( 4, 8 ) );
}
}
event1_fallingsign()
{
level endon( "subway_gate_closed" );
trigger_wait( "trig_e1_rooftop_ambient", "targetname" );
startOrg = ( 1881, -2096, 1625 );
startAngles = ( 12.5, 131, 45.4 );
endOrg = getstruct_safe( "struct_fallingsign_rocketImpact", "targetname" ).origin;
thread fallingsign_dialogue();
// fire preview rockets
thread event1_fallingsign_preview_rockets();
// let preview rockets go first
wait( 0.8 );
level.pauseRandomShake = true;
// fire impact rocket
event1_fallingsign_fire_rocket( startOrg, startAngles, endOrg, 1.5 );
PlayFX( level._effect["fallingsign_exp"], endOrg ); // explosion FX
//Kevin adding explosion sound
explosion = getstruct( "struct_fallingsign_rocketImpact", "targetname" );
playsoundatposition("explosion",explosion.origin);
level thread event1_fallingsign_playersquake( startOrg );
Earthquake( 0.4, 3, endOrg, 2048 );
// 1-3 are left-right relative to how the player first sees them
letter1 = GetEnt( "e1_rooftopsign_letter1", "targetname" );
letter2 = GetEnt( "e1_rooftopsign_letter2", "targetname" );
letter3 = GetEnt( "e1_rooftopsign_letter3", "targetname" );
ASSERTEX( IsDefined( letter1 ), "Can't find rooftop letter 1!" );
ASSERTEX( IsDefined( letter2 ), "Can't find rooftop letter 2!" );
ASSERTEX( IsDefined( letter3 ), "Can't find rooftop letter 3!" );
// now using animated version
useAnim = true;
if( useAnim )
{
// animated version
animSpot = getnode_safe( "node_sign_reference_origin", "targetname" );
letter1.script_linkto = "e_jnt";
letter2.script_linkto = "g_jnt";
letter3.script_linkto = "n_jnt";
level.fallingsign_letters = [];
level.fallingsign_letters[0] = letter1;
level.fallingsign_letters[1] = letter2;
level.fallingsign_letters[2] = letter3;
maps\_anim::anim_ents( level.fallingsign_letters, "sign_fall", undefined, undefined, animSpot, "fallingsign_controlmodel" );
// delete letters
thread delete_group( level.fallingsign_letters, 2 );
}
else
{
// scripted version
wait( 0.7 );
level thread event1_fallingsign_dropletter( letter2 );
wait( 0.45 );
level thread event1_fallingsign_dropletter( letter1 );
wait( 0.25 );
level thread event1_fallingsign_dropletter( letter3 );
}
level.pauseRandomShake = false;
//TUEY SetMusicState to run the underscore
setmusicstate("SIGN_FELL");
}
fallingsign_dialogue()
{
wait( 2 );
flag_wait( "intro_execution_dialogue_done" );
if( !flag( "aisneak_dialogue_thread_started" ) )
{
// "This is madness - Our rockets are tearing the city apart!"
level.hero1 playsound_generic_facial( "Ber2_IGD_006A_CHER" );
// "Get inside."
level.sarge playsound_generic_facial( "Ber2_IGD_007A_REZN" );
}
flag_set( "fallingsign_dialogue_done" );
}
event1_fallingsign_preview_rockets()
{
numCycles = 9;
moveTime = 1.1;
startOrgs = [];
startAngles = [];
endOrgs = [];
moveTimes = [];
startOrgs[0] = ( 2597, -1076, 1305 );
startAngles[0] = ( 2, 167, 18 );
endOrgs[0] = ( -1131, -260, 1081 );
moveTimes[0] = moveTime;
startOrgs[1] = ( 2357, -1636, 1657 );
startAngles[1] = ( 7, 131, 47 );
endOrgs[1] = ( -299, 1148, 1113 );
moveTimes[1] = moveTime;
startOrgs[2] = ( 1929, -2064, 1625 );
startAngles[2] = ( 5, 140, 44 );
endOrgs[2] = ( -1111, 336, 1193 );
moveTimes[2] = moveTime;
cycles = 0;
while( cycles < numCycles )
{
for( i = 0; i < startOrgs.size; i++ )
{
thread event1_fallingsign_fire_rocket( startOrgs[i], startAngles[i], endOrgs[i], moveTimes[i] );
wait( RandomFloatRange( 0.25, 0.4 ) );
}
wait( RandomFloatRange( 0.45, 0.75 ) );
cycles++;
}
}
event1_fallingsign_fire_rocket( startOrg, startAngles, endOrg, moveTime )
{
rocket = Spawn( "script_model", startOrg );
rocket SetModel( "katyusha_rocket" );
rocket.angles = startAngles;
rocket playloopsound( "katy_rocket_run_sign" );
PlayFxOnTag( level._effect["katyusha_rocket_trail"], rocket, "tag_origin" );
thread play_sound_in_space( "katyusha_launch", rocket.origin );
rocket notify( "rocket_fired" );
rocket MoveTo( endOrg, moveTime );
rocket waittill( "movedone" );
// per Tuey: to mask a "pop" when the katy_rocket_run_sign loop ends
thread play_sound_in_space( "katy_explode_dirt", rocket.origin );
rocket Delete();
}
// wrapper functions to figure out which letter is detaching
fallingsign_detachN( parentModel )
{
letter = level.fallingsign_letters[2];
fallingsign_letter_detachFX( letter );
}
fallingsign_detachG( parentModel )
{
letter = level.fallingsign_letters[1];
fallingsign_letter_detachFX( letter );
}
fallingsign_detachE( parentModel )
{
letter = level.fallingsign_letters[0];
fallingsign_letter_detachFX( letter );
}
fallingsign_letter_detachFX( letter )
{
PlayFX( level._effect["rooftopsign_breakaway_dust"], letter.origin );
}
trail_debug()
{
self endon( "death" );
while( 1 )
{
level thread trailprint( self.origin );
wait( 0.1 );
}
}
trailprint( drawOrigin )
{
while( 1 )
{
print3D( drawOrigin, "*", (1,1,1), 1, 10 );
wait( 0.05 );
}
}
event1_fallingsign_playersquake( expOrg )
{
players = get_players();
for( i = 0; i < players.size; i++ )
{
players[i] thread fallingsign_rumble();
Earthquake( 0.55, 2.8, players[i].origin, 500 );
//Kevin: Notify to play arty hitting building before the sign falls.
level notify("ber2_earthquake");
}
}
fallingsign_rumble()
{
self endon( "death" );
self endon( "disconnect" );
self PlayRumbleOnEntity( "explosion_generic" );
wait( 0.2 );
duration = 2;
stopTime = GetTime() + ( duration * 1000 );
while( GetTime() <= stopTime )
{
self PlayRumbleOnEntity( "damage_heavy" );
wait( 0.05 );
}
}
event1_fallingsign_dropletter( letter )
{
rollAngle = RandomIntRange( 100, 130 );
rollTime = RandomFloatRange( 0.7, 1 );
accelTime = rollTime * 0.25;
letter RotateRoll( rollAngle, rollTime, accelTime );
wait( rollTime * 0.65 );
PlayFX( level._effect["rooftopsign_breakaway_dust"], letter.origin );
letter MoveGravity( ( 20, 20, -10 ), 3 );
wait( 3.5 );
letter Delete();
}
// spawnfunc
roof_ambient_runner()
{
spot = getstruct_safe( "struct_roof_runners_target", "targetname" );
target = Spawn( "script_origin", spot.origin );
trig = getent_safe( "trig_roofrunner_startfiring", "targetname" );
self.goalradius = 24;
self thread roof_ambient_runner_shoot( target, trig );
level thread roof_ambient_runner_spawners_delete();
self waittill( "goal" );
if( IsDefined( self ) )
{
self notify( "death" );
}
wait( 0.05 );
target Delete();
if( IsDefined( self ) )
{
self Delete();
}
}
roof_ambient_runner_spawners_delete()
{
if( IsDefined( level.roof_ambient_runner_spawners_delete ) )
{
return;
}
if( !IsDefined( level.roof_ambient_runner_spawners_delete ) )
{
level.roof_ambient_runner_spawners_delete = true;
}
wait( 4 );
spawners = GetEntArray( "spawner_roof_ambientrunner", "targetname" );
for( i = 0; i < spawners.size; i++ )
{
if( !is_active_ai( spawners[i] ) && IsDefined( spawners[i] ) )
{
spawners[i] Delete();
}
}
}
roof_ambient_runner_shoot( target, trig )
{
self endon( "death" );
while( !self IsTouching( trig ) )
{
wait( 0.05 );
}
wait( RandomFloat( 1 ) );
self.ignoreall = false;
self SetEntityTarget( target );
wait( RandomFloatRange( 2, 5 ) );
self ClearEntityTarget();
self.ignoreall = true;
}
event1_fallingdebris_triggers_setup()
{
trigs = GetEntArray( "trig_e1_fallingDebris", "targetname" );
ASSERTEX( IsDefined( trigs ) && trigs.size > 0, "Can't find any falling debris triggers!" );
array_thread( trigs, ::event1_fallingdebris_think );
}
// self = the trigger
event1_fallingdebris_think()
{
level endon( "subway_gate_closed" );
self waittill( "trigger" );
ASSERTEX( IsDefined( self.target ), "falling debris target not found for trigger at origin " + self.origin );
debrisGroup = GetEntArray( self.target, "targetname" );
if( !IsDefined( debrisGroup ) || debrisGroup.size <= 0 )
{
ASSERTMSG( "falling debris not found for trigger at origin " + self.origin );
return;
}
level.pauseRandomShake = true;
players = get_players();
for( i = 0; i < players.size; i++ )
{
players[i] thread generic_rumble_loop( 1.35 );
Earthquake( 0.5, 2, players[i].origin, 64 );
//Kevin: Notify to play arty hitting building with crashing debris.
level notify("ber2_earthquake");
}
array_thread( debrisGroup, ::event1_fallingdebris_drop );
level.pauseRandomShake = false;
wait( 1 );
self Delete();
}
// self = a piece of debris
event1_fallingdebris_drop()
{
if( IsDefined( self.script_delay ) && self.script_delay >= 0 )
{
wait( self.script_delay );
}
else
{
wait( RandomFloatRange( 0.15, 0.45 ) );
}
PlayFX( level._effect["fallingboards_fire"], self.origin );
wait( 0.25 );
// turn off collision so we don't damage AIs
self NotSolid();
self PhysicsLaunch( ( RandomInt( 50 ), RandomInt( 50 ), RandomInt( 50 ) ), ( 0, 0, -15 ) );
if( !IsDefined( level.boardsdropped ) )
{
level.boardsdropped = true;
thread event1_fallingdebris_dialogue();
}
}
event1_fallingdebris_dialogue()
{
if( !level.coopOptimize )
{
redshirt = get_randomfriend_notsarge_excluding( level.hero1 );
redshirt say_dialogue( "redshirt", "lookout", true, true );
}
sarge_giveorder( "watchyourheads", true );
level.hero1 say_dialogue( "hero1", "building_collapsing" );
}
event1_smoky_hallway()
{
level endon( "subway_gate_closed" );
playervisiontrig = GetEnt( "trig_e1_hallway_smoke_hurt", "targetname" );
areatrig = GetEnt( "trig_e1_hallway_smoke_area", "targetname" );
ASSERTEX( IsDefined( playervisiontrig ), "Can't find the smoky hallway player vision changer trigger!" );
ASSERTEX( IsDefined( areatrig ), "Can't find the smoky hallway area trigger!" );
thread event1_smoky_hallway_playervision( playervisiontrig );
thread event1_smoky_hallway_playerspeed( areatrig );
thread event1_smoky_hallway_sargewarning( areatrig );
while( 1 )
{
areatrig waittill( "trigger", guy );
if( !IsAlive( guy ) )
{
continue;
}
guy thread ignore_triggers( 0.2 ); // let others trigger the trigger
if( !IsPlayer( guy ) && is_active_ai( guy ) )
{
// don't kick off a new thread unless we have to
if( !IsDefined( guy.smoky_hallway_crouched ) || !guy.smoky_hallway_crouched )
{
guy thread event1_smoky_hallway_aicrouch( areatrig );
}
}
}
}
event1_smoky_hallway_playervision( trig )
{
level endon( "subway_gate_closed" );
timerLimit = 2;
visionStayTime = 0.4; // how long the effect will continue to last once players leave the trigger
visionSetSmoke = "ber2_smoke_stand";
//visionSetCrouch = "ber2_smoke_crouch";
visionSetClear = "ber2_interior";
visionTransInTime = 1;
visionTransOutTime = 0.5;
timeBetweenCoughs = 4 * 1000;
numTimers = 0;
while( 1 )
{
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( !IsDefined( players[i].ber2_hallwaytimer ) )
{
players[i].ber2_hallwaytimer = "hallwaytimer_" + numTimers;
numTimers++;
}
if( players[i] IsTouching( trig ) )
{
set_timer( players[i].ber2_hallwaytimer, timerLimit );
players[i] thread player_vision_change( visionSetSmoke, visionSetClear, visionTransInTime, visionTransOutTime );
if( !IsDefined( players[i].lastCough ) || ( ( GetTime() - players[i].lastCough ) > timeBetweenCoughs ) )
{
players[i].lastCough = GetTime();
players[i] thread player_cough();
}
players[i].ber2_inSmoke = true;
}
else
{
if( !IsDefined( players[i].ber2_inSmoke ) || players[i].ber2_inSmoke )
{
set_timer( players[i].ber2_hallwaytimer, visionStayTime );
players[i].ber2_inSmoke = false;
}
}
}
wait( 0.1 );
}
}
player_cough()
{
self endon( "death" );
self endon( "disconnect" );
if( !IsDefined( self.isCoughing ) || !self.isCoughing )
{
self.isCoughing = true;
}
else
{
return;
}
coughType = "cough_a";
if( RandomInt( 100 ) > 50 )
{
coughType = "cough_b";
}
self PlaySound( coughType, "cough_done" );
self waittill( "cough_done" );
self.isCoughing = false;
}
event1_smoky_hallway_playerspeed( areatrig )
{
level endon( "subway_gate_closed" );
baseSpeedScale = 1;
speedScaleMultiplier = 0.76;
while( 1 )
{
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( players[i] IsTouching( areatrig ) )
{
if( !IsDefined( players[i].ber2_speedAdjust ) || !players[i].ber2_speedAdjust )
{
players[i].ber2_speedAdjust = true;
players[i] thread adjust_hallway_playerspeed( areatrig, baseSpeedScale, speedScaleMultiplier );
}
}
}
wait( 0.1 );
}
}
// self = a player
adjust_hallway_playerspeed( areatrig, baseSpeedScale, speedScaleMultiplier )
{
self endon( "death" );
self endon( "disconnect" );
self thread scr_player_speedscale( ( baseSpeedScale * speedScaleMultiplier ), 1.5 );
while( self IsTouching( areatrig ) )
{
wait( 0.1 );
}
// set speed back
self thread scr_player_speedscale( baseSpeedScale, 1.2 );
self.ber2_speedAdjust = false;
}
scr_player_speedscale( newSpeedScale, time )
{
self notify( "end_speedscale" );
self endon( "end_speedscale" );
if( !IsDefined( self.speedscale ) )
{
self.speedscale = 1;
}
if( newSpeedScale == self.speedscale )
{
return;
}
self SetMoveSpeedScale( newSpeedScale );
self.speedscale = newSpeedScale;
}
// self = a player
player_vision_change( visionSetChangeTo, visionSetChangeBack, visionTransInTime, visionTransOutTime )
{
self endon( "death" );
self endon( "disconnect" );
if( IsDefined( self.diffVisionOn ) && self.diffVisionOn )
{
return;
}
else
{
self.diffVisionOn = true;
}
self VisionSetNaked( visionSetChangeTo, visionTransInTime );
while( !timer_expired( self.ber2_hallwaytimer ) )
{
wait( 0.1 );
}
self VisionSetNaked( visionSetChangeBack, visionTransOutTime );
self.diffVisionOn = false;
}
// sarge tells the player to get down
event1_smoky_hallway_sargewarning( areatrig )
{
level endon( "subway_gate_closed" );
while( 1 )
{
areatrig waittill( "trigger", guy );
if( IsPlayer( guy ) )
{
// we know a player is in the area, so send the notify
level notify( "e1_smoky_hallway_start" );
//Kevin's notify for the gramophone to start playing in the loading dock.
SetClientSysState("levelNotify","requiem");
thread smoky_hallway_dialogue();
break;
}
}
}
smoky_hallway_dialogue()
{
// "Stay low - Smoke can steal your breath."
level.sarge playsound_generic_facial( "Ber2_IGD_051A_REZN" );
// "I can barely see."
level.hero1 thread playsound_generic_facial( "Ber2_IGD_052A_CHER" );
wait( 1 );
if( !level.coopOptimize )
{
// "I can hardly breathe."
redshirt = get_randomfriend_notsarge_excluding( level.hero1 );
redshirt playsound_generic_facial( "Ber2_IGD_053A_RUR1" );
}
// "Complaining will not help - just keep low."
level.sarge playsound_generic_facial( "Ber2_IGD_054A_REZN" );
// "In Stalingrad, Dimitri and I crawled through many smoke filled buildings."
level.sarge playsound_generic_facial( "Ber2_IGD_055A_REZN" );
// "Do you hear him complaining?"
level.sarge playsound_generic_facial( "Ber2_IGD_056A_REZN" );
}
loadingdock_dialogue()
{
flag_wait( "player_can_damage_artycrew" );
// "We are almost at the street!"
level.sarge playsound_generic_facial( "Ber2_IGD_057A_REZN" );
}
// self = an ai
event1_smoky_hallway_aicrouch( areatrig )
{
self endon( "death" );
self.smoky_hallway_crouched = true;
// initial setup for hitting the trigger
wait( RandomFloatRange( 0.25, 1.25 ) ); // we don't want them all doing the same thing at the same spot
// play sfx of him coughing
// sarge doesn't cough because he's tough like that
if( self != level.sarge )
{
self thread ai_hallway_coughing();
}
self AllowedStances( "crouch" );
self.moveplaybackrate = 1.5;
// wait while in trigger, possibly do stuff like random coughing or bitching
while( self IsTouching( areatrig ) )
{
wait( 1 );
}
self AllowedStances( "stand", "crouch", "prone" );
self.moveplaybackrate = 1;
}
ai_hallway_coughing()
{
numCoughs = RandomIntRange( 1, 3 );
for( i = 0; i < numCoughs; i++ )
{
//iprintlnbold( self.name + " is coughing" );
if( RandomInt( 100 ) > 50 )
{
coughType = "cough_player_a";
}
else
{
coughType = "cough_player_b";
}
self playsound_generic_facial( coughType );
wait( RandomFloatRange( 0.35, 0.8 ) );
}
}
// --- SNEAKY ACTION STUFF ---
event1_aisneak_apt1()
{
flag_init( "aisneak_alarm_sounded" );
flag_init( "aisneak_scene_finished" );
trigger_wait( "trig_e1_aisneak_apt1", "targetname" );
thread event1_aisneak_alarm_flag();
thread event1_aisneak_sarge_dialogue();
thread event1_aisneak_battlechatter();
// setup friends & enemies for sneaking
thread aisneak_friends_setup();
thread aisneak_setup_enemies_apt1();
thread aisneak_enemy_dialogue();
}
event1_aisneak_alarm_flag()
{
level waittill( "sound_alarm" );
flag_set( "aisneak_alarm_sounded" );
}
event1_aisneak_sarge_dialogue()
{
flag_set( "aisneak_dialogue_thread_started" );
flag_wait( "fallingsign_dialogue_done" );
sarge_giveorder( "shhh", true );
sarge_giveorder( "movequietly", true );
flag_set( "aisneak_sarge_firstdialogue_done" );
if( !flag( "aisneak_alarm_sounded" ) && !flag( "aisneak_scene_finished" ) )
{
waittill_either( "aisneak_alarm_sounded", "aisneak_scene_finished" );
}
if( flag( "aisneak_scene_finished" ) )
{
sarge_giveorder( "takethemdown", true );
if( !flag( "aisneak_alarm_sounded" ) )
{
sarge_giveorder( "are_you_ready", true );
}
wait( 1 );
level.sarge AllowedStances( "stand" );
wait( 1 );
// sarge gets impatient
sarge_giveorder( "cut_them_down", true );
array_thread( level.friends, ::scr_ignoreall, false );
wait( 2 );
sarge_giveorder( "kill_them_all" );
level.sarge AllowedStances( "stand", "crouch", "prone" );
}
else
{
// alarm sounded, Germans are also talking right now, so wait to let them stop
wait( 2 );
sarge_giveorder( "kill_them_all" );
}
flag_wait( "aisneak_mapreaders_dead" );
flag_wait( "aisneak_riflemen_dead" );
flag_wait( "aisneak_telegrapher_dead" );
wait 1;
sarge_giveorder( "roomclear" );
}
event1_aisneak_battlechatter()
{
thread battlechatter_off( "allies" );
thread battlechatter_off( "axis" );
level waittill( "sound_alarm" );
thread battlechatter_on( "allies" );
thread battlechatter_on( "axis" );
}
aisneak_friends_setup()
{
allies = GetAIArray( "allies" );
for( i = 0; i < allies.size; i++ )
{
allies[i].ignoreall = true;
allies[i].cqbwalking = true;
}
thread aisneak_friends_reset();
}
aisneak_friends_reset()
{
level waittill( "sound_alarm" );
allies = GetAIArray( "allies" );
for( i = 0; i < allies.size; i++ )
{
allies[i].ignoreall = false;
allies[i].cqbwalking = false;
}
}
aisneak_setup_enemies_apt1()
{
// spawn guys separately
level thread aisneak_setup_mapreaders();
level thread aisneak_setup_riflemen();
level thread aisneak_setup_telegrapher();
}
aisneak_enemy_dialogue()
{
level endon( "sound_alarm" );
trigger_wait( "trig_e1_aisneak_apt1", "targetname" );
mapreader1 = level.mapreader1;
mapreader2 = level.mapreader2;
telegrapher = level.telegrapher;
mapreader1 endon( "death" );
mapreader2 endon( "death" );
telegrapher endon( "death" );
if( !IsDefined( mapreader1 ) || !IsDefined( mapreader2 ) || !IsDefined( telegrapher ) )
{
ASSERTMSG( "Can't find one of the apt 1 sneaky action AIs who need to say dialogue." );
return;
}
flag_wait( "aisneak_sarge_firstdialogue_done" );
// "Russische Soldaten zum fortzufahren, sich einw<6E>rts zu bewegen (Russian soldiers to continue to move inward)."
mapreader1 playsound_generic_facial( "Ber2_IGD_012A_GMP1" );
// "Wir werden fast vollst<73>ndig umgeben. (We are nearly completely surrounded)."
mapreader2 playsound_generic_facial( "Ber2_IGD_013A_GMP2" );
// "Unsere Situation ist unhaltbar. (Our situation is untenable)."
mapreader1 playsound_generic_facial( "Ber2_IGD_014A_GMP1", mapreader2 );
// "Ist das, was Sie mich w<>nschen, zum des Generals zu erkl<6B>ren? (Is that what you want me to tell the General?)"
telegrapher playsound_generic_facial( "Ber2_IGD_015A_TEOP", mapreader1 );
// "Der General sollte bereits verwirklichen. (The General should already realize). "
mapreader2 playsound_generic_facial( "Ber2_IGD_016A_GMP2", telegrapher );
// "Es sei denn er wie das fuhrer arrogant ist. (Unless he shares the fuhrer's arrogance)."
mapreader2 playsound_generic_facial( "Ber2_IGD_017A_GMP2", mapreader1 );
// "Sie werden die Tunnels <20>berschwemmen. (Command has already given the order to flood the subway tunnels)."
telegrapher playsound_generic_facial( "Ber2_IGD_018A_TEOP", mapreader1 );
// "Sie f<>rchten sich, dass die Russen sie verwenden k<>nnen, um das Konigsplatz zu erreichen<65> (They fear the Russians may use them to reach the Konigsplatz...)"
mapreader1 playsound_generic_facial( "Ber2_IGD_019A_GMP1", telegrapher );
// "Gott helfen uns allen<65> (God help us all...)"
mapreader2 playsound_generic_facial( "Ber2_IGD_020A_GMP2", mapreader1 );
wait( 1 );
flag_set( "aisneak_scene_finished" );
}
aisneak_setup_mapreaders()
{
mapreaders = SpawnStruct();
mapreaders.guys = [];
mapreader_spawners = GetEntArray( "spawner_aisneak_mapreaders_apt1", "script_noteworthy" );
ASSERTEX( IsDefined( mapreader_spawners ) && mapreader_spawners.size > 1, "couldn't find the mapreader spawners!" );
array_thread( mapreader_spawners, ::spawner_think, mapreaders );
array_thread( mapreader_spawners, ::aisneak_mapreader_setup );
mapreaders waittill( "spawned_guy" );
waittillframeend;
level thread aisneak_mapreaders_anims( mapreaders.guys );
thread delete_group( mapreader_spawners, 5 );
mapreaders.guys = remove_dead_from_array( mapreaders.guys );
waittill_dead( mapreaders.guys );
flag_set( "aisneak_mapreaders_dead" );
}
aisneak_setup_riflemen()
{
riflemen = SpawnStruct();
riflemen.guys = [];
riflemen_spawners = GetEntArray( "spawner_aisneak_riflemen_apt1", "script_noteworthy" );
ASSERTEX( IsDefined( riflemen_spawners ) && riflemen_spawners.size > 1, "couldn't find the riflemen spawners!" );
array_thread( riflemen_spawners, ::spawner_think, riflemen );
array_thread( riflemen_spawners, ::aisneak_rifleman_setup );
riflemen waittill( "spawned_guy" );
waittillframeend;
animnames[0] = "rifleman1";
animnames[1] = "rifleman2";
animnames[2] = "rifleman3";
guys = riflemen.guys;
for( i = 0; i < guys.size; i++ )
{
guys[i].animSpot = getstruct_safe( guys[i].target, "targetname" );
guys[i].animSpot.origin = groundpos( guys[i].animSpot.origin );
guys[i].animname = animnames[i];
guys[i] thread aisneak_rifleman_anim();
}
thread delete_group( riflemen_spawners, 5 );
guys = remove_dead_from_array( guys );
if( guys.size > 0 )
{
waittill_dead( guys );
}
flag_set( "aisneak_riflemen_dead" );
}
aisneak_rifleman_anim()
{
self endon( "death" );
self.ignoreme = true;
self.ignoreall = true;
self.goalradius = 24;
self.animSpot thread anim_loop_solo( self, "loop", undefined, "stop_looping_anim" );
level waittill( "sound_alarm" );
self.animSpot notify( "stop_looping_anim" );
if( is_active_ai( self ) )
{
self.animSpot anim_single_solo( self, "reaction" );
}
if( is_active_ai( self ) )
{
self.goalradius = 350;
self.ignoreall = false;
self.ignoreme = false;
}
}
aisneak_setup_telegrapher()
{
telegrapher_spawner = GetEnt( "spawner_aisneak_telegrapher_apt1", "script_noteworthy" );
ASSERTEX( IsDefined( telegrapher_spawner ), "couldn't find the telegrapher spawner!" );
telegrapher_spawner thread aisneak_telegrapher_setup();
telegrapher_spawner waittill( "spawned", telegrapher );
waittillframeend;
level.telegrapher = telegrapher;
level thread aisneak_telegrapher_anims( telegrapher );
telegrapher_spawner thread scr_delete( 5 );
while( is_active_ai( telegrapher ) )
{
wait( 0.1 );
}
flag_set( "aisneak_telegrapher_dead" );
}
aisneak_mapreader_setup()
{
mapreader = self DoSpawn();
if( spawn_failed( mapreader ) )
{
ASSERTMSG( "mapreader spawn failed!" );
return;
}
mapreader endon( "death" );
//mapreader.ignoreall = true;
mapreader thread stop_ignoring_player_when_shot( "sound_alarm" );
level waittill( "sound_alarm" );
if( is_active_ai( mapreader ) )
{
mapreader.ignoreall = false;
}
}
aisneak_rifleman_setup()
{
rifleman = self DoSpawn();
if( spawn_failed( rifleman ) )
{
ASSERTMSG( "rifleman spawn failed!" );
return;
}
rifleman thread stop_ignoring_player_when_shot( "sound_alarm", undefined, true );
level waittill( "sound_alarm" );
if( is_active_ai( rifleman ) )
{
rifleman.ignoreall = false;
}
}
fire_at_targets( targets, ender )
{
self endon( "death" );
level endon( ender );
org = Spawn( "script_origin", targets[0].origin );
while( 1 )
{
targets = array_randomize( targets );
for( i = 0; i < targets.size; i++ )
{
self SetEntityTarget( org );
wait( 1 );
org MoveTo( targets[i].origin, 5 );
org waittill( "movedone" );
}
}
}
aisneak_telegrapher_setup()
{
waittillframeend; // HACK asynchronous threading issue
telegrapher = self DoSpawn();
if( spawn_failed( telegrapher ) )
{
ASSERTMSG( "telegrapher spawn failed!" );
return;
}
//telegrapher.ignoreall = true;
telegrapher thread stop_ignoring_player_when_shot( "sound_alarm" );
level waittill( "sound_alarm" );
if( IsDefined( telegrapher ) )
{
telegrapher.ignoreall = false;
}
}
aisneak_mapreaders_anims( guys )
{
guy1 = guys[0];
guy2 = guys[1];
node1 = GetNode( guy1.target, "targetname" );
node2 = GetNode( guy2.target, "targetname" );
ASSERTEX( IsDefined( guy1 ) && IsDefined( guy2 ), "Couldn't find one of the mapreaders!" );
ASSERTEX( IsDefined( node1 ) && IsDefined( node2 ), "Couldn't find one of the mapreader nodes!" );
guy1.animname = "mapreader1";
guy2.animname = "mapreader2";
level.mapreader1 = guy1;
level.mapreader2 = guy2;
node1 thread anim_loop_solo( guy1, "readingmap", undefined, "stop_mapreading" );
node2 thread anim_loop_solo( guy2, "readingmap", undefined, "stop_mapreading" );
level waittill( "sound_alarm" );
node1 notify( "stop_mapreading" );
node2 notify( "stop_mapreading" );
if( IsDefined( guy1 ) && IsAlive( guy1 ) )
{
node1 thread anim_single_solo( guy1, "readingmap_surprise" );
}
if( IsDefined( guy2 ) && IsAlive( guy2 ) )
{
node2 thread anim_single_solo( guy2, "readingmap_surprise" );
}
}
aisneak_telegrapher_anims( guy )
{
chair = getent_safe( "e1_telegrapher_chair", "targetname" );
chairclip = getent_safe( chair.target, "targetname" );
ASSERTEX( IsDefined( guy ), "Couldn't find the telegrapher!" );
ASSERTEX( IsDefined( chair ), "Couldn't find the telegrapher's chair!" );
chair NotSolid();
// start telegraph sound
telegraph = GetEnt( "telegraph","targetname" );
telegraph PlayLoopSound( "morse_code" );
guy.animname = "telegrapher";
guy.nodeathragdoll = true;
guy.deathanim = level.scr_anim["telegrapher"]["tapping_death"];
guy.telegraph = telegraph;
level thread telegrapher_stoptapping_ondeath( guy );
chair thread telegrapher_chair_anims( guy );
chair thread anim_loop_solo( guy, "tapping", "tag_align", "stop_telegraphing" );
level waittill( "sound_alarm" );
chair notify( "stop_telegraphing" );
if( IsDefined( guy ) && IsAlive( guy ) )
{
guy.deathanim = undefined;
guy thread telegrapher_stoptapping();
chair anim_single_solo( guy, "tapping_surprise", "tag_align" );
// only delete the chair clip if the guy kicks it out of the way
chairclip ConnectPaths();
chairclip Delete();
}
}
telegrapher_stoptapping_ondeath( telegrapher )
{
telegrapher waittill( "death" );
telegrapher telegrapher_stoptapping();
}
telegrapher_stoptapping()
{
// stop telegraph sound
self.telegraph StopLoopSound();
}
// --- END SNEAKY ACTION STUFF ---
// --- KNEES EXECUTION VIGNETTE ---
event1_knees_execution( triggerTN )
{
trig = GetEnt( triggerTN, "targetname" );
ASSERTEX( IsDefined( trig ), "trigger can't be found." );
animSpot = GetStruct( trig.target, "targetname" );
ASSERTEX( IsDefined( animSpot ), "anim spot (targetname " + trig.target + ") can't be found." );
watcherSpot = GetStruct( animSpot.target, "targetname" );
ASSERTEX( IsDefined( watcherSpot ), "anim spot (targetname " + animSpot.target + ") can't be found." );
trig waittill ("trigger");
trig Delete();
thread event1_knees_execution_interruptflag();
// spawn the guys
victim_spawner = GetEnt( "e1_execution_friendly_spawner", "targetname" );
executioner_spawner = GetEnt( "e1_execution_enemy_spawner", "targetname" );
watcher_spawner = GetEnt( "e1_execution_enemy_watcher_spawner", "targetname" );
// spawn victim
victim = victim_spawner spawn_ai();
if ( spawn_failed( victim ) )
{
ASSERTMSG( "Key friendly failed to spawn." );
return;
}
victim thread magic_bullet_shield_safe();
victim.ignoreme = true;
victim.anim_disableLongDeath = true;
victim.nodeathragdoll = true;
victim.grenadeammo = 0;
victim.dropweapon = 0;
victim.og_pathenemyfightdist = victim.pathenemyfightdist;
victim.og_pathenemylookahead = victim.pathenemylookahead;
victim.og_goalheight = victim.goalheight;
victim.og_goalradius = victim.goalradius;
victim.og_animname = victim.animname;
victim.pathenemyfightdist = 0;
victim.pathenemylookahead = 0;
victim.goalheight = 64;
victim.goalradius = 32;
victim.animname = "knees_execution_victim";
victim.targetname = "knees_execution_guys";
victim remove_gear();
// spawn executioner
executioner = executioner_spawner spawn_ai();
if ( spawn_failed( executioner ) )
{
ASSERTMSG( "Key enemy failed to spawn." );
return;
}
executioner.ignoreme = true;
executioner.og_goalradius = executioner.goalradius;
executioner.og_animname = executioner.animname;
executioner.og_health = executioner.health;
executioner.goalradius = 32;
executioner.animname = "knees_execution_executioner";
executioner.targetname = "knees_execution_guys";
executioner.victim = victim;
executioner.allowdeath = true;
executioner.health = 1;
victim.executioner = executioner;
// kick off the victim/executioner anims
level thread event1_knees_execution_2man_anims( victim, executioner, animSpot );
// spawn watcher
watcher = watcher_spawner spawn_ai();
if ( spawn_failed( watcher ) )
{
ASSERTMSG( "Key enemy failed to spawn." );
return;
}
watcher.ignoreme = true;
watcher.og_goalradius = watcher.goalradius;
watcher.og_animname = watcher.animname;
watcher.goalradius = 32;
watcher.animname = "mapreader2";
watcher.targetname = "knees_execution_guys";
watcher animscripts\shared::placeWeaponOn( victim.executioner.sidearm, "right" );
level thread watcher_alarm_on_death( watcher, "execution_interrupted" );
watcher thread stop_ignoring_player_when_shot( "execution_interrupted" );
watcher thread event1_knees_execution_watcher_anims( watcherSpot, "execution_interrupted", victim );
// clean up spawners
victim_spawner thread scr_delete( 5 );
executioner_spawner thread scr_delete( 5 );
watcher_spawner thread scr_delete( 5 );
}
// tells the watcher that something else is interrupting the execution
event1_knees_execution_interruptflag()
{
level endon( "execution_done" );
level waittill( "execution_interrupted" );
if( !flag( "execution_interrupted" ) )
{
flag_set( "execution_interrupted" );
}
}
event1_knees_execution_2man_anims( victim, executioner, animSpot )
{
victim thread event1_knees_execution_victim_saved( executioner, animSpot );
knees_execution_guys_array = [];
knees_execution_guys_array[0] = victim;
knees_execution_guys_array[1] = executioner;
// run the anim
animSpot thread anim_single( knees_execution_guys_array, "execute" );
animSpot thread victim_death( victim );
}
// self = the victim
event1_knees_execution_victim_saved( executioner, animSpot )
{
self endon( "death" );
level endon( "execution_done" );
while( !flag( "execution_interrupted" ) && IsAlive( executioner ) )
{
wait( 0.05 );
}
if( !flag( "execution_interrupted" ) )
{
flag_set( "execution_interrupted" );
}
if( IsAlive( executioner ) )
{
executioner thread event1_knees_executioner_reset();
}
self StopAnimScripted();
animSpot anim_single_solo( self, "saved_getup" ); // you're saved, get up
// set values back
self.pathenemyfightdist = self.og_pathenemyfightdist;
self.pathenemylookahead = self.og_pathenemylookahead;
self.goalheight = self.og_goalheight;
self.animname = self.og_animname;
self.anim_disableLongDeath = false;
// if not coop...
if( !level.coopOptimize )
{
// add to level.friends and the color chain
self friend_add();
self set_force_color( "b" );
}
// find a cover node
self.goalradius = 4;
self SetGoalNode( GetNode( "auto736", "targetname" ) );
self waittill( "goal" );
self.goalradius = self.og_goalradius;
// give him a fighting chance
wait( 2 );
self.ignoreme = false;
self thread stop_magic_bullet_shield_safe();
self.allowdeath = true;
// if coop, kill him off
if( level.coopOptimize )
{
wait( 5 );
self thread bloody_death( true, 1 );
}
}
event1_knees_executioner_reset()
{
self endon( "death" );
self StopAnimScripted();
self.goalradius = self.og_goalradius;
self.health = self.og_health;
self.animname = self.og_animname;
wait( 1 );
self.ignoreme = false;
}
// self = the animspot
victim_death( victim )
{
level endon( "execution_interrupted" );
// wait for the headshot notetrack
victim.executioner waittillmatch( "single anim", "shot_in_the_head" );
flag_set( "execution_done" );
victim thread stop_magic_bullet_shield_safe();
victim DoDamage( victim.health + 5, ( 0, 0, 0 ) );
// wait for the shooting animation to finish
victim waittillmatch( "single anim", "end" );
}
watcher_alarm_on_death( watcher, alarm )
{
level endon( "execution_interrupted" );
level endon( "execution_done" );
watcher waittill( "death" );
wait( 0.21 );
level notify( alarm );
}
// self = the guy watching the execution
event1_knees_execution_watcher_anims( animSpot, notifystring, victim )
{
self endon( "death" );
// watching loop
animSpot thread anim_loop_solo( self, "readingmap", undefined, "execution_watcher_stoploop", undefined );
while( !flag( "execution_interrupted" ) && !flag( "execution_done" ) )
{
wait( 0.05 );
}
// stop the loop
animSpot notify( "execution_watcher_stoploop" );
self notify( "bulletwhizby" );
// if the player interrupted it...
if( flag( "execution_interrupted" ) )
{
// play surprised anim
animSpot thread anim_single_solo( self, "readingmap_surprise" );
self PlaySound( "Ber2_IGD_036A_GER3" ); // "The Russians!"
self waittillmatch( "single anim", "end" );
}
// reset values
self.ignoreme = false;
self.animname = self.og_animname;
// get ready for pathfinding
self.goalradius = 128;
// if the player interrupted it...
if( flag( "execution_interrupted" ) )
{
// attack the closest player!
self SetGoalPos( get_closest_player( self.origin ).origin );
}
else
{
// head for cover
self SetGoalNode( GetNode( "auto723", "targetname" ) );
}
// reset pathfinding values
self waittill( "goal" );
self.goalradius = self.og_goalradius;
}
event1_knees_execution_gunshotFX( executioner )
{
executioner endon( "death" );
PlayFxOnTag( level._effect["rifleflash"], executioner, "tag_flash" );
wait( 0.2 );
PlayFxOnTag( level._effect["rifle_shelleject"], executioner, "tag_brass" );
}
// not really a headshot anymore, ah well
event1_knees_execution_headshotFX( executioner )
{
executioner endon( "death" );
victim = executioner.victim;
victimFXTag = "J_Clavicle_RI"; // used to be "J_Brow_RI"
if( is_mature() )
{
forward = AnglesToForward( ( executioner GetTagAngles( "tag_flash" ) ) );
backward = forward * -1;
PlayFX( level._effect["headshot"], victim GetTagOrigin( victimFXTag ), forward );
PlayFX( level._effect["headshot"], victim GetTagOrigin( victimFXTag ), backward );
victim PlaySound( "bullet_large_flesh" );
wait( 0.3 );
PlayFxOnTag( level._effect["bloodspurt"], victim, victimFXTag );
wait( 1.6 );
PlayFxOnTag( level._effect["bloodspurt"], victim, victimFXTag );
}
}
// --- END KNEES EXECUTION VIGNETTE ---
event1_atrium_dialogue()
{
level endon( "subway_gate_closed" );
level endon( "atrium_mger_done" );
trigger_wait( "trig_script_color_allies_b10", "targetname" );
// "The area is heavily defended!"
level.sarge playsound_generic_facial( "Ber2_IGD_042A_REZN" );
wait( 2 );
// "Someone, throw a Molotov down there - Burn them!"
level.sarge playsound_generic_facial( "Ber2_IGD_043A_REZN" );
trigTN = "e1_blocker_audioprompt";
trigger_wait( trigTN, "targetname" );
// "MG42!"
level.hero1 playsound_generic_facial( "Ber2_IGD_045A_CHER" );
// "Head on attack is impossible!"
level.sarge playsound_generic_facial( "Ber2_IGD_046A_REZN" );
// "We'll keep fire on him!"
level.hero1 playsound_generic_facial( "Ber2_IGD_047A_CHER" );
// "Move upstairs! Flank them from above!"
level.sarge playsound_generic_facial( "Ber2_IGD_048A_REZN" );
wait( 10 );
trigger_wait( trigTN, "targetname" );
// "Find another way!"
level.scr_sound["hero1"]["find_another_way"] = "Ber2_IGD_049A_REZN";
}
// --- SCRIPTED MGERS ---
// spawnfunc
// self = the atrium MGer
event1_atrium_mger()
{
//TUEYS HACK to get music in tonight!
setmusicstate("BANK");
alertNotify = "atrium_mg_alert";
killTrig = getent_safe( "trig_script_color_allies_b14", "targetname" );
self.scripted_mger_getawayTime = 5;
level thread event1_atrium_mger_alert( alertNotify );
level thread event1_atrium_mger_alertflag( alertNotify );
level thread event1_atrium_mger_friendlies( self, alertNotify );
level thread scripted_mger_think( self, alertNotify, killTrig, true, ( 3792, -1876, -12 ), false );
}
event1_atrium_mger_alert( alertNotify )
{
trigger_wait( "trig_script_color_allies_b13", "targetname" );
wait( 1.5 );
level notify( alertNotify );
}
event1_atrium_mger_alertflag( alertNotify )
{
level waittill( alertNotify );
flag_set( "atrium_mger_alerted" );
}
event1_atrium_mger_friendlies( mger, alertNotify )
{
while( is_active_ai( mger ) && !flag( "atrium_mger_alerted" ) )
{
wait( 0.1 );
}
level notify( "atrium_mger_done" );
// "Good work."
level.sarge playsound_generic_facial( "Ber2_IGD_050A_REZN" );
// move friendlies up
if( IsDefined( GetEnt( "trig_script_color_allies_b13", "targetname" ) ) )
{
set_color_chain( "trig_script_color_allies_b13" );
}
// remove hallway color chain, if possible
chaintrig = GetEnt( "trig_script_color_allies_b12", "targetname" );
if( IsDefined( chaintrig ) )
{
chaintrig Delete();
}
}
// spawnfunc
// self = the balcony MGer
event1_balcony_mger()
{
// BJoyal - weird things happen when you throw a grenade at him. Turning off grenade awareness
self.grenadeawareness = 0;
self.ignoreall = true;
alertNotify = "balcony_mg_alert";
killTrig = getent_safe( "trig_script_color_allies_b10", "targetname" );
level thread event1_balcony_mger_alert( alertNotify );
level thread scripted_mger_think( self, alertNotify, killTrig, true, ( 1148, -1108, 440 ) );
}
event1_balcony_mger_alert( alertNotify )
{
trigger_wait( "trig_killspawner_3", "targetname" );
wait( 0.75 );
level notify( alertNotify );
}
scripted_mger_think( guy, alertNotify, killTrig, killAtEnd, goalPos, scaredOfPlayer )
{
if( !IsDefined( scaredOfPlayer ) )
{
scaredOfPlayer = false;
}
guy.ignoreme = true;
guy.anim_disableLongDeath = true;
guy.og_pathenemyfightdist = guy.pathenemyfightdist;
guy.og_pathenemylookahead = guy.pathenemylookahead;
guy.pathenemyfightdist = 0;
guy.pathenemylookahead = 0;
if( !IsDefined( guy.proxAlertDist ) )
{
guy.proxAlertDist = 96;
}
guy.alerted = false;
node = getnode_safe( guy.target, "targetname" );
turret = getent_safe( node.target, "targetname" );
targets = undefined;
if( IsDefined( turret.target ) )
{
targets = GetEntArray( turret.target, "targetname" );
}
guy thread scripted_mger_catch_alert( alertNotify );
guy thread scripted_mger_kill( alertNotify, killTrig );
if( scaredOfPlayer )
{
guy thread stop_ignoring_player_when_shot( alertNotify, undefined, true );
}
level thread maps\_mgturret::mg42_setdifficulty( turret, GetDifficulty() );
turret SetMode( "auto_ai" );
turret SetTurretIgnoreGoals( true );
while( IsDefined( guy ) && IsAlive( guy ) && !guy.alerted && IsDefined( turret ) )
{
if( !IsDefined( guy GetTurret() ) )
{
guy UseTurret( turret );
}
level waittill_notify_or_timeout( alertNotify, 0.05 );
}
if( IsDefined( turret ) )
{
turret SetMode( "manual_ai" );
}
if( is_active_ai( guy ) )
{
guy StopUseTurret();
guy.pacifist = 0;
guy.ignoreall = false;
guy.ignoreme = false;
guy.pathenemyfightdist = guy.og_pathenemyfightdist;
guy.pathenemylookahead = guy.og_pathenemylookahead;
}
if( is_active_ai( guy ) && IsDefined( goalPos ) )
{
guy.goalradius = 96;
guy SetGoalPos( goalPos );
guy waittill( "goal" );
}
if( is_active_ai( guy ) && IsDefined( goalPos ) )
{
guy.anim_disableLongDeath = false;
if( !IsDefined( guy.scripted_mger_getawayTime ) )
{
guy.scripted_mger_getawayTime = 10;
}
wait( guy.scripted_mger_getawayTime );
}
if( !IsDefined( killAtEnd ) )
{
killAtEnd = true;
}
if( is_active_ai( guy ) && killAtEnd )
{
guy thread bloody_death( true );
}
wait( 1 );
if( IsDefined( targets ) )
{
thread delete_group( targets );
}
}
scripted_mger_catch_alert( alertNotify )
{
self endon( "death" );
level waittill( alertNotify );
self.alerted = true;
}
scripted_mger_kill( alertNotify, trig )
{
self endon( "death" );
level endon( alertNotify );
trig waittill( "trigger" );
if( IsDefined( self ) && IsAlive( self ) )
{
self thread bloody_death( true );
}
}
// --- END SCRIPTED MGERS ---
// self = an AI
event1_loadingdock_patroller()
{
self endon( "death" );
self waittill( "enemy" );
// send them to their last node on the chain
node = GetNode( self.target, "targetname" );
while( IsDefined( node ) )
{
if( IsDefined( node.target ) )
{
node = GetNode( node.target, "targetname" );
}
else
{
self SetGoalNode( node );
break;
}
}
self.ignoreall = false;
self.maxsightdistsqrd = 1024*1024;
self.pacifist = false;
}
// --- STREET ACTION ---
street_action()
{
thread street_action_tanks();
thread street_bank_window();
thread street_ambient_rockets();
thread street_fakefire();
thread building_collapse_setup();
flag_wait( "player_outside" );
set_objective( 1 );
// wet up the friendlies
level thread ais_wetness_change( 1, 10, false, "allies" );
// cap the AI count
SetAILimit( 26 );
thread street_dialogue();
//TUEY Set Music State to Street Entrance and the bus state to STREET
setmusicstate("STREET_ENTRANCE");
setbusstate("STREET");
//level waittill( "building_collapse_fallout_done" );
level waittill( "building_critical_hit" );
// uncap AI count
ResetAILimit();
// kick off street executions
// level progression continues from this thread
level thread building_collapse_street_executions();
}
street_action_tanks()
{
level waittill ( "spawnvehiclegroup6" );
wait( 0.1 );
tank = getent_safe( "e1_street_tank", "targetname" );
tank SetVehicleLookAtText( "Nizhny Machine Factory No. 195" );
tank2 = getent_safe( "e1_street_tank2", "targetname" );
tank2 SetVehicleLookAtText( "12th Guards Tank Division" );
tank3 = getent_safe( "e1_street_tank3", "targetname" );
tank3 SetVehicleLookAtText( "Kirovsky Factory No. 501" );
tank thread tank1_strat(); // this tank takes down the building
tank2 thread tank2_strat(); // first one to die
tank3 thread tank3_strat(); // second to die
}
// this tank takes down the building
tank1_strat()
{
veh_stop_at_node( "street_tank_stopnode_1", "script_noteworthy", 10, 10 );
// wait for player to move up a bit
if( !flag( "street_charge_moveup1" ) )
{
self SetSpeed( 0, 15, 15 );
self thread tank_idle_fire_turret( ( 2791, 1282, 472 ), true );
flag_wait( "street_charge_moveup1" );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
self thread tank_reset_turret();
}
self SetSpeed( 8, 8, 8 );
stopnode = getvehiclenode_safe( "street_tank_stopnode_2", "script_noteworthy" );
stopnode waittill( "trigger" );
// wait for player again
if( !flag( "tank3_blowup" ) )
{
self SetSpeed( 0, 15, 15 );
self notify( "end_tank_reset_turret" );
fireSpot = ( -186, -700.8, 544 );
self thread tank_idle_fire_turret( fireSpot, true );
flag_wait( "tank3_blowup" );
}
wait( 1 ); // wait a sec so we don't steal the second destroyed tank's spotlight
// move to building shoot spot
self SetSpeed( 6, 3, 3 );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
self thread tank_move_with_player( 6, 3, 3, "veh_stop_at_node" );
veh_stop_at_node( "street_tank_bldgfire_node", "script_noteworthy", 10, 10 );
// make sure player has gotten there
if( !flag( "street_charge_bldgfire" ) )
{
fireSpot = ( -186, -700.8, 544 );
self thread tank_idle_fire_turret( fireSpot, true );
flag_wait( "street_charge_bldgfire" );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
}
set_objective( 2 );
// finally, fire at the building
bldgTarget1 = getstruct_safe( "struct_building_collapse_tankshot1", "targetname" );
bldgTarget2 = getstruct_safe( "struct_building_collapse_tankshot2", "targetname" );
// slow down the turret rotation
self.turretrotscale = 0.45;
// turn off coax mg
self.script_turretmg = 0;
// this section interfaces with building_collapse_think() to drop the building
// first shot
self tank_fire_at_struct( bldgTarget1, 1.3, 2 );
wait( 0.2 ); // time to fly through air
playsoundatposition("explosion2",bldgTarget1.origin); // //Kevin playing custom explosion
PlayFX( level._effect["building_t34_impact"], bldgTarget1.origin ); // HACK t34 impact fx are weaksauce
flag_set( "building_hit1" ); // shake the building
level waittill( "building_hit1_anim_done" );
// second shot
wait( 0.4 );
self tank_fire_at_struct( bldgTarget2, 1.2, 2 );
wait( 0.2 ); // time to fly through air
//Kevin playing custom explosion
playsoundatposition("explosion",bldgTarget2.origin);
PlayFX( level._effect["building_t34_impact"], bldgTarget2.origin ); // HACK t34 impact fx are weaksauce
flag_set( "building_critical_hit" ); // drop the building
//clientNotify( "bch" ); // no need to send this - 'collapse' is already sent, using that to kill the fake fire on the client instead.
level waittill( "building_hit2_anim_done" );
flag_wait( "building_collapse_fallout_done" );
level.pauseRandomShake = false;
// tank keep a rollin
self ResumeSpeed( 8, 8, 8 );
self waittill( "reached_end_node" );
}
tank_move_with_player( goSpeed, accel, decel, ender )
{
self endon( "death" );
if( IsDefined( ender ) )
{
self endon( ender );
}
movedist = 356;
while( IsDefined( self ) )
{
players = get_players();
foundOne = false;
for( i = 0; i < players.size; i++ )
{
// adjust tank origin to match player's Y origin
tankOrgAdjusted = ( self.origin[0], players[i].origin[1], self.origin[2] );
if(
( Distance2D( players[i].origin, tankOrgAdjusted ) < movedist ) ||
( players[i].origin[0] < self.origin[0] ) ) // or is ahead of the tank
{
foundOne = true;
break;
}
}
if( foundOne )
{
if( self GetSpeed() < goSpeed )
{
self SetSpeed( goSpeed, accel, decel );
}
}
else
{
if( self GetSpeed() > 0 )
{
self SetSpeed( 0, accel, decel );
}
}
wait( 1 );
}
}
// first tank to die
tank2_strat()
{
self thread tank_invincible();
self SetSpeed( 8, 8, 8 );
stopnode = getvehiclenode_safe( "street_tank2_stopnode_1", "script_noteworthy" );
stopnode waittill( "trigger" );
if( !flag( "player_outside" ) )
{
self SetSpeed( 0, 15, 15 );
flag_wait( "player_outside" );
self SetSpeed( 8, 8, 8 );
}
hitnode = getvehiclenode_safe( "vnode_tank2_hit", "script_noteworthy" );
hitnode waittill( "trigger" );
if( !flag( "street_charge_moveup1" ) )
{
self SetSpeed( 0, 15, 15 );
self thread tank_idle_fire_turret();
flag_wait( "street_charge_moveup1" );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
}
self SetSpeed( 3, 4.5, 4.5 );
self notify( "stop_tank_invincibility" );
self.health = 1;
self.rollingdeath = 1;
wait( 1 ); // let the tank get going
while( !OkTospawn() )
{
wait( 0.1 );
}
rocketStart = ( 1320, -384, -56 );
MagicBullet( "panzerschrek", rocketStart, self.origin + ( 0, 0, 64 ) );
self SetTurretTargetVec( rocketStart );
wait( 1 );
if( IsDefined( self ) )
{
RadiusDamage( self.origin, 10, self.health + 1, self.health + 1 );
}
flag_set( "tank2_dead" );
}
// second tank to die
tank3_strat()
{
self SetSpeed( 8, 8, 8 );
stopnode = getvehiclenode_safe( "street_tank3_stopnode_1", "script_noteworthy" );
stopnode waittill( "trigger" );
// wait for player to get outside
if( !flag( "player_outside" ) )
{
self SetSpeed( 0, 15, 15 );
self thread tank_idle_fire_turret( ( 2791, 1282, 472 ), true );
flag_wait( "player_outside" );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
self thread tank_reset_turret();
}
self SetSpeed( 8, 8, 8 );
stopnode = getvehiclenode_safe( "street_tank3_stopnode_2", "script_noteworthy" );
stopnode waittill( "trigger" );
// wait for player to move up
if( !flag( "street_charge_moveup1" ) )
{
self SetSpeed( 0, 15, 15 );
self notify( "end_tank_reset_turret" );
wait( 0.05 );
self thread tank_idle_fire_turret( ( 2791, 1282, 472 ), true );
flag_wait( "street_charge_moveup1" );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
self SetSpeed( 8, 8, 8 );
self thread tank_reset_turret();
}
hitnode = getvehiclenode_safe( "vnode_tank3_hit", "script_noteworthy" );
hitnode waittill( "trigger" );
// wait for player again
if( !flag( "tank3_blowup" ) )
{
self SetSpeed( 0, 15, 15 );
self notify( "end_tank_reset_turret" );
fireSpot = ( 1142, -250, -96 ) + ( 0, 0, 75 );
self thread tank_idle_fire_turret( fireSpot, true );
flag_wait( "tank3_blowup" );
}
self SetSpeed( 3, 4.5, 4.5 );
self notify( "stop_tank_idle_fire" );
self notify( "end_tank_fire_at" );
wait( 0.05 );
self notify( "stop_tank_invincibility" );
self.health = 100;
self.rollingdeath = 1;
wait( 1 ); // let the tank get going
while( !OkTospawn() )
{
wait( 0.1 );
}
rocketStart = ( 44, 274, 492 );
MagicBullet( "panzerschrek", rocketStart, self.origin + ( 0, 0, 64 ) );
self SetTurretTargetVec( rocketStart );
wait( 1.2 );
if( IsDefined( self ) )
{
RadiusDamage( self.origin, 10, self.health + 1, self.health + 1 );
}
}
// self = the tank
tank_invincible()
{
self endon( "death" );
self endon( "stop_tank_invincibility" );
level endon( "subway_gate_closed" );
bighealth = 1000000;
self.health = bighealth;
while( IsDefined( self ) )
{
self waittill( "damage" );
if( self.health < bighealth )
{
self.health = bighealth;
}
}
}
street_fakefire()
{
flag_wait( "player_outside" );
if( IsDefined( level.clientscripts ) && level.clientscripts )
{
clientNotify( "sfs" );
}
else
{
firePoints = GetStructArray( "struct_street_fakefire", "targetname" );
ASSERTEX( IsDefined( firePoints ) && firePoints.size > 0, "Can't find fakefire points." );
array_thread( firePoints, maps\ber2_fx::ambient_fakefire, "building_critical_hit", true );
firePoints = [];
firePoints = GetStructArray( "struct_street_building_fakefire", "targetname" );
ASSERTEX( IsDefined( firePoints ) && firePoints.size > 0, "Can't find fakefire points." );
array_thread( firePoints, maps\ber2_fx::ambient_fakefire, "building_tower_fall", true );
}
}
street_bank_window()
{
opel = getent_safe( "bldg_collapse_dest_opel_close", "script_noteworthy" );
flag_wait( "player_outside" );
// kill this first opel so we can clear our view for the money explosion
if( IsDefined( opel ) )
{
player_look_wait_timeout( opel.origin + ( 0, 0, 42 ), 5, "street_opel", 0.9 );
if( IsDefined( opel ) )
{
RadiusDamage( opel.origin, 10, opel.health + 1, opel.health + 1 );
}
}
wait( 5 );
flag_wait( "street_charge_moveup1" );
thread street_bank_window_exp();
street_bank_flag_sequence();
flag_wait( "tank2_dead" ); // wait for 1st tank to blow up
wait( 3 );
thread street_bank_window_exp();
flag_wait( "tank3_blowup" );
wait( 5 );
thread street_bank_window_exp();
}
player_look_wait_timeout( lookatOrg, timeout, timerIDString, reqDot )
{
if( !IsDefined( reqDot ) )
{
reqDot = 0.8;
}
timerName = "player_look" + timerIDString;
set_timer( timerName, timeout );
while( !timer_expired( timerName ) )
{
players = get_players();
foundOne = false;
for( i = 0; i < players.size; i++ )
{
// check basic sight trace
if( SightTracePassed( players[i] GetEye(), lookatOrg, false, undefined ) )
{
normal = VectorNormalize( lookatOrg - players[i].origin );
player_angles = players[i] GetPlayerAngles();
player_forward = AnglesToForward( player_angles );
dot = VectorDot( player_forward, normal );
// make sure it's centered up in the player view
if( dot >= reqDot )
{
foundOne = true;
break;
}
}
}
if( foundOne )
{
return;
}
wait( 0.05 );
}
}
street_bank_window_exp()
{
rocketStartOrg = ( 1608.6, 408, 270 );
fxSpot = getstruct_safe( "bank_money_exp", "targetname" );
while( !OkTospawn() )
{
wait( 0.1 );
}
MagicBullet( "panzerschrek", rocketStartOrg, fxSpot.origin );
wait( 0.75 ); // rocket travel time
PlayFX( level._effect["bank_window_money_exp"], fxSpot.origin, AnglesToForward( fxSpot.angles ) );
windowFxSpots = GetStructArray( fxSpot.target, "targetname" );
windowFX = level._effect["window_explosion"];
array_thread( windowFxSpots, ::scr_playfx, windowFX, RandomFloatRange( 0.15, 0.35 ) );
}
scr_playfx( fx, delay )
{
if( IsDefined( delay ) && delay >= 0 )
{
wait( delay );
}
PlayFX( fx, self.origin, AnglesToForward( self.angles ) );
}
street_bank_flag_sequence()
{
animSpot = Spawn( "script_origin", ( 2075, -688, 232 ) );
//animSpot = getnode_safe( "anim_bank_flag", "targetname" );
guy = Spawn( "script_model", animSpot.origin );
guy.angles = animSpot.angles;
guy setup_ally_char_model();
guy UseAnimTree( #animtree );
guy.animname = "flag_guy";
flag = Spawn( "script_model", animSpot.origin );
flag Hide();
flag SetModel( level.scr_model["big_flag"] );
flag.animname = "big_flag";
animSpot thread street_bank_flag_anims( flag, guy );
animSpot anim_single_solo( guy, "unfurl" );
guy Delete();
}
street_ambient_rockets()
{
flag_wait( "player_outside" );
startSpots = GetStructArray( "street_charge_ambient_rockets", "targetname" );
ASSERTEX( IsDefined( startSpots ) && startSpots.size > 0, "couldn't find any street charge ambient rocket spots." );
array_thread( startSpots, ::street_ambient_rocket_think );
}
street_ambient_rocket_think()
{
level endon( "kill_ambient_rockets" );
if( IsDefined( self.script_ender ) )
{
level endon( self.script_ender );
}
// start delay
wait( RandomFloat( 2, 6 ) );
while( 1 )
{
// derive target spot
randomizedAngles = self.angles + ( RandomFloatRange( 1, 5 ), RandomFloatRange( -5, 5 ), 0 );
forward = AnglesToForward( randomizedAngles );
targetOrg = self.origin + vectorScale( forward, 1000 );
while( !OkTospawn() )
{
wait( 0.1 );
}
MagicBullet( "panzerschrek", self.origin, targetOrg );
wait( RandomFloatRange( 10, 16 ) );
}
}
// --- END STREET ACTION ---
// -- TURRET STUFF: IW steez, primarily --
building_collapse_mgers()
{
flag_wait( "building_mgs_start" );
mgs = GetEntArray( "collapsingbuilding_manual_mg", "targetname" );
ASSERTEX( IsDefined( mgs ) && mgs.size > 0, "Can't find the mg turrets in the collapsing building." );
array_thread( mgs, ::scr_setmode, "manual" );
gun1 = mgs[ 0 ];
gun2 = mgs[ 1 ];
array_thread( mgs, ::player_mg_fire );
gun1 thread manual_mg_fire();
wait( 0.15 );
gun2 thread manual_mg_fire();
gun1 thread shoot_mg_targets();
wait( 0.25 );
gun2 thread shoot_mg_targets();
flag_wait( "building_critical_hit" );
wait( 0.05 );
for( i = 0; i < mgs.size; i++ )
{
mgs[i] Delete();
}
// also delete their targets
thread delete_group( GetEntArray( "auto1052", "targetname" ), 0.5 );
}
scr_setmode( mode )
{
self setmode( mode );
}
// try to kill players if they get too far up the street
player_mg_fire()
{
self endon( "death" );
level endon( "building_critical_hit" );
while( IsDefined( self ) )
{
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( !IsDefined( players[i].isMgTargeted ) )
{
players[i].isMgTargeted = false;
}
if( players[i].origin[0] < 1378 && !players[i].isMgTargeted )
{
self thread mg_playerdamage( players[i] );
}
}
wait( 0.1 );
}
}
mg_playerdamage( player )
{
player endon( "death" );
player endon( "disconnect" );
self endon( "death" );
level endon( "building_critical_hit" );
self notify( "stop_firing" );
player.isMgTargeted = true;
if( self GetTurretTarget() != player )
{
self SetTargetEntity( player );
}
turretSightChecker = ( 100, 202, 340 );
// make sure we're not behind cover
if( BulletTracePassed( player GetEye(), turretSightChecker, false, player ) )
{
self thread mg_fake_tracers( player );
player DoDamage( 30, self.origin );
}
player.isMgTargeted = false;
}
mg_fake_tracers( player )
{
player endon( "death" );
player endon( "disconnect" );
self endon( "death" );
level endon( "building_critical_hit" );
for( i = 0; i < 3; i++ )
{
BulletTracer( self GetTagOrigin( "tag_flash" ), player GetEye(), true );
wait( RandomFloatRange( 0.05, 0.12 ) );
}
}
manual_mg_fire()
{
self endon( "stop_firing" );
level endon( "building_critical_hit" );
self.turret_fires = true;
for ( ;; )
{
timer = randomfloatrange( 0.2, 0.7 ) * 20;
if ( self.turret_fires )
{
for ( i = 0; i < timer; i++ )
{
self shootturret();
wait( 0.05 );
}
}
wait( randomfloat( 0.5, 2 ) );
}
}
shoot_mg_targets()
{
self endon( "stop_firing" );
level endon( "building_critical_hit" );
//thread do_in_order( ::flag_wait, "player_enters_apartment_rubble_area", ::send_notify, "stop_firing" );
thread stop_firing_when_shot();
targets = getentarray( self.target, "targetname" );
for ( ;; )
{
target = random( targets );
self settargetentity( target );
wait( randomfloatrange( 1, 5 ) );
}
}
stop_firing_when_shot()
{
self endon( "stop_firing" );
level endon( "building_critical_hit" );
trigger = getent_safe( self.script_linkto, "script_linkname" );
shots_until_stop = randomintrange( 3, 5 );
for ( ;; )
{
trigger waittill( "damage", damage, other, direction, origin, damage_type );
if ( !IsPlayer( other ) )
continue;
if ( explosive_damage( damage_type ) )
{
self.turret_fires = false;
return;
}
shots_until_stop--;
if ( shots_until_stop > 0 )
continue;
shots_until_stop = randomintrange( 3, 4 );
self.turret_fires = false;
wait( randomfloatrange( 4, 7 ) );
self.turret_fires = true;
}
}
explosive_damage( type )
{
return issubstr( type, "GRENADE" );
}
// -- END TURRET STUFF --
// --- BUILDING COLLAPSE STUFF ---
building_collapse_setup()
{
thread building_collapse_mgers();
thread building_collapse_think();
}
/#
building_collapse_debug()
{
level waittill("first_player_ready", player);
iprintlnbold( "building collapse debug enabled" );
animSpot = Spawn( "script_origin", ( 28, 225, -112 ) );
animSpot.angles = ( 0, 0, 0 );
waittill_player_within_range( animSpot.origin, 1000, 0.5 );
// mg position geo
mgGeo[0] = getent_safe( "sb_model_bldg_collapse_window_boards", "targetname" );
sandbags = GetEntArray( "bldg_collapse_window_sandbags", "targetname" );
for( i = 0; i < sandbags.size; i++ )
{
mgGeo[mgGeo.size] = sandbags[i];
}
// gets pieces, sets them up in an animation-friendly array
pieces = building_collapse_setup_anim_pieces();
thread building_collapse_hit1_fx( pieces );
maps\_anim::anim_ents( pieces, "hit1", undefined, undefined, animSpot, "buildingcollapse_rig" );
level thread building_collapse_fx( pieces );
array_thread( mgGeo, ::mg_geo_delete );
wait( 1 );
maps\_anim::anim_ents( pieces, "hit2", undefined, undefined, animSpot, "buildingcollapse_rig" );
wait( 5 );
thread tower_fx( pieces );
maps\_anim::anim_ents( pieces, "towerfall", undefined, undefined, animSpot, "buildingcollapse_rig" );
wait( 2 );
animSpot Delete();
}
#/
building_collapse_think()
{
// set up the pieces to animate
// the node is in paul's layer, so manufacture it
animSpot = Spawn( "script_origin", ( 28, 225, -112 ) );
animSpot.angles = ( 0, 0, 0 );
// mg position geo
mgGeo[0] = getent_safe( "sb_model_bldg_collapse_window_boards", "targetname" );
sandbags = GetEntArray( "bldg_collapse_window_sandbags", "targetname" );
for( i = 0; i < sandbags.size; i++ )
{
mgGeo[mgGeo.size] = sandbags[i];
}
// gets pieces, sets them up in an animation-friendly array
pieces = building_collapse_setup_anim_pieces();
level waittill( "building_hit1" );
// TODO: need to warp players here so they don't see AI being killed off if they are further behind
level thread street_executions_playerspeed(); // slow players down for a while
thread building_collapse_kill_ais(); // kill off remaining street AIs
thread building_collapse_hit1_fx( pieces );
maps\_anim::anim_ents( pieces, "hit1", undefined, undefined, animSpot, "buildingcollapse_rig" );
level notify( "building_hit1_anim_done" );
level waittill( "building_critical_hit" );
level thread alleyway_fx();
level thread building_collapse_players_shock(); // shock em if they're up where they shouldn't be
thread building_collapse_fx( pieces );
array_thread( mgGeo, ::mg_geo_delete );
maps\_anim::anim_ents( pieces, "hit2", undefined, undefined, animSpot, "buildingcollapse_rig" );
level notify( "building_hit2_anim_done" );
// wait for players and then drop the tower
flag_wait( "building_tower_fall" );
//Kevin playing tower sounds
tower_impact = getent("tower_impact","targetname");
// animSpot playsound("tower1"); // Moved this to the client
tower_impact playsound("tower_impact1");
thread tower_fx( pieces );
maps\_anim::anim_ents( pieces, "towerfall", undefined, undefined, animSpot, "buildingcollapse_rig" );
level notify( "building_towerfall_anim_done" );
wait( 2 );
animSpot Delete();
}
building_collapse_setup_anim_pieces()
{
// automating the process of getting the pieces
// each line should do the same as this:
// pieces["rig 1:tower01"] = getent_safe( "sb_model_tower_01", "targetname" );
// ...etc.
startNum = 1;
numPieces = 32;
bonePrefix = "tower";
sbModelPrefix = "sb_model_tower_";
pieces = [];
for( i = startNum; i <= numPieces; i++ )
{
if( i < 10 )
{
tagName = bonePrefix + "0" + i;
sbModelTNString = sbModelPrefix + "0" + i;
}
else
{
tagName = bonePrefix + i;
sbModelTNString = sbModelPrefix + i;
}
piece = getent_safe( sbModelTNString, "targetname" );
piece transmittargetname();
piece.script_linkto = tagName;
pieces[pieces.size] = piece;
}
ASSERTEX( IsDefined( pieces ) && pieces.size == 32, "Something went wrong with getting the collapsing building pieces." );
return pieces;
}
building_collapse_kill_ais()
{
getent_safe( "trig_script_killspawner_113", "targetname" ) notify( "trigger" );
getent_safe( "trig_script_killspawner_115", "targetname" ) notify( "trigger" );
wait( 0.1 );
guys = GetAIArray( "axis" );
wait( 0.1 );
array_thread( guys, ::bloody_death, true, 2 );
}
building_collapse_hit1_fx( pieces )
{
clientNotify( "bch" ); // now clientsided
//array_thread( pieces, ::building_collapse_oneshot_fx_randomchance, 15 );
}
// obscures the alleyway from where we need to spawn friendlies
alleyway_fx()
{
origin = ( 1252, -772, -120 );
PlayFX( level._effect["smokescreen"], origin );
}
building_collapse_fx( pieces )
{
//TODO TUEY: Set up busses so that there is NO sound
//Kevin client notify for bus change and to start tinnitus
level notify( "tinnitus" );
//TUEY Set Music STate to BOOM Mofo
setmusicstate("BOOM_MFER");
fxSpot = getstruct_safe( "struct_building_corner_fxsource", "targetname", "collapsing building fx script struct" );
collapseFX = level._effect["building_collapse"];
falloutFX = level._effect["building_collapse_fallout"];
//lingeringSmokeFX = level._effect["battle_smoke_heavy"]; // this has been clientsided
clientNotify( "bcf" ); // now clientsided
//array_thread( pieces, ::building_collapse_oneshot_fx_randomchance, 50 );
wait( 1.2 );
PlayFX( collapseFX, fxSpot.origin );
wait( 1.5 );
PlayFX( falloutFX, fxSpot.origin, AnglesToForward( fxSpot.angles ) );
wait( 0.8 );
// destroy opels in the area
thread building_collapse_destroy_opels();
thread building_collapse_player_damage();
wait( 0.5 );
flag_set( "building_collapse_fallout_done" );
clientNotify("lsmoke"); // lingering smoke has been clientsided
flag_wait( "metrogate_executions_done" );
clientNotify("lsmokedone"); // lingering smoke has been clientsided
}
building_collapse_oneshot_fx_randomchance( chancePercent )
{
if( chancePercent < 0 )
{
chancePercent = 0;
}
if( chancePercent > 100 )
{
chancePercent = 100;
}
if( RandomInt( 100 ) < chancePercent )
{
self building_collapse_oneshot_fx();
}
}
building_collapse_oneshot_fx()
{
oneShotFX = level._effect["building_collapse_oneshot"];
wait( RandomFloat( 0.5 ) );
players = get_players();
player = players[0];
if( players.size > 1 )
{
player = get_random( players );
}
angles = VectorToAngles( self.origin - player.origin );
PlayFX( oneShotFX, self.origin, angles );
}
building_collapse_destroy_opels()
{
opels = GetEntArray( "bldg_collapse_dest_opel", "script_noteworthy" );
if( IsDefined( opels ) && opels.size > 0 )
{
array_thread( opels, ::building_collapse_opel_destroy );
}
}
building_collapse_opel_destroy()
{
wait( RandomFloat( 2 ) );
if( IsDefined( self ) )
{
RadiusDamage( self.origin, 10, self.health + 1, self.health + 1 );
}
}
building_collapse_player_damage()
{
// do some damage to the area in case players are close
//RadiusDamage( ( 52, 236, 208 ), 770, 50, 100 );
origin = ( 52, 236, 208 );
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( Distance( origin, players[i].origin ) <= 770 )
{
players[i] DoDamage( RandomIntRange( 50, 100 ), origin );
}
}
}
mg_geo_delete()
{
wait( 1 );
self Delete();
}
tower_fx( pieces )
{
fxPieces = [];
fxPieces[0] = pieces[1];
fxPieces[1] = pieces[2];
fxPieces[2] = pieces[3];
fxPieces[3] = pieces[4];
fxPieces[4] = pieces[5];
clientNotify( "bct" ); // now clientsided
//array_thread( fxPieces, ::building_collapse_oneshot_fx );
array_thread( fxPieces, ::tower_fx_piece );
}
tower_fx_piece()
{
while( !OkTospawn() )
{
wait( 0.1 );
}
org = Spawn( "script_model", self.origin );
org SetModel( "tag_origin" );
org LinkTo( self );
/*
tags[0] = "tag_origin";
org thread tags_debug_print( tags );
*/
PlayFxOnTag( level._effect["tower_dust_trail"], org, "tag_origin" );
level waittill( "building_towerfall_anim_done" );
org Unlink();
org Delete();
}
// --- END BUILDING COLLAPSE STUFF ---
street_dialogue()
{
wait( 3 );
// "Berlin will be in ruins by the time war is over."
level.sarge playsound_generic_facial( "Ber2_IGD_058A_REZN" );
flag_wait( "street_charge_moveup1" );
wait( RandomFloatRange( 1, 3 ) );
// "Stay with the tanks!"
level.sarge playsound_generic_facial( "Ber2_IGD_061A_REZN" );
flag_wait( "building_mgs_start" );
wait( RandomFloatRange( 1, 2 ) );
// "MGs up ahead - third floor!"
level.sarge playsound_generic_facial( "Ber2_IGD_060A_REZN" );
// "Keep moving"
level.sarge playsound_generic_facial( "Ber2_IGD_062A_REZN" );
}
// --- STREET EXECUTION STUFF ---
building_collapse_street_executions()
{
// wait at least 2 seconds from now to spawn the guys on the ground
timeToSpawnGroundGuys = GetTime() + 2000; // 2000 ms = 2 seconds
animSpots = GetStructArray( "struct_e1_street_executions", "targetname" );
ASSERTEX( IsDefined( animSpots ) && animSpots.size > 0, "Couldn't find any animspot script_structs for street executions." );
// set up the metrogate regroup nodes for street_execution guys to join the crew after doing their thing
level.metrogateRegroupNodes = GetNodeArray( "node_street_regroup", "targetname" );
ASSERTEX( IsDefined( level.metrogateRegroupNodes ) && level.metrogateRegroupNodes.size > 0, "Couldn't find any metrogate regroup nodes." );
// "extras" = molotov throwers
numMetrogateExtras = 4;
totalRequiredRedshirts = animSpots.size + numMetrogateExtras;
// clear level.friends color nodes
array_thread( level.friends, ::clear_force_color );
// get squad redshirts
killers = [];
for( i = 0; i < level.friends.size; i++ )
{
if( IsDefined( level.friends[i] )
&& IsAlive( level.friends[i] )
&& ( level.friends[i] != level.sarge )
&& ( level.friends[i] != level.hero1 ) )
{
killers[killers.size] = level.friends[i];
}
}
// get other friendly redshirts
redshirts = get_ai_group_ai( "ai_outside_russians_1" );
// turn off color respawning on these guys & kill their spawners
array_thread( redshirts, ::disable_replace_on_death );
getent_safe( "trig_killspawner_421", "targetname" ) notify( "trigger" );
killers = array_combine( killers, redshirts );
// if we don't have enough guys for our animations, we have to spawn more
if( killers.size < totalRequiredRedshirts )
{
numToSpawn = totalRequiredRedshirts - killers.size;
spawner = getent_safe( "spawner_street_extraguy", "targetname" );
for( i = 0; i < numToSpawn; i++ )
{
spawner.count++;
extra = undefined;
while( !IsDefined( extra ) )
{
while( !OkToSpawn() )
{
wait( 0.05 );
}
extra = spawn_guy( spawner );
wait( 0.1 );
}
extra thread magic_bullet_shield_safe();
// just move out of the way
extra SetGoalPos( ( 1122, -408, -92 ) );
// add to the killers array
killers[killers.size] = extra;
wait( 0.1 );
}
// clean up spawner
spawner thread scr_delete( 1 );
}
ASSERTEX( killers.size >= totalRequiredRedshirts, "Too many execution animations for the number of available redshirts!" );
flag_init( "street_executions_death_watcher_started" );
flag_init( "street_execution_guys_spawning" );
lastIndex = 0;
// some guys do street executions
for( i = 0; i < animSpots.size; i++ )
{
if( IsDefined( killers[i] ) )
{
killers[i] thread street_execution_anim( animSpots[i], timeToSpawnGroundGuys );
}
lastIndex = i;
}
level thread building_collapse_monitor_victims( animSpots.size );
// rest of them are metrogate extras
metrogateExtras = [];
for( i = ( lastIndex + 1 ); i < killers.size; i++ )
{
metrogateExtras[metrogateExtras.size] = killers[i];
}
level thread metrogate_execution( metrogateExtras );
flag_wait( "metrogate_executions_done" );
// take level.friends guys out of the killers array
killers = array_exclude( killers, level.friends );
// repopulate level.friends up to a given size, removing the repopulated guys from killers
// now we will have an array of redshirts who are ok to kill off during the mortar run
levelFriendsTargetSize = undefined;
if( level.coopOptimize )
{
// we want level.friends to be smaller in coop
levelFriendsTargetSize = 4;
}
else
{
levelFriendsTargetSize = 6;
}
while( level.friends.size < levelFriendsTargetSize )
{
newfriend = get_random( killers );
newfriend friend_add();
newfriend set_force_color( "b" );
newfriend thread replace_on_death();
killers = array_remove( killers, newfriend );
}
// squad regroup and rocket barrage
// level progression continues in this thread
level thread street_regroup( killers );
}
// need this to keep players from running ahead
building_collapse_players_shock()
{
wait( 1 );
duration = 8;
stanceLockDuration = 7;
players = get_players();
for( i = 0; i < players.size; i++ )
{
player = players[i];
if( player.origin[0] < 1900 )
{
player thread building_collapse_player_shock( duration, stanceLockDuration );
}
}
wait( stanceLockDuration );
set_objective( 3 );
thread street_executions_dialogue();
}
building_collapse_player_shock( duration, stanceLockDuration )
{
self endon( "death" );
self endon( "disconnect" );
//self EnableInvulnerability( true );
self VisionSetNaked( "ber2_collapse", ( duration * 0.25 ) );
// origin, duration, shock_range, nMaxDamageBase, nRanDamageBase, nMinDamageBase, nExposed, customShellShock, stanceLockDuration
self thread maps\_shellshock::main( self.origin, duration, 64, 2, 1, 1, undefined, undefined, stanceLockDuration );
wait( duration * 0.75 );
self VisionSetNaked( "ber2", ( duration * 0.25 ) );
wait( duration * 0.25 );
//self EnableInvulnerability( false );
}
street_executions_playerspeed()
{
baseSpeedScale = 1;
speedScaleMultiplier = 0.6;
array_thread( get_players(), ::scr_player_speedscale, ( baseSpeedScale * speedScaleMultiplier ), 1 );
flag_wait( "metrogate_reach_done" );
// set speed back
array_thread( get_players(), ::scr_player_speedscale, baseSpeedScale, 3 );
}
street_executions_dialogue()
{
if( flag( "metrogate_execution_player_close" ) )
{
return;
}
level endon( "metrogate_execution_player_close" );
// "Make sure they are all dead."
level.sarge playsound_generic_facial( "Ber2_IGD_063A_REZN" );
wait( 2 );
// "Kill them all."
level.sarge playsound_generic_facial( "Ber2_IGD_064A_REZN" );
// "Wipe this scum from the streets."
level.sarge playsound_generic_facial( "Ber2_IGD_065A_REZN" );
}
street_regroup( guys )
{
//set_objective( 4 ); // removed objective 4
autosave_by_name( "ber2_street_escape" );
thread rocket_barrage( guys ); // redshirts move in this thread
//TUEY Set the music state to RUN_MORTARS
setmusicstate("RUN_MORTARS");
wait( 2 ); // wait for sarge to react
thread rocket_barrage_redshirts_go( guys );
set_objective( 5 );
// level.friends go
array_thread( level.friends, ::set_force_color, "b" );
// level progression continues: kick off the subway gate action
level thread subway_gate_action();
}
rocket_barrage( guys )
{
level.pauseRandomShake = true;
/#
level thread maps\_debug::set_event_printname( "Event 2 - Rocket Barrage", true );
#/
thread players_safe_belowground();
waveSpots1 = GetStructArray( "metrogate_rocket_wave1", "targetname" );
waveSpots2 = GetStructArray( "metrogate_rocket_wave2", "targetname" );
waveSpots3 = GetStructArray( "metrogate_rocket_wave3", "targetname" );
waveSpots4 = GetStructArray( "metrogate_rocket_wave4", "targetname" );
ASSERTEX( array_validate( waveSpots1 ), "Couldn't find wave 1 rocket spots" );
ASSERTEX( array_validate( waveSpots2 ), "Couldn't find wave 2 rocket spots" );
ASSERTEX( array_validate( waveSpots3 ), "Couldn't find wave 3 rocket spots" );
ASSERTEX( array_validate( waveSpots4 ), "Couldn't find wave 4 rocket spots" );
thread rocket_wave( waveSpots1, undefined, "rocket_barrage_wave1_done" );
level waittill( "rocket_wave_done" );
wait( RandomFloatRange( 2.5, 3.5 ) );
thread rocket_wave( waveSpots2 );
delayThread( 1, ::tank_rocket_hit );
level waittill( "rocket_wave_done" );
wait( RandomFloatRange( 2.5, 3.5 ) );
thread rocket_wave( waveSpots3 );
level waittill( "rocket_wave_done" );
wait( RandomFloatRange( 2.5, 3.5 ) );
// start thinking about killing off stragglers
array_thread( get_players(), ::rocket_barrage_target_player );
// loop until the gate is closed
numLoops = 0;
while( !flag( "subway_gate_closed" ) )
{
if( numLoops % 2 == 0 )
{
thread rocket_wave( waveSpots4 );
}
else
{
thread rocket_wave( waveSpots3 );
}
numLoops++;
level waittill( "rocket_wave_done" );
wait( RandomFloatRange( 2.5, 3.5 ) );
}
level.pauseRandomShake = false;
}
rocket_barrage_redshirts_go( guys )
{
array_thread( guys, ::stop_magic_bullet_shield_safe );
// redshirts go
redshirtsRunSpot = ( 1264, -1092, -88 );
for( i = 0; i < guys.size; i++ )
{
if( IsDefined( guys[i] ) )
{
guys[i].goalradius = 128;
guys[i] SetGoalPos( redshirtsRunSpot );
guys[i] thread bloody_death_delayed( 8, 10 );
}
}
}
players_safe_belowground()
{
belowground = -176;
xGate = 1600;
while( !flag( "subway_gate_closed" ) )
{
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( IsDefined( players[i] ) && IsAlive( players[i] ) && players[i].origin[2] < belowground && players[i].origin[0] < xGate )
{
SetPlayerIgnoreRadiusDamage( true );
}
else
{
SetPlayerIgnoreRadiusDamage( false );
}
}
wait( 0.2 );
}
// once the gate is closed, wait a bit and make them all take radius damage again
wait( 10 );
players = get_players();
for( i = 0; i < players.size; i++ )
{
if( IsDefined( players[i] ) && IsAlive( players[i] ) )
{
SetPlayerIgnoreRadiusDamage( false );
}
}
}
rocket_barrage_target_player()
{
self endon( "death" );
self endon( "disconnect" );
rocketTravelDelay = 1.5;
xGate = 1600;
while( !flag( "subway_gate_closed" ) )
{
if( self.origin[2] > -130 || self.origin[0] > xGate )
{
thread rocket_wave_rocket( origin_offset_2D( self.origin, -64, 64 ) );
thread rocket_wave_rocket( origin_offset_2D( self.origin, -128, 128 ) );
thread rocket_wave_rocket( origin_offset_2D( self.origin, -256, 256 ) );
wait( rocketTravelDelay );
}
else
{
wait( 0.5 );
}
}
}
origin_offset_2D( refOrg, minOffset, maxOffset )
{
return refOrg + ( RandomIntRange( minOffset, maxOffset ), RandomIntRange( minOffset, maxOffset ), 0 );
}
rocket_wave( waveSpots, waveRepeats, extraNotify )
{
if( !IsDefined( waveRepeats ) || waveRepeats < 1 )
{
waveRepeats = 2;
}
for( j = 0; j < waveRepeats; j++ )
{
for( i = 0; i < waveSpots.size; i++ )
{
thread rocket_wave_rocket( waveSpots[i].origin );
if( i != ( waveSpots.size - 1 ) )
{
wait( RandomFloatRange( 0.3, 0.5 ) );
}
}
}
if( IsDefined( extraNotify ) )
{
level notify( extraNotify );
}
level notify( "rocket_wave_done" );
}
rocket_wave_rocket( expOrg )
{
rocket = Spawn( "script_model", expOrg + ( 0, 0, 6000 ) );
rocket SetModel( "katyusha_rocket" );
rocket.angles = ( 90, 0, 0 );
rocket playloopsound( "katy_rocket_run" );
PlayFxOnTag( level._effect["katyusha_rocket_trail"], rocket, "tag_origin" );
thread play_sound_in_space( "katyusha_launch", rocket.origin );
rocket MoveTo( expOrg, RandomFloatRange( 1, 2 ) );
rocket waittill( "movedone" );
rocket Delete();
PlayFX( level._effect["katyusha_rocket_explosion"], expOrg );
RadiusDamage( expOrg, 196, 25, 45 );
array_thread( get_players(), ::generic_rumble_explosion );
array_thread4( get_players(), ::scr_earthquake, 0.45, 0.4, expOrg, 3000 );
}
tank_rocket_hit()
{
tank = GetEnt( "e1_street_tank", "targetname" );
if( !IsDefined( tank ) || !IsAlive( tank ) )
{
return;
}
//spot = getstruct_safe( "subway_rocket_tankblast", "targetname" );
rocket_wave_rocket( tank.origin );
wait( 0.1 );
if( IsDefined( self ) && IsDefined( self.health ) && self.health > 0 )
{
RadiusDamage( self.origin, 10, self.health + 1, self.health + 1 );
}
}
bloody_death_delayed( delayMin, delayMax )
{
wait( delayMin );
self thread bloody_death( true, delayMax - delayMin );
}
// self = the executioner
street_execution_anim( animSpot, timeToSpawnGroundGuys )
{
executionNum = RandomIntRange( 1, 4 );
// set up the execution anims
victim_animname = "wounded_execution" + executionNum + "_victim" ;
executioner_animname = "wounded_execution" + executionNum + "_executioner";
street_execution_waitfor_spawn( timeToSpawnGroundGuys );
flag_set( "street_execution_guys_spawning" );
// TODO set up guys with the right weapons
// set up the executioner
if( !IsDefined( self.magic_bullet_shield ) || !self.magic_bullet_shield )
{
self thread magic_bullet_shield();
self.street_execution_bullet_shield = true;
}
self.og_animname = self.animname;
//self.cqbwalking = true;
self.animname = executioner_animname;
self pushPlayer( true );
victim = undefined;
guys = [];
if( is_german_build() )
{
}
else
{
// spawn the victim & set him up
spawner = getent_safe( animSpot.target, "targetname", "street execution victim spawner" );
spawner.count = 1;
victim = spawn_guy( spawner );
if( !IsDefined( victim ) )
{
ASSERTMSG( "Can't spawn the victim!" );
return;
}
// don't kill this guy until we start the death watcher thread
victim thread keep_safe_til_flag( "street_executions_death_watcher_started" );
// clean up spawner
spawner thread scr_delete( 5 );
victim.ignoreme = true;
victim.ignoreall = true;
victim.health = 1;
victim.nodeathragdoll = true;
victim.allowdeath = true;
victim.a.pose = "prone";
victim.a.nodeath = true;
victim.animname = victim_animname;
victim.grenadeammo = 0;
victim.dropweapon = 0;
victim animscripts\shared::DropAIWeapon();
self.victim = victim;
victim.executioner = self;
//guys = [];
guys[0] = self;
guys[1] = victim;
self.executionDone = false;
// start the victim's wounded loop
notifystring = "stop_wounded_loop";
animSpot thread anim_loop_solo( victim, "wounded_loop", undefined, notifystring, undefined );
animSpot thread street_execution_abort( self, victim, executionNum, notifystring );
thread street_execution_victimdeath( self, victim, notifystring );
}
// make sure players are close enough to get a chance to participate
flag_wait( "street_executions_start" );
// stagger the friendlies moving a bit
wait( RandomFloat( 3.3 ) );
// get the executioner into position
animSpot anim_reach_solo( self, "execution_shot" );
if( is_german_build() )
{
}
else
{
// if the wounded guy is still alive...
if( is_active_ai( victim ) )
{
self.isExecuting = true;
animSpot anim_single( guys, "execution_shot" );
}
}
self.isExecuting = false;
self.executionDone = true;
// reset the executioner
if( IsDefined( self.street_execution_bullet_shield ) && self.street_execution_bullet_shield )
{
self thread stop_magic_bullet_shield_safe();
}
//self.cqbwalking = false;
self.animname = self.og_animname;
self pushPlayer( false );
if( !flag( "metrogate_executions_done" ) )
{
// send to a node near the metro gate
self metrogate_assign_regroupnode();
}
}
street_execution_waitfor_spawn( timeToSpawnGroundGuys )
{
// WAIT for the particle to fill up the read, if necessary
if( GetTime() < timeToSpawnGroundGuys )
{
wait( ( timeToSpawnGroundGuys - GetTime() ) * .001 ); // = seconds
}
}
keep_safe_til_flag( safeFlag )
{
self magic_bullet_shield_safe();
flag_wait( safeFlag );
self stop_magic_bullet_shield_safe();
}
street_execution_victimdeath( executioner, victim, notifystring )
{
victim endon( "death" );
executioner endon( "execution_abort" );
executioner waittill( "execution_gunshot" );
executioner.executionSuccess = true;
victim notify( notifystring );
victim DoDamage( victim.health + 5, (0,0,0) );
}
// self = the animspot
street_execution_abort( executioner, victim, executionNum, notifystring )
{
victim thread execution_abort_for_progression();
// the victim's health is 1, so any damage will kill him
victim waittill( "damage", amount, attacker, direction_vec, point, type );
if( !IsDefined( victim.abortedForProgression ) )
{
// if the damage didn't come from the player...
// and if the damage is NOT explosive
if( !attacker is_player() && type != "MOD_GRENADE" && type != "MOD_GRENADE_SPLASH" && type != "MOD_EXPLOSIVE" )
{
// kill the thread
return;
}
}
// otherwise do custom stuff since a player or an explosive interrupted things
executioner notify( "execution_abort" );
// kill looping anim
victim anim_stopanimscripted();
self notify( notifystring );
victim StartRagdoll();
// only StopAnimScripted if the guy is actually doing an animscripted
if( IsDefined( executioner.isExecuting ) && executioner.isExecuting )
{
executioner StopAnimScripted();
}
}
execution_abort_for_progression()
{
self endon( "death" );
flag_wait( "metrogate_executions_done" );
self.abortedForProgression = true;
self DoDamage( 1, self.origin, get_players()[0] );
}
metrogate_assign_regroupnode()
{
nodes = level.metrogateRegroupNodes;
if( nodes.size < 1 )
{
// FALLBACK - used all the nodes, just go to a pos
self SetGoalPos( ( 596, 564, -104 ) );
}
else
{
node = get_random( level.metrogateRegroupNodes );
self SetGoalNode( node );
// take that node out of the array so nobody else uses it
level.metrogateRegroupNodes = array_remove( level.metrogateRegroupNodes, node );
}
}
building_collapse_monitor_victims( numExecutions )
{
// wait for the guys to spawn
flag_wait( "street_execution_guys_spawning" );
wait( 0.1 );
if( is_german_build() )
{
flag_set( "street_executions_death_watcher_started" );
wait( 1 );
flag_set( "e1_street_executions_done" );
return;
}
victims = get_ai_group_ai( "ai_e1_street_execution_victims" );
ASSERTEX( IsDefined( victims ) && victims.size > 0, "Couldn't find any street execution victims!" );
ASSERTEX( victims.size == numExecutions, "Didn't find enough victims for the number of street executions" );
flag_set( "street_executions_death_watcher_started" );
// BJoyal - Added a limit to how long the script waits due to a rare bug of this event never ending.
thread building_collapse_monitor_victims_safety_net();
while( 1 )
{
victimsAlive = false;
for( i = 0; i < victims.size; i++ )
{
if( is_active_ai( victims[i] ) )
{
// if any are alive, that's all we need to know
victimsAlive = true;
break;
}
}
// if none are still around, break the loop
if( !victimsAlive || flag( "metrogate_executions_done" ) )
{
break;
}
wait( 0.1 );
}
flag_set( "e1_street_executions_done" );
}
building_collapse_monitor_victims_safety_net()
{
wait(15);
flag_set( "e1_street_executions_done" );
wait(5);
flag_set( "metrogate_reach_done" );
}
street_execution_gunshotFX( executioner )
{
// collect all the info about the victim before he dies
victim = executioner.victim;
fxSpot_head = undefined;
fxSpot_clavicle_left = undefined;
fxSpot_clavicle_right = undefined;
fxSpot_spine = undefined;
// the victim could be dead already
if( IsDefined( victim ) )
{
fxSpot_head = victim GetTagOrigin( "J_Brow_RI" );
fxSpot_clavicle_left = victim GetTagOrigin( "j_clavicle_le" );
fxSpot_clavicle_right = victim GetTagOrigin( "j_clavicle_ri" );
fxSpot_spine = victim GetTagOrigin( "j_spine4" );
}
executioner notify( "execution_gunshot" );
PlayFxOnTag( level._effect["rifleflash"], executioner, "tag_flash" ); // muzzleflash
if( IsDefined( fxSpot_head ) && is_mature() )
{
forward = AnglesToForward( ( executioner GetTagAngles( "tag_flash" ) ) );
PlayFX( level._effect["headshot"], fxSpot_head, forward ); // blood
}
wait( 0.2 );
PlayFxOnTag( level._effect["rifle_shelleject"], executioner, "tag_brass" ); // shell eject
}
// --- END STREET EXECUTION STUFF ---
// --- METROGATE EXECUTION STUFF ---
metrogate_execution( guys )
{
thread battlechatter_off( "allies" );
thread battlechatter_off( "axis" );
/#
level thread maps\_debug::set_event_printname( "Event 2 - Metro Gate Execution", true );
#/
flag_init( "molotovs_throw" );
flag_init( "molotovs_cancel" );
gate = GetEnt( "subway_entrance_gate", "targetname" );
ASSERTEX( IsDefined( gate ), "Can't find the subway gate!" );
sarge = level.sarge;
hero1 = level.hero1;
redshirts[0] = guys[0];
redshirts[1] = guys[1];
redshirts[2] = guys[2];
redshirts[3] = guys[3];
array_thread( redshirts, ::magic_bullet_shield_safe );
sarge set_animname_custom( "metrogate_exe_sarge" );
hero1 set_animname_custom( "metrogate_exe_hero1" );
redshirts[0] set_animname_custom( "metrogate_exe_redshirt1" );
redshirts[1] set_animname_custom( "metrogate_exe_redshirt2" );
redshirts[2] set_animname_custom( "metrogate_exe_redshirt3" );
redshirts[3] set_animname_custom( "metrogate_exe_redshirt4" );
//array_thread( redshirts, ::metrogate_print_animname );
// put into one array for convenience
friends[0] = sarge;
friends[1] = hero1;
friends = array_combine( friends, redshirts );
array_thread( friends, ::scr_ignoreall, true );
// send friends to correct spots
// metrogate_reach also kicks off all of the molotov throwing logic
thread metrogate_reach_watcher( friends.size );
array_thread( friends, ::metrogate_reach, gate );
axis = [];
if( is_german_build() == false )
{
spawners[0] = getent_safe( "metrogate_axis_1", "targetname" );
spawners[1] = getent_safe( "metrogate_axis_2", "targetname" );
spawners[2] = getent_safe( "metrogate_axis_3", "targetname" );
//axis = [];
for( i = 0; i < spawners.size; i++ )
{
animname = "metrogate_exe_german" + ( i + 1 );
spawners[i].origin = GetStartOrigin( gate.origin, gate.angles, level.scr_anim[animname]["scene"] );
spawners[i].angles = GetStartAngles( gate.origin, gate.angles, level.scr_anim[animname]["scene"] );
guy = spawn_guy( spawners[i] );
guy.ignoreme = true;
guy.ignoreall = true;
guy.goalradius = 12;
guy SetCanDamage( false );
guy.allowdeath = false;
guy.health = 1000000;
guy set_animname_custom( animname );
axis[axis.size] = guy;
}
// clean up spawners
thread delete_group( spawners, 5 );
level.metrogate_axis = axis;
level thread metrogate_axis_deathwatcher( axis );
}
// BJoyal - Moved this to before the way to prevent issues where player beats AI to the metro, causing progression issues
if( is_german_build() == false )
{
for( i = 0; i < axis.size; i++ )
{
axis[i] SetCanDamage( true );
axis[i].allowdeath = true;
axis[i].health = 1;
}
}
// wait for guys to reach
flag_wait( "metrogate_reach_done" );
// Alex Liu: The range check was not accurate. If player skirts around the perimeter
// he can get a clear shot at the enemies, without triggering the event.
// A trigger has been placed across the entire width of the street, so the
// player must have hit it before getting a view down the subway entrance.
// wait for player to get close
//waittill_player_within_range( ( 888, 760, -144 ), 500, 0.05 );
close_trigger = getent( "metro_gate_approaching", "targetname" );
close_trigger waittill( "trigger" );
// warp players into scene
enable_trigger_with_noteworthy( "trig_coop_warp_metrogate_execution" );
// BJoyal - Old place for turning on the ability for the germans to be killed
gate notify( "heroes_stopidle" );
flag_set( "metrogate_execution_player_close" );
// save
autosave_by_name( "ber2_metrogate_execution" );
if( is_german_build() )
{
flag_set( "molotovs_cancel" );
}
else
{
// play scene
actors[0] = sarge;
actors[1] = hero1;
allies = []; // BJoyal - keep a seperate copy of the allies array
allies = actors;
actors = array_combine( actors, axis );
if( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
gate thread anim_single( actors, "scene" );
metrogate_scene_abort( actors[0] );
}
if( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
// start idles
// BJoyal - Seperated the axis from the rest of those in the animation in case only one is killed, which causes popping issues
//gate thread anim_loop( actors, "idle2", undefined, "stop_wait_loop" );
gate thread anim_loop( allies, "idle2", undefined, "stop_wait_loop" );
for(i = 0; i < axis.size; i++)
{
if( isdefined(axis[i]) && isalive(axis[i]) )
{
gate thread anim_loop_solo( axis[i], "idle2" );
}
}
level thread metrogate_playerchoice_wait( 5 );
}
while( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
wait( 0.05 );
}
}
gate notify( "stop_wait_loop" );
friends = array_remove( friends, hero1 );
friends = array_remove( friends, sarge );
// if molotovs were cancelled, gun the guys down
if( flag( "molotovs_cancel" ) )
{
array_thread( axis, ::scr_ignoreme, false );
array_thread( friends, ::scr_ignoreall, false );
}
// different anims/dialogue based on what happened
sargeAnim = undefined;
if( flag( "molotovs_cancel" ) )
{
sargeAnim = "escape_mercy";
russian_diary_event( "good" );
}
else
{
sargeAnim = "escape_burn";
russian_diary_event( "evil" );
}
gate thread anim_single_solo( hero1, "escape" );
gate thread anim_single_solo( sarge, sargeAnim );
// TODO wait for danger notetrack
sarge waittillmatch( "single anim", "mortar_fire" );
array_thread( redshirts, ::stop_magic_bullet_shield_safe );
sarge thread scr_ignoreall( false );
hero1 thread scr_ignoreall( false );
array_thread( friends, ::scr_ignoreall, false );
array_thread( friends, ::reset_animname );
flag_set( "metrogate_executions_done" );
}
/*
metrogate_print_animname()
{
self endon( "death" );
while( 1 )
{
Print3D( self.origin, self.animname, ( 1, 1, 1 ), 1, 0.3 );
wait( 0.05 );
}
}
*/
// stops the "scene" part of the sequence short if the player shoots someone
metrogate_scene_abort( guy )
{
level endon( "molotovs_cancel" );
level endon( "molotovs_throw" );
thread metrogate_scene_heroes_reset();
guy waittillmatch( "single anim", "end" );
level notify( "metrogate_scene_segment_done" );
}
// the heroes look dumb if they keep doing their scripted anims when the scene is supposed to be aborted
metrogate_scene_heroes_reset()
{
level endon( "metrogate_scene_segment_done" );
flag_wait_either( "molotovs_cancel", "molotovs_throw" );
level.sarge StopAnimScripted();
level.hero1 StopAnimScripted();
}
metrogate_reach_watcher( requiredReaches )
{
numReaches = 0;
while( numReaches < requiredReaches )
{
level waittill( "metrogate_reached" );
numReaches++;
}
flag_set( "metrogate_reach_done" );
}
metrogate_reach( gate )
{
if( self == level.sarge || self == level.hero1 )
{
gate anim_reach_solo( self, "scene" );
self thread metrogate_reach_notifysafe();
gate anim_loop_solo( self, "idle1", undefined, "heroes_stopidle" );
}
else
{
gate anim_reach_solo( self, "molotov_takeout" );
self thread metrogate_reach_notifysafe();
// spawn a temp reference org so we can notify just for this guy to stop his first idle
org = Spawn( "script_origin", gate.origin );
org.angles = gate.angles;
org thread anim_loop_solo( self, "molotov_idle1", undefined, "stop_idle1" );
while( !flag( "metrogate_execution_player_close" ) )
{
wait( 0.1 );
}
if( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
wait( RandomFloatRange( 1, 2.25 ) );
}
org notify( "stop_idle1" );
if( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
self thread anim_light_molotov();
gate anim_single_solo( self, "molotov_takeout" );
self thread metrogate_redshirt_think( gate );
}
if( !flag( "molotovs_cancel" ) && !flag( "molotovs_throw" ) )
{
gate anim_loop_solo( self, "molotov_idle2", undefined, "molotov_guys_stopidle" );
}
org Delete();
}
}
anim_light_molotov()
{
self endon( "death" );
molotov = "weapon_rus_molotov_grenade";
molotov_linkTag = "tag_inhand";
molotov_flameFX = level._effect["molotov_flame"];
molotov_flameTag = "tag_fx";
zippo = "weapon_rus_zippo";
zippo_linkTag = "tag_weapon_left";
zippo_flameFX = level._effect["zippo_flame"];
zippo_flameTag = "tag_fx";
// attach molotov
self waittillmatch( "single anim", "attach_molotov" );
self.molotov = Spawn( "script_model", self GetTagOrigin( molotov_linkTag ) );
self.molotov.angles = self GetTagAngles( molotov_linkTag );
self.molotov SetModel( molotov );
self.molotov LinkTo( self, molotov_linkTag );
// attach zippo and light it
self waittillmatch( "single anim", "attach_zippo" );
self.zippo = Spawn( "script_model", self GetTagOrigin( zippo_linkTag ) );
self.zippo.angles = self GetTagAngles( zippo_linkTag );
self.zippo SetModel( zippo );
self.zippo LinkTo( self, zippo_linkTag );
PlayFxOnTag( zippo_flameFX, self.zippo, zippo_flameTag );
// light molotov
// semi-hacky since I have to spawn a new script_model so I can snuff the flame separately
// (can't attach b/c the character already has a gun attached with a tag_flash)
self waittillmatch( "single anim", "light" );
self.molotov_fxOrg = Spawn( "script_model", self.molotov GetTagOrigin( molotov_flameTag ) );
self.molotov_fxOrg SetModel( "tag_origin" );
self.molotov_fxOrg.angles = self.molotov GetTagAngles( molotov_flameTag );
self.molotov_fxOrg LinkTo( self.molotov, molotov_flameTag );
PlayFxOnTag( molotov_flameFX, self.molotov_fxOrg, "tag_origin" );
// detach zippo
self waittillmatch( "single anim", "detach_zippo" );
self.zippo Unlink();
self.zippo Delete();
// wait for either animation to handle detaching and killing molotov FX
self thread molotov_waitforthrow();
self thread molotov_waitforputaway();
}
molotov_waitforthrow()
{
self endon( "molotov_threadkill" );
self waittillmatch( "single anim", "throw" );
self.molotov_fxOrg Unlink();
self.molotov_fxOrg Delete();
self.molotov Unlink();
self.molotov Delete();
self notify( "molotov_threadkill" );
}
molotov_waitforputaway()
{
self endon( "molotov_threadkill" );
self waittillmatch( "single anim", "snuff" );
self.molotov_fxOrg Unlink();
self.molotov_fxOrg Delete();
self waittillmatch( "single anim", "detach_molotov" );
self.molotov Unlink();
self.molotov Delete();
self notify( "molotov_threadkill" );
}
metrogate_reach_notifysafe()
{
if( !IsDefined( level.metrogate_notifying ) )
{
level.metrogate_notifying = true;
}
else
{
while( level.metrogate_notifying )
{
wait( 0.05 );
}
}
level notify( "metrogate_reached" );
level.metrogate_notifying = false;
}
metrogate_axis_deathwatcher( guys )
{
level endon( "molotovs_throw" );
while( 1 )
{
foundOne = false;
guy = undefined;
for( i = 0; i < guys.size; i++ )
{
if( !IsAlive( guys[i] ) )
{
foundOne = true;
guy = guys[i];
break;
}
}
if( foundOne )
{
break;
}
wait( 0.05 );
}
// if he died from grenade/molotov damage, the player was not merciful
if( IsDefined( guy ) && IsDefined( guy.damagemod ) &&
( guy.damagemod == "MOD_BURNED" || guy.damagemod == "MOD_GRENADE_SPLASH" ) )
{
flag_set( "molotovs_throw" );
}
else
{
flag_set( "molotovs_cancel" );
}
}
metrogate_playerchoice_wait( waitTime )
{
level endon( "molotovs_cancel" );
timer = "metrogate_playerchoice_timer";
set_timer( timer, waitTime );
println( "make your choice!" );
while( !timer_expired( timer ) )
{
wait( 1 );
}
flag_set( "molotovs_throw" );
}
metrogate_redshirt_think( gate )
{
level endon( "molotovs_cancel" );
self thread metrogate_abort_throw( gate );
flag_wait( "molotovs_throw" );
gate notify( "molotov_guys_stopidle" );
self thread metrogate_throw_molotov();
gate anim_single_solo( self, "molotov_throw" );
}
metrogate_abort_throw( gate )
{
level endon( "molotovs_throw" );
flag_wait( "molotovs_cancel" );
gate notify( "molotov_guys_stopidle" );
gate anim_single_solo( self, "molotov_putaway" );
}
metrogate_throw_molotov()
{
self waittillmatch( "single anim", "throw" );
self.ogGrenadeWeapon = self.grenadeweapon;
self.grenadeweapon = "molotov";
self.grenadeAmmo++;
// default throw target, in case we can't find a dude for some reason
throwTarget_default = ( 946, 648, -164 );
throwTarget = throwTarget_default;
// tags that will be satisfying throw targets
targetTags[0] = "J_Head";
targetTags[1] = "J_SpineUpper";
targetTags[2] = "J_SpineLower";
targetTags[3] = "J_Ankle_LE";
livingTarget = undefined;
for( i = 0; i < level.metrogate_axis.size; i++ )
{
guy = level.metrogate_axis[i];
if( IsDefined( guy ) )
{
if( !IsDefined( guy.molotovTargeted ) )
{
guy.molotovTargeted = true;
livingTarget = guy;
break;
}
}
}
// if we didn't find an untargeted guy...
if( !IsDefined( livingTarget ) )
{
// maybe they're all targeted already, let's just try to get a random one
guys = array_randomize( level.metrogate_axis );
for( k = 0; k < guys.size; k++ )
{
if( IsDefined( guys[k] ) )
{
livingTarget = guys[k];
break;
}
}
}
// so, if we found a target...
if( IsDefined( livingTarget ) )
{
// pick a tag and set that as the throw target
throwTarget = livingTarget GetTagOrigin( get_random( targetTags ) );
}
// two guys need custom stuff b/c MagicGrenade is not working for them
// TODO THIS IS STILL NOT WORKING, MAGIC GRENADE DOES NOT LIKE IT FOR SOME REASON
if( self.animname == "metrogate_exe_redshirt3" || self.animname == "metrogate_exe_redshirt4" )
{
if( IsDefined( livingTarget ) )
{
livingTarget.molotovTargeted = false;
}
throwTarget = throwTarget_default;
}
throwStart = self GetTagOrigin( "tag_inhand" );
self MagicGrenade( throwStart, throwTarget );
self.grenadeweapon = self.ogGrenadeWeapon;
wait( 0.9 );
// force him on fire - only once though
if( IsDefined( livingTarget ) && !IsDefined( livingTarget.molotovDeath ) )
{
livingTarget.molotovDeath = true;
livingTarget animscripts\death::flame_death_fx();
if( IsAlive( livingTarget ) )
{
deathArray[0] = %ai_flame_death_A;
deathArray[1] = %ai_flame_death_B;
deathArray[2] = %ai_flame_death_C;
deathArray[3] = %ai_flame_death_D;
livingTarget.deathanim = get_random( deathArray );
livingTarget DoDamage( self.health + 1, ( 0, 0, 0 ) );
}
}
}
// --- END METROGATE EXECUTION STUFF ---
// --- SUBWAY GATE OPENING STUFF ---
subway_gate_action()
{
//objective_ring( 0 );
set_color_chain( "trig_script_color_allies_b23" );
holder = level.sarge;
opener = level.hero1;
// open gate
level thread open_subway_gate( opener, holder );
//TUEY Sets music state to Subway
setmusicstate("SUBWAY");
}
open_subway_gate( opener, holder)
{
gate = GetEnt( "subway_entrance_gate", "targetname" );
gateclip = GetEnt( "subway_entrance_gate_clip", "targetname" );
ASSERTEX( IsDefined( gate ), "Can't find the subway gate!" );
ASSERTEX( IsDefined( gateclip ), "Can't find the subway gate clip!" );
// set these guys up
guys = [];
guys[0] = opener;
guys[1] = holder;
for( i = 0; i < guys.size; i++ )
{
guy = guys[i];
guy.og_animname = guy.animname;
guy PushPlayer( true );
guy.ignoreme = true;
guy.ignoreall = true;
}
opener.animname = "metro_gate_opener";
holder.animname = "metro_gate_holder";
// turn off lightning so it doesn't mess with subway fog settings
level.disableLightning = true;
// friendlies run to gate
gate anim_reach( guys, "open" );
// open gate
gate notify( "stop_beam_resting_idle" );
gate thread subway_gate_woodbeam_anim_single( "open" );
opener thread opener_move( "open" );
gate thread subway_gate_openanim();
gate thread gate_hold_loop( holder );
gate thread anim_single_solo_earlyout( opener, "open" );
gate thread anim_single_solo( holder, "open" );
// wait for opener to be out of the way
holder waittillmatch( "single anim", "gate_clear" );
// move the clip out of the way
gateclip ConnectPaths();
gateclip.og_origin = gateclip.origin;
gateclip MoveTo( gateclip.origin + ( 0, 0, -10000 ), 0.05 );
gateclip waittill( "movedone" );
level notify( "subway_gate_opened" );
objective_position( 0, getstruct_safe( "struct_into_subway_gate", "targetname" ).origin );
ASSERTEX( level.friends.size > 3, "Not enough guys in level.friends to move some over to a new chain." );
// set half of the friendlies onto the second color chain
secondChainGroup = [];
for( i = 0; i < level.friends.size; i++ )
{
// we only want half of them moving over
if( secondChainGroup.size >= ( (level.friends.size / 2) ) )
{
break;
}
guy = level.friends[i];
if( guy != level.sarge )
{
secondChainGroup[secondChainGroup.size] = guy;
}
}
ASSERTEX( secondChainGroup.size > 0, "The second chain group of AIs didn't get set up." );
for( i = 0; i < secondChainGroup.size; i++ )
{
secondChainGroup[i] set_force_color( "p" );
}
// send friendlies into the metro
set_color_chain( "trig_script_color_allies_b24" );
heightGate = -276; // the Z value that player origins have to be lower than for the gate to close
xGate = 1600; // greater than this and it's a fail because we have another metro entrance down the street
// wait for AIs to move through
while( 1 )
{
// make a new array that doesn't include the gate holder, since we know he's behind the gate
nonHolders = [];
for( i = 0; i < level.friends.size; i++ )
{
if( level.friends[i] != holder )
{
nonHolders[nonHolders.size] = level.friends[i];
}
}
aiIsAboveGround = false;
for( i = 0; i < nonHolders.size; i++ )
{
guy = nonHolders[i];
if( guy.origin[2] > heightGate || guy.origin[0] > xGate )
{
// fail
aiIsAboveGround = true;
break;
}
}
if( !aiIsAboveGround )
{
break;
}
else
{
wait( 0.05 );
}
}
playerBugTime = 8;
level.isBuggingPlayer = false;
level.nextGateBug = GetTime() + ( playerBugTime * 1000 );
// wait for players to move through
while( 1 )
{
players = get_players();
playerIsAboveGround = false;
for( i = 0; i < players.size; i++ )
{
player = players[i];
if( player.origin[2] >= heightGate || player.origin[0] > xGate )
{
// fail
playerIsAboveGround = true;
break;
}
}
if( !playerIsAboveGround )
{
break;
}
else
{
if( !level.isBuggingPlayer )
{
if( GetTime() > level.nextGateBug )
{
thread subway_gate_bugplayer();
level.nextGateBug = GetTime() + ( playerBugTime * 1000 );
}
}
wait( 0.05 );
}
}
// move clip back into place
gateclip MoveTo( gateclip.og_origin, 0.05 );
gateclip waittill( "movedone" );
gateclip DisconnectPaths();
// animate gate/beam and AI closing
gate notify( "stop_holding_gate" );
gate thread subway_gate_closeanim();
gate notify( "stop_beam_holding_idle" );
gate thread subway_gate_woodbeam_anim_single( "close" );
gate anim_single_solo( holder, "close" );
set_objective( 6 );
flag_set( "subway_gate_closed" );
clientNotify( "subway_gate_closed" );
// reset guys to pre-anim states
for( i = 0; i < guys.size; i++ )
{
guy = guys[i];
guy.animname = guy.og_animname;
guy PushPlayer( false );
guy.ignoreme = false;
guy.ignoreall = false;
}
// put the holder back on the color chain
holder set_force_color( "b" );
thread battlechatter_on( "allies" );
thread battlechatter_on( "axis" );
// "Split up - cover both platforms."
level.sarge playsound_generic_facial( "Ber2_IGD_080A_REZN" );
}
gate_hold_loop( holder )
{
holder waittillmatch( "single anim", "end" );
// hold gate
self thread anim_loop_solo( holder, "hold", undefined, "stop_holding_gate" );
// wood beam animate
self thread subway_gate_woodbeam_anim_loop( "hold", "stop_beam_holding_idle" );
}
subway_gate_bugplayer()
{
level.isBuggingPlayer = true;
bugLines[0] = "Ber2_IGD_076A_REZN"; // "This way!"
bugLines[1] = "Ber2_IGD_077A_REZN"; // "Get down here!"
bugLines[2] = "Ber2_IGD_078A_REZN"; // "Hurry!"
bugLines[3] = "Ber2_IGD_201A_REZN"; // "Move faster - We have to escape the barrage!"
level.sarge playsound_generic_facial( get_random( bugLines ) );
level.isBuggingPlayer = false;
}
// puts opener back on his color chain once his anim is done
opener_move( anime )
{
animtime = GetAnimLength( level.scr_anim[self.animname][anime] );
wait( animtime - 2 );
self.og_goalradius = self.goalradius;
self.goalradius = 16;
// Alex Liu: The original position chernov was sent to is too deep into the subway
// It resulted in a sharp turn and drop-into-ground effect when blending
// out of the scripted anim. This new node is directly in front of chernov
// and very close. Hopefully he will go straight forward a few steps before
// turning.
intermediate_node = getnode( "chernov_gate_enter_middle_node", "targetname" );
self SetGoalNode( intermediate_node );
self waittill( "goal" );
self.goalradius = self.og_goalradius;
self set_force_color( "b" );
}
// --- END SUBWAY GATE OPENING STUFF ---
// --- AMBIENT STUFF ---
event1_rooftop_rockets()
{
level endon( "subway_gate_closed" );
startSpots = GetStructArray( "struct_e1_rooftop_rockets", "targetname" );
if( !IsDefined( startSpots ) || startSpots.size <= 0 )
{
ASSERTMSG( "can't find any rocket start spots for event1_rooftop_rockets()!" );
return;
}
rocketModel = "katyusha_rocket";
while( 1 )
{
startSpots = array_randomize( startSpots );
for( i = 0; i < startSpots.size; i++ )
{
thread rocket_fake_fire( startSpots[i], rocketModel, 7 );
//Kevin adjusted this to match the _katyusha.gsc salvo speed
wait( RandomFloatRange( 0.2, 0.25 ) );
}
// wait for next flurry
wait( RandomFloatRange( 10, 20 ) );
}
}
// spawns a rocket model at an entity's location and "fires" it off
rocket_fake_fire( startSpot, rocketModel, moveTime, speed )
{
while( !OkTospawn() )
{
wait( 0.1 );
}
rocket = Spawn( "script_model", startSpot.origin );
rocket SetModel( rocketModel );
rocket.angles = startSpot.angles;
rocket playloopsound( "katy_rocket_run" );
thread rocket_move( rocket, moveTime, speed );
}
// actually moves the rocket when it is "fired"
rocket_move( rocket, moveTime, speed )
{
if( !IsDefined( moveTime ) || moveTime <= 0 )
{
moveTime = 5.0;
}
if( !IsDefined( speed ) || speed <= 0 )
{
speed = 3000;
}
// derive velocity
velocity = AnglesToForward( rocket.angles ) * speed;
// start the particle
PlayFxOnTag( level._effect["katyusha_rocket_trail"], rocket, "tag_origin" );
// play launching sound
thread play_sound_in_space( "katyusha_launch", rocket.origin );
// move the rocket
rocket MoveGravity( velocity, moveTime );
// notify that we fired the rocket
rocket notify( "rocket_fired" );
// wait until the rocket is done moving, then delete it
wait( moveTime );
rocket Delete();
}
// --- END AMBIENT STUFF ---
// --- CHAIR ANIMTREE ---
#using_animtree( "ber2_chair" );
telegrapher_chair_anims( guy )
{
self UseAnimTree( #animtree );
tappingAnim = level.scr_anim["telegrapher_chair"]["tapping"];
self thread telegrapher_chair_animloop( guy, tappingAnim );
self waittill( "stop_telegraphing" );
if( IsAlive( guy ) )
{
self SetFlaggedAnimKnob( "death_anim", level.scr_anim["telegrapher_chair"]["tapping_death"], 1.0, 0.2, 1.0 );
self waittillmatch( "death_anim", "end" );
}
else
{
self ClearAnim( tappingAnim, 0 );
}
}
telegrapher_chair_animloop( guy, tappingAnim )
{
self endon( "stop_telegraphing" );
while( IsAlive( guy ) )
{
self SetFlaggedAnimKnob( "idle_anim", tappingAnim, 1.0, 0.2, 1.0 );
self waittillmatch( "idle_anim", "end" );
}
}
// --- GATE ANIMTREE ---
#using_animtree( "ber2_metro_entrance_gate" );
// self = the subway gate
subway_gate_openanim()
{
self UseAnimTree( #animtree );
self SetFlaggedAnimKnob( "metrogate_anim", level.scr_anim["metro_gate_model"]["open"], 1.0, 0.2, 1.0 );
self waittillmatch( "metrogate_anim", "end" );
}
// self = the subway gate
subway_gate_closeanim()
{
self SetFlaggedAnimKnob( "metrogate_anim", level.scr_anim["metro_gate_model"]["close"], 1.0, 0.2, 1.0 );
self waittillmatch( "metrogate_anim", "end" );
}
// --- WOODBEAM ANIMTREE ---
#using_animtree( "ber2_woodbeam" );
// spawns the wooden beam for the subway gate and idles it
subway_gate_woodbeam_init()
{
animRef = "resting";
anime = level.scr_anim["metrogate_woodbeam"][animRef][0];
gate = getent_safe( "subway_entrance_gate", "targetname" );
origin = GetStartOrigin( gate.origin, gate.angles, anime );
angles = GetStartAngles( gate.origin, gate.angles, anime );
beam = Spawn( "script_model", origin );
beam.angles = angles;
beam SetModel( "berlin_wood_beam_short" );
beam UseAnimTree( #animtree );
beam.animname = "metrogate_woodbeam";
level.metrogate_woodbeam = beam;
gate thread subway_gate_woodbeam_anim_loop( animRef, "stop_beam_resting_idle" );
}
subway_gate_woodbeam_anim_single( animRef )
{
self anim_single_solo( level.metrogate_woodbeam, animRef );
level.metrogate_woodbeam waittillmatch( "single anim", "end" );
}
subway_gate_woodbeam_anim_loop( animRef, ender )
{
self thread anim_loop_solo( level.metrogate_woodbeam, animRef, undefined, ender );
}
// --- FLAG ANIMTREE ---
#using_animtree( "ber2_flag" );
street_bank_flag_anims( flag, guy )
{
anime = level.scr_anim[flag.animname]["unfurl"];
guy waittillmatch( "single anim", "start_flag" );
flag Show();
flag.origin = GetStartOrigin( self.origin, self.angles, anime );
flag UseAnimTree( #animtree );
flag SetFlaggedAnimKnob( "unfurl_anim", anime, 1.0, 0.2, 1.0 );
flag waittillmatch( "unfurl_anim", "end" );
while( 1 )
{
flag SetFlaggedAnimKnob( "idle_anim", level.scr_anim[flag.animname]["idle"], 1.0, 0.2, 1.0 );
flag waittillmatch( "idle_anim", "end" );
}
}