IW4-Dump-Files/maps/_slowmo_breach.gsc

3950 lines
129 KiB
Plaintext
Raw Normal View History

2017-07-08 11:47:21 -07:00
#include maps\_utility;
#include common_scripts\utility;
#include maps\_anim;
/*QUAKED trigger_use_breach (0.1 0.3 1.0) STARTOFF HAS_USER
Starts a slow mo breach sequence.
*/
/*QUAKED trigger_multiple_breachIcon (0.1 0.3 1.0) ? AI_AXIS AI_ALLIES AI_NEUTRAL NOTPLAYER VEHICLE TRIGGER_SPAWN TOUCH_ONCE
Targets a script origin. Makes the breach icon appear when you look at a breach door.
*/
/*QUAKED info_volume_breachdoor (0.1 0.3 1.0) ?
Include this volume in a breach prefab (with trigger, door posts, etc). Used to determine if there are any friendlies near the door to assist in muultiple-entry point breaches.
*/
/*QUAKED info_volume_breachroom (0.1 0.3 1.0) ?
Targeted by the breach icon script_origin on the breach door prefab. Can be targeted by multiple script_origins for multiple-entry point breaches. Used to set a flag when the room is cleared (add key "script_flag"). Also used to detect enemy and hostage status within the room.
*/
/*QUAKED info_volume_breachsafearea (0.1 0.3 1.0) ?
Optional. Add script_slomobreach number as a key/value to check to see if there are any enemies in this volume before allowing the breach to be performed.
*/
slowmo_breach_init()
{
level.last_player_damage = 0;
level.slomobreachduration = 3.5; // duration of slomo breach
level.breachEnemies_active = 0;
level.player_one_already_breached = undefined;
level.breachEnemies_alive = 0;
level.has_special_breach_anim = [];
level.breach_passive_time = 0;
//set up dynamic sound channels that will only be partially affected by slomo (Values given by the audio dept)
SoundSetTimeScaleFactor( "Music", 0 );
SoundSetTimeScaleFactor( "Menu", 0 );
SoundSetTimeScaleFactor( "local3", 0.0 );
SoundSetTimeScaleFactor( "Mission", 0.0 );
SoundSetTimeScaleFactor( "Announcer", 0.0 );
SoundSetTimeScaleFactor( "Bulletimpact", .60 );
SoundSetTimeScaleFactor( "Voice", 0.40 );
SoundSetTimeScaleFactor( "effects2", 0.20 );
SoundSetTimeScaleFactor( "local", 0.40 );
SoundSetTimeScaleFactor( "physics", 0.20 );
SoundSetTimeScaleFactor( "ambient", 0.50 );
SoundSetTimeScaleFactor( "auto", 0.50 );
//setDvarIfUninitialized( "breach_weapons", "0" );
SetDvarIfUninitialized( "breach_debug", "0" );
SetDvarIfUninitialized( "breach_requires_friendlies_in_position", "1" );
//set dvar "hostage_missionfail" to "1" in your own script to have hostage deaths trigger a mission failure
SetDvarIfUninitialized( "hostage_missionfail", "0" );
PreCacheItem( "usp_scripted" );
PreCacheShader( "breach_icon" );
PreCacheModel( "weapon_parabolic_knife" );
precacheString( &"SCRIPT_WAYPOINT_BREACH" );
precacheString( &"SCRIPT_PLATFORM_BREACH_ACTIVATE" );
precacheString( &"SCRIPT_BREACH_NEED_PLAYER" );
precacheString( &"SCRIPT_BREACH_NEED_FRIENDLY" );
precacheString( &"SCRIPT_BREACH_TOO_MANY_ENEMIES" );
precacheString( &"SCRIPT_BREACH_ILLEGAL_WEAPON" );
precacheString( &"SCRIPT_BREACH_PARTNER_NOT_READY" );
precacheString( &"SCRIPT_BREACH_YOU_NOT_READY" );
precacheString( &"SCRIPT_MISSIONFAIL_KILLEDHOSTAGE_THROUGH_ENEMY" );
precacheString( &"SCRIPT_MISSIONFAIL_KILLEDHOSTAGE" );
precacheString( &"SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED" );
precacheString( &"SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED_USEMULTIDOOR" );
precacheString( &"SCRIPT_BREACH_RELOADING" );
level._slowmo_functions = [];
level._effect[ "breach_door" ] = LoadFX( "explosions/breach_door" );
level._effect[ "breach_room" ] = LoadFX( "explosions/breach_room" );
level._effect[ "breach_room_residual" ] = LoadFX( "explosions/breach_room_residual" );
level._effect[ "breach_knife_execution" ] = LoadFX( "impacts/flesh_hit_knife" );
script_models();
player_animations();
friendly_animations();
breach_anims();
create_slowmo_breaches_from_entities();
// this trigger will show the breach icon on the door when you look at it
icon_triggers = GetEntArray( "trigger_multiple_breachIcon", "classname" );
array_thread( icon_triggers, ::icon_trigger_setup );
// entities that aren't meant to exist if a slowmo breach will happen
breach_deletables = GetEntArray( "breach_solid_delete", "targetname" );
array_call( breach_deletables, ::ConnectPaths );
array_thread( breach_deletables, ::self_delete );
// entities that aren't meant to exist if a slowmo breach will happen
breach_deletables = GetEntArray( "breach_delete", "targetname" );
array_thread( breach_deletables, ::self_delete );
breach_fx = GetEntArray( "breach_fx", "targetname" );
array_thread( breach_fx, ::breach_fx_setup );
level.has_special_breach_anim[ "aa12" ] = true;
level.has_special_breach_anim[ "aa12_reflex" ] = true;
level.has_special_breach_anim[ "aa12_hb" ] = true;
level.has_special_breach_anim[ "aug_reflex" ] = true;
level.has_special_breach_anim[ "aug_scope" ] = true;
level.has_special_breach_anim[ "barrett" ] = true;
level.has_special_breach_anim[ "beretta" ] = true;
level.has_special_breach_anim[ "beretta393" ] = true;
level.has_special_breach_anim[ "cheytac_silencer" ] = true;
level.has_special_breach_anim[ "fal" ] = true;
level.has_special_breach_anim[ "fal_acog" ] = true;
level.has_special_breach_anim[ "fal_reflex" ] = true;
level.has_special_breach_anim[ "fal_shotgun" ] = true;
level.has_special_breach_anim[ "fal_shotgun_attach" ] = true;
level.has_special_breach_anim[ "famas" ] = true;
level.has_special_breach_anim[ "famas_arctic" ] = true;
level.has_special_breach_anim[ "famas_arctic_eotech" ] = true;
level.has_special_breach_anim[ "famas_arctic_reflex" ] = true;
level.has_special_breach_anim[ "famas_mp2" ] = true;
level.has_special_breach_anim[ "famas_shotgun" ] = true;
level.has_special_breach_anim[ "famas_shotgun_attach" ] = true;
level.has_special_breach_anim[ "famas_woodland" ] = true;
level.has_special_breach_anim[ "famas_woodland_eotech" ] = true;
level.has_special_breach_anim[ "famas_woodland_acog" ] = true;
level.has_special_breach_anim[ "famas_woodland_reflex" ] = true;
level.has_special_breach_anim[ "famas_woodland_shotgun" ] = true;
level.has_special_breach_anim[ "famas_woodland_shotgun_attach" ] = true;
level.has_special_breach_anim[ "fn2000" ] = true;
level.has_special_breach_anim[ "fn2000_acog" ] = true;
level.has_special_breach_anim[ "fn2000_eotech" ] = true;
level.has_special_breach_anim[ "fn2000_reflex" ] = true;
level.has_special_breach_anim[ "fn2000_scope" ] = true;
level.has_special_breach_anim[ "fn2000_shotgun" ] = true;
level.has_special_breach_anim[ "fn2000_shotgun_attach" ] = true;
level.has_special_breach_anim[ "fn2000_silencer" ] = true;
level.has_special_breach_anim[ "fn2000_thermal" ] = true;
level.has_special_breach_anim[ "glock" ] = true;
level.has_special_breach_anim[ "kriss" ] = true;
level.has_special_breach_anim[ "kriss_reflex" ] = true;
level.has_special_breach_anim[ "kriss_eotech" ] = true;
level.has_special_breach_anim[ "kriss_acog_silencer" ] = true;
level.has_special_breach_anim[ "m1014" ] = true;
level.has_special_breach_anim[ "m14_scoped" ] = true;
level.has_special_breach_anim[ "m14_scoped_arctic" ] = true;
level.has_special_breach_anim[ "m14_scoped_ghil" ] = true;
level.has_special_breach_anim[ "m14_scoped_silencer" ] = true;
level.has_special_breach_anim[ "m14_scoped_silencer_woodland" ] = true;
level.has_special_breach_anim[ "m14_scoped_woodland" ] = true;
level.has_special_breach_anim[ "m14ebr" ] = true;
level.has_special_breach_anim[ "m14ebr_thermal" ] = true;
level.has_special_breach_anim[ "m203" ] = true;
level.has_special_breach_anim[ "m16_acog" ] = true;
level.has_special_breach_anim[ "m16_basic" ] = true;
level.has_special_breach_anim[ "m16_grenadier" ] = true;
level.has_special_breach_anim[ "m16_reflex" ] = true;
level.has_special_breach_anim[ "m16_silencer" ] = true;
level.has_special_breach_anim[ "m21_baseasset" ] = true;
level.has_special_breach_anim[ "m21_scoped_arctic_silenced" ] = true;
level.has_special_breach_anim[ "mp5" ] = true;
level.has_special_breach_anim[ "mp5_arctic" ] = true;
level.has_special_breach_anim[ "mp5_arctic_reflex" ] = true;
level.has_special_breach_anim[ "mp5_eotech" ] = true;
level.has_special_breach_anim[ "mp5_reflex" ] = true;
level.has_special_breach_anim[ "mp5_silencer" ] = true;
level.has_special_breach_anim[ "mp5_silencer_reflex" ] = true;
level.has_special_breach_anim[ "ranger" ] = true;
level.has_special_breach_anim[ "striker" ] = true;
level.has_special_breach_anim[ "striker_reflex" ] = true;
level.has_special_breach_anim[ "striker_woodland" ] = true;
level.has_special_breach_anim[ "striker_woodland_reflex" ] = true;
level.has_special_breach_anim[ "tavor_acog" ] = true;
level.has_special_breach_anim[ "tavor_digital_acog" ] = true;
level.has_special_breach_anim[ "tavor_digital_eotech" ] = true;
level.has_special_breach_anim[ "tavor_digital_mars" ] = true;
level.has_special_breach_anim[ "tavor_digital_reflex" ] = true;
level.has_special_breach_anim[ "tavor_eotech" ] = true;
level.has_special_breach_anim[ "tavor_mars" ] = true;
level.has_special_breach_anim[ "tavor_reflex" ] = true;
level.has_special_breach_anim[ "tavor_woodland_acog" ] = true;
level.has_special_breach_anim[ "tavor_woodland_eotech" ] = true;
level.has_special_breach_anim[ "tavor_woodland_mars" ] = true;
level.has_special_breach_anim[ "tavor_woodland_reflex" ] = true;
level.has_special_breach_anim[ "tmp" ] = true;
level.has_special_breach_anim[ "tmp_reflex" ] = true;
level.has_special_breach_anim[ "tmp_silencer" ] = true;
level.has_special_breach_anim[ "ump45" ] = true;
level.has_special_breach_anim[ "ump45_acog" ] = true;
level.has_special_breach_anim[ "ump45_arctic" ] = true;
level.has_special_breach_anim[ "ump45_arctic_acog" ] = true;
level.has_special_breach_anim[ "ump45_arctic_reflex" ] = true;
level.has_special_breach_anim[ "ump45_reflex" ] = true;
level.has_special_breach_anim[ "ump45_silencer" ] = true;
level.has_special_breach_anim[ "ump45_eotech" ] = true;
level.has_special_breach_anim[ "wa2000" ] = true;
level.has_special_breach_anim[ "wa2000_thermal" ] = true;
level.has_special_breach_anim[ "g36c" ] = true;
level.has_special_breach_anim[ "g36c_grenadier" ] = true;
level.has_special_breach_anim[ "gl_g36c" ] = true;
level.has_special_breach_anim[ "m240" ] = true;
level.has_special_breach_anim[ "m240_reflex" ] = true;
level.has_special_breach_anim[ "m240_acog" ] = true;
level.has_special_breach_anim[ "m4_grenadier" ] = true;
level.has_special_breach_anim[ "m4_grunt" ] = true;
level.has_special_breach_anim[ "m4_shotgun" ] = true;
level.has_special_breach_anim[ "m4_shotgun_attach" ] = true;
level.has_special_breach_anim[ "m4_silencer" ] = true;
level.has_special_breach_anim[ "m4_silencer_acog" ] = true;
level.has_special_breach_anim[ "m4m203_acog" ] = true;
level.has_special_breach_anim[ "m4m203_reflex" ] = true;
level.has_special_breach_anim[ "m4m203_reflex_arctic" ] = true;
level.has_special_breach_anim[ "m4m203_silencer" ] = true;
level.has_special_breach_anim[ "m4m203_silencer_reflex" ] = true;
level.has_special_breach_anim[ "m4m203_eotech" ] = true;
level.has_special_breach_anim[ "scar_h" ] = true;
level.has_special_breach_anim[ "scar_h_fgrip" ] = true;
level.has_special_breach_anim[ "scar_h_acog" ] = true;
level.has_special_breach_anim[ "scar_h_grenadier" ] = true;
level.has_special_breach_anim[ "scar_h_reflex" ] = true;
level.has_special_breach_anim[ "scar_h_shotgun" ] = true;
level.has_special_breach_anim[ "scar_h_shotgun_attach" ] = true;
level.has_special_breach_anim[ "scar_h_silencer" ] = true;
level.has_special_breach_anim[ "scar_h_thermal_silencer" ] = true;
level.has_special_breach_anim[ "scar_h_thermal" ] = true;
level.has_special_breach_anim[ "scar_h_m203" ] = true;
level.has_special_breach_anim[ "m203_m4" ] = true;
level.has_special_breach_anim[ "m203_m4_acog" ] = true;
level.has_special_breach_anim[ "m203_m4_eotech" ] = true;
level.has_special_breach_anim[ "m203_m4_reflex" ] = true;
level.has_special_breach_anim[ "m203_m4_silencer" ] = true;
level.has_special_breach_anim[ "m203_m4_silencer_reflex" ] = true;
level.has_special_breach_anim[ "m203_m4_reflex_arctic" ] = true;
level.has_special_breach_anim[ "coltanaconda" ] = true;
level.has_special_breach_anim[ "deserteagle" ] = true;
level.has_special_breach_anim[ "pp2000" ] = true;
level.has_special_breach_anim[ "pp2000_reflex" ] = true;
level.has_special_breach_anim[ "pp2000_silencer" ] = true;
level.has_special_breach_anim[ "pp2000_thermal" ] = true;
level.has_special_breach_anim[ "ak47" ] = true;
level.has_special_breach_anim[ "ak47_acog" ] = true;
level.has_special_breach_anim[ "ak47_arctic" ] = true;
level.has_special_breach_anim[ "ak47_arctic_acog" ] = true;
level.has_special_breach_anim[ "ak47_arctic_eotech" ] = true;
level.has_special_breach_anim[ "ak47_arctic_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_arctic_reflex" ] = true;
level.has_special_breach_anim[ "ak47_desert" ] = true;
level.has_special_breach_anim[ "ak47_desert_acog" ] = true;
level.has_special_breach_anim[ "ak47_desert_eotech" ] = true;
level.has_special_breach_anim[ "ak47_desert_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_desert_reflex" ] = true;
level.has_special_breach_anim[ "ak47_digital" ] = true;
level.has_special_breach_anim[ "ak47_digital_acog" ] = true;
level.has_special_breach_anim[ "ak47_digital_eotech" ] = true;
level.has_special_breach_anim[ "ak47_digital_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_digital_reflex" ] = true;
level.has_special_breach_anim[ "ak47_eotech" ] = true;
level.has_special_breach_anim[ "ak47_fall" ] = true;
level.has_special_breach_anim[ "ak47_fall_acog" ] = true;
level.has_special_breach_anim[ "ak47_fall_eotech" ] = true;
level.has_special_breach_anim[ "ak47_fall_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_fall_reflex" ] = true;
level.has_special_breach_anim[ "ak47_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_reflex" ] = true;
level.has_special_breach_anim[ "ak47_shotgun" ] = true;
level.has_special_breach_anim[ "ak47_shotgun_attach" ] = true;
level.has_special_breach_anim[ "ak47_silencer" ] = true;
level.has_special_breach_anim[ "ak47_thermal" ] = true;
level.has_special_breach_anim[ "ak47_woodland" ] = true;
level.has_special_breach_anim[ "ak47_woodland_acog" ] = true;
level.has_special_breach_anim[ "ak47_woodland_eotech" ] = true;
level.has_special_breach_anim[ "ak47_woodland_grenadier" ] = true;
level.has_special_breach_anim[ "ak47_woodland_reflex" ] = true;
level.has_special_breach_anim[ "gl_ak47" ] = true;
level.has_special_breach_anim[ "gl_ak47_arctic" ] = true;
level.has_special_breach_anim[ "masada" ] = true;
level.has_special_breach_anim[ "masada_acog" ] = true;
level.has_special_breach_anim[ "masada_dcburn_mt_black_off" ] = true;
level.has_special_breach_anim[ "masada_dcburn_mt_black_on" ] = true;
level.has_special_breach_anim[ "masada_digital" ] = true;
level.has_special_breach_anim[ "masada_digital_acog" ] = true;
level.has_special_breach_anim[ "masada_digital_eotech" ] = true;
level.has_special_breach_anim[ "masada_digital_grenadier_eotech" ] = true;
level.has_special_breach_anim[ "gl_masada_digital_eotech" ] = true;
level.has_special_breach_anim[ "masada_digital_reflex" ] = true;
level.has_special_breach_anim[ "masada_eotech" ] = true;
level.has_special_breach_anim[ "masada_grenadier_acog" ] = true;
level.has_special_breach_anim[ "masada_reflex" ] = true;
level.has_special_breach_anim[ "masada_silencer_motion_tracker_off" ] = true;
level.has_special_breach_anim[ "masada_silencer_motion_tracker_on" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_black_off" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_black_on" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_camo_off" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_camo_on" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_dust_off" ] = true;
level.has_special_breach_anim[ "masada_silencer_mt_dust_on" ] = true;
level.has_special_breach_anim[ "uzi" ] = true;
level.has_special_breach_anim[ "uzi_sd" ] = true;
level.has_special_breach_anim[ "uzi_silencer" ] = true;
level.has_special_breach_anim[ "uzi_akimbo" ] = true;
level.has_special_breach_anim[ "p90" ] = true;
level.has_special_breach_anim[ "p90_acog" ] = true;
level.has_special_breach_anim[ "p90_eotech" ] = true;
level.has_special_breach_anim[ "p90_reflex" ] = true;
level.has_special_breach_anim[ "p90_silencer" ] = true;
level.has_special_breach_anim[ "p90_arctic" ] = true;
level.has_special_breach_anim[ "p90_arctic_acog" ] = true;
level.has_special_breach_anim[ "p90_arctic_eotech" ] = true;
level.has_special_breach_anim[ "p90_arctic_reflex" ] = true;
level.has_special_breach_anim[ "rpd" ] = true;
level.has_special_breach_anim[ "rpd_acog" ] = true;
level.has_special_breach_anim[ "rpd_grip" ] = true;
level.has_special_breach_anim[ "rpd_reflex" ] = true;
level.has_special_breach_anim[ "sa80" ] = true;
level.has_special_breach_anim[ "sa80_scope" ] = true;
level.has_special_breach_anim[ "sa80lmg" ] = true;
level.has_special_breach_anim[ "sa80lmg_reflex" ] = true;
level.has_special_breach_anim[ "sa80lmg_scope" ] = true;
level.has_special_breach_anim[ "at4" ] = true;
level.has_special_breach_anim[ "at4_straight" ] = true;
level.has_special_breach_anim[ "model1887" ] = true;
level.has_special_breach_anim[ "usp" ] = true;
level.has_special_breach_anim[ "usp_airport" ] = true;
level.has_special_breach_anim[ "usp_silencer" ] = true;
level.has_special_breach_anim[ "dragunov" ] = true;
level.has_special_breach_anim[ "dragunov_arctic" ] = true;
level.has_special_breach_anim[ "dragunov_desert" ] = true;
level.has_special_breach_anim[ "dragunov_fall" ] = true;
level.has_special_breach_anim[ "dragunov_woodland" ] = true;
level.has_special_breach_anim[ "mg4" ] = true;
level.has_special_breach_anim[ "mg4_acog" ] = true;
level.has_special_breach_anim[ "mg4_arctic" ] = true;
level.has_special_breach_anim[ "mg4_arctic_reflex" ] = true;
level.has_special_breach_anim[ "mg4_arctic_thermal" ] = true;
level.has_special_breach_anim[ "mg4_reflex" ] = true;
level.has_special_breach_anim[ "mg4_thermal" ] = true;
level.has_special_breach_anim[ "spas12" ] = true;
level.has_special_breach_anim[ "spas12_arctic" ] = true;
level.has_special_breach_anim[ "spas12_arctic_eotech" ] = true;
level.has_special_breach_anim[ "spas12_arctic_grip" ] = true;
level.has_special_breach_anim[ "spas12_arctic_heartbeat" ] = true;
level.has_special_breach_anim[ "spas12_arctic_heartbeat_attach" ] = true;
level.has_special_breach_anim[ "spas12_arctic_reflex" ] = true;
level.has_special_breach_anim[ "spas12_eotech" ] = true;
level.has_special_breach_anim[ "spas12_grip" ] = true;
level.has_special_breach_anim[ "spas12_heartbeat" ] = true;
level.has_special_breach_anim[ "spas12_heartbeat_attach" ] = true;
level.has_special_breach_anim[ "spas12_reflex" ] = true;
level.has_special_breach_anim[ "spas12_silencer" ] = true;
flag_init( "breaching_on" );
flag_init( "no_mercy" );
}
check_missing_animation()
{
if ( !isdefined( self.animation ) )
return;
if ( self will_be_manhandled() && self.script_noteworthy == "manhandled" )
{
AssertEx( IsDefined( self.target ), "Manhandled spawner with export " + self.export + " has no target." );
spawner = GetEnt( self.target, "targetname" );
AssertEx( IsDefined( spawner ) && IsSpawner( spawner ), "Manhandled spawner with export " + self.export + " has no spawner." );
level.manhandled_spawners[ self.export ] = spawner;
}
parms = self.script_parameters;
if ( IsDefined( parms ) )
{
level.missing_animation_parameters[ parms ] = true;
}
level.missing_animations[ self.animation ] = true;
}
is_breach_anim_loop_setup( anime, index, animation )
{
if ( !isdefined( level.scr_anim[ "generic" ][ anime ] ) )
return false;
if ( !isdefined( level.scr_anim[ "generic" ][ anime ][ index ] ) )
return false;
// if ( level.scr_anim[ "generic" ][ anime ][ index ] != animation )
// return false;
PrintLn( " level.scr_anim[ \"generic\" ][ \"" + anime + "\" ][ " + index + " ] = %" + animation + ";" );
return true;
}
is_breach_anim_single_setup( anime, animation )
{
if ( !isdefined( level.scr_anim[ "generic" ][ anime ] ) )
return false;
// if ( level.scr_anim[ "generic" ][ anime ] != animation )
// return false;
PrintLn( " level.scr_anim[ \"generic\" ][ \"" + anime + "\" ] = %" + animation + ";" );
return true;
}
dump_missing_anims()
{
if ( !level.missing_animations.size )
return;
PrintLn( "^3Add these lines to the generic_human.atr section of " + level.script + "_anim.gsc:" );
PrintLn( " // _slowmo_breach anims" );
adds = [];
adds[ adds.size ] = "_survives";
adds[ adds.size ] = "_death";
adds[ adds.size ] = "_death2";
adds[ adds.size ] = "_idle";
adds[ adds.size ] = "_manhandled_guarded";
adds[ adds.size ] = "_manhandled";
adds[ adds.size ] = "_manhandled_guarded_idle";
adds[ adds.size ] = "_manhandled_idle";
adds[ adds.size ] = "_manhandled_guarded_prepare_idle";
adds[ adds.size ] = "_manhandled_prepare_idle";
adds[ adds.size ] = "_manhandled_guarded_prepare";
adds[ adds.size ] = "_manhandled_prepare";
// make new versions of the adds that h
add_suffix = [];
foreach ( parm, _ in level.missing_animation_parameters )
{
foreach ( add in adds )
{
add_suffix[ add_suffix.size ] = add + parm;
}
}
adds = array_combine( adds, add_suffix );
foreach ( anime, _ in level.missing_animations )
{
printed = false;
if ( IsDefined( level.scr_stub[ "generic" ][ anime ] ) )
{
if ( IsArray( level.scr_stub[ "generic" ][ anime ] ) )
{
foreach ( index, animation in level.scr_stub[ "generic" ][ anime ] )
{
if ( !is_breach_anim_loop_setup( anime, index, animation ) )
printed = true;
}
}
else
{
if ( !is_breach_anim_single_setup( anime, level.scr_stub[ "generic" ][ anime ] ) )
printed = true;
}
}
foreach ( add in adds )
{
check_anime = anime + add;
if ( !isdefined( level.scr_stub[ "generic" ][ check_anime ] ) )
continue;
if ( IsArray( level.scr_stub[ "generic" ][ check_anime ] ) )
{
foreach ( index, animation in level.scr_stub[ "generic" ][ check_anime ] )
{
if ( !is_breach_anim_loop_setup( check_anime, index, animation ) )
printed = true;
}
}
else
{
if ( !is_breach_anim_single_setup( check_anime, level.scr_stub[ "generic" ][ check_anime ] ) )
printed = true;
}
}
if ( printed )
level.missing_animations[ anime ] = undefined;
}
PrintLn( " " );
//assertEx( !level.missing_animations.size, "Important! See missing slow mo breach anim defines above!" );
level.missing_animation_parameters = undefined;
level.missing_animations = undefined;
}
#using_animtree( "generic_human" );
breach_anims()
{
// these AI are killed by the breach so they have self.skipDeathAnim set.
level.breach_death_anims = [];
//knife props for executions, etc
addNotetrack_attach( "generic", "attach knife right", "weapon_parabolic_knife", "TAG_INHAND" );
addNotetrack_detach( "generic", "detach knife right", "weapon_parabolic_knife", "TAG_INHAND", "breach_react_knife_charge" );
//Friendly hostage takedowns
level.scr_stub[ "generic" ][ "takedown_room2B_soldier" ] = "takedown_room2B_soldier";
level.scr_stub[ "generic" ][ "takedown_room2B_soldier_idle" ][ 0 ] = "takedown_room2B_soldier_idle";
level.scr_stub[ "generic" ][ "takedown_room1Alt_soldier" ] = "takedown_room1Alt_soldier";
level.scr_stub[ "generic" ][ "takedown_room1Alt_soldier_idle" ][ 0 ] = "takedown_room1Alt_soldier_idle";
level.scr_stub[ "generic" ][ "takedown_room2A_soldier" ] = "takedown_room2A_soldier";
level.scr_stub[ "generic" ][ "takedown_room2A_soldier_idle" ][ 0 ] = "takedown_room2A_soldier_end_idle";
level.scr_stub[ "generic" ][ "takedown_room1B_soldier" ] = "takedown_room1B_soldier";
level.scr_stub[ "generic" ][ "takedown_room1B_soldier_idle" ][ 0 ] = "takedown_room1B_soldier_idle";
level.scr_stub[ "generic" ][ "takedown_room1A_soldier" ] = "takedown_room1A_soldier";
level.scr_stub[ "generic" ][ "takedown_room1A_soldier_idle" ][ 0 ] = "takedown_room1A_soldier_idle";
//C4 Rigged Chair
level.scr_stub[ "generic" ][ "hostage_chair_twitch2" ] = "hostage_chair_twitch2";
level.scr_stub[ "generic" ][ "hostage_chair_twitch2_idle" ] [ 0 ] = "hostage_chair_idle";
level.scr_stub[ "generic" ][ "hostage_chair_twitch" ] = "hostage_chair_twitch";
level.scr_stub[ "generic" ][ "hostage_chair_twitch_idle" ] [ 0 ] = "hostage_chair_idle";
add_slowmo_breach_custom_function( "hostage_chair_twitch2", ::_slomo_breach_c4_hostage );
add_slowmo_breach_custom_function( "hostage_chair_twitch", ::_slomo_breach_c4_hostage );
//human shield execution
level.scr_stub[ "generic" ][ "execution_shield_soldier" ] = "execution_shield_soldier";
level.scr_stub[ "generic" ][ "execution_shield_hostage" ] = "execution_shield_hostage";
level.scr_stub[ "generic" ][ "execution_shield_hostage_death" ] = "execution_shield_hostage_death";
level.scr_stub[ "generic" ][ "execution_shield_hostage_survives" ] = "execution_shield_hostage_survives";
level.scr_stub[ "generic" ][ "execution_shield_hostage_idle" ][ 0 ] = "hostage_knees_idle";
add_slowmo_breach_custom_function( "execution_shield_soldier", ::_slomo_breach_executioner_pistol );
add_slowmo_breach_custom_function( "execution_shield_hostage", ::_slomo_breach_executed_guy );
//knife execution #1
level.scr_stub[ "generic" ][ "execution_knife_soldier" ] = "execution_knife_soldier";
level.scr_stub[ "generic" ][ "execution_knife_hostage" ] = "execution_knife_hostage";
level.scr_stub[ "generic" ][ "execution_knife_hostage_death" ] = "execution_knife_hostage_death";
level.scr_stub[ "generic" ][ "execution_knife_hostage_idle" ][ 0 ] = "hostage_knees_idle";
level.scr_stub[ "generic" ][ "execution_knife_hostage_manhandled" ] = "takedown_room2B_hostageA";
level.scr_stub[ "generic" ][ "execution_knife_hostage_manhandled_idle" ][ 0 ] = "takedown_room2B_hostageA_idle";
add_slowmo_breach_custom_function( "execution_knife_hostage", ::_slomo_breach_executed_guy );
add_slowmo_breach_custom_function( "execution_knife_soldier", ::_slomo_breach_executioner_knife );
//knife execution #2
level.scr_stub[ "generic" ][ "execution_knife2_soldier" ] = "execution_knife2_soldier";
level.scr_stub[ "generic" ][ "execution_knife2_hostage" ] = "execution_knife2_hostage";
level.scr_stub[ "generic" ][ "execution_knife2_hostage_death" ] = "execution_knife2_hostage_death";
level.scr_stub[ "generic" ][ "execution_knife2_hostage_idle" ][ 0 ] = "hostage_stand_idle";
add_slowmo_breach_custom_function( "execution_knife2_hostage", ::_slomo_breach_executed_guy );
add_slowmo_breach_custom_function( "execution_knife2_soldier", ::_slomo_breach_executioner_knife );
//enemy headshots hostage on his knees
level.scr_stub[ "generic" ][ "execution_onknees_soldier" ] = "execution_onknees_soldier";
level.scr_stub[ "generic" ][ "execution_onknees_hostage" ] = "execution_onknees_hostage";
level.scr_stub[ "generic" ][ "execution_onknees_hostage_idle" ][ 0 ] = "execution_onknees_hostage_survives";
level.scr_stub[ "generic" ][ "execution_onknees_hostage_death" ] = "execution_onknees_hostage_death";
level.scr_stub[ "generic" ][ "execution_onknees_hostage_manhandled_guarded" ] = "takedown_room1A_hostageB";
level.scr_stub[ "generic" ][ "execution_onknees_hostage_manhandled_guarded_idle" ][ 0 ] = "takedown_room1A_hostageB_idle";
add_slowmo_breach_custom_function( "execution_onknees_soldier", ::_slomo_breach_executioner_pistol );
add_slowmo_breach_custom_function( "execution_onknees_hostage", ::_slomo_breach_executed_guy );
//enemy headshots hostage on his knees (V2)
level.scr_stub[ "generic" ][ "execution_onknees2_soldier" ] = "execution_onknees2_soldier";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage" ] = "execution_onknees2_hostage";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_survives" ] = "execution_onknees2_hostage_survives";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_death" ] = "execution_onknees2_hostage_death";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_manhandled_guarded" ] = "takedown_room2B_hostageB";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_manhandled_guarded_idle" ][ 0 ] = "takedown_room2B_hostageB_idle";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_manhandled_guarded_prepare_idleV2" ][ 0 ] = "takedown_room2A_hostageB_start_idle";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_manhandled_guardedV2" ] = "takedown_room2A_hostageB";
level.scr_stub[ "generic" ][ "execution_onknees2_hostage_manhandled_guarded_idleV2" ][ 0 ] = "takedown_room2A_hostageB_end_idle";
add_slowmo_breach_custom_function( "execution_onknees2_soldier", ::_slomo_breach_executioner_pistol );
add_slowmo_breach_custom_function( "execution_onknees2_hostage", ::_slomo_breach_executed_guy_pushed_to_floor );
//enemy slams hostage against wall
level.scr_stub[ "generic" ][ "execution_slamwall_soldier" ] = "execution_slamwall_soldier";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage" ] = "execution_slamwall_hostage";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_idle" ][ 0 ] = "hostage_stand_idle";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_death" ] = "execution_slamwall_hostage_death";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_manhandled_prepare" ] = "takedown_room2A_hostageA_flee";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_manhandled_prepare_idle" ][ 0 ] = "takedown_room2A_hostageA_hide_idle";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_manhandled" ] = "takedown_room2A_hostageA";
level.scr_stub[ "generic" ][ "execution_slamwall_hostage_manhandled_idle" ][ 0 ] = "takedown_room2A_hostageA_end_idle";
add_slowmo_breach_custom_function( "execution_slamwall_soldier", ::_slomo_breach_executioner_pistol );
add_slowmo_breach_custom_function( "execution_slamwall_hostage", ::_slomo_breach_executed_guy );
//hostage shoulder-rams enemy but gets pushed to the floor and executed
level.scr_stub[ "generic" ][ "execution_fightback_guy1_03" ] = "execution_fightback_guy1_03";
level.scr_stub[ "generic" ][ "execution_fightback_guy2_03" ] = "execution_fightback_guy2_03";
level.scr_stub[ "generic" ][ "execution_fightback_guy2_03_death" ] = "execution_fightback_guy2_03_death";
level.scr_stub[ "generic" ][ "execution_fightback_guy2_03_survives" ] = "execution_fightback_guy2_03_survives";
add_slowmo_breach_custom_function( "execution_fightback_guy1_03", ::_slomo_breach_executioner_pistol );
add_slowmo_breach_custom_function( "execution_fightback_guy2_03", ::_slomo_breach_executed_guy_pushed_to_floor );
//generic enemy surprised
level.scr_stub[ "generic" ][ "patrol_bored_react" ] = "patrol_bored_react_walkstop";
level.scr_stub[ "generic" ][ "exposed_idle_react" ] = "exposed_idle_reactA";
level.scr_stub[ "generic" ][ "chess_surprise" ] = "parabolic_chessgame_surprise_b";
level.scr_stub[ "generic" ][ "breach_chair_reaction_v1" ] = "breach_chair_reaction_v1";
//level.scr_stub[ "generic" ][ "seated_guard_goforgun_1" ] = "seated_guard_goforgun_1";
//level.scr_stub[ "generic" ][ "seated_guard_goforgun_2" ] = "seated_guard_goforgun_2";
level.scr_stub[ "generic" ][ "patrol_bored_react_walkstop" ] = "patrol_bored_react_walkstop";
level.scr_stub[ "generic" ][ "exposed_idle_reactA" ] = "exposed_idle_reactA";
//generic hostage standalone
level.scr_stub[ "generic" ][ "hostage_stand_react_front" ] = "hostage_stand_react_front";
level.scr_stub[ "generic" ][ "hostage_stand_react_front_idle" ][ 0 ] = "hostage_stand_idle";
level.scr_stub[ "generic" ][ "hostage_stand_react_front_manhandled" ] = "takedown_room1Alt_hostage";
level.scr_stub[ "generic" ][ "hostage_stand_react_front_manhandled_idle" ][ 0 ] = "takedown_room1Alt_hostage_idle";
// a few more anims useful for breaching
level.scr_stub[ "generic" ][ "death_explosion_stand_B_v3" ] = "death_explosion_stand_B_v3";
level.scr_stub[ "generic" ][ "breach_stackL_approach" ] = "breach_stackL_approach";
level.scr_stub[ "generic" ][ "react_stand_2_run_R45" ] = "react_stand_2_run_R45";
level.breach_death_anims[ "death_explosion_stand_B_v3" ] = true;
add_slowmo_breach_custom_function( "hostage_stand_react_front", ::_slomo_breach_hostage_react );
level.scr_stub[ "generic" ][ "hostage_stand_fall" ] = "hostage_stand_fall";
level.scr_stub[ "generic" ][ "hostage_stand_fall_idle" ][ 0 ] = "hostage_knees_idle";
level.scr_stub[ "generic" ][ "hostage_stand_fall_idle" ][ 1 ] = "hostage_knees_twitch";
level.scr_stub[ "generic" ][ "hostage_stand_fall_manhandled" ] = "takedown_room1A_hostageA";
level.scr_stub[ "generic" ][ "hostage_stand_fall_manhandled_idle" ][ 0 ] = "takedown_room1A_hostageA_idle";
level.scr_stub[ "generic" ][ "hostage_stand_fall_manhandledV2" ] = "takedown_room1B_hostage";
level.scr_stub[ "generic" ][ "hostage_stand_fall_manhandled_idleV2" ][ 0 ] = "takedown_room1B_hostage_idle";
add_slowmo_breach_custom_function( "hostage_stand_fall", ::_slomo_breach_hostage_react );
//enemy charges at door with a knife
level.scr_stub[ "generic" ][ "breach_react_knife_idle" ] = "breach_react_knife_idle";
level.scr_stub[ "generic" ][ "breach_react_knife_charge" ] = "breach_react_knife_charge";
level.scr_stub[ "generic" ][ "breach_react_knife_charge_death" ] = "death_shotgun_back_v1";
add_slowmo_breach_custom_function( "breach_react_knife_charge", ::_slomo_breach_knife_charger );
//enemies blown back from the door explosive
level.scr_stub[ "generic" ][ "breach_react_blowback_v1" ] = "breach_react_blowback_v1";
level.scr_stub[ "generic" ][ "breach_react_blowback_v2" ] = "breach_react_blowback_v1";
level.scr_stub[ "generic" ][ "breach_react_blowback_v3" ] = "breach_react_blowback_v1";
level.scr_stub[ "generic" ][ "dying_crawl_back" ] = "dying_crawl_back";
add_slowmo_breach_custom_function( "breach_react_blowback_v1", ::_slomo_breach_blowback_guy );
add_slowmo_breach_custom_function( "breach_react_blowback_v2", ::_slomo_breach_blowback_guy );
//enemies jumping behind/across desks
level.scr_stub[ "generic" ][ "breach_react_desk_v1" ] = "breach_react_desk_v1";
level.scr_stub[ "generic" ][ "breach_react_desk_v2" ] = "breach_react_desk_v2";
level.scr_stub[ "generic" ][ "breach_react_desk_v3" ] = "breach_react_desk_v3";
level.scr_stub[ "generic" ][ "breach_react_desk_v4" ] = "breach_react_desk_v4";
level.scr_stub[ "generic" ][ "breach_react_desk_v5" ] = "breach_react_desk_v5";
level.scr_stub[ "generic" ][ "breach_react_desk_v6" ] = "breach_react_desk_v6";
//kicking down desk
level.scr_stub[ "generic" ][ "breach_react_desk_v7" ] = "breach_react_desk_v7";
add_slowmo_breach_custom_function( "breach_react_desk_v7", ::_slomo_breach_desk_guy );
//guy pushing other guy out of the way
level.scr_stub[ "generic" ][ "breach_react_push_guy1" ] = "breach_react_push_guy1";
level.scr_stub[ "generic" ][ "breach_react_push_guy2" ] = "breach_react_push_guy2";
//2 enemies tossing guns to one another
level.scr_stub[ "generic" ][ "breach_react_guntoss_v1_guy1" ] = "breach_react_guntoss_v1_guy1";
level.scr_stub[ "generic" ][ "breach_react_guntoss_v1_guy2" ] = "breach_react_guntoss_v1_guy2";
level.scr_stub[ "generic" ][ "breach_react_guntoss_v2_guy1" ] = "breach_react_guntoss_v2_guy1";
level.scr_stub[ "generic" ][ "breach_react_guntoss_v2_guy2" ] = "breach_react_guntoss_v2_guy2";
//enemy hiding behind the chair
level.scr_stub[ "generic" ][ "breach_chair_hide_reaction_v1" ] = "breach_chair_hide_reaction_v1";
level.scr_stub[ "generic" ][ "breach_chair_hide_reaction_v1_death" ] = "covercrouch_death_1";
level.scr_stub[ "generic" ][ "breach_chair_hide_reaction_v1_death2" ] = "covercrouch_death_2";
level.scr_stub[ "generic" ][ "breach_chair_hide_reaction_v2" ] = "breach_chair_hide_reaction_v2";
level.scr_stub[ "generic" ][ "breach_chair_hide_reaction_v2_death" ] = "breach_chair_hide_reaction_death_v2";
add_slowmo_breach_custom_function( "breach_chair_hide_reaction_v1", ::_slomo_breach_chair_guy_normal );
add_slowmo_breach_custom_function( "breach_chair_hide_reaction_v2", ::_slomo_breach_chair_guy_animated );
//other hostage fighback animations (too slow and clunky...)
//level.scr_stub[ "generic" ][ "execution_fightback_guy1_01" ] = "execution_fightback_guy1_01";
//level.scr_stub[ "generic" ][ "execution_fightback_guy2_01" ] = "execution_fightback_guy2_01";
level.scr_stub[ "generic" ][ "execution_fightback_guy1_02" ] = "execution_fightback_guy1_02";
level.scr_stub[ "generic" ][ "execution_fightback_guy2_02" ] = "execution_fightback_guy2_02";
//XXX TODO - Need to implement
//level.scr_stub[ "generic" ][ "execution_fightback_7" ] = "execution_fightback_7";
//level.scr_stub[ "generic" ][ "execution_slam_2" ] = "execution_slam_2";
//level.scr_stub[ "generic" ][ "execution_shield_12" ] = "execution_shield_12";
//level.scr_stub[ "generic" ][ "execution_knife_3" ] = "execution_knife_3";
//level.scr_stub[ "generic" ][ "execution_knife_4" ] = "execution_knife_4";
//level.scr_stub[ "generic" ][ "execution_hostage_idle_1" ] = "execution_hostage_idle_1";
// fallback anims
level.scr_anim[ "generic" ][ "hostage_knees_loop" ][ 0 ] = %hostage_knees_idle;
level.scr_anim[ "generic" ][ "hostage_knees_loop" ][ 1 ] = %hostage_knees_twitch;
}
friendly_animations()
{
level.scr_anim[ "generic" ][ "breach_friend_idle_01" ][ 0 ] = %breach_flash_R1_idle;
level.scr_anim[ "generic" ][ "breach_friend_enter_01" ] = %breach_flash_R1_enter;
level.scr_anim[ "generic" ][ "breach_friend_idle_02" ][ 0 ] = %breach_flash_R2_idle;
level.scr_anim[ "generic" ][ "breach_friend_enter_02" ] = %breach_flash_R2_enter;
}
breach_fx_setup()
{
AssertEx( IsDefined( self.script_fxid ), "Breach_fx at " + self.origin + " has no script_fxid" );
AssertEx( IsDefined( self.script_slowmo_breach ), "Breach_fx at " + self.origin + " has no script_slowmo_breach" );
fxid = self.script_fxid;
index = self.script_slowmo_breach;
ent = createExploder( fxid );
ent.v[ "origin" ] = self.origin;
ent.v[ "angles" ] = self.angles;
ent.v[ "fxid" ] = fxid;
ent.v[ "delay" ] = 0;
ent.v[ "exploder" ] = "breach_" + index;
ent.v[ "soundalias" ] = "nil";
}
create_slowmo_breaches_from_entities()
{
// construct the slow mo breach scenes from breach entities in the level
breaches = [];
left_door_posts = GetEntArray( "breach_left_org", "targetname" );
right_door_posts = GetEntArray( "breach_right_org", "targetname" );
breach_enemy_spawners = GetEntArray( "breach_enemy_spawner", "targetname" );
breach_hostage_spawners = GetEntArray( "breach_hostage_spawner", "targetname" );
breach_friendlyenemy_spawners = GetEntArray( "breach_friendlyenemy_spawner", "targetname" );
breach_friendlyhostage_spawners = GetEntArray( "breach_friendlyhostage_spawner", "targetname" );
breach_coopenemy_spawners = GetEntArray( "breach_coopenemy_spawner", "targetname" ); // optional enemies for multi - door coop breaches for a door that neither player breaches
breach_coophostage_spawners = GetEntArray( "breach_coophostage_spawner", "targetname" ); // optional hostages for multi - door coop breaches for a door that neither player breaches
breach_path_solids = GetEntArray( "breach_solid", "targetname" );
breach_door_volumes = GetEntArray( "breach_door_volume", "targetname" );
breach_safe_volumes = GetEntArray( "breach_safe_volume", "targetname" );
breach_triggers = GetEntArray( "trigger_use_breach", "classname" );
breach_lookat_triggers = GetEntArray( "trigger_multiple_breachIcon", "classname" );
breach_start_triggers = GetEntArray( "trigger_use_breach", "classname" );
breach_damage_trigger = GetEntArray( "breach_damage_trigger", "targetname" );
/#
level.missing_animations = [];
level.missing_animation_parameters = [];
level.manhandled_spawners = [];
array_thread( breach_enemy_spawners, ::check_missing_animation );
array_thread( breach_hostage_spawners, ::check_missing_animation );
array_thread( breach_friendlyenemy_spawners, ::check_missing_animation );
array_thread( breach_friendlyhostage_spawners, ::check_missing_animation );
array_thread( breach_coopenemy_spawners, ::check_missing_animation );
array_thread( breach_coophostage_spawners, ::check_missing_animation );
array_thread( level.manhandled_spawners, ::check_missing_animation );
dump_missing_anims();
#/
level.scr_stub = undefined;
foreach ( post in left_door_posts )
{
index = post.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach door post at " + post.origin + " had no script_slowmo_breach" );
AssertEx( !isdefined( breaches[ index ] ), "Breach door post at " + post.origin + " used script_slowmo_breach " + index + " which is already in use elsewhere in the level. Each breach must have its own script_slowmo_breach." );
// set up door type - default is "wood"
doorType = "wood";
if ( IsDefined( post.script_slowmo_breach_doortype ) )
{
switch( post.script_slowmo_breach_doortype )
{
case "wood":
case "estate_wood":
case "estate_wood_backwards":
case "caves_wood":
case "metal":
case "none":
doorType = post.script_slowmo_breach_doortype;
break;
default:
AssertEx( "Illegal slowmo breach doortype " + post.script_slowmo_breach_doortype );
}
}
ent = SpawnStruct();
ent.left_post = post;
ent.doorType = doorType;
ent.spawners = [];
ent.spawners[ "enemy" ] = [];
ent.spawners[ "hostage" ] = [];
ent.spawners[ "friendlyenemy" ] = [];
ent.spawners[ "friendlyhostage" ] = [];
ent.spawners[ "coopenemy" ] = []; // optional enemies to set up at a door that is not breached by either coop player
ent.spawners[ "coophostage" ] = []; // optional hostages to set up at a door that is not breached by either coop player
ent.lookat_triggers = [];
ent.path_solids = [];
ent.enabled = true;
ent.door_volume = [];
ent.room_volume = [];
ent.safe_volume = undefined; // Optional. Used to check if there are any live enemies in the area before allowing breach
ent.friendly_anim_ent = []; // used for positioning breaching friendlies on multiple - door rooms
breaches[ index ] = ent;
}
foreach ( post in right_door_posts )
{
index = post.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach door post at " + post.origin + " had no script_slowmo_breach" );
AssertEx( IsDefined( breaches[ index ] ), "Breach door post at " + post.origin + " used script_slowmo_breach " + index + " but there is no corrosponding left_door_post with that script_slowmo_breach." );
breaches[ index ].right_post = post;
//create animation entity for friendly breaches from right door post
anim_org = Spawn( "script_origin", post.origin );
anim_org.angles = post.angles;
// translate the anim_ent into the proper position for the animations
ent = SpawnStruct();
ent.entity = anim_org;
ent.yaw = -90;
ent translate_local();
breaches[ index ].friendly_anim_ent = anim_org;
}
foreach ( spawner in breach_enemy_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "enemy" );
}
foreach ( spawner in breach_hostage_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "hostage" );
}
foreach ( spawner in breach_friendlyenemy_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "friendlyenemy" );
}
foreach ( spawner in breach_friendlyhostage_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "friendlyhostage" );
}
foreach ( spawner in breach_coopenemy_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "coopenemy" );
}
foreach ( spawner in breach_coophostage_spawners )
{
breaches = spawner breach_spawner_setup( breaches, "coophostage" );
}
AssertEx( breach_lookat_triggers.size, "No breach lookat trigger!" );
foreach ( trigger in breach_lookat_triggers )
{
index = trigger.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach lookat trigger at " + trigger.origin + " had no script_slowmo_breach" );
breaches[ index ].lookat_triggers[ breaches[ index ].lookat_triggers.size ] = trigger;
AssertEx( breaches[ index ].lookat_triggers.size < 2, "You have more than one trigger_multiple_breachIcon associated with script_slowmo_breach number " + index + ". Only one lookat trigger is allowed per breach door" );
//get the script origin targeted by lookat trig
trigger_org = GetEnt( trigger.target, "targetname" );
trigger.breach_origin = trigger_org.origin;
AssertEx( IsDefined( trigger_org ), "Breach_lookat_trigger at " + trigger.origin + " needs to target a script_origin to be used for the breach icon." );
//get the room volume targeted by the look_at trigger's script_origin
room_volume = GetEnt( trigger_org.target, "targetname" );
AssertEx( IsDefined( room_volume ), "The script origin targeted by the breach_lookat_trigger at " + trigger_org.origin + " needs to target an info_volume that encompasses the room being breached." );
room_volume.breached = false;
breaches[ index ].room_volume = room_volume;
//Show icon on door origin
trigger_org thread breach_icon_think( trigger, index, room_volume );
//initialize a flag for designer use (set when this room volume is breached and cleared)
sFlagname = room_volume.script_flag;
AssertEx( IsDefined( sFlagname ), "Breach room volume at " + room_volume.origin + " needs to have a unique script_flag key to set a flag once it has been breached and cleared." );
flag_init( sFlagname );
}
foreach ( trigger in breach_start_triggers )
{
trigger UseTriggerRequireLookAt();
index = trigger.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach trigger at " + trigger.origin + " had no script_slowmo_breach" );
breaches[ index ].trigger = trigger;
if ( IsDefined( trigger.script_breachgroup ) )
trigger thread breach_group_trigger_think();
}
foreach ( volume in breach_door_volumes )
{
index = volume.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach volume at " + volume.origin + " had no script_slowmo_breach" );
breaches[ index ].door_volume = volume;
}
foreach ( volume in breach_safe_volumes )
{
index = volume.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach safe volume at " + volume.origin + " had no script_slowmo_breach" );
breaches[ index ].safe_volume = volume;
}
foreach ( pathSolid in breach_path_solids )
{
index = pathSolid.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach path solid at " + pathSolid.origin + " had no script_slowmo_breach" );
breaches[ index ].path_solids[ breaches[ index ].path_solids.size ] = pathSolid;
}
foreach ( index, breach in breaches )
{
level thread slowmo_breach_think( breach, index );
}
foreach ( trigger in breach_damage_trigger )
{
index = trigger.script_slowmo_breach;
Assert( IsDefined( index ) );
trigger thread slowmo_breach_damage_trigger_think( index );
}
level.breach_groups = breaches;
}
/*
=============
///ScriptDocBegin
"Name: objective_breach( <obj> , <breach_index1> , <breach_index2> , <breach_index3> , <breach_index4> )"
"Summary: Adds objective positions at the correct location and name for breaching"
"Module: SlowMo Breach"
"MandatoryArg: <obj>: The index for the objective"
"OptionalArg: <breach_index1>: A breach to show the position for"
"OptionalArg: <breach_index2>: A breach to show the position for"
"OptionalArg: <breach_index3>: A breach to show the position for"
"OptionalArg: <breach_index4>: A breach to show the position for"
"Example: objective_breach( 2, breach_indices[ 0 ], breach_indices[ 1 ], breach_indices[ 2 ], breach_indices[ 3 ] );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
objective_breach( obj, breach_index1, breach_index2, breach_index3, breach_index4 )
{
Objective_SetPointerTextOverride( obj, &"SCRIPT_WAYPOINT_BREACH" );
objective_position( obj, ( 0, 0, 0 ) );
breaches = [];
if ( isdefined( breach_index1 ) )
{
breaches[ breaches.size ] = breach_index1;
}
if ( isdefined( breach_index2 ) )
{
breaches[ breaches.size ] = breach_index2;
}
if ( isdefined( breach_index3 ) )
{
breaches[ breaches.size ] = breach_index3;
}
if ( isdefined( breach_index4 ) )
{
breaches[ breaches.size ] = breach_index4;
}
count = 0;
foreach ( breach_index in breaches )
{
assertex( isdefined( level.breach_groups[ breach_index ] ), "No breach group " + breach_index );
ent = level.breach_groups[ breach_index ].left_post;
if ( !isdefined( ent ) )
return;
right = anglestoright( ent.angles );
origin = ent.origin + right * -22.5 + (0,0,56);
objective_additionalposition( obj, count, origin );
count++;
}
}
assign_script_breachgroup_to_ents( breach_ents )
{
foreach ( ent in breach_ents )
{
found_breach = false;
foreach ( breach_index, breach_array in level.breach_groups )
{
foreach ( trigger in breach_array.lookat_triggers )
{
// close enough?
if ( distance( ent.origin, trigger.breach_origin ) > 80 )
continue;
ent.script_slowmo_breach = breach_index;
found_breach = true;
break;
}
if ( found_breach )
break;
}
assertex( found_breach, "Didnt find breach for " + ent.targetname + " at " + ent.origin );
}
}
get_breach_indices_from_ents( interiorBreaches )
{
breach_indices = [];
foreach ( breach in interiorBreaches )
{
breach_indices[ breach_indices.size ] = breach.script_slowmo_breach;
}
return breach_indices;
}
slowmo_breach_damage_trigger_think( index )
{
AssertEx( self.classname == "trigger_radius", "Breach damage trigger at " + self.origin + " needs to be a trigger_radius in order to work with slomo_breach script." );
level waittill( "breaching_number_" + index );
wait( 3 );
ai = GetAIArray();
ai = array_merge( ai, level.players );
foreach ( guy in ai )
{
if ( guy IsTouching( self ) )
return;
}
RadiusDamage( self.origin, self.radius, 500, 500 );
self Delete();
}
icon_trigger_setup()
{
// set the flag and then run the trigger_looking logic on it
self.script_flag = "breach_door_icon_" + self.script_slowmo_breach;
level thread maps\_load::trigger_looking( self );
}
breach_icon_think( trigger, index, room_volume )
{
//self ==> the script_origin in the door targeted by the lookat trigger
self thread breach_debug_display_animnames( room_volume );
icon = NewHudElem();
icon SetShader( "breach_icon", 1, 1 );
icon.alpha = 0;
icon.color = ( 1, 1, 1 );
icon.x = self.origin[ 0 ];
icon.y = self.origin[ 1 ];
icon.z = self.origin[ 2 ];
icon SetWayPoint( true, true );
// spawn a hidden charge model so it gets streamed in early
model = Spawn( "script_model", self.origin );
model SetModel( "mil_frame_charge" );
model HidePart( "j_frame_charge" );
wait( 0.05 );
sFlag = "breach_door_icon_" + index;
flag_wait( sFlag );
while ( !room_volume.breached )
{
/*
// using objective marker for breaches now
wait( 0.05 );
icon FadeOverTime( 0.2 );
while ( flag( sFlag ) )
{
icon.alpha = 0.3;
wait( 0.05 );
}
icon FadeOverTime( 0.2 );
icon.alpha = 0;
*/
wait( 0.05 );
}
icon Destroy();
model Delete();
}
breach_spawner_setup( breaches, type )
{
index = self.script_slowmo_breach;
AssertEx( IsDefined( index ), "Breach spawner at " + self.origin + " had no script_slowmo_breach" );
group = 0;
if ( IsDefined( self.script_slowmo_breach_spawners ) )
{
if ( ( type == "enemy" ) || ( type == "hostage" ) ) // randomized decisions of which .script_slowmo_breach_spawners group only is supported for anemies / hostages in the door breached by the player...not by frienlies or other atrrangements
group = self.script_slowmo_breach_spawners;
}
if ( !isdefined( breaches[ index ].spawners[ type ][ group ] ) )
breaches[ index ].spawners[ type ][ group ] = [];
array = breaches[ index ].spawners[ type ][ group ];
array[ array.size ] = self;
breaches[ index ].spawners[ type ][ group ] = array;
if ( breaches.size )
return breaches;
else
return undefined;
}
slowmo_breach_think( breach_array, breach_index )
{
// The left door post is the root of the animated elements (that aren't fx related).
left_door_post = breach_array.left_post;
right_door_post = breach_array.right_post;
breach_enemy_spawners = breach_array.spawners[ "enemy" ];
breach_hostage_spawners = breach_array.spawners[ "hostage" ];
trigger = breach_array.trigger;
solids = breach_array.path_solids;
door_volume = breach_array.door_volume;
room_volume = breach_array.room_volume;
breach_friendlyenemy_spawners = [];
breach_friendlyhostage_spawners = [];
breach_coopenemy_spawners = [];
breach_coophostage_spawners = [];
// I guess this was put in here to mitigate the error of LDs placing "script_slowmo_breach_spawners" numbers by mistake on groups of AI for friendlies (?) JK
if ( IsDefined( breach_array.spawners[ "friendlyenemy" ][ 0 ] ) )
{
breach_friendlyenemy_spawners = breach_array.spawners[ "friendlyenemy" ][ 0 ];
breach_array.spawners[ "friendlyenemy" ] = breach_friendlyenemy_spawners;
}
if ( IsDefined( breach_array.spawners[ "friendlyhostage" ][ 0 ] ) )
{
breach_friendlyhostage_spawners = breach_array.spawners[ "friendlyhostage" ][ 0 ];
breach_array.spawners[ "friendlyhostage" ] = breach_friendlyhostage_spawners;
}
if ( IsDefined( breach_array.spawners[ "coopenemy" ][ 0 ] ) )
{
breach_coopenemy_spawners = breach_array.spawners[ "coopenemy" ][ 0 ];
breach_array.spawners[ "coopenemy" ] = breach_coopenemy_spawners;
}
if ( IsDefined( breach_array.spawners[ "coophostage" ][ 0 ] ) )
{
breach_coophostage_spawners = breach_array.spawners[ "coophostage" ][ 0 ];
breach_array.spawners[ "coophostage" ] = breach_coophostage_spawners;
}
// translate the posts into the proper positions for the animations
ent = SpawnStruct();
ent.entity = left_door_post;
ent.forward = 5;
ent.right = 6;
ent.yaw = -90;
ent translate_local();
ent = SpawnStruct();
ent.entity = right_door_post;
ent.right = -2;
ent.yaw = 90;
ent translate_local();
// hostages = GetEntArray( "hostage_delete", "script_noteworthy" );
// array_thread( hostages, maps\_utility::self_delete );
// remove all but one random subset of the spawners
keys = [];
foreach ( index, spawner in breach_enemy_spawners )
{
keys[ index ] = index;
}
foreach ( index, spawner in breach_hostage_spawners )
{
keys[ index ] = index;
}
//AssertEx( keys.size, "Slowmo breach set up with no spawners!" );
if ( keys.size )
{
random_key = random( keys );
if ( IsDefined( breach_enemy_spawners[ random_key ] ) )
breach_enemy_spawners = breach_enemy_spawners[ random_key ];
else
breach_enemy_spawners = [];
if ( IsDefined( breach_hostage_spawners[ random_key ] ) )
breach_hostage_spawners = breach_hostage_spawners[ random_key ];
else
breach_hostage_spawners = [];
}
breach_array.spawners[ "enemy" ] = breach_enemy_spawners;
breach_array.spawners[ "hostage" ] = breach_hostage_spawners;
//spawner callback functions for enemies and hostages
array_thread( breach_enemy_spawners, ::add_spawn_function, ::breach_enemy_spawner_think );
array_thread( breach_hostage_spawners, ::add_spawn_function, ::breach_hostage_spawner_think );
array_thread( breach_friendlyenemy_spawners, ::add_spawn_function, ::breach_enemy_spawner_think );
array_thread( breach_friendlyhostage_spawners, ::add_spawn_function, ::breach_hostage_spawner_think );
array_thread( breach_coopenemy_spawners, ::add_spawn_function, ::breach_enemy_spawner_think );
array_thread( breach_coophostage_spawners, ::add_spawn_function, ::breach_hostage_spawner_think );
// Hold &&1 to breach.
trigger SetHintString( &"SCRIPT_PLATFORM_BREACH_ACTIVATE" );
if ( !isdefined( level.breach_use_triggers ) )
level.breach_use_triggers = [];
level.breach_use_triggers = array_add( level.breach_use_triggers, trigger );
// pick door type
doorAnimModel = undefined;
switch( breach_array.doorType )
{
case "metal":
doorAnimModel = "breach_door_model_metal";
break;
case "estate_wood":
doorAnimModel = "breach_door_model_estate";
break;
case "estate_wood_backwards":
doorAnimModel = "breach_door_model_estate";
break;
case "caves_wood":
doorAnimModel = "breach_door_model_caves";
break;
case "wood":
case "none":
doorAnimModel = "breach_door_model";
break;
default:
AssertEx( "Illegal slowmo breach doortype " + breach_array.doorType );
break;
}
door = spawn_anim_model( doorAnimModel );
Assert( IsDefined( door ) );
if ( breach_array.doorType == "none" || breach_array.doorType == "estate_wood_backwards" )
door Hide();
level.breach_doors = [];
level.breach_doors[ breach_index ] = door;
charge = spawn_anim_model( "breach_door_charge" );
Assert( IsDefined( charge ) );
charge hide_notsolid();
left_door_post.scene_models = [];
left_door_post add_scene_model( "active_breacher_rig" );
active_breacher_rig = left_door_post.scene_models[ "active_breacher_rig" ];
Assert( IsDefined( active_breacher_rig ) );
AssertEx( IsSubStr( active_breacher_rig.model, "viewhands_player" ), "View hands must be player world hands, not some random model." );
active_breacher_rig Hide();
passive_breacher_rig = undefined;
if ( is_coop() )
{
left_door_post add_coop_scene_models();
passive_breacher_rig = left_door_post.scene_models[ "passive_breacher_rig" ];// only exists in coop
Assert( IsDefined( passive_breacher_rig ) );
AssertEx( IsSubStr( passive_breacher_rig.model, "viewhands_player" ), "View hands must be player world hands, not some random model." );
passive_breacher_rig Hide();
}
left_door_post.door = door;
left_door_post.charge = charge;
left_door_post.post = right_door_post;
left_door_post.breach_index = breach_index;
left_door_post anim_first_frame_solo( door, "breach" );
left_door_post anim_first_frame_solo( charge, "breach" );
left_door_post anim_first_frame_solo( active_breacher_rig, "breach_player_anim" );
if ( is_coop() )
left_door_post anim_first_frame_solo( passive_breacher_rig, "breach_player_anim" );
left_door_post wait_for_breach_or_deletion( breach_array );
foreach ( model in left_door_post.scene_models )
model Delete();
if ( IsDefined( trigger ) )
trigger Delete();
if ( IsDefined( door ) )
door Delete();
if ( IsDefined( charge ) )
charge Delete();
if ( IsDefined( left_door_post ) )
left_door_post Delete();
if ( IsDefined( right_door_post ) )
right_door_post Delete();
}
breach_should_be_skipped( script_slowmo_breach )
{
if ( !isdefined( level.skip_breach ) )
return false;
if ( !isdefined( level.skip_breach[ script_slowmo_breach ] ) )
return false;
return true;
}
coop_player_touching_valid_door_volume( door_volume, other_player )
{
door_volumes = [];
door_volumes[ 0 ] = door_volume;
if ( IsDefined( door_volume.script_breachgroup ) )
{
door_volumes = get_door_volumes_from_breachgroup( door_volume.script_breachgroup );
}
foreach ( volume in door_volumes )
{
if ( other_player IsTouching( volume ) )
return true;
}
return false;
}
get_door_volumes_from_breachgroup( breachgroup )
{
aArray = [];
aDoorVolumes = GetEntArray( "breach_door_volume", "targetname" );
foreach ( volume in aDoorVolumes )
{
if ( ( IsDefined( volume.script_breachgroup ) ) && ( volume.script_breachgroup == breachgroup ) )
{
aArray[ aArray.size ] = volume;
}
}
return aArray;
}
breach_participants_ready_to_proceed( player, breach_friendlies, door_volume )
{
// Check for player proximity in co-op, no AI should be involved.
if ( is_coop() )
{
/* This might need to be revisited. Currently the script doesn't support players at two
breach points, thus being two active breachers. If we need it, we'll revisit the code
but until then simply requiring everyone to be in the same breach volume in co-op.
if ( !isdefined( breach_near_player( get_other_player( player ) ) ) )
return false; */
other_player = get_other_player( player );
if ( IsDefined( other_player.coop_downed ) && ( other_player.coop_downed ) )
return false;
if ( coop_player_touching_valid_door_volume( door_volume, other_player ) )
return true;
else
return false;
}
// Do we need to care about AI friendlies at all?
if ( breach_friendlies.size == 0 )
return true;
if ( !room_has_multiple_doors( door_volume ) )
return true;
// Check if friendlies are ready to breach...
if ( !breach_friendlies_ready_at_other_door( door_volume, true ) )
{
if ( GetDvar( "breach_requires_friendlies_in_position" ) == "1" )
{
if ( !breachfriendlies_can_teleport( breach_friendlies, door_volume ) )
return false;
}
}
return true;
}
wait_for_breach_or_deletion( ent )
{
trigger = ent.trigger;
if ( !isdefined( trigger ) )
{
return;// the breach was deleted
}
door_volume = ent.door_volume;
trigger endon( "death" );
for ( ;; )
{
trigger waittill( "trigger", other, passive );
if ( gettime() == level.breach_passive_time )
passive = level.breach_passive_player;
is_passive = isdefined( passive );
if ( !ent.enabled )
return;
if ( isalive( other ) && !is_passive )
{
if ( breach_failed_to_start() )
continue;
}
/*----------------------------------------------
MAKE SURE THERE ARE NO ENEMIES, IF WE CARE
------------------------------------------------*/
if ( ( IsDefined( ent.safe_volume ) ) && ( !is_specialop() ) )
{
if ( IsPlayer( other ) && IsAlive( other ) )
{
enemies = ent.safe_volume get_ai_touching_volume( "axis" );
if ( enemies.size )
{
thread breach_too_many_enemies_hint();
continue;
}
}
}
/*----------------------------------------------
TRIGGER THE PLAYER BREACH, FRIENDLY BREACH, OR JUST OPEN THE DOOR
------------------------------------------------*/
breach_friendlies = get_available_breachfriendlies( door_volume );
if ( IsPlayer( other ) && IsAlive( other ) )
{
if ( breach_should_be_skipped( trigger.script_slowmo_breach ) )
break;
if ( breach_participants_ready_to_proceed( other, breach_friendlies, door_volume ) )
{
if ( player_breach( ent, other ) )
break;
}
else
{
thread breach_friendly_hint();
}
}
else
if ( breach_friendlies.size )
{
friendlies_breach( ent, breach_friendlies );
break;
}
else
{
breachless_door_opens( ent );
break;
}
}
}
breachfriendlies_can_teleport( breachFriendlies, door_volume )
{
numGuysReady = 0;
player_volume = get_player_volume( door_volume );
foreach ( guy in breachFriendlies )
{
if ( IsDefined( player_volume ) )
{
if ( guy goalpos_within_volume( player_volume ) )
{
continue;
}
}
if ( !guy friendly_can_teleport() )
{
continue;
}
numGuysReady++;
}
if ( numGuysReady >= 2 )
{
return true;
}
return false;
}
friendly_can_teleport()
{
if ( player_can_see_ai( self ) )
{
return false;
}
if ( Distance( level.player.origin, self.origin ) < 96 )
{
return false;
}
return true;
}
breach_friendly_hint()
{
if ( is_coop() )
{
// Both players must be near a door to breach
thread breach_hint_create( &"SCRIPT_BREACH_NEED_PLAYER" );
}
else
{
// Waiting for friendlies to get in position...
thread breach_hint_create( &"SCRIPT_BREACH_NEED_FRIENDLY" );
}
}
breach_too_many_enemies_hint()
{
// Cannot breach. Clear the area of all enemies
thread breach_hint_create( &"SCRIPT_BREACH_TOO_MANY_ENEMIES" );
}
breach_reloading_hint()
{
// Cannot breach while reloading
thread breach_hint_create( &"SCRIPT_BREACH_RELOADING" );
}
breach_bad_weapon_hint()
{
// Cannot breach with this weapon
thread breach_hint_create( &"SCRIPT_BREACH_ILLEGAL_WEAPON" );
}
breach_not_ready_hint()
{
if ( is_coop() )
{
// Both players are not ready to breach
thread breach_hint_create( &"SCRIPT_BREACH_PARTNER_NOT_READY" );
return;
}
// You are not ready to breach
thread breach_hint_create( &"SCRIPT_BREACH_YOU_NOT_READY" );
}
breach_hint_create( message )
{
level notify( "breach_hint_cleanup" );
level endon( "breach_hint_cleanup" );
hint_offset = 20;
if ( issplitscreen() )
hint_offset = -23;
thread hint( message, 3, hint_offset );
thread breach_hint_cleanup();
}
breach_hint_cleanup()
{
level notify( "breach_hint_cleanup" );
level endon( "breach_hint_cleanup" );
foreach ( trigger in level.breach_use_triggers )
{
if ( isdefined( trigger ) )
trigger SetHintString( "" );
}
level waittill_notify_or_timeout( "breaching", 3 );
hint_fade();
foreach ( trigger in level.breach_use_triggers )
{
if ( isdefined( trigger ) )
trigger SetHintString( &"SCRIPT_PLATFORM_BREACH_ACTIVATE" );
}
}
room_has_multiple_doors( door_volume )
{
if ( IsDefined( door_volume.script_breachgroup ) )
return true;
return false;
}
breach_friendlies_take_grenades()
{
if ( !isdefined( level.breachfriendlies ) )
return;
// This ensures that the friendlies were actually a part of *this* breach action later.
level.breachfriendlies_grenades_empty = true;
foreach ( guy in level.breachfriendlies )
{
guy.grenadeammo_prebreach = guy.grenadeammo;
guy.grenadeammo = 0;
}
}
breach_friendlies_restore_grenades()
{
if ( !isdefined( level.breachfriendlies ) )
return;
if ( !isdefined( level.breachfriendlies_grenades_empty ) )
return;
foreach ( guy in level.breachfriendlies )
{
guy.grenadeammo = guy.grenadeammo_prebreach;
guy.grenadeammo_prebreach = undefined;
}
level.breachfriendlies_grenades_empty = undefined;
}
breach_friendlies_ready_at_other_door( door_volume, teleportOk )
{
script_breachgroup = door_volume.script_breachgroup;
AssertEx( IsDefined( script_breachgroup ), "Breach door volume at " + door_volume.origin + " has no script_breachgroup assigned even though it is a multiple entry room" );
aDoorVolumes = get_door_volumes_from_breachgroup( door_volume.script_breachgroup );
AssertEx( aDoorVolumes.size > 1, "There should be at least 2 volumes with a script_breachgroup value of " + script_breachgroup + " but there is only " + aDoorVolumes.size );
aDoorVolumes = array_remove( aDoorVolumes, door_volume );
AssertEx( aDoorVolumes.size == 1, "Slomo breach only currently supports 2 doors maximum. Script_breachgroup number " + script_breachgroup + " has " + aDoorVolumes.size + 1 );
volume_to_check = aDoorVolumes[ 0 ];
foreach ( guy in level.breachfriendlies )
{
// if teleporting this guy, as long as he's on the way to the volume, we don't care
// if he's not touching his volume if we can't see him
if ( IsDefined( teleportOk ) && teleportOk )
{
if ( guy goalpos_within_volume( volume_to_check ) )
{
if ( guy friendly_can_teleport() )
{
return true;
}
}
}
if ( guy IsTouching( volume_to_check ) )
return true;
}
return false;
}
add_coop_scene_models()
{
add_scene_model( "passive_breacher_rig" );
add_scene_model( "active_breacher_3rd_person" );
add_scene_model( "passive_breacher_3rd_person" );
}
add_scene_model( animname )
{
self.scene_models[ animname ] = spawn_anim_model( animname );
self.scene_models[ animname ] Hide();
}
set_room_to_breached( trigger, room_volume )
{
room_volume.breached = true;
breach_notify = get_breach_notify( trigger.script_breachgroup );
if ( IsDefined( trigger.script_breachgroup ) )
level notify( breach_notify );
room_volume notify( "breached" );
trigger trigger_off();
}
breachless_door_opens( breach_array )
{
trigger = breach_array.trigger;
room_volume = breach_array.room_volume;
solids = breach_array.path_solids;
door = self.door;
charge = self.charge;
set_room_to_breached( trigger, room_volume );
// DOOR OPEN: Player used trigger of door in same breachgroup, and there are NO level.breachfriendlies defined
// (door will just open while player isn't looking)
array_call( solids, ::ConnectPaths );
array_thread( solids, ::self_delete );
ent = SpawnStruct();
ent.entity = door;
ent.forward = 4;
ent.right = 10;
ent.yaw = -170;
ent translate_local();
charge Delete();
if ( !is_coop() )
return;
//If CO-OP, wait for the room to be breached so we can spawn AI
while ( !room_volume.breached )
wait( 0.05 );
//spawn specific coop AI or the AI that would have spawned if the player had breached that door
breach_enemy_spawners = undefined;
breach_hostage_spawners = undefined;
if ( breach_array.spawners[ "coopenemy" ].size )
{
breach_enemy_spawners = breach_array.spawners[ "coopenemy" ];
breach_hostage_spawners = breach_array.spawners[ "coophostage" ];
}
else
{
breach_enemy_spawners = breach_array.spawners[ "enemy" ];
breach_hostage_spawners = breach_array.spawners[ "hostage" ];
}
if ( breach_enemy_spawners.size )
array_call( breach_enemy_spawners, ::StalingradSpawn );
if ( breach_hostage_spawners.size )
array_call( breach_hostage_spawners, ::StalingradSpawn );
}
friendlies_breach( breach_array, aBreachFriendlies )
{
trigger = breach_array.trigger;
door_volume = breach_array.door_volume;
room_volume = breach_array.room_volume;
breach_enemy_spawners = breach_array.spawners[ "enemy" ];
breach_hostage_spawners = breach_array.spawners[ "hostage" ];
breach_friendlyenemy_spawners = breach_array.spawners[ "friendlyenemy" ];
breach_friendlyhostage_spawners = breach_array.spawners[ "friendlyhostage" ];
solids = breach_array.path_solids;
charge = self.charge;
player_rig = self.scene_models[ "active_breacher_rig" ];
set_room_to_breached( trigger, room_volume );
// FRIENDLY BREACH: Player used trigger of door in same breachgroup, and there are level.breachfriendlies defined and available
anim_ent = breach_array.friendly_anim_ent;
player_volume = get_player_volume( door_volume );
breachRequiresFriendlies = ( GetDvar( "breach_requires_friendlies_in_position" ) == "1" );
if ( !breachRequiresFriendlies )
{
// override teleport if they're already there and we can rely on normal breaching logic
if ( IsDefined( player_volume ) && breach_friendlies_ready_at_other_door( player_volume ) )
{
// now that we are switching from teleport logic to regular logic, pare down the breachfriendlies array:
// make sure that we restrict breaching only to guys who are in the door volume
foreach ( guy in aBreachFriendlies )
{
if ( !guy IsTouching( door_volume ) )
{
aBreachFriendlies = array_remove( aBreachFriendlies, guy );
}
}
AssertEx( aBreachFriendlies.size, "Couldn't find any breach friendlies!" );
breachRequiresFriendlies = true;
}
}
if ( breachRequiresFriendlies )
{
breaching_friendly1 = getClosest( anim_ent.origin, aBreachFriendlies );
}
else
{
breaching_friendly1 = get_teleport_optimized_breachfriendly( anim_ent.origin, door_volume, aBreachFriendlies, player_volume );
}
AssertEx( IsDefined( breaching_friendly1 ), "Can't get an AI to breach from position 1 (breaching_friendly1)." );
aBreachFriendlies = array_remove( aBreachFriendlies, breaching_friendly1 );
// spawn the guys in the room
if ( breach_friendlyenemy_spawners.size )
{
level.breachenemies = array_spawn( breach_friendlyenemy_spawners, true );
}
if ( breach_friendlyhostage_spawners.size )
array_call( breach_friendlyhostage_spawners, ::StalingradSpawn );
array_call( solids, ::ConnectPaths );
array_thread( solids, ::self_delete );
breaching_friendly1 thread friendly_breach( 1, anim_ent, room_volume );
if ( aBreachFriendlies.size )
{
if ( breachRequiresFriendlies )
{
// if they're required to be at the breach volume already, the next closest guy is our second breacher
breaching_friendly2 = getClosest( anim_ent.origin, aBreachFriendlies );
}
else
{
// if teleporting, be more selective
breaching_friendly2 = get_teleport_optimized_breachfriendly( anim_ent.origin, door_volume, aBreachFriendlies, player_volume );
}
// we don't HAVE to have a second breaching friendly
if ( IsDefined( breaching_friendly2 ) )
{
breaching_friendly2 thread friendly_breach( 2, anim_ent, room_volume );
}
else
{
PrintLn( "Second friendly breaching position couldn't be filled because all possible breach friendlies to teleport are visible to the player." );
}
}
wait( 1 );
breach_friendlies_take_grenades();
self anim_single_solo( player_rig, "breach_player_anim" );
}
get_player_volume( door_volume )
{
player_volume = undefined;
groupedDoorVolumes = get_grouped_doorvolumes( door_volume );
foreach ( volume in groupedDoorVolumes )
{
if ( level.player IsTouching( volume ) )
{
player_volume = volume;
break;
}
}
return player_volume;
}
// finds a friendly who's the most optimal candidate for teleporting to the spot
get_teleport_optimized_breachfriendly( breachOrigin, door_volume, breachFriendlies, player_volume )
{
// eliminate everyone the player can see and who's in the player breach volume
sortedGuys = [];
foreach ( guy in breachFriendlies )
{
if ( IsDefined( player_volume ) )
{
if ( guy goalpos_within_volume( player_volume ) )
{
continue;
}
}
if ( !guy friendly_can_teleport() )
{
continue;
}
sortedGuys[ sortedGuys.size ] = guy;
}
if ( !sortedGuys.size )
{
//println( "All possible breach friendlies to teleport are visible to the player." );
return undefined;
//sortedGuys = breachFriendlies;
}
// before we consider goal positions, the closest guy is the best
sortedGuys = get_array_of_closest( breachOrigin, sortedGuys );
bestMatch = sortedGuys[ 0 ];
// if anyone has their goalpos set to within the doorvolume, that's the best guy
foreach ( guy in sortedGuys )
{
// see if his goalpos is inside the door volume
if ( guy goalpos_within_volume( door_volume ) )
{
bestMatch = guy;
break;
}
}
return bestMatch;
}
goalpos_within_volume( volume )
{
if ( IsDefined( self.goalpos ) )
{
// offset the goalpos vertically a bit to compensate for volume not touching ground
if ( origin_within_volume( self.goalpos + ( 0, 0, 40 ), volume ) )
{
return true;
}
}
return false;
}
origin_within_volume( origin, volume )
{
testOrg = Spawn( "script_origin", origin );
returnVal = false;
if ( testOrg IsTouching( volume ) )
{
returnVal = true;;
}
testOrg Delete();
return returnVal;
}
get_grouped_doorvolumes( doorVolume )
{
grouped_volumes = [];
grouped_volumes[ 0 ] = doorVolume;
if ( IsDefined( doorVolume.script_breachgroup ) )
{
foreach ( breach in level.breach_groups )
{
if ( !IsDefined( breach.door_volume ) )
{
continue;
}
if ( breach.door_volume == doorVolume )
{
continue;
}
if ( !IsDefined( breach.door_volume.script_breachgroup ) )
{
continue;
}
if ( IsDefined( breach.door_volume.script_breachgroup ) && breach.door_volume.script_breachgroup == doorVolume.script_breachgroup )
{
grouped_volumes[ grouped_volumes.size ] = breach.door_volume;
}
}
}
return grouped_volumes;
}
sort_breachers( ent, breach_players )
{
// if active player is to the right of the door switch active and passive.
active = breach_players[ "active" ];
vec1 = anglestoright( self.angles );
vec2 = vectornormalize( active.origin - ent.trigger.origin );
dot = VectorDot( vec1, vec2 );
if ( dot < 0 )
{
breach_players[ "active" ] = breach_players[ "passive" ];
breach_players[ "passive" ] = active;
}
return breach_players;
}
player_breach( ent, player )
{
AssertEx( IsDefined( player ), "player_breach() was called but player is undefined" );
AssertEx( IsDefined( ent ), "player_breach() was called but ent is undefined" );
breach_players = [];
breach_players[ "active" ] = player;
active_breacher_rig = self.scene_models[ "active_breacher_rig" ];
also_passive_breaching = false;
passive_breacher_rig = undefined;
if ( is_coop() )
{
breach_players[ "passive" ] = get_other_player( breach_players[ "active" ] );
passive_breacher_rig = self.scene_models[ "passive_breacher_rig" ];
breach_array = breach_near_player( breach_players[ "passive" ] );
AssertEx( IsDefined( breach_array ), "player_breach() was called in co-op but all players were not in position for breaching" );
if ( breach_array == ent )
{
// Someone is breaching passively at this breach
also_passive_breaching = true;
breach_array.room_volume.has_passive_breacher = true;
// Find out who...
breach_players = self sort_breachers( ent, breach_players );
}
else
{
// other player breaches over there
level.breach_passive_player = breach_players[ "passive" ];
level.breach_passive_time = gettime();
breach_array.trigger notify( "trigger", breach_players[ "passive" ], "passive" );
}
}
breach_players[ "active" ] EnableBreaching();
if ( also_passive_breaching )
breach_players[ "passive" ] DisableWeapons();
foreach ( player in breach_players )
{
if ( !isdefined( level.slowmo_breach_disable_stancemod ) )
{
player EnableInvulnerability();
player DisableWeaponSwitch();
player DisableOffhandWeapons();
player AllowCrouch( false );
player AllowProne( false );
player AllowSprint( false );
player AllowJump( false );
}
player _disableUsability();
//don't get a .prebreachCurrentWeapon twice by mistake during multiple entry point breaches
if ( !isdefined( player.prebreachCurrentWeapon ) )
player.prebreachCurrentWeapon = player GetCurrentWeapon();
}
level notify( "breaching" );
level notify( "breaching_number_" + self.script_slowmo_breach );
setsaveddvar( "objectiveHide", true );
room_volume = ent.room_volume;
set_room_to_breached( ent.trigger, room_volume );
// Time to set stuff based on whether or not primary breacher has a special breach anim
breach_sound_delay = undefined;
is_special_breach = IsDefined( level.has_special_breach_anim[ breach_players[ "active" ].prebreachCurrentWeapon ] );
if ( is_special_breach )
{
level.slowmo_breach_start_delay = 2.25;
set_door_charge_anim_special();
breach_sound_delay = 0.5;
}
else
{
level.slowmo_breach_start_delay = 2.15;
set_door_charge_anim_normal();
breach_sound_delay = 0.20;
}
//breach_players[ "active" ] delayCall( breach_sound_delay, ::playsound, "detpack_wall_plant" );// 0.4
breach_players[ "active" ] thread play_detpack_plant_sound( breach_sound_delay );
//need to reset these variables differently for coop, otherwise number of enemies is mis-calculated
if ( is_coop() )
{
if ( !isdefined( level.player_one_already_breached ) )
{
level.breachEnemies_alive = 0;
level.breachEnemies_active = 0;
level.player_one_already_breached = true;
}
}
//Always reset these variables to zero if we are not in co-op
else
{
level.breachEnemies_alive = 0;
level.breachEnemies_active = 0;
}
// Spawn the enemies in the room
breach_enemy_spawners = ent.spawners[ "enemy" ];
array_call( breach_enemy_spawners, ::StalingradSpawn );
// Spawn the hostages in the room
breach_hostage_spawners = ent.spawners[ "hostage" ];
array_call( breach_hostage_spawners, ::StalingradSpawn );
// Smoothly hooks the player up to the animating tag
breach_players[ "active" ] PlayerLinkToBlend( active_breacher_rig, "tag_player", 0.2, 0.1, 0.1 );
if ( IsDefined( breach_players[ "active" ].dont_unlink_after_breach ) )
thread open_up_fov( 0.2, active_breacher_rig, "tag_player", 45, 45, 90, 45 );
if ( also_passive_breaching )
{
breach_players[ "passive" ] PlayerLinkToBlend( passive_breacher_rig, "tag_player", 0.2, 0.1, 0.1 );
if ( IsDefined( breach_players[ "passive" ].dont_unlink_after_breach ) )
thread open_up_fov( 0.2, passive_breacher_rig, "tag_player", 45, 45, 90, 45 );
}
breach_players[ "active" ] thread take_prebreach_weapons();
if ( !is_special_breach )
wait( 0.05 );
charge = self.charge;
self thread anim_single_solo( charge, "breach" );
charge show();
breach_players[ "active" ] thread restore_prebreach_weapons();
pause_breach_cleanup = false;
if ( also_passive_breaching )
{
self thread anim_single_solo( passive_breacher_rig, "breach_player_anim" );
breach_players[ "passive" ].animname = "passive_breacher_3rd_person";
breach_players[ "passive" ] thread anim_single_solo( breach_players[ "passive" ], "breach_player_anim" );
breach_players[ "passive" ] thread enable_passive_weapons();
breach_players[ "active" ].animname = "active_breacher_3rd_person";
breach_players[ "active" ] thread anim_single_solo( breach_players[ "active" ], "breach_player_anim" );
}
self anim_single_solo( active_breacher_rig, "breach_player_anim" );
level notify( "sp_slowmo_breachanim_done" );
//check for dead enemies
thread flag_set_when_room_cleared( room_volume );
// This moved to after the animation to ensure the paths aren't cleared.
// Otherwise the AI breacher is likely to stick his head into the explosion.
solids = ent.path_solids;
array_call( solids, ::ConnectPaths );
array_thread( solids, ::self_delete );
foreach ( player in breach_players )
{
if ( IsDefined( player.dont_unlink_after_breach ) )
{
pause_breach_cleanup = true;
special_gulag_adjustment();
}
else
{
player Unlink();
}
player Show();
}
if ( pause_breach_cleanup )
{
// if script wants the player to stay stuck in the breach
level waittill( "breach_concludes" );
}
// Don't stop breaching until we are ready to put player back in normal state.
// Otherwise you get the charge animation playing because that's the "raise weapon" anim during breaches.
breach_players[ "active" ] DisableBreaching();
foreach ( player in breach_players )
{
if ( !isdefined( level.slowmo_breach_disable_stancemod ) )
{
player DisableInvulnerability();
player EnableWeaponSwitch();
player EnableOffhandWeapons();
player AllowCrouch( true );
player AllowProne( true );
player AllowSprint( true );
player AllowJump( true );
}
player _enableUsability();
}
return true;
}
play_detpack_plant_sound( breach_sound_delay )
{
self endon( "death" );
wait( breach_sound_delay );
self playsound( "detpack_wall_plant" );
}
flag_set_when_room_cleared( room_volume )
{
sFlagName = room_volume.script_flag;
level endon( sFlagName );
aEnemies = room_volume get_ai_touching_volume( "bad_guys" );
waittill_dead( aEnemies );
level notify( "breach_room_has_been_cleared" );
level.breachenemies = undefined;
flag_set( sFlagName );
}
take_prebreach_weapons()
{
// give the usp_scripted weapon, which plays the charge setting and breaching viewmodel & camera animation
self GiveWeapon( "usp_scripted" );
self SwitchToWeaponImmediate( "usp_scripted" );
// must quick switch back to the weapon if it has a special breach anim
if ( IsDefined( level.has_special_breach_anim[ self.prebreachCurrentWeapon ] ) )
self SwitchToWeaponImmediate( self.prebreachCurrentWeapon );
}
restore_prebreach_weapons()
{
wait( 0.5 );
self TakeWeapon( "usp_scripted" );
if ( IsDefined( self.prebreachCurrentWeapon ) )
{
weapon = self.prebreachCurrentWeapon;
self SwitchToWeapon( weapon );
// if we're on easy/normal, make sure we have at least one magazine's worth of ammo for the active weapon
if ( self should_topoff_breach_weapon( weapon ) )
{
clipSize = WeaponClipSize( weapon );
if ( self GetWeaponAmmoClip( weapon ) < clipSize )
self SetWeaponAmmoClip( weapon, clipSize );
}
self.prebreachCurrentWeapon = undefined;
}
}
enable_passive_weapons()
{
wait 2.2;
self EnableWeapons();
}
should_topoff_breach_weapon( weapon )
{
if ( level.gameskill > 1 )
{
return false;
}
if ( !IsDefined( self.prebreachCurrentWeapon ) )
{
return false;
}
if ( weapon != self.prebreachCurrentWeapon )
{
return false;
}
return true;
}
friendly_breach( stackPosition, anim_ent, room_volume )
{
sFlagName = room_volume.script_flag;
if ( stackPosition == 2 )
{
sAnimIdle = "breach_friend_idle_02";
sAnimEnter = "breach_friend_enter_02";
}
else
{
sAnimIdle = "breach_friend_idle_01";
sAnimEnter = "breach_friend_enter_01";
}
self.breaching = true;
invulnerableBeforeBreach = true;
if ( !isdefined( self.magic_bullet_shield ) )
{
invulnerableBeforeBreach = false;
self thread magic_bullet_shield();
}
wait( 0.5 );
self ForceTeleport( anim_ent.origin, anim_ent.angles );
anim_ent thread anim_generic_loop( self, sAnimIdle, "stop_idle" );
self SetGoalPos( self.origin );
wait( 3 );
self thread friendlies_shoot_while_breaching( stackPosition );
anim_ent notify( "stop_idle" );
anim_ent anim_generic( self, sAnimEnter );
self SetGoalPos( self.origin );
level notify( "friendlies_finished_breach" );
//wait until all enemies dead before stopping magic_bullet_sheild
flag_wait( sFlagName );
//restore vulnerability (if they were vulnerable in the first place)
if ( !invulnerableBeforeBreach )
self stop_magic_bullet_shield();
self.breaching = undefined;
}
friendlies_shoot_while_breaching( stackPosition )
{
//friendlies fire scripted bullets when weapons are lined up with breach enemies
if ( stackPosition == 1 )
wait( 1 );
else
wait( 2 );
level endon( "friendlies_finished_breach" );
level endon( "breach_room_has_been_cleared" );
while ( !isdefined( level.breachenemies ) )
wait( 0.05 );
while ( ( IsDefined( level.breachenemies ) ) && ( level.breachenemies.size ) )
{
wait( 0.05 );
if ( !isdefined( level.breachenemies ) )
break;
level.breachenemies = remove_dead_from_array( level.breachenemies );
foreach ( enemy in level.breachenemies )
{
if ( ( !isalive( enemy ) ) || ( !isdefined( enemy ) ) )
continue;
enemy_head_org = enemy GetTagOrigin( "tag_eye" );
myGunPos = self GetMuzzlePos();
myEyeOffset = ( self GetShootAtPos() - myGunPos );
if ( self CanShoot( enemy_head_org, myEyeOffset ) )
{
MagicBullet( self.weapon, self GetMuzzlePos(), enemy_head_org );
BulletTracer( self GetMuzzlePos(), enemy_head_org, true );
wait( 0.5 );
}
}
}
}
get_available_breachfriendlies( volume )
{
available_friendlies = [];
if ( !isdefined( level.breachfriendlies ) )
{
return available_friendlies;
}
// don't want dead/removed guys to be considered
available_friendlies = array_removeDead( level.breachfriendlies );
breachRequiresFriendlies = ( GetDvar( "breach_requires_friendlies_in_position" ) == "1" );
foreach ( guy in available_friendlies )
{
// remove him if he's already breaching
if ( ( IsDefined( guy.breaching ) ) && ( guy.breaching == true ) )
{
available_friendlies = array_remove( available_friendlies, guy );
continue;
}
// if the breach requires that friendlies walk there, and the guy hasn't walked there yet, remove him
if ( breachRequiresFriendlies && !guy IsTouching( volume ) )
{
available_friendlies = array_remove( available_friendlies, guy );
continue;
}
}
return available_friendlies;
}
get_breach_notify( script_breachgroup )
{
if ( !isdefined( script_breachgroup ) )
script_breachgroup = "none";
return "A door in breach group " + script_breachgroup + " has been activated.";
}
breach_group_trigger_think()
{
//self ==> the breach trigger calling the function
// Waits for any other trigger in the breachGroup to be activated, then cancels its breach functionality (creaks opens the door)
sBreachGroup = self.script_breachgroup;
breach_notify = get_breach_notify( sBreachGroup );
level waittill( breach_notify );
waittillframeend;// give other players a chance to breach here before fake breaching it
self notify( "trigger" );
}
slowmo_player_cleanup()
{
AssertEx( IsPlayer( self ), "slowmo_player_cleanup() called on a non-player." );
if ( IsDefined( level.playerSpeed ) )
self SetMoveSpeedScale( level.playerSpeed );
else
self SetMoveSpeedScale( 1 );
}
slowmo_begins( rig )
{
if ( ( IsDefined( level.breaching ) ) && ( level.breaching == true ) )
{
return;
}
level.breaching = true;
flag_set( "breaching_on" );
level notify( "slowmo_go" );
level endon( "slowmo_go" );
slomoLerpTime_in = 0.5;
slomoLerpTime_out = 0.75;
slomobreachplayerspeed = 0.2;
if ( IsDefined( level.slomobreachplayerspeed ) )
{
slomobreachplayerspeed = level.slomobreachplayerspeed;
}
player = level.player;
other_player = undefined;
if ( is_coop() )
other_player = get_other_player( player );
player thread play_sound_on_entity( "slomo_whoosh" );
player thread player_heartbeat();
thread slomo_breach_vision_change( ( slomoLerpTime_in * 2 ), ( slomoLerpTime_out / 2 ) );
thread slomo_difficulty_dvars();
flag_clear( "can_save" );
slowmo_start();
player thread set_breaching_variable();
if ( IsDefined( other_player ) )
other_player thread set_breaching_variable();
player AllowMelee( false ); ///melee is useless and causes bugs during slomo
if ( IsDefined( other_player ) )
other_player AllowMelee( false ); ///melee is useless and causes bugs during slomo
slowmo_setspeed_slow( 0.25 );
slowmo_setlerptime_in( slomoLerpTime_in );
slowmo_lerp_in();
player SetMoveSpeedScale( slomobreachplayerspeed );
if ( IsDefined( other_player ) )
other_player SetMoveSpeedScale( slomobreachplayerspeed );
startTime = GetTime();
endTime = startTime + ( level.slomobreachduration * 1000 );
// Only worry about weapon status changes in single player.
if ( !is_coop() )
player thread catch_weapon_switch();// called after the player weapons are force - changed, so this is cool to put here
player thread catch_mission_failed();
if ( IsDefined( other_player ) )
other_player thread catch_mission_failed();
// be lenient about some slowmo-ending activities at the start of the slowmo period
reloadIgnoreTime = 500;// ms
switchWeaponIgnoreTime = 1000;
// wait for slowmo timeout, or wait for conditions to be met that will interrupt the slowmo
for ( ;; )
{
if ( IsDefined( level.forced_slowmo_breach_slowdown ) )
{
if ( !level.forced_slowmo_breach_slowdown )
{
if ( IsDefined( level.forced_slowmo_breach_lerpout ) )
slomoLerpTime_out = level.forced_slowmo_breach_lerpout;
break;
}
wait( 0.05 );
continue;
}
if ( GetTime() >= endTime )
break;
// is everyone dead?
if ( level.breachEnemies_active <= 0 )
{
// lerp out a little slower so we see more of the last guy's death in slowmo
slomoLerpTime_out = 1.15;
break;
}
// Only worry about weapon status changes in single player.
if ( !is_coop() )
{
// did the player start reloading after the reload ignore time window has expired?
if ( player.lastReloadStartTime >= ( startTime + reloadIgnoreTime ) )
{
break;
}
// did player switch weapons?
if ( player.switchedWeapons && ( ( GetTime() - startTime ) > switchWeaponIgnoreTime ) )
{
break;
}
}
// did we fail the mission during slowmo?
if ( is_specialop() && flag( "special_op_terminated" ) )
{
break;
}
// did we fail the mission during slowmo?
if ( player.breach_missionfailed || ( is_coop() && other_player.breach_missionfailed ) )
{
// lerp out fast so we can restart faster
slomoLerpTime_out = 0.5;
break;
}
wait( 0.05 );
}
level notify( "slowmo_breach_ending", slomoLerpTime_out );
level notify( "stop_player_heartbeat" );
player thread play_sound_on_entity( "slomo_whoosh" );
slowmo_setlerptime_out( slomoLerpTime_out );
slowmo_lerp_out();
player AllowMelee( true ); ///melee is useless and causes bugs during slomo
if ( IsDefined( other_player ) )
other_player AllowMelee( true ); ///melee is useless and causes bugs during slomo
player delaythread( slomoLerpTime_out, ::clear_breaching_variable );
if ( IsDefined( other_player ) )
other_player delaythread( slomoLerpTime_out, ::clear_breaching_variable );
slowmo_end();
flag_set( "can_save" );
level.player_one_already_breached = undefined;
player slowmo_player_cleanup();
if ( IsDefined( other_player ) )
other_player slowmo_player_cleanup();
level notify( "slomo_breach_over" );
level.breaching = false;
flag_clear( "breaching_on" );
setsaveddvar( "objectiveHide", false );
}
set_breaching_variable()
{
self endon( "clear_breaching_variable" );
self.isbreaching = 1;
self.breaching_shots_fired = 0;
self.achieve_slowmo_breach_kills = undefined;
ammo = self getcurrentweaponclipammo();
self notifyonPlayercommand( "player_shot_fired", "+attack" );
while( isdefined( self.isbreaching ) )
{
self waittill( "player_shot_fired" );
self.breaching_shots_fired = ammo - self getcurrentweaponclipammo();
wait .05;
while( self isFiring() )
{
self.breaching_shots_fired = ammo - self getcurrentweaponclipammo();
wait .05;
}
}
}
clear_breaching_variable()
{
self.isbreaching = undefined;
self thread notify_delay( "clear_breaching_variable", .25 );
}
slomo_difficulty_dvars()
{
//Get current viewKick values
old_bg_viewKickScale = GetDvar( "bg_viewKickScale" ); // 0.8
old_bg_viewKickMax = GetDvar( "bg_viewKickMax" ); // 90
SetSavedDvar( "bg_viewKickScale", 0.3 ); // make the view kick a little easier
SetSavedDvar( "bg_viewKickMax", "15" ); // make the view kick a little easier
SetSavedDvar( "bullet_penetration_damage", 0 ); // Disable bullet penetration damage so that hostages are less likely to be shot through enemies
level waittill( "slowmo_breach_ending" );
//Restore all values when slomo is over
SetSavedDvar( "bg_viewKickScale", old_bg_viewKickScale ); // set view kick back to whatever it was
SetSavedDvar( "bg_viewKickMax", old_bg_viewKickMax ); // set view kick back to whatever it was
wait( 2 ); //wait a few seconds before resetting bullet dvar
SetSavedDvar( "bullet_penetration_damage", 1 ); // Re - enable bullet penetration
}
slomo_breach_vision_change( lerpTime_in, lerpTime_out )
{
if ( !IsDefined( level.slomoBasevision ) )
{
return;
}
VisionSetNaked( "slomo_breach", lerpTime_in );
level waittill( "slowmo_breach_ending", newLerpTime );
// maybe update the lerp time in case things changed in the main thread
if ( IsDefined( newLerpTime ) )
{
lerpTime_out = newLerpTime;
}
wait( 1 );
VisionSetNaked( level.slomoBasevision, lerpTime_out );
}
player_heartbeat()
{
level endon( "stop_player_heartbeat" );
while ( true )
{
self PlayLocalSound( "breathing_heartbeat" );
wait .5;
}
}
catch_weapon_switch()
{
level endon( "slowmo_breach_ending" );
self.switchedWeapons = false;
self waittill_any( "weapon_switch_started", "night_vision_on", "night_vision_off" );
self.switchedWeapons = true;
}
catch_mission_failed()
{
level endon( "slowmo_breach_ending" );
self.breach_missionfailed = false;
level waittill( "mission failed" );
self.breach_missionfailed = true;
}
breach_enemy_spawner_think()
{
reference = self.spawner;
self endon( "death" );
self add_damage_function( ::record_last_player_damage );
self thread breach_enemy_ignored_by_friendlies();
self thread breach_enemy_ragdoll_on_death();
level thread breach_enemy_track_status( self );
self.reference = reference;
self anim_generic_first_frame( self, self.animation );
AssertEx( IsDefined( level.scr_anim[ "generic" ][ self.animation ] ), "Have not defined level.scr_anim[ generic ] for anim " + self.animation );
if ( IsDefined( level._slowmo_functions[ self.animation ] ) )
{
// run a custom function for this animation set
custom_function = level._slowmo_functions[ self.animation ];
self thread [[ custom_function ]]();
}
self.grenadeammo = 0;
self.allowdeath = true;
self.health = 10;
self.baseaccuracy = 5000;
if ( IsDefined( self.script_threatbias ) )
{
self.threatbias = self.script_threatbias;
}
if ( IsDefined( level.breach_death_anims[ self.animation ] ) )
self.skipDeathAnim = true;
wait( level.slowmo_breach_start_delay );
self script_delay();
self notify( "starting_breach_reaction" );
reference anim_generic( self, self.animation );
self notify( "finished_breach_start_anim" );
}
record_last_player_damage( damage, attacker, direction_vec, point, type, modelName, tagName )
{
if ( !isalive( attacker ) )
return;
if ( !IsPlayer( attacker ) )
return;
if ( !self IsBadGuy() )
return;
level.last_player_damage = GetTime();
}
breach_enemy_ignored_by_friendlies()
{
//all enemies in a breached room should be ignored by friendlies
//until slowmo is over. Any friendlies that are actually breaching
//will fire their weapons anyway through anim notetracks
self endon( "death" );
if ( !flag( "no_mercy" ) )
self.ignoreme = true;
level waittill_either( "slomo_breach_over", "friendlies_finished_breach" );
if ( IsDefined( self ) )
self.ignoreme = false;
}
// immediately sends guys without custom death anims into ragdoll when they die
// in a breach situation. helps eliminate animation popping
//
// NOTE: kill this with breach_enemy_cancel_ragdoll() if you have a scripted death animation
// that matches the breach start animation
breach_enemy_ragdoll_on_death()
{
self endon( "breach_enemy_cancel_ragdoll_death" );
// cause death.gsc to do StartRagdollFromImpact() for us
self.ragdoll_immediate = true;
msg = self waittill_any_return( "death", "finished_breach_start_anim" );
if ( msg == "finished_breach_start_anim" )
{
self.ragdoll_immediate = undefined;
}
}
breach_enemy_cancel_ragdoll()
{
self notify( "breach_enemy_cancel_ragdoll_death" );
self.ragdoll_immediate = undefined;
}
breach_enemy_track_status( enemy )
{
level.breachEnemies_active++;
ent = SpawnStruct();
ent.enemy = enemy;
ent thread breach_enemy_waitfor_death( enemy );
ent thread breach_enemy_waitfor_death_counter( enemy );
ent thread breach_enemy_catch_exceptions( enemy );
ent thread breach_enemy_waitfor_breach_ending();
ent waittill( "breach_status_change", status );
level.breachEnemies_active--;
ent = undefined;
}
breach_enemy_waitfor_death( enemy )
{
self endon( "breach_status_change" );
enemy waittill( "death" );
self notify( "breach_status_change", "death" );
}
// Special version of the death wait that allows us to know when all enemies are dead.
breach_enemy_waitfor_death_counter( enemy )
{
level.breachEnemies_alive++;
enemy waittill( "death" );
level.breachEnemies_alive--;
if ( level.breachEnemies_alive <= 0 )
breach_friendlies_restore_grenades();
level notify( "breach_all_enemies_dead" );
}
breach_enemy_catch_exceptions( enemy )
{
self endon( "breach_status_change" );
while ( IsAlive( enemy ) )
{
wait( 0.05 );
}
self notify( "breach_status_change", "exception" );
}
breach_enemy_waitfor_breach_ending()
{
self endon( "breach_status_change" );
level waittill( "slowmo_breach_ending" );
self notify( "breach_status_change", "breach_ending" );
}
breach_hostage_spawner_think()
{
self endon( "death" );
self.breachfinished = false;
reference = self.spawner;
self.reference = reference;
self endon( "cancel_breach_behavior" );
self thread hostage_mission_fail();
self anim_generic_first_frame( self, self.animation );
self.health = 10; // needs to be low to allow enemies and player to kill with one bullet
self.no_friendly_fire_penalty = true; //mission fail is done separately for hostages
self.IgnoreRandomBulletDamage = true; // don't get killed by stray bullets from friendlies or enemies
wait( level.slowmo_breach_start_delay );
if ( IsDefined( level._slowmo_functions[ self.animation ] ) )
{
// run a custom function for this animation set
custom_function = level._slowmo_functions[ self.animation ];
self thread [[ custom_function ]]();
}
self.allowdeath = true;
reference anim_generic( self, self.animation );
self notify( "finished_breach_start_anim" );
//self.IgnoreRandomBulletDamage = false; //allow to be killed by stray bullets now that sequence is over
if ( IsDefined( self.skipEndingIdle ) )
return;
if ( anim_exists( self.animation + "_idle" ) )
thread anim_generic_loop( self, self.animation + "_idle", "stop_idle" );
else
{
loop = "hostage_knees_loop";
self thread anim_generic_loop( self, loop, "stop_idle" );
}
self.breachfinished = true;
}
hostage_health_regen()
{
baseHealth = self.health;
self endon( "death" );
self endon( "saved" );
while ( IsDefined( self ) )
{
self waittill( "damage", amount, attacker );
if ( IsDefined( attacker ) )
{
if ( IsPlayer( attacker ) )
{
//self DoDamage( self.health + 100, self.origin, level.player );
self Kill( self.origin, level.player );
break;
}
else if ( ( IsDefined( attacker.team ) ) && ( attacker.team == "allies" ) )
self.health = baseHealth;
}
}
}
get_room_volume_from_slomo_breach_number( script_slomo_breach )
{
return level.breach_groups[ script_slomo_breach ].room_volume;
}
hostage_mission_fail()
{
if ( is_specialop() )
level endon( "special_op_terminated" );
level endon( "mission failed" );
baseHealth = self.health;
missionFailedHostage = false;
self thread hostage_health_regen();
room_volume = get_room_volume_from_slomo_breach_number( self.script_slowmo_breach );
if ( GetDvar( "hostage_missionfail" ) == "0" )
return;
while ( IsDefined( self ) )
{
self waittill( "death", attacker );
// Mission failed. A hostage was executed.
if ( IsDefined( attacker ) )
{
if ( IsPlayer( attacker ) )
{
level notify( "player_shot_a_hostage" );
// Mission failed. You killed a hostage.
waittillframeend;// give level.last_player_damage a chance to get set
//if ( ( level.last_player_damage + 50 < GetTime() ) || ( level.last_player_damage == GetTime() ) )
if ( level.last_player_damage == GetTime() )
{
// Mission failed. You shot a hostage through an enemy.\nSome weapons have deep bullet penetration.
//breach_set_deadquote( &"SCRIPT_MISSIONFAIL_KILLEDHOSTAGE_THROUGH_ENEMY", "@SCRIPT_MISSIONFAIL_KILLEDHOSTAGE_THROUGH_ENEMY" );
// Mission failed. You killed a hostage.
//(not doing bullet penetration anymore on any skill level, but will leave check here in case
//we want a message about shotguns, etc)
breach_set_deadquote( &"SCRIPT_MISSIONFAIL_KILLEDHOSTAGE", "@SCRIPT_MISSIONFAIL_KILLEDHOSTAGE" );
}
else
{
// Mission failed. You killed a hostage.
breach_set_deadquote( &"SCRIPT_MISSIONFAIL_KILLEDHOSTAGE", "@SCRIPT_MISSIONFAIL_KILLEDHOSTAGE" );
}
missionFailedHostage = true;
}
else if ( ( IsDefined( attacker.team ) ) && ( attacker.team == "allies" ) && ( !IsPlayer( attacker ) ) )
{
AssertMsg( "hostage was accidentally killed by a friendly...need a script fix for this" );
// Mission failed. A hostage was executed.
breach_set_deadquote( &"SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED", "@SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED" );
missionFailedHostage = true;
}
else
{
if ( self coop_breached_from_same_door_in_a_muliti_door_room( room_volume ) )
{
// Mission failed. A hostage was executed.\nTry breaching from opposite doors.
breach_set_deadquote( &"SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED_USEMULTIDOOR", "@SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED_USEMULTIDOOR" );
}
else
{
// Mission failed. A hostage was executed.
breach_set_deadquote( &"SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED", "@SCRIPT_MISSIONFAIL_HOSTAGEEXECUTED" );
}
missionFailedHostage = true;
}
}
if ( missionFailedHostage == true )
{
thread maps\_utility::missionFailedWrapper();
level notify( "mission failed" );
}
}
}
// This is lame and ugly, but the cleanest for now.
breach_set_deadquote( deadquote, so_deadquote )
{
assert( isdefined( deadquote ) && isdefined( so_deadquote ) );
if ( is_specialop() )
maps\_specialops::so_force_deadquote( so_deadquote );
else
setDvar( "ui_deadquote", deadquote );
}
coop_breached_from_same_door_in_a_muliti_door_room( room_volume )
{
if ( !is_specialop() )
return false;
if ( !is_coop() )
return false;
if ( IsDefined( room_volume.has_passive_breacher ) )
return true;
else
return false;
}
#using_animtree( "script_model" );
script_models()
{
// wooden door
level.scr_anim[ "breach_door_model" ][ "breach" ] = %breach_player_door_v2;
level.scr_animtree[ "breach_door_model" ] = #animtree;
level.scr_model[ "breach_door_model" ] = "com_door_01_handleright";
level.scr_anim[ "breach_door_hinge" ][ "breach" ] = %breach_player_door_hinge_v1;
level.scr_animtree[ "breach_door_hinge" ] = #animtree;
level.scr_model[ "breach_door_hinge" ] = "com_door_piece_hinge";
// metal door
level.scr_anim[ "breach_door_model_metal" ][ "breach" ] = %breach_player_door_v2;
level.scr_animtree[ "breach_door_model_metal" ] = #animtree;
level.scr_model[ "breach_door_model_metal" ] = "breach_door_metal_right";
level.scr_anim[ "breach_door_hinge_metal" ][ "breach" ] = %breach_player_door_metal;
level.scr_animtree[ "breach_door_hinge_metal" ] = #animtree;
level.scr_model[ "breach_door_hinge_metal" ] = "breach_door_metal_right_dst";
// explosive charge
// this gets overwritten at time of breach
level.scr_anim[ "breach_door_charge" ][ "breach" ] = %breach_player_frame_charge_v3;
level.scr_animtree[ "breach_door_charge" ] = #animtree;
level.scr_model[ "breach_door_charge" ] = "mil_frame_charge";
level.scr_animtree[ "desk" ] = #animtree;
level.scr_anim[ "desk" ][ "breach_react_desk_v7_desk" ] = %breach_react_desk_v7_desk;
level.scr_animtree[ "chair" ] = #animtree;
level.scr_anim[ "chair" ][ "breach_chair_hide_reaction_v2_chair" ] = %breach_chair_hide_reaction_v2_chair;
level.scr_anim[ "chair" ][ "breach_chair_hide_reaction_death_v2_chair" ] = %breach_chair_hide_reaction_death_v2_chair;
}
set_door_charge_anim_normal()
{
level.scr_anim[ "breach_door_charge" ][ "breach" ] = %breach_player_frame_charge_v3;
}
set_door_charge_anim_special()
{
level.scr_anim[ "breach_door_charge" ][ "breach" ] = %breach_player_frame_charge;
}
//#using_animtree( "player" );
#using_animtree( "multiplayer" );
player_animations()
{
if ( !isdefined( level.slowmo_viewhands ) )
level.slowmo_viewhands = "viewhands_player_sas_woodland";
level.scr_animtree[ "active_breacher_rig" ] = #animtree;
level.scr_model[ "active_breacher_rig" ] = level.slowmo_viewhands;
level.scr_anim[ "active_breacher_rig" ][ "breach_player_anim" ] = %breach_coop_player_1;
if ( is_coop() )
{
level.scr_anim[ "active_breacher_rig" ][ "breach_player_anim" ] = %breach_coop_player_1;
level.scr_animtree[ "active_breacher_3rd_person" ] = #animtree;
level.scr_model[ "active_breacher_3rd_person" ] = level.slowmo_viewhands;
level.scr_anim[ "active_breacher_3rd_person" ][ "breach_player_anim" ] = %breach_coop_player_1_3rdPerson;
level.scr_anim[ "active_breacher_3rd_person" ][ "root" ] = %code;
level.scr_animtree[ "passive_breacher_rig" ] = #animtree;
level.scr_model[ "passive_breacher_rig" ] = level.slowmo_viewhands;
level.scr_anim[ "passive_breacher_rig" ][ "breach_player_anim" ] = %breach_coop_player_2;
level.scr_animtree[ "passive_breacher_3rd_person" ] = #animtree;
level.scr_model[ "passive_breacher_3rd_person" ] = level.slowmo_viewhands;
level.scr_anim[ "passive_breacher_3rd_person" ][ "breach_player_anim" ] = %breach_coop_player_2_3rdPerson;
level.scr_anim[ "passive_breacher_3rd_person" ][ "root" ] = %code;
}
// can add custom functions to occur when the breach happens
level._slowmo_breach_funcs = [];
add_breach_func( ::breach_explosion );
addNotetrack_customFunction( "active_breacher_rig", "explode", ::breach_functions );
addNotetrack_customFunction( "active_breacher_rig", "slowmo", ::slowmo_begins );
}
add_breach_func( func )
{
/#
foreach ( existing_func in level._slowmo_breach_funcs )
{
AssertEx( func != existing_func, "Breach func was added twice" );
}
#/
level._slowmo_breach_funcs[ level._slowmo_breach_funcs.size ] = func;
}
breach_functions( breach_rig )
{
foreach ( func in level._slowmo_breach_funcs )
{
thread [[ func ]]( breach_rig );
}
}
breach_explosion( breach_rig )
{
breach_array = level.breach_groups[ self.breach_index ];
expSound = undefined;
destroyedModelAlias = undefined;
switch( breach_array.doorType )
{
case "wood":
expSound = "detpack_explo_wood";
destroyedModelAlias = "breach_door_hinge";
break;
case "estate_wood":
expSound = "detpack_explo_wood";
destroyedModelAlias = "breach_door_hinge_estate";
break;
case "estate_wood_backwards":
expSound = "detpack_explo_wood";
destroyedModelAlias = undefined;
break;
case "caves_wood":
expSound = "detpack_explo_wood";
destroyedModelAlias = "breach_door_hinge_caves";
break;
case "metal":
expSound = "detpack_explo_metal";
destroyedModelAlias = "breach_door_hinge_metal";
break;
case "none":
expSound = "detpack_explo_concrete";
break;
default:
AssertEx( "Illegal slowmo breach doortype " + breach_array.doorType );
break;
}
if ( IsDefined( expSound ) )
thread play_sound_in_space( expSound, self.charge.origin );
exploder( "breach_" + self.breach_index );
thread breach_rumble( self.charge.origin );
self.charge Delete();
level notify( "breach_explosion" );
if ( IsDefined( destroyedModelAlias ) )
{
destroyedModel = spawn_anim_model( destroyedModelAlias );
self.post thread anim_single_solo( destroyedModel, "breach" );
}
wait( 0.05 );
if ( IsDefined( self.door ) )
{
self.door Delete();
}
}
breach_rumble( org )
{
dummy = Spawn( "script_origin", org );
dummy.origin = org;
dummy PlayRumbleOnEntity( "grenade_rumble" );
wait( 4 );
dummy Delete();
}
// CUSTOM FUNCTIONS FOR SLOWMO BREACH GUYS
_slomo_breach_executioner_knife()
{
self endon( "death" );
self.doDamageToAll = true;
self thread knife_guy_cleanup();
}
_slomo_breach_executioner_pistol()
{
self endon( "death" );
self.doDamageToAll = true;
self thread _slomo_breach_pistol_guy();
}
_slomo_breach_pistol_guy()
{
self animscripts\shared::noteTrackPistolPickup();
}
_slomo_breach_blowback_guy()
{
self endon( "death" );
//self breach_enemy_cancel_ragdoll();
if ( !flag( "no_mercy" ) )
self.ignoreme = true;
self.forceLongDeath = true;
self waittill_notetrack_or_damage( "bodyfall large" );
self waittill( "finished_breach_start_anim" );
self DoDamage( self.health - 1, self.origin );
}
_slomo_breach_executed_guy()
{
if ( ( self.animation == "execution_knife_hostage" ) || ( self.animation == "execution_knife2_hostage" ) )
self thread _slomo_breach_knife_hostage_death();
//Additional check to see if this hostage will be manhandled
if ( self will_be_manhandled() )
self thread get_manhandled();
self.skipEndingIdle = true; // will skip the generic knees loop that most hostages go into
self endon( "death" );
self set_generic_deathanim( self.animation + "_death" );
self waittill( "finished_breach_start_anim" );
//don't play end looping anim if we want to manhandle this hostage
if ( IsDefined( self.manhandled ) )
return;
if ( anim_exists( self.animation + "_survives" ) )
{
self.reference anim_generic( self, self.animation + "_survives" );
}
thread anim_generic_loop( self, self.animation + "_idle", "stop_idle" );
self.breachfinished = true;
}
_slomo_breach_hostage_react()
{
//Additional check to see if this hostage will be manhandled
if ( self will_be_manhandled() )
self thread get_manhandled();
self.skipEndingIdle = true; // will skip the generic knees loop that most hostages go into
self waittill( "finished_breach_start_anim" );
//don't play end looping anim if we want to manhandle this hostage
if ( IsDefined( self.manhandled ) )
return;
if ( anim_exists( self.animation + "_idle" ) )
thread anim_generic_loop( self, self.animation + "_idle", "stop_idle" );
self.breachfinished = true;
}
_slomo_breach_c4_hostage()
{
eChair = Spawn( "script_model", self.reference.origin );
eChair SetModel( "com_restaurantchair_2" );
eChair.angles = self.reference.angles + ( 0, 90, 0 );
eChair.origin = self.reference.origin;
}
_slomo_breach_knife_hostage_death()
{
eKiller = get_closest_ai( self.origin, "bad_guys" );
self waittill( "finished_breach_start_anim" );
if ( ( IsDefined( eKiller ) ) && ( IsAlive( eKiller ) ) )
{
PlayFXOnTag( getfx( "breach_knife_execution" ), self, "J_neck" );
self thread play_sound_on_entity( "melee_knife_hit_body" );
self Kill();
}
}
_slomo_breach_executed_guy_pushed_to_floor()
{
self.skipEndingIdle = true; // will skip the generic knees loop that most hostages go into
self endon( "death" );
//Additional check to see if this hostage will be manhandled
if ( self will_be_manhandled() )
self thread get_manhandled();
self waittillmatch( "single anim", "bodyfall large" );
self set_generic_deathanim( self.animation + "_death" );
self waittill( "finished_breach_start_anim" );
self anim_generic( self, self.animation + "_survives" );
//don't play end looping anim if we want to manhandle this hostage
if ( IsDefined( self.manhandled ) )
return;
self thread anim_generic_loop( self, "hostage_knees_loop", "stop_idle" );
self.breachfinished = true;
}
_slomo_breach_fightback_guy()
{
self.skipEndingIdle = true; // will skip the generic knees loop that most hostages go into
self endon( "death" );
self waittill( "finished_breach_start_anim" );
self set_generic_deathanim( self.animation + "_death" );
self anim_generic( self, self.animation + "_survives" );
self thread anim_generic_loop( self, "hostage_knees_loop", "stop_idle" );
self.breachfinished = true;
}
_slomo_breach_knife_charger()
{
self endon( "death" );
self breach_enemy_cancel_ragdoll();
self set_generic_deathanim( self.animation + "_death" );
self waittillmatch( "single anim", "stab" );
wait( .1 );
self thread knife_guy_stabs_player();
self waittill( "finished_breach_start_anim" );
//self gun_recall();
}
knife_guy_stabs_player()
{
player = get_closest_player( self.origin );
dist = Distance( player.origin, self.origin );
if ( dist <= 50 )
{
player PlayRumbleOnEntity( "grenade_rumble" );
player thread play_sound_on_entity( "melee_knife_hit_body" );
player EnableHealthShield( false );
player EnableDeathShield( false );
waittillframeend;
player DoDamage( player.health + 50000, self GetTagOrigin( "tag_weapon_right" ), self );
player.breach_missionfailed = true;// tells slowmo to stop
}
}
knife_guy_cleanup()
{
wait 0.5;
self waittill_either( "damage", "finished_breach_start_anim" );
if ( IsDefined( self ) )
self Detach( "weapon_parabolic_knife", "TAG_INHAND" );
}
_slomo_breach_chair_guy_normal()
{
self endon( "death" );
self breach_enemy_cancel_ragdoll();
iRand = RandomIntRange( 1, 3 );
if ( cointoss() )
self set_generic_deathanim( self.animation + "_death" );
else
self set_generic_deathanim( self.animation + "_death2" );
}
_slomo_breach_chair_guy_animated()
{
self endon( "death" );
self breach_enemy_cancel_ragdoll();
self set_generic_deathanim( self.animation + "_death" );
self thread chair_animate();
self waittill( "finished_breach_start_anim" );
self thread _slomo_breach_chair_guy_normal();
}
chair_animate()
{
eChair = Spawn( "script_model", self.reference.origin );
eChair SetModel( "furniture_chair_metal01" );
eChair.animname = "chair";
eChair assign_animtree();
eChair.reference = Spawn( "script_origin", self.reference.origin );
eChair.reference.angles = self.reference.angles;
//ent = SpawnStruct();
//ent.entity = eChair;
//ent.forward = 40;
//ent translate_local();
self waittill( "starting_breach_reaction" );
eChair.reference thread anim_single_solo( eChair, "breach_chair_hide_reaction_v2_chair" );
//if guy finishes breach reaction, don't play the chair tipping over when he dies
self endon( "finished_breach_start_anim" );
//if the guy is still alive and hasn't finished breach reaction, play the chair tipping over when he dies
if ( IsAlive( self ) )
{
self waittill( "death" );
eChair.reference thread anim_single_solo( eChair, "breach_chair_hide_reaction_death_v2_chair" );
}
}
_slomo_breach_desk_guy()
{
self endon( "death" );
self thread desk_animate();
self waittill( "finished_breach_start_anim" );
}
desk_animate()
{
eDesk = Spawn( "script_model", self.reference.origin );
eDesk SetModel( "furniture_long_desk_animate" );
eDesk.animname = "desk";
eDesk assign_animtree();
eDesk.reference = Spawn( "script_origin", self.reference.origin );
eDesk.reference.angles = self.reference.angles;
//ent = SpawnStruct();
//ent.entity = eDesk;
//ent.forward = 40;
//ent translate_local();
self waittill( "starting_breach_reaction" );
eDesk.reference thread anim_single_solo( eDesk, "breach_react_desk_v7_desk" );
}
// CUSTOM FUNCTIONS FOR SLOWMO BREACH GUYS
breach_near_player( player )
{
foreach ( ent in level.breach_groups )
{
if ( player IsTouching( ent.door_volume ) )
return ent;
}
}
get_breach_groups()
{
// Returns all breach groups in the level
keys = GetArrayKeys( level.breach_groups );
return keys;
}
make_empty_breach( group_num )
{
// Make no AI or hostages spawn for the specified group number. The breach remains functional.
AssertEx( IsDefined( group_num ), "You must specify a breach group number to make empty" );
AssertEx( IsDefined( level.breach_groups[ group_num ] ), group_num + " is not a valid breach group number" );
level.breach_groups[ group_num ].enabled = false;
}
delete_breach( group_num )
{
// Removes the breach group and all associated entities.
AssertEx( IsDefined( group_num ), "You must specify a breach group number to delete" );
AssertEx( IsDefined( level.breach_groups[ group_num ] ), group_num + " is not a valid breach group number" );
AssertEx( IsDefined( level.breach_groups[ group_num ].trigger ), "Tried to delete breach that was already deleted" );
level.breach_groups[ group_num ].trigger Delete();
solids = level.breach_groups[ group_num ].path_solids;
array_call( solids, ::ConnectPaths );
array_thread( solids, ::self_delete );
foreach ( trigger in level.breach_groups[ group_num ].lookat_triggers )
trigger Delete();
}
breach_debug_display_animnames( room_volume )
{
if ( !isdefined( self ) )
return;
org = self.origin;
wait( 0.05 );
if ( GetDvar( "breach_debug" ) == "0" )
return;
//self ==> the script_origin in the door targeted by the lookat trigger
aBreachAI = [];
aAI1 = GetEntArray( "breach_enemy_spawner", "targetname" );
aAI2 = GetEntArray( "breach_hostage_spawner", "targetname" );
aBreachAI = array_merge( aAI1, aAI2 );
foreach ( spawner in aBreachAI )
{
if ( !spawner IsTouching( room_volume ) )
aBreachAI = array_remove( aBreachAI, spawner );
}
while ( !room_volume.breached )
{
neworg = org;
foreach ( spawner in aBreachAI )
{
if ( IsDefined( spawner.animation ) )
{
thread debug_message( spawner.animation, neworg, 1 );
neworg = neworg - ( 0, 0, 10 );
}
}
wait( 1 );
}
//room_volume waittill( "breached" );
}
will_be_manhandled()
{
if ( is_coop() )
return false;
if ( ( IsDefined( level.hostagemanhandle ) ) && ( level.hostagemanhandle == false ) )
return false;
if ( IsDefined( self.script_noteworthy ) )
{
return self.script_noteworthy == "manhandled" || self.script_noteworthy == "manhandled_guarded";
}
return false;
}
manhandler_hold()
{
// First need all enemies dead...
if ( level.breachEnemies_alive > 0 )
return true;
if ( !self.startManhandling )
return true;
return false;
}
get_manhandled()
{
self endon( "death" );
self.manhandled = true; // will make hostage skip default ending behavior / idle
self.readyToBeManhandled = false;
self.startManhandling = false;
manhandlerSpawner = undefined;
/*-----------------------
FIND THE MANHANDLER ASSOCIATED WITH THIS HOSTAGE
-------------------------*/
if ( self.script_noteworthy == "manhandled" )
{
manhandlerSpawner = GetEnt( self.target, "targetname" );
AssertEx( IsDefined( manhandlerSpawner ), "Hostage with export " + self.export + " is tagged with a script_noteworthy of 'manhandled', but is not targeting a friendly spawner that will do the manhandling." );
}
friendly_manhandler = undefined; // may not spawn a manhandler if this hostage will just be guarded....the hostage getting manhandled will take care of spawning him
/*-----------------------
BUILD LIST OF ANIMS FOR THIS HOSTAGE
-------------------------*/
sAnimManhandledPrepare = undefined;
sAnimManhandledPrepareIdle = undefined;
sAnimManhandled = undefined;
sAnimManhandledIdle = undefined;
sAnimVariationSuffix = "";
//Is there a suffix to add to the anims? Hostages with the same breach anim
//may have different takedowns for different rooms
if ( IsDefined( self.script_parameters ) )
sAnimVariationSuffix = self.script_parameters;
//script_noteworthy defines whether hostage is taken down by a friendly or just guarded
switch( self.script_noteworthy )
{
case "manhandled":
sAnimManhandled = self.animation + "_manhandled";
break;
case "manhandled_guarded":
sAnimManhandled = self.animation + "_manhandled_guarded";
break;
}
sAnimManhandledIdle = sAnimManhandled + "_idle" + sAnimVariationSuffix;
sAnimManhandledPrepare = sAnimManhandled + "_prepare" + sAnimVariationSuffix;
sAnimManhandledPrepareIdle = sAnimManhandled + "_prepare_idle" + sAnimVariationSuffix;
sAnimManhandled = sAnimManhandled + sAnimVariationSuffix;
/*-----------------------
MAKE SURE THERE ARE AT LEAST THE MANHANDLED ANIM AND THE IDLE
-------------------------*/
self assert_if_anim_not_defined( sAnimManhandled );
self assert_if_anim_not_defined( sAnimManhandledIdle );
/*-----------------------
SPAWN MANHANDLER OUTSIDE DOOR, ONLY FOR THE ONE GETTING TAKEN DOWN
-------------------------*/
if ( self.script_noteworthy == "manhandled" )
{
friendly_manhandler = manhandlerSpawner spawn_ai( true );
friendly_manhandler Hide();
AssertEx( IsDefined( level.scr_anim[ "generic" ][ friendly_manhandler.animation ] ), "Manhandling friendly with export " + self.export + " does not have animation " + friendly_manhandler.animation + " defined in a level.scr_anim" );
self.reference anim_generic_first_frame( friendly_manhandler, friendly_manhandler.animation );
friendly_manhandler PushPlayer( true );
level.manhandler = friendly_manhandler;
friendly_manhandler.readyToManhandle = false;
friendly_manhandler thread manhandler_think();
}
/*-----------------------
WAIT UNTIL PLAYER PLANTS CHARGE
-------------------------*/
wait( 1 );
/*-----------------------
SHOW MANHANDLER, IF THERE IS ONE TO SHOW
-------------------------*/
if ( self.script_noteworthy == "manhandled" )
friendly_manhandler Show();
/*-----------------------
WAIT TO FINISH REGULAR BREACH ANIM
-------------------------*/
self waittill( "finished_breach_start_anim" );
/*-----------------------
PLAY MANHANDLE PREPARE ANIMS
-------------------------*/
if ( anim_exists( sAnimManhandledPrepare ) )
{
self.reference anim_generic( self, sAnimManhandledPrepare );
}
/*-----------------------
PLAY MANHANDLE PREPARE IDLE UNTIL MANHANDLER READY TO COME IN
-------------------------*/
if ( anim_exists( sAnimManhandledPrepareIdle ) )
self.reference thread anim_generic_loop( self, sAnimManhandledPrepareIdle, "stop_idle" );
else
sAnimManhandledPrepareIdle = undefined;
self.readyToBeManhandled = true;
/*-----------------------
ONLY WAIT IF THE HOSTAGE HAS AN APPROPRIATE LOOP
-------------------------*/
if ( IsDefined( sAnimManhandledPrepareIdle ) )
{
while ( self manhandler_hold() )
wait( 0.05 );
}
/*-----------------------
PLAY MANHANDLE ANIMS
-------------------------*/
self.reference notify( "stop_idle" );
self notify( "stop_idle" );
//Play friendly manhandler anim if there
if ( self.script_noteworthy == "manhandled" )
{
self.reference thread anim_generic( friendly_manhandler, friendly_manhandler.animation );
}
//play hostage manhandle
self.reference anim_generic( self, sAnimManhandled );
/*-----------------------
PLAY MANHANDLE IDLES
-------------------------*/
//Play friendly manhandler idle if there
if ( ( IsDefined( friendly_manhandler ) ) && ( IsDefined( level.scr_anim[ "generic" ][ friendly_manhandler.animation + "_idle" ] ) ) )
{
self.reference thread anim_generic_loop( friendly_manhandler, friendly_manhandler.animation + "_idle", "stop_idle" );
}
//play hostage manhandle idle if there
if ( IsDefined( level.scr_anim[ "generic" ][ sAnimManhandledIdle ] ) )
{
self.reference thread anim_generic_loop( self, sAnimManhandledIdle, "stop_idle" );
}
}
special_gulag_adjustment()
{
angles = level.player GetPlayerAngles();
angles = ( 0, angles[ 1 ], 0 );
forward = AnglesToForward( angles );
up = AnglesToUp( angles );
tag_origin = spawn_tag_origin();
tag_origin.origin = level.player.origin;
tag_origin.angles = angles;
level.player PlayerLinkToDelta( tag_origin, "tag_origin", 1, 45, 45, 90, 45, true );
time = 0.45;
//tag_origin MoveTo( tag_origin.origin + forward * 32 + up * -16, time, time * 0.4, time * 0.4 );
tag_origin MoveTo( tag_origin.origin + forward * 32 + up * -14, time, time * 0.4, time * 0.4 );
// tag_origin thread maps\_debug::drawtagforever( "tag_origin" );
wait( time );
level.player SetMoveSpeedScale( 0.5 );
thread player_slows_down();
level.player unlink();
level.price_breach_ent thread price_breach_ent_rotatesto_player();
level.price_breach_ent thread price_breach_ent_movesto_player();
}
player_slows_down()
{
wait( 0.25 );
blend = level create_blend( ::player_loses_speedscale, 0.5, 0 );
blend.time = 1.5;
}
price_breach_ent_movesto_player()
{
self endon( "stop_following_player" );
wait( 1.5 );
for ( ;; )
{
self moveto( level.player.origin, 2, 0, 0 );
wait( 0.05 );
}
}
price_breach_ent_rotatesto_player()
{
self endon( "stop_following_player" );
ent = spawn( "script_origin", (0,0,0) );
for ( ;; )
{
start = level.player.origin;
start = set_z( start, 0 );
end = self.origin;
end = set_z( end, 0 );
angles = vectortoangles( start - end );
forward = anglestoforward( angles );
my_forward = anglestoforward( self.angles );
my_right = anglestoright( self.angles );
dot = vectordot( forward, my_right );
ent.angles = self.angles;
yaw_dif = abs( acos( dot ) );
rotate = 2;
if ( rotate > yaw_dif )
rotate = yaw_dif;
if ( dot < 0 )
{
ent addyaw( rotate );
}
else
{
ent addyaw( rotate * -1 );
}
//self rotateto( angles, 0.1, 0, 0 );
self rotateto( ent.angles, 0.15 );
//println( "dot " + dot * 100 );
//angles += (0,-90,0);
//self rotateto( angles, 0.1, 0, 0 );
//self moveto( level.player.origin, 0.1, 0, 0 );
wait( 0.15 );
}
}
player_loses_speedscale( progress, start, end )
{
level.player setmovespeedscale( start * ( 1 - progress ) + end * progress );
}
manhandler_think()
{
level endon( "mission failed" );
if ( GetDvar( "hostage_missionfail" ) == "1" )
{
level endon( "player_shot_a_hostage" );
}
self thread magic_bullet_shield();
self setFlashbangImmunity( true );
if ( !flag( "no_mercy" ) )
self.ignoreme = true;
self.grenadeawareness = 0;
wait( 1 );
/*-----------------------
GET POINTERS TO THE HOSTAGES I WILL MANHANDLE
-------------------------*/
aHostages = [];
aAI = GetAISpeciesArray( "neutral", "civilian" );
foreach ( guy in aAI )
{
if ( !isdefined( guy.readyToBeManhandled ) )
continue;
if ( ( IsDefined( guy.script_slowmo_breach ) ) && ( guy.script_slowmo_breach == self.script_slowmo_breach ) )
{
if ( ( IsDefined( guy.script_noteworthy ) ) && ( IsSubStr( guy.script_noteworthy, "manhandled" ) ) )
aHostages = array_add( aHostages, guy );
}
}
AssertEx( aHostages.size > 0, "Manhandler with export " + self.export + " can not find any hostages with script_noteworthy containing 'manhandled*' in its script_slomo_breach number" );
/*-----------------------
WAIT FOR ALL ASSOCIATED HOSTAGES TO BE READY TO BE MANHANDLED
-------------------------*/
iCounter = aHostages.size;
tempArray = aHostages;
while ( iCounter > 0 )
{
wait( 0.05 );
foreach ( guy in tempArray )
{
// This shouldn't be necessary?
if ( !isdefined( guy ) || guy.readyToBeManhandled == true )
{
tempArray = array_remove( tempArray, guy );
iCounter--;
}
}
}
/*-----------------------
TELL MANHANDLER TO START MANHANDLE SEQUENCE
-------------------------*/
foreach ( guy in aHostages )
{
// This shouldn't be necessary?
if ( IsDefined( guy ) )
guy.startManhandling = true;
}
}
assert_if_anim_not_defined( sAnim )
{
AssertEx( anim_exists( sAnim ), "Hostage with export " + self.export + " does not have animation " + sAnim + " defined in level.scr_anim" );
}
anim_exists( sAnim )
{
if ( IsDefined( level.scr_anim[ "generic" ][ sAnim ] ) )
return true;
else
return false;
}
/*
=============
///ScriptDocBegin
"Name: add_slowmo_breach_custom_function( <animation> , <function> )"
"Summary: Adds a custom slowmo breach function for guys using a specific animation"
"Module: SlowMo Breach"
"MandatoryArg: <animation>: The animation the guy plays "
"OptionalArg: <function>: The function to call on this guy"
"Example: add_slowmo_breach_custom_function( "execution_onknees_soldier", ::_slomo_breach_pistol_guy );"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_slowmo_breach_custom_function( animation, function )
{
AssertEx( !isdefined( level._slowmo_functions[ animation ] ), "Tried to redefined function for slow mo breach guys with animation " + animation );
level._slowmo_functions[ animation ] = function;
}
/*
=============
///ScriptDocBegin
"Name: add_slowmo_breacher()"
"Summary: Adds an AI to level.breachfriendlies so they will be considered as candidates to breach into rooms with multiple breach points"
"Module: SlowMo Breach"
"CallOn: Any friendly AI"
"Example: level.price add_slowmo_breacher();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
add_slowmo_breacher()
{
if ( !isdefined( self ) )
return;
AssertEx( IsAI( self ), "add_slomo_breacher() can only be called on a friendly AI" );
if ( !isdefined( level.breachfriendlies ) )
{
level.breachfriendlies = [];
level.breachfriendlies[ 0 ] = self;
}
else if ( is_in_array( level.breachfriendlies, self ) )
{
return;
}
else
level.breachfriendlies = array_add( level.breachfriendlies, self );
}
/*
=============
///ScriptDocBegin
"Name: remove_slowmo_breacher()"
"Summary: Removes an AI from level.breachfriendlies so they will not be considered as a candidate to breach into rooms with multiple breach points"
"Module: SlowMo Breach"
"CallOn: Any friendly AI"
"Example: level.price remove_slowmo_breacher();"
"SPMP: singleplayer"
///ScriptDocEnd
=============
*/
remove_slowmo_breacher()
{
if ( !isdefined( self ) )
return;
AssertEx( IsAI( self ), "remove_slomo_breacher() can only be called on a friendly AI" );
if ( !isdefined( level.breachfriendlies ) )
return;
if ( is_in_array( level.breachfriendlies, self ) )
{
level.breachfriendlies = array_remove( level.breachfriendlies, self );
}
}
breach_failed_to_start()
{
fail_funcs = [];
fail_funcs[ fail_funcs.size ] = ::isMeleeing;
fail_funcs[ fail_funcs.size ] = ::isSwitchingWeapon;
fail_funcs[ fail_funcs.size ] = ::IsThrowingGrenade;
foreach ( player in level.players )
{
if ( player IsReloading() )
{
thread breach_reloading_hint();
return true;
}
if ( player using_illegal_breach_weapon() )
{
thread breach_bad_weapon_hint();
return true;
}
foreach ( func in fail_funcs )
{
if ( player call [[ func ]]() )
{
thread breach_not_ready_hint();
return true;
}
}
}
return false;
}
using_illegal_breach_weapon()
{
illegal_weapons = [];
illegal_weapons[ "riotshield" ] = true;
illegal_weapons[ "claymore" ] = true;
illegal_weapons[ "c4" ] = true;
illegal_weapons[ "none" ] = true;
weapon = self getCurrentWeapon();
return isdefined( illegal_weapons[ weapon ] );
}