566 lines
12 KiB
Plaintext
566 lines
12 KiB
Plaintext
#include maps\_utility;
|
|
#include common_scripts\utility;
|
|
#include maps\_anim;
|
|
#include maps\_hud_util;
|
|
#include maps\favela_escape_code;
|
|
#include maps\_specialops;
|
|
#include maps\_specialops_code;
|
|
|
|
#using_animtree( "generic_human" );
|
|
|
|
main()
|
|
{
|
|
level.friendlyfire_warnings = true;
|
|
|
|
so_delete_all_by_type( ::type_spawn_trigger, ::type_spawners );
|
|
|
|
base_map_assets();
|
|
precacheItem( "briefcase_bomb_defuse_sp" );
|
|
precacheModel( "prop_suitcase_bomb" );
|
|
|
|
precachestring( &"SO_DEFUSE_FAVELA_ESCAPE_DEFUSING" );
|
|
|
|
level._effect[ "light_c4_blink_nodlight" ] = loadfx( "misc/light_c4_blink_nodlight" );
|
|
level.cosine[ "60" ] = cos( 60 );
|
|
|
|
maps\_load::main();
|
|
maps\_compass::setupMiniMap("compass_map_favela_escape");
|
|
thread maps\favela_escape_amb::main();
|
|
|
|
defuse_setup();
|
|
}
|
|
|
|
base_map_assets()
|
|
{
|
|
maps\createart\favela_escape_art::main();
|
|
maps\createfx\favela_escape_fx::main();
|
|
maps\favela_escape_precache::main();
|
|
maps\favela_escape_fx::main();
|
|
maps\_hiding_door_anims::main();
|
|
}
|
|
|
|
defuse_setup()
|
|
{
|
|
airliner_delete();
|
|
|
|
clean_up_setup();
|
|
|
|
// 5 minutes in all.
|
|
level.challenge_time_limit = 300;
|
|
|
|
flag_init( "defuse_update_score" );
|
|
|
|
array_spawn_function_targetname( "civilian", ::civilian );
|
|
// add_global_spawn_function( "axis", ::ai_on_death );
|
|
// array_thread ( level.players, ::player_defuse_kill_clear );
|
|
|
|
// delete some spawners if not coop
|
|
if ( !is_coop() )
|
|
{
|
|
spawners = getentarray( "coop_only", "script_noteworthy" );
|
|
array_call( spawners, ::delete );
|
|
}
|
|
|
|
level thread enable_escape_warning();
|
|
level thread enable_escape_failure();
|
|
|
|
level thread open_door( "sbmodel_market_door_1" );
|
|
level thread open_door( "sbmodel_vista1_door1" );
|
|
|
|
change_combatmode_setup();
|
|
|
|
thread fade_challenge_in();
|
|
music_loop( "so_defuse_favela_escape_music", 99 );
|
|
|
|
level thread enable_challenge_timer( "defuse_start", "defuse_complete" );
|
|
level thread fade_challenge_out( "defuse_complete" );
|
|
|
|
level thread defuse_objectives();
|
|
}
|
|
|
|
defuse_objectives()
|
|
{
|
|
level endon( "special_op_terminated" );
|
|
|
|
level.defuse_count = 0;
|
|
|
|
// each index corresponds to a script_index key on the bomb in radiant.
|
|
level.objective_arr = [];
|
|
level.objective_arr[0] = &"SO_DEFUSE_FAVELA_ESCAPE_OBJ_BOMB_MARKET";
|
|
level.objective_arr[1] = &"SO_DEFUSE_FAVELA_ESCAPE_OBJ_BOMB_APARTMENT";
|
|
level.objective_arr[2] = &"SO_DEFUSE_FAVELA_ESCAPE_OBJ_BOMB_STORE";
|
|
|
|
defuse_location_arr = getentarray( "defuse_briefcase", "targetname" );
|
|
array_thread( defuse_location_arr, ::defuse_location_handler );
|
|
|
|
foreach( location in defuse_location_arr )
|
|
{
|
|
obj_id = location.script_index;
|
|
Objective_Add( location.script_index, "current", level.objective_arr[ obj_id ], location.origin + (0,0,24) );
|
|
location thread obj_switch_text( 400, obj_id );
|
|
}
|
|
|
|
while( level.defuse_count != 0 )
|
|
{
|
|
obj_id = flag_wait( "defuse_update_score" ); // obj_id passed from ::briefcase_defuse( ... )
|
|
flag_clear( "defuse_update_score" );
|
|
|
|
if ( isdefined( obj_id ) )
|
|
objective_state( obj_id, "done" );
|
|
}
|
|
|
|
flag_set( "defuse_complete" );
|
|
}
|
|
|
|
obj_switch_text( dist, obj_id )
|
|
{
|
|
// stop switching text once the bomb is defused
|
|
self endon( "briefcase_bomb_defused" );
|
|
|
|
while( true )
|
|
{
|
|
wait 0.05;
|
|
|
|
close = false;
|
|
foreach( player in level.players )
|
|
{
|
|
if ( squared( dist ) > distancesquared( self.origin, player.origin ) )
|
|
{
|
|
close = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( close )
|
|
{
|
|
Objective_SetPointerTextOverride( obj_id, &"SO_DEFUSE_FAVELA_ESCAPE_OBJ_TEXT" );
|
|
dist = 800;
|
|
}
|
|
else
|
|
{
|
|
Objective_SetPointerTextOverride( obj_id, "" );
|
|
dist = 400;
|
|
}
|
|
}
|
|
}
|
|
|
|
open_door( door_name )
|
|
{
|
|
door = getent( door_name, "targetname" );
|
|
linker = GetEnt( door.target, "targetname" );
|
|
door LinkTo( linker );
|
|
door ConnectPaths();
|
|
linker RotateTo( linker.script_angles, .5 );
|
|
linker waittill( "rotatedone" );
|
|
door Unlink();
|
|
}
|
|
|
|
defuse_location_handler()
|
|
{
|
|
self ent_flag_init( "briefcase_bomb_defused" );
|
|
|
|
level.defuse_count++;
|
|
|
|
bomb_array = getentarray( self.target, "targetname" );
|
|
array_thread( bomb_array, ::defuse_c4_light, self );
|
|
|
|
while( !self ent_flag( "briefcase_bomb_defused" ) )
|
|
{
|
|
self makeusable();
|
|
self sethintstring( &"SO_DEFUSE_FAVELA_ESCAPE_DEFUSE_HINT" );
|
|
|
|
self waittill( "trigger", player );
|
|
self MakeUnusable();
|
|
player briefcase_defuse( self );
|
|
}
|
|
}
|
|
|
|
defuse_c4_light( briefcase )
|
|
{
|
|
if ( self.model == "weapon_c4" )
|
|
{
|
|
wait randomfloat( 0.5 );
|
|
fx = PlayLoopedFX( getfx( "light_c4_blink_nodlight" ), 1, self gettagorigin( "tag_fx" ) );
|
|
briefcase ent_flag_wait( "briefcase_bomb_defused" );
|
|
fx delete();
|
|
}
|
|
}
|
|
|
|
change_combatmode_setup()
|
|
{
|
|
array = getstructarray( "change_combatmode_node", "script_noteworthy" );
|
|
array_thread( array, ::change_combatmode_node );
|
|
|
|
array = getentarray( "change_combatmode_trigger", "targetname" );
|
|
array_thread( array, ::change_combatmode_trigger );
|
|
}
|
|
|
|
change_combatmode_trigger()
|
|
{
|
|
origin_ent = getent( self.target, "targetname" );
|
|
|
|
dist_sqrd = origin_ent.radius * origin_ent.radius;
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "trigger", player );
|
|
assert( isplayer( player ) );
|
|
|
|
ai_array = getaiarray( "axis" );
|
|
foreach( ai in ai_array )
|
|
{
|
|
if ( distancesquared( ai.origin, origin_ent.origin ) > dist_sqrd )
|
|
continue;
|
|
|
|
ai notify( "stop_going_to_node" );
|
|
ai.combatMode = "cover";
|
|
ai.goalradius = 640;
|
|
ai setgoalentity( player );
|
|
}
|
|
wait 10;
|
|
}
|
|
}
|
|
|
|
change_combatmode_node()
|
|
{
|
|
assert( isdefined( self.script_combatmode ) );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "trigger", ai );
|
|
assert( isai( ai ) );
|
|
ai.combatMode = self.script_combatmode;
|
|
}
|
|
}
|
|
|
|
clean_up_setup()
|
|
{
|
|
add_global_spawn_function( "axis", ::clean_up_spawnfunc );
|
|
|
|
array = getentarray( "clean_up_volume", "targetname" );
|
|
array_thread( array, ::clean_up_volume );
|
|
|
|
array = getentarray( "clean_up_respawn_trigger", "script_noteworthy" );
|
|
array_thread( array, ::clean_up_respawn_trigger );
|
|
|
|
}
|
|
|
|
clean_up_volume()
|
|
{
|
|
assert( isdefined( self.script_group ) );
|
|
|
|
volume = self;
|
|
|
|
while( true )
|
|
{
|
|
wait 1;
|
|
player_in_volume = false;
|
|
foreach( player in level.players )
|
|
{
|
|
if ( player istouching( volume ) )
|
|
{
|
|
player_in_volume = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !player_in_volume )
|
|
level notify( "clean_up", volume.script_group );
|
|
}
|
|
}
|
|
|
|
clean_up_spawnfunc()
|
|
{
|
|
self endon( "death" );
|
|
|
|
if ( !isdefined( self.script_group ) )
|
|
return;
|
|
|
|
spawner = self.spawner;
|
|
|
|
while( true )
|
|
{
|
|
level waittill( "clean_up", script_group );
|
|
if ( self.script_group != script_group )
|
|
continue;
|
|
|
|
// to avoid multiple traces on the same frame
|
|
wait randomfloat( .3 );
|
|
|
|
can_be_seen = false;
|
|
foreach( player in level.players )
|
|
{
|
|
if ( self SightConeTrace( player geteye(), player ) )
|
|
{
|
|
can_be_seen = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( !can_be_seen )
|
|
{
|
|
spawner.count++;
|
|
self delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
clean_up_respawn_trigger()
|
|
{
|
|
assert( isdefined( self.script_group ) );
|
|
|
|
while( true )
|
|
{
|
|
self waittill( "trigger" );
|
|
while( true )
|
|
{
|
|
level waittill( "clean_up", script_group );
|
|
|
|
if ( self.script_group != script_group )
|
|
continue;
|
|
|
|
self thread maps\_spawner::trigger_spawner( self );
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
civilian()
|
|
{
|
|
self endon( "death" );
|
|
|
|
self thread civilian_death();
|
|
|
|
self waittill( "reached_path_end" );
|
|
|
|
timer = 0;
|
|
dist = 2000 * 2000;
|
|
|
|
while( timer < 10 )
|
|
{
|
|
is_safe = true;
|
|
foreach( player in level.players )
|
|
{
|
|
if ( DistanceSquared( self.origin, player.origin ) < dist )
|
|
{
|
|
is_safe = false;
|
|
break;
|
|
}
|
|
|
|
if ( within_fov( player.origin, player.angles, self.origin, level.cosine[ "60" ] ) )
|
|
{
|
|
is_safe = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if ( is_safe )
|
|
{
|
|
timer++;
|
|
}
|
|
else
|
|
{
|
|
timer = 0;
|
|
}
|
|
|
|
wait 0.5;
|
|
}
|
|
|
|
self delete();
|
|
}
|
|
|
|
civilian_death()
|
|
{
|
|
level endon( "defuse_complete" );
|
|
level endon( "missionfailed" );
|
|
level endon( "special_op_terminated" );
|
|
|
|
self waittill( "death", killer, type );
|
|
|
|
if ( !isplayer( killer ) )
|
|
return;
|
|
|
|
so_force_deadquote( "@SO_DEFUSE_FAVELA_ESCAPE_MISSION_FAILED_CIVILIAN" );
|
|
thread missionfailedwrapper();
|
|
}
|
|
|
|
ai_on_death()
|
|
{
|
|
self waittill( "death", attacker, cause );
|
|
|
|
if ( !isplayer( attacker ) )
|
|
return;
|
|
|
|
attacker.defuse_kills++;
|
|
if ( self isFlashed() )
|
|
attacker.defuse_flashed_kills++;
|
|
if ( common_scripts\_destructible::getDamageType( cause ) == "splash" )
|
|
attacker.defuse_frag_kills++;
|
|
if ( common_scripts\_destructible::getDamageType( cause ) == "melee" )
|
|
attacker.defuse_knife_kills++;
|
|
|
|
flag_set( "defuse_update_score" );
|
|
}
|
|
|
|
player_defuse_kill_clear()
|
|
{
|
|
self.defuse_kills = 0;
|
|
self.defuse_flashed_kills = 0;
|
|
self.defuse_frag_kills = 0;
|
|
self.defuse_knife_kills = 0;
|
|
}
|
|
|
|
briefcase_defuse( briefcase )
|
|
{
|
|
// link player
|
|
self playerLinkTo( briefcase );
|
|
self PlayerLinkedOffsetEnable();
|
|
|
|
// get current weapon?
|
|
lastWeapon = self getCurrentWeapon();
|
|
|
|
// give briefcase weapon
|
|
self giveWeapon( "briefcase_bomb_defuse_sp" );
|
|
self setWeaponAmmoStock( "briefcase_bomb_defuse_sp", 0 );
|
|
self setWeaponAmmoClip( "briefcase_bomb_defuse_sp", 0 );
|
|
self switchToWeapon( "briefcase_bomb_defuse_sp" );
|
|
self DisableWeaponSwitch();
|
|
self DisableOffhandWeapons();
|
|
self AllowMelee( false );
|
|
|
|
briefcase hide();
|
|
|
|
self thread downed_while_defusing( lastWeapon, briefcase );
|
|
|
|
self waittill_either( "coop_downed", "weapon_change" );
|
|
|
|
if ( !self ent_flag_exist( "coop_downed" ) || !self ent_flag( "coop_downed" ) )
|
|
{
|
|
// Add 3D Person Briefcase
|
|
self attach_briefcase_model();
|
|
|
|
if ( !self ent_flag_exist( "coop_downed" ) || !self ent_flag( "coop_downed" ) )
|
|
{
|
|
// display usebar
|
|
if ( self defuse_use_bar( 4.5, briefcase ) )
|
|
{
|
|
briefcase ent_flag_set( "briefcase_bomb_defused" );
|
|
level.defuse_count--;
|
|
flag_set( "defuse_update_score", briefcase.script_index );
|
|
}
|
|
}
|
|
|
|
// remove 3d person briefcase
|
|
self detach_briefcase_model();
|
|
}
|
|
|
|
briefcase show();
|
|
|
|
primary_weapons = self GetWeaponsListPrimaries();
|
|
assert( primary_weapons.size > 0 );
|
|
|
|
// defensive, I don't know that I'll always save a valid primary weapon.
|
|
if ( !is_in_array( self GetWeaponsListPrimaries(), lastWeapon ) )
|
|
{
|
|
lastWeapon = primary_weapons[0];
|
|
}
|
|
|
|
// switch back to lastWeapon
|
|
self switchToWeapon( lastWeapon );
|
|
self unlink();
|
|
self waittill( "weapon_change" );
|
|
|
|
wait .5; // buffer time between tries.
|
|
|
|
self AllowMelee( true );
|
|
self EnableOffhandWeapons();
|
|
self EnableWeaponSwitch();
|
|
}
|
|
|
|
downed_while_defusing( lastWeapon, briefcase )
|
|
{
|
|
if ( self ent_flag_exist( "coop_downed" ) )
|
|
{
|
|
briefcase endon( "briefcase_bomb_defused" );
|
|
self ent_flag_wait( "coop_downed" );
|
|
|
|
self SwitchToWeaponImmediate( lastWeapon );
|
|
}
|
|
}
|
|
|
|
defuse_use_bar( fill_time, briefcase )
|
|
{
|
|
briefcase.defuse_time = 0;
|
|
|
|
buttonTime = briefcase.defuse_time;
|
|
totalTime = fill_time;
|
|
bar = self createClientProgressBar( self, 57 );
|
|
|
|
text = self createClientFontString( "default", 1.2 );
|
|
text setPoint( "CENTER", undefined, 0, 45 ); // old 20
|
|
text settext( &"SO_DEFUSE_FAVELA_ESCAPE_DEFUSING" );
|
|
|
|
while ( self use_active() )
|
|
{
|
|
bar updateBar( buttonTime / totalTime );
|
|
wait( 0.05 );
|
|
buttonTime += 0.05;
|
|
if ( buttonTime > totalTime )
|
|
{
|
|
self notify_area_clear();
|
|
text destroyElem();
|
|
bar destroyElem();
|
|
return true;
|
|
}
|
|
}
|
|
|
|
briefcase.defuse_time = buttonTime;
|
|
text destroyElem();
|
|
bar destroyElem();
|
|
|
|
return false;
|
|
}
|
|
|
|
notify_area_clear()
|
|
{
|
|
if ( isdefined( level.area_clear_time ) )
|
|
{
|
|
if ( ( level.area_clear_time + 5000 ) > gettime() )
|
|
return;
|
|
}
|
|
|
|
level.area_clear_time = gettime();
|
|
self notify( "so_bcs_area_secure" );
|
|
}
|
|
|
|
use_active()
|
|
{
|
|
if ( !self UseButtonPressed() )
|
|
return false;
|
|
if ( flag( "special_op_failed" ) )
|
|
return false;
|
|
if ( self ent_flag_exist( "coop_downed" ) && self ent_flag( "coop_downed" ) )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
attach_briefcase_model()
|
|
{
|
|
wait ( 0.6 );
|
|
self attach( "prop_suitcase_bomb", "tag_inhand", true );
|
|
}
|
|
|
|
|
|
detach_briefcase_model()
|
|
{
|
|
wait ( 0.5 );
|
|
self detach( "prop_suitcase_bomb", "tag_inhand", true );
|
|
}
|
|
|
|
airliner_delete()
|
|
{
|
|
ents = GetEntArray( "sbmodel_airliner_flyby", "targetname" );
|
|
array_call( ents, ::delete );
|
|
}
|