2097 lines
56 KiB
Plaintext
2097 lines
56 KiB
Plaintext
#include common_scripts\utility;
|
|
#include maps\mp\alien\_persistence;
|
|
#include maps\mp\_utility;
|
|
#include maps\mp\alien\_perk_utility;
|
|
#include maps\mp\alien\_utility;
|
|
|
|
CONST_TRAP_OUTLINE_COLOR_INDEX = 3;
|
|
CONST_TRAP_OUTLINE_RED_COLOR_INDEX = 1;
|
|
CONST_FLARE_THREATBIAS = 1000;
|
|
CONST_FLARE_TIME = 20;
|
|
CONST_UPGRADED_FLARE_TIME = 30;
|
|
CONST_FLARE_HEALTH = 900000;
|
|
CONST_TURRET_OVERHEAT_TIME = 2;
|
|
CONST_TURRET_COOLDOWN_TIME = 1;
|
|
|
|
|
|
// traps go here!
|
|
traps_init()
|
|
{
|
|
// setup parameter of fire that burns aliens when crossed
|
|
level.outline_switch[ "traps_fire" ] = true;
|
|
level thread fire_trap_init();
|
|
|
|
// electrofied puddles that shocks aliens when stepped into
|
|
level.outline_switch[ "traps_puddle" ] = true;
|
|
level thread electric_puddle_init();
|
|
|
|
// electric fences that shocks aliens when climbing
|
|
level.outline_switch[ "traps_fence" ] = true;
|
|
level thread electric_fence_init();
|
|
level.spawnGlow["enemy"] = loadfx( "fx/misc/flare_ambient" );
|
|
|
|
}
|
|
|
|
// global trap activation condition
|
|
can_activate_trap( trap )
|
|
{
|
|
assertex( isdefined( trap ) && isdefined( trap.cost ), "[int] trap_struct.cost is not defined" );
|
|
|
|
// self is player
|
|
return self player_has_enough_currency( int( trap.cost ) );
|
|
}
|
|
|
|
|
|
|
|
|
|
//*****************************************************************
|
|
// FIRE TRAP
|
|
//*****************************************************************
|
|
|
|
CONST_FIRE_TRAP_DURATION = 50; // seconds fire last
|
|
CONST_FIRE_TRAP_DURATION_SMALL = 50; // seconds fire last(custom)
|
|
CONST_FIRE_TRAP_DURATION_MED = 80; // seconds fire last(custom)
|
|
CONST_FIRE_TRAP_DURATION_LARGE = 110; // seconds fire last(custom)
|
|
CONST_FIRE_TRAP_DAMAGE_SMALL = 400; // total damage
|
|
CONST_FIRE_TRAP_DAMAGE_MED = 600; // total damage
|
|
CONST_FIRE_TRAP_DAMAGE_LARGE = 800; // total damage
|
|
|
|
CONST_FIRE_TRAP_DAMAGE_TIME = 6; // total time for said damage to be applied
|
|
CONST_FIRE_TRAP_DAMAGE_PLAYER = 33; // total damage
|
|
CONST_FIRE_TRAP_DAMAGE_TIME_PLAYER = 3; // total time for said damage to be applied
|
|
|
|
CONST_FIRE_TRAP_COST = 750; // currency cost to enable fence
|
|
CONST_FIRE_TRAP_COST_SMALL = 300; // small
|
|
CONST_FIRE_TRAP_COST_MED = 750; // med
|
|
CONST_FIRE_TRAP_COST_LARGE = 1000; // large
|
|
|
|
CONST_POURING_TIME = 2.5; // time it takes to pour the gas after buying
|
|
|
|
fire_trap_init()
|
|
{
|
|
barrels = getentarray( "fire_trap_barrel", "targetname" );
|
|
if ( !isdefined( barrels ) || barrels.size == 0 )
|
|
return;
|
|
|
|
// wait before players are ready, we decide how to setup usage based on match type
|
|
while ( !isdefined( level.players ) || level.players.size < 1 )
|
|
wait 0.05;
|
|
|
|
level.fire_traps = [];
|
|
foreach ( barrel in barrels )
|
|
{
|
|
barrel.damagefeedback = false;
|
|
fire_trap = fire_trap_setup( barrel );
|
|
level.fire_traps[ level.fire_traps.size ] = fire_trap;
|
|
fire_trap thread fire_trap_think();
|
|
}
|
|
}
|
|
|
|
// per trap setup ( 3 entities involved per trap )
|
|
// barrel_xmodel -> gas_scriptmodel -> activation_trigger -> damage_trigger -> fire_link_struct_1 -> fire_link_struct_2 etc...
|
|
fire_trap_setup( barrel )
|
|
{
|
|
fire_trap = SpawnStruct();
|
|
fire_trap.trap_type = "traps_fire";
|
|
|
|
// setup barrel for use
|
|
fire_trap.barrel = barrel;
|
|
|
|
// setup damage trigger
|
|
fire_trap.burn_trig = getent( fire_trap.barrel.target, "targetname" );
|
|
|
|
//needed for tracking kills with the fire trap
|
|
fire_trap.burn_trig.script_noteworthy = "fire_trap";
|
|
|
|
// setup fire fx locations
|
|
fire_link_structs = [];
|
|
cur_loc = fire_trap.burn_trig;
|
|
|
|
while ( isdefined( cur_loc ) && isdefined( cur_loc.target ) )
|
|
{
|
|
fire_loc = getstruct( cur_loc.target, "targetname" );
|
|
|
|
if ( !isdefined( fire_loc ) )
|
|
break;
|
|
|
|
// break once we have gone full circle
|
|
if ( isdefined( fire_link_structs[ 0 ] ) && fire_loc == fire_link_structs[ 0 ] )
|
|
{
|
|
assertmsg( "Looping fire location structs is not allowed" );
|
|
break;
|
|
}
|
|
|
|
fire_link_structs[ fire_link_structs.size ] = fire_loc;
|
|
cur_loc = fire_loc;
|
|
}
|
|
|
|
// setup points betweenlinked fire location structs to play actual fire fx with defined spacing
|
|
interval_dist = 45; // fx spacing
|
|
max_fire_fx = 15; // leak safe, max fx allowed per trap
|
|
fire_fx_array = []; // list of fx spawned
|
|
|
|
for ( i = 0; i < fire_link_structs.size - 1; i++ )
|
|
{
|
|
dist_to_next = distance( fire_link_structs[ i ].origin, fire_link_structs[ i + 1 ].origin );
|
|
for ( j = 0; j < int( dist_to_next / interval_dist ); j++ )
|
|
{
|
|
// position calculated between the next fire link struct
|
|
position = VectorLerp( fire_link_structs[ i ].origin, fire_link_structs[ i + 1 ].origin, j/int( dist_to_next / interval_dist ) );
|
|
|
|
// track spawned fx for clean up later
|
|
fire_fx_array[ fire_fx_array.size ] = position;
|
|
|
|
// leak safe
|
|
if ( fire_fx_array.size > max_fire_fx ) { break; }
|
|
}
|
|
// leak safe
|
|
if ( fire_fx_array.size > max_fire_fx ) { break; }
|
|
}
|
|
|
|
// array of vectors where fire fx will play
|
|
fire_trap.fire_fx_locs = fire_fx_array;
|
|
|
|
// structs where fire fx will play
|
|
fire_trap.fire_link_structs = fire_link_structs;
|
|
|
|
// setup fire fx
|
|
fire_trap.fire_fx = LoadFX( "vfx/gameplay/alien/vfx_alien_trap_fire" );
|
|
|
|
// setup fire trap initial values
|
|
fire_trap.burning = false;
|
|
fire_trap.duration = CONST_FIRE_TRAP_DURATION;
|
|
fire_trap.base_duration = CONST_FIRE_TRAP_DURATION;
|
|
fire_trap.DoT = CONST_FIRE_TRAP_DAMAGE_MED;
|
|
fire_trap.damage_time = CONST_FIRE_TRAP_DAMAGE_TIME;
|
|
fire_trap.DoT_player = CONST_FIRE_TRAP_DAMAGE_PLAYER;
|
|
fire_trap.damage_time_player = CONST_FIRE_TRAP_DAMAGE_TIME_PLAYER;
|
|
|
|
// setup different sizes that costs different
|
|
fire_trap fire_trap_setup_sizes();
|
|
|
|
fire_trap.barrel SetCursorHint( "HINT_NOICON" );
|
|
fire_trap.barrel SetHintString( fire_trap.hintString );
|
|
fire_trap.barrel MakeUsable();
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list ( barrel, fire_trap.cost );
|
|
|
|
return fire_trap;
|
|
}
|
|
|
|
// cost override from radiant setup
|
|
fire_trap_setup_sizes()
|
|
{
|
|
sizes = [ "small", "medium", "large" ];
|
|
custom_lifespan_array = [];
|
|
custom_lifespan_array["small"] = CONST_FIRE_TRAP_DURATION_SMALL;
|
|
custom_lifespan_array["medium"] = CONST_FIRE_TRAP_DURATION_MED;
|
|
custom_lifespan_array["large"] = CONST_FIRE_TRAP_DURATION_LARGE;
|
|
|
|
size_index = 1; // default medium
|
|
if ( isdefined( self.barrel.script_noteworthy ) )
|
|
{
|
|
if ( self.barrel.script_noteworthy == "small" )
|
|
size_index = 0;
|
|
if ( self.barrel.script_noteworthy == "large" )
|
|
size_index = 2;
|
|
//ex: "custom small large" for small cost, large lifespan
|
|
if(IsSubStr( self.barrel.script_noteworthy, "custom"))
|
|
{
|
|
tokens = strtok(self.barrel.script_noteworthy, " ");
|
|
switch ( tokens[1] )
|
|
{
|
|
case "small":
|
|
size_index = 0;
|
|
break;
|
|
case "medium":
|
|
size_index = 1;
|
|
break;
|
|
case "large":
|
|
size_index = 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
self.custom = tokens[2];
|
|
}
|
|
}
|
|
|
|
if ( isPlayingSolo() )
|
|
size_index = int( max( 0, size_index - 1 ) );
|
|
|
|
if ( sizes[ size_index ] == "small" )
|
|
{
|
|
self.DoT = CONST_FIRE_TRAP_DAMAGE_SMALL;
|
|
self.cost = CONST_FIRE_TRAP_COST_SMALL;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FIRE_TRAP_SMALL";
|
|
}
|
|
if ( sizes[ size_index ] == "medium" )
|
|
{
|
|
self.DoT = CONST_FIRE_TRAP_DAMAGE_MED;
|
|
self.cost = CONST_FIRE_TRAP_COST_MED;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FIRE_TRAP_MED";
|
|
}
|
|
if ( sizes[ size_index ] == "large" )
|
|
{
|
|
self.DoT = CONST_FIRE_TRAP_DAMAGE_LARGE;
|
|
self.cost = CONST_FIRE_TRAP_COST_LARGE;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FIRE_TRAP_LARGE";
|
|
}
|
|
|
|
if(IsDefined(self.custom))
|
|
{
|
|
self.base_duration = custom_lifespan_array[self.custom];
|
|
}
|
|
}
|
|
|
|
trap_BBprint( trap_type, owner, loc )
|
|
{
|
|
// =========================== blackbox print [START] ===========================
|
|
level.alienBBData[ "traps_used" ]++;
|
|
owner maps\mp\alien\_persistence::eog_player_update_stat( "traps", 1 );
|
|
|
|
trapx = loc[ 0 ];
|
|
trapy = loc[ 1 ];
|
|
trapz = loc[ 2 ];
|
|
traptype = trap_type;
|
|
ownername = "";
|
|
if ( isdefined( owner.name ) )
|
|
ownername = owner.name;
|
|
|
|
/#
|
|
if ( GetDvarInt( "alien_bbprint_debug" ) > 0 )
|
|
{
|
|
IPrintLnBold( "^8bbprint: alientrap \n" +
|
|
" traptype=" + traptype +
|
|
" trapx,y,z=" + loc +
|
|
" ownername=" + ownername );
|
|
}
|
|
#/
|
|
|
|
bbprint( "alientrap",
|
|
"traptype %s trapx %f trapy %f trapz %f ownername %s ",
|
|
traptype,
|
|
trapx,
|
|
trapy,
|
|
trapz,
|
|
ownername );
|
|
// =========================== [END] blackbox print ===========================
|
|
}
|
|
|
|
// main loop
|
|
fire_trap_think()
|
|
{
|
|
// self is fire_trap struct
|
|
self endon( "disable_fire_trap" );
|
|
level endon( "game_ended" );
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
// [off] - wait for activate
|
|
while ( !self.burning )
|
|
{
|
|
self.barrel waittill( "trigger", owner );
|
|
|
|
if ( owner can_activate_trap( self ) )
|
|
{
|
|
thread trap_BBprint( "fire", owner, self.barrel.origin );
|
|
|
|
level thread maps\mp\alien\_music_and_dialog::playVOForTrapActivation( owner, self.trap_type);
|
|
|
|
self.owner = owner;
|
|
|
|
self.barrel SetHintString( "" );
|
|
self.barrel MakeUnUsable();
|
|
|
|
// cost the activator
|
|
price = int( self.cost * ( owner perk_GetTrapCostScalar() ));
|
|
discount = ( self.cost - price );
|
|
owner take_player_currency( price, false, "trap" );
|
|
|
|
self.duration = int( self.base_duration * ( owner perk_GetTrapDurationScalar() ) );
|
|
|
|
//self thread fire_trap_burn( impact_loc );
|
|
self thread sounds_fire_trap( self.barrel.origin );
|
|
|
|
self thread fire_trap_burn( self.barrel.origin );
|
|
owner thread stop_firetrap_on_disconnect( self );
|
|
|
|
// break into [on]
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
wait 0.05;
|
|
owner setLowerMessage( "no_money", &"ALIEN_COLLECTIBLES_NO_MONEY", 3 );
|
|
//owner iprintlnBold( "Fire Trap costs $" + self.cost );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::remove_from_outline_watch_list( self.barrel );
|
|
|
|
self waittill( "fire_trap_exhausted" );
|
|
|
|
// padding
|
|
wait 0.5;
|
|
|
|
//self.owner = undefined;
|
|
MarkDangerousNodesInTrigger( self.burn_trig, false );
|
|
self.burning = false;
|
|
self.barrel SetHintString( self.hintString );
|
|
self.barrel MakeUsable();
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list( self.barrel, self.cost );
|
|
|
|
// ^ loop ^
|
|
}
|
|
}
|
|
|
|
sounds_fire_trap( barrel )
|
|
{
|
|
sound_flame_burst = spawn( "script_origin", barrel );
|
|
sound_flame_burst playsound( "alien_incendiary_impact" );
|
|
|
|
self.sound_flames = spawn( "script_origin", barrel );
|
|
self.sound_flames playloopsound( "fire_trap_fire_lp" );
|
|
}
|
|
|
|
// wait for trap ignition
|
|
fire_trap_wait_for_impact()
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "fire_trap_exhausted" );
|
|
|
|
while ( 1 )
|
|
{
|
|
// wait for player activation by igniting the fire
|
|
self.activation_trig waittill( "damage", damage, attacker, direction_vec, impact_loc, damage_type );
|
|
|
|
if ( !isPlayer( attacker ) )
|
|
{
|
|
if ( isdefined( attacker.owner ) && isPlayer( attacker.owner ) )
|
|
{
|
|
attacker = attacker.owner;
|
|
}
|
|
else
|
|
{
|
|
wait 0.05;
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( !isdefined( damage_type ) )
|
|
{
|
|
wait 0.05;
|
|
continue;
|
|
}
|
|
|
|
// skip no fire igniting damages
|
|
type = ToLower( damage_type );
|
|
switch( type )
|
|
{
|
|
case "unknown":
|
|
case "mod_impact":
|
|
case "mod_melee":
|
|
case "mod_crush":
|
|
case "melee":
|
|
wait 0.05;
|
|
continue;
|
|
default:
|
|
return impact_loc;
|
|
}
|
|
|
|
return impact_loc;
|
|
}
|
|
|
|
return undefined;
|
|
}
|
|
|
|
// burn and do damage
|
|
fire_trap_burn( fire_start_loc )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "fire_trap_exhausted" );
|
|
|
|
self thread monitor_fire_trap_exhausted( fire_start_loc );
|
|
MarkDangerousNodesInTrigger( self.burn_trig, true );
|
|
|
|
wait 2; // padding so players don't get burned
|
|
|
|
while ( true )
|
|
{
|
|
// waittill someone touches fire
|
|
self.burn_trig waittill( "trigger", victim );
|
|
|
|
if ( !isdefined( victim )
|
|
|| !isReallyAlive( victim )
|
|
|| ( !isplayer( victim ) && !IsAgent( victim ) )
|
|
|| ( isDefined( victim.burning ) && victim.burning ) )
|
|
{
|
|
continue;
|
|
}
|
|
|
|
self thread do_damage_over_time( victim );
|
|
}
|
|
}
|
|
|
|
do_damage_over_time( victim )
|
|
{
|
|
// self is fire trap struct
|
|
level endon( "game_ended" );
|
|
victim endon( "death" );
|
|
|
|
if( !isDefined( self.owner ) )
|
|
return;
|
|
|
|
// only one instance of burn
|
|
victim notify( "fire_trap_burning" );
|
|
victim endon( "fire_trap_burning" );
|
|
|
|
victim.burning = true;
|
|
|
|
if ( isplayer( victim ) )
|
|
{
|
|
duration = self.damage_time_player;
|
|
DoT = self.DoT_player;
|
|
}
|
|
else
|
|
{
|
|
duration = self.damage_time * ( self.owner perk_GetTrapDamageScalar() );
|
|
DoT = self.DoT * level.alien_health_per_player_scalar[ level.players.size ] * ( self.owner perk_GetTrapDamageScalar() );
|
|
}
|
|
|
|
should_use_fire_fx = victim is_alien_agent();
|
|
|
|
if ( should_use_fire_fx )
|
|
victim maps\mp\alien\_alien_fx::alien_fire_on();
|
|
|
|
interval_time = 1;
|
|
damage_per_interval = DoT / ( duration / interval_time );
|
|
|
|
victim do_damage_until_timeout( damage_per_interval, duration, interval_time, self );
|
|
if ( should_use_fire_fx )
|
|
{
|
|
victim maps\mp\alien\_alien_fx::alien_fire_off();
|
|
}
|
|
victim.burning = undefined;
|
|
}
|
|
|
|
do_damage_until_timeout( damage, duration, interval, attacker )
|
|
{
|
|
self endon( "death" );
|
|
attacker.owner endon( "disconnect" );
|
|
elapsed_time = 0;
|
|
while ( elapsed_time < duration && isDefined( attacker.owner ) )
|
|
{
|
|
// create marker for passing in damage type
|
|
attacker.owner.burning_victim = true;
|
|
|
|
if ( isplayer( self ) )
|
|
self DoDamage( damage, self.origin, undefined, attacker.burn_trig );
|
|
else if ( IsDefined( attacker ) && isDefined( attacker.owner ) )
|
|
self DoDamage( damage, self.origin, attacker.owner, attacker.burn_trig );
|
|
|
|
elapsed_time += 1.0;
|
|
wait interval;
|
|
}
|
|
}
|
|
|
|
|
|
// notifies when fire burns out, in predefined time
|
|
monitor_fire_trap_exhausted( fire_start_loc )
|
|
{
|
|
level endon( "game_ended" );
|
|
self endon( "owner_disconnected" );
|
|
|
|
// sort fire fx locs array by closest to fire start loc
|
|
// this is to start fire chain originated from point of fire start, ex: bullet impact location
|
|
fire_fx_locs = sort_vectors_by_distance( self.fire_fx_locs, fire_start_loc );
|
|
fx_interval = 0.25; // fx interval timing
|
|
|
|
// play fx in sequence
|
|
self.fire_fx_array = play_fire( fire_fx_locs, fx_interval );
|
|
|
|
// wait total burn duration
|
|
wait self.duration;
|
|
|
|
// end it
|
|
self notify( "fire_trap_exhausted" );
|
|
if( isDefined( self.fire_fx_array ) )
|
|
self kill_fire( self.fire_fx_array );
|
|
}
|
|
|
|
sort_vectors_by_distance( array, vec )
|
|
{
|
|
sorted_array = [];
|
|
|
|
while ( array.size )
|
|
{
|
|
idx = get_closest_vec_index( array, vec );
|
|
sorted_array[ sorted_array.size ] = array[ idx ];
|
|
array = array_remove( array, array[idx] );
|
|
}
|
|
|
|
return sorted_array;
|
|
}
|
|
|
|
get_closest_vec_index( array, vec )
|
|
{
|
|
closest_key = 0;
|
|
closest_element = array[ 0 ];
|
|
foreach ( key, element in array )
|
|
{
|
|
if ( distance( element, vec ) <= distance( closest_element, vec ) )
|
|
{
|
|
closest_key = key;
|
|
closest_element = element;
|
|
}
|
|
}
|
|
return closest_key;
|
|
}
|
|
|
|
// param 1 = array of locations to play fx, param 2 = delay per fx
|
|
play_fire( fire_fx_locs, fx_interval )
|
|
{
|
|
fire_fx_array = []; // list of fx spawned
|
|
for ( i = 0; i < fire_fx_locs.size; i++ )
|
|
{
|
|
// spawn fire fx per spacing
|
|
gasFire = SpawnFx( self.fire_fx, fire_fx_locs[ i ] );
|
|
triggerFx( gasFire );
|
|
|
|
// track spawned fx for clean up later
|
|
fire_fx_array[ fire_fx_array.size ] = gasFire;
|
|
|
|
// wait delay between fx
|
|
wait fx_interval;
|
|
}
|
|
|
|
return fire_fx_array;
|
|
}
|
|
|
|
// kill all fire fx
|
|
kill_fire( fire_fx_array )
|
|
{
|
|
if ( isDefined( self.sound_flames ) )
|
|
self thread sounds_kill_flames();
|
|
//clean up list of fx spawned
|
|
if ( isDefined( fire_fx_array ) )
|
|
{
|
|
foreach ( gasFire in fire_fx_array )
|
|
{
|
|
if ( isDefined( gasFire ) )
|
|
gasFire delete();
|
|
}
|
|
}
|
|
}
|
|
|
|
sounds_kill_flames()
|
|
{
|
|
self.sound_flames endon( "death" );
|
|
|
|
flames_end = spawn( "script_origin", self.sound_flames.origin );
|
|
flames_end playsound( "fire_trap_fire_end_lp" );
|
|
|
|
wait 0.5;
|
|
|
|
self.sound_flames stopsounds();
|
|
wait 0.1;
|
|
self.sound_flames delete();
|
|
}
|
|
|
|
//*****************************************************************
|
|
// ELECTRIC TRAPS: COMMON
|
|
//*****************************************************************
|
|
|
|
CONST_SHOCK_INTERVAL = 0.35; // time delay between shocks
|
|
CONST_GENERATOR_POWER = 20; // number of shock attacks for max capacity
|
|
|
|
// run electric generator
|
|
run_generator()
|
|
{
|
|
self notify( "electric_trap_turned_on" );
|
|
|
|
self eletric_trap_asserts();
|
|
|
|
self.running = true;
|
|
self.capacity = self.max_capacity;
|
|
self.generator SetHintString( "" );
|
|
self.generator MakeUnUsable();
|
|
}
|
|
|
|
// shocks victim!
|
|
trap_shock( victim, damage_override, use_capacity )
|
|
{
|
|
self endon( "electric_trap_turned_off" );
|
|
victim endon( "death" );
|
|
|
|
if( !isDefined( self.owner ) )
|
|
return;
|
|
|
|
self eletric_trap_asserts();
|
|
|
|
shock_damage = self.shock_damage * level.alien_health_per_player_scalar[ level.players.size ] * (self.owner perk_GetTrapDamageScalar() );
|
|
if ( isdefined( damage_override ) )
|
|
shock_damage = damage_override;
|
|
|
|
// self is trap struct
|
|
victim.shocked = true;
|
|
if ( !isalive( victim ) )
|
|
return;
|
|
|
|
// fx on victim
|
|
playfx( self.shock_fx[ "shock" ], victim.origin + ( 0, 0, 32 ) );
|
|
playfx( self.shock_fx[ "sparks" ], victim.origin + ( 0, 0, 32 ) );
|
|
|
|
debug_electric_trap_print( victim.origin, "hp:" + victim.health, ( 0.5, 0.5, 1 ), 0.75, 2, 1 );
|
|
|
|
victim.pain_registered = true; // no pain logic when shocked, for now
|
|
wait 0.05;
|
|
|
|
// allow damage of self if victim is player
|
|
owner = self.owner;
|
|
if ( isplayer( victim ) )
|
|
owner = self.generator;
|
|
|
|
if ( isdefined( use_capacity ) && use_capacity )
|
|
{
|
|
// remove one shock amount from total shock capacity
|
|
self.capacity--;
|
|
}
|
|
|
|
victim DoDamage( shock_damage, victim.origin, owner, self.generator, "MOD_EXPLOSIVE" );
|
|
|
|
// sfx for trap shock
|
|
playSoundAtPos( victim.origin, "alien_fence_shock" );
|
|
|
|
if ( !isalive( victim ) && IsAgent( victim ) )
|
|
{
|
|
pos = victim wait_for_ragdoll_pos();
|
|
if ( isdefined( pos ) )
|
|
{
|
|
wait 0.1;
|
|
PhysicsExplosionSphere( pos, 300, 150, 5 );
|
|
playfx( self.shock_fx[ "shock" ], pos );
|
|
playfx( self.shock_fx[ "sparks" ], pos );
|
|
}
|
|
}
|
|
|
|
random_delay = RandomFloatRange( self.shock_interval/2, self.shock_interval*1.5 );
|
|
victim thread time_out_shocked_state( random_delay );
|
|
}
|
|
|
|
// this will catch shock state reset in case trap times out during shock and kills trap_shock()
|
|
time_out_shocked_state( delay )
|
|
{
|
|
self endon( "death" );
|
|
self endon( "disconnect" );
|
|
wait delay;
|
|
self.shocked = false;
|
|
}
|
|
|
|
wait_for_ragdoll_pos()
|
|
{
|
|
self endon( "ragdoll_timed_out" );
|
|
self thread ragdoll_timeout( 1 );
|
|
self waittill( "in_ragdoll", pos );
|
|
return pos;
|
|
}
|
|
|
|
ragdoll_timeout( time )
|
|
{
|
|
wait time;
|
|
if ( isdefined( self ) )
|
|
self notify( "ragdoll_timed_out" );
|
|
}
|
|
|
|
debug_electric_trap_print( pos, string, color, alpha, scale, time )
|
|
{
|
|
/#
|
|
if ( GetDvarInt( "debug_trap" ) == 1 )
|
|
thread debug_electric_trap_print_raw( pos, string, color, alpha, scale, time );
|
|
#/
|
|
}
|
|
|
|
debug_electric_trap_print_raw( pos, string, color, alpha, scale, time )
|
|
{
|
|
level endon ( "game_ended" );
|
|
while ( time > 0 )
|
|
{
|
|
Print3d( pos, string, color, alpha, scale, 1 );
|
|
time -= 0.05;
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
// global electric trap think
|
|
run_electric_trap( play_trap_on_fx, play_trap_off_fx, play_ambient_shocks )
|
|
{
|
|
// self is trap struct
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
self eletric_trap_asserts();
|
|
|
|
// shows health of trap
|
|
if ( IsDefined( play_ambient_shocks ) )
|
|
self thread [[ play_ambient_shocks ]]();
|
|
|
|
while ( isdefined( self ) )
|
|
{
|
|
// [off] - wait for activate
|
|
while ( !self.running )
|
|
{
|
|
self.generator waittill( "trigger", owner );
|
|
|
|
if ( owner can_activate_trap( self ) )
|
|
{
|
|
thread trap_BBprint( "electric", owner, self.generator.origin );
|
|
|
|
level thread maps\mp\alien\_music_and_dialog::playVOForTrapActivation( owner, self.trap_type );
|
|
|
|
self.owner = owner;
|
|
owner thread stop_electric_trap_on_disconnect( self );
|
|
|
|
if ( isdefined( play_trap_on_fx ) )
|
|
self thread [[ play_trap_on_fx ]]();
|
|
|
|
// sfx loop for trap ambient hum
|
|
self.shock_trig playLoopSound( "alien_fence_hum_lp" );
|
|
|
|
// sfx loop for generator running
|
|
self.generator playLoopSound( "alien_fence_gen_lp" );
|
|
|
|
// cost the activator
|
|
price = int( self.cost * ( owner perk_GetTrapCostScalar() ) );
|
|
discount = self.cost - price;
|
|
owner take_player_currency( price, false, "trap" );
|
|
|
|
self.capacity = int( CONST_GENERATOR_POWER * owner perk_GetTrapDurationScalar() );
|
|
// break into [on]
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
wait 0.05;
|
|
owner setLowerMessage( "no_money", &"ALIEN_COLLECTIBLES_NO_MONEY", 3 );
|
|
//owner iprintlnBold( trap_cost_string );
|
|
continue;
|
|
}
|
|
}
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::remove_from_outline_watch_list( self.generator );
|
|
|
|
// run generator visuals
|
|
self thread run_generator();
|
|
|
|
on_time = gettime();
|
|
|
|
// [on] - wait for victim
|
|
while ( self.running && self.capacity > 0 && isDefined( self.owner ) && IsSentient( self.owner ) )
|
|
{
|
|
elapsed_time = max( 0, ( GetTime() - on_time ) / 1000 );
|
|
time_left = max( 5, self.life_span - elapsed_time ); // 5 seconds linger time for consecutive shocks
|
|
|
|
victim = self wait_for_trigger_timeout( time_left * ( self.owner perk_GetTrapDurationScalar() ) );
|
|
|
|
// undefined if above wait earlied out due to time out
|
|
if ( !isdefined( victim ) && ( isdefined( self.trap_timed_out ) && self.trap_timed_out ) )
|
|
break;
|
|
|
|
if ( self.capacity <= 0 || !isDefined( self.owner ) )
|
|
{
|
|
// break into [off]
|
|
break;
|
|
}
|
|
|
|
if ( isAgent( victim ) && isalive( victim ) && !( isdefined( victim.shocked ) && victim.shocked ) )
|
|
self thread trap_shock( victim, undefined, true );
|
|
|
|
if ( isdefined( self.player_damage ) && isPlayer( victim ) && isAlive( victim ) && !( isdefined( victim.shocked ) && victim.shocked ) )
|
|
self thread trap_shock( victim, self.player_damage, false );
|
|
}
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list( self.generator, self.cost );
|
|
|
|
self notify( "electric_trap_turned_off" );
|
|
|
|
if( isDefined( self.owner ) && isALive( self.owner ) )
|
|
{
|
|
if ( self.trap_type == "traps_fence" )
|
|
self.owner setLowerMessage( "electric_fence_offline", &"ALIEN_COLLECTIBLES_ELECTRIC_FENCE_OFFLINE", 3 );
|
|
else
|
|
self.owner setLowerMessage( "electric_fence_offline", &"ALIENS_PATCH_ELECTRIC_TRAP_OFFLINE", 3 );
|
|
}
|
|
|
|
// padding
|
|
wait 0.5;
|
|
|
|
if ( isdefined( play_trap_off_fx ) )
|
|
self thread [[ play_trap_off_fx ]]();
|
|
|
|
self.owner = undefined;
|
|
self.running = false;
|
|
self.generator SetHintString( self.hintString );
|
|
self.generator MakeUsable();
|
|
self.generator stopLoopSound( "alien_fence_gen_lp" );
|
|
self.generator playSound( "alien_fence_gen_off" );
|
|
self.shock_trig stopLoopSound( "alien_fence_hum_lp" );
|
|
|
|
// ^ loop ^
|
|
}
|
|
}
|
|
|
|
//self = the player who activatged the trap
|
|
stop_electric_trap_on_disconnect( trap )
|
|
{
|
|
trap endon( "electric_trap_turned_off" );
|
|
trap endon( "timed_out" );
|
|
|
|
self waittill( "disconnect" );
|
|
trap notify ( "electric_trap_turned_off" );
|
|
|
|
}
|
|
stop_firetrap_on_disconnect( trap )
|
|
{
|
|
trap endon( "timed_out" );
|
|
trap endon( "fire_trap_exhausted" );
|
|
self waittill( "disconnect" );
|
|
|
|
trap thread kill_fire( trap.fire_fx_array );
|
|
trap notify ( "owner_disconnected" );
|
|
trap notify( "fire_trap_exhausted" );
|
|
}
|
|
|
|
wait_for_trigger_timeout( timeout )
|
|
{
|
|
self endon( "electric_trap_turned_off" );
|
|
self thread timeout_watch( timeout );
|
|
|
|
self endon( "timed_out" );
|
|
|
|
self.shock_trig waittill( "trigger", victim );
|
|
return victim;
|
|
}
|
|
|
|
timeout_watch( timeout )
|
|
{
|
|
self endon( "electric_trap_turned_off" );
|
|
self.trap_timed_out = false;
|
|
|
|
wait timeout;
|
|
|
|
self notify( "timed_out" );
|
|
self.trap_timed_out = true;
|
|
}
|
|
|
|
eletric_trap_asserts()
|
|
{
|
|
generic_msg = " is not defined in eletric trap setup function.";
|
|
|
|
assertex( isdefined( self.cost ), "trap_struct.cost" + generic_msg );
|
|
assertex( isdefined( self.hintString ), "trap_struct.hintString" + generic_msg );
|
|
assertex( isdefined( self.running ), "trap_struct.running" + generic_msg );
|
|
assertex( isdefined( self.generator ), "trap_struct.generator" + generic_msg );
|
|
|
|
assertex( isdefined( self.shock_fx ), "trap_struct.shock_fx" + generic_msg );
|
|
assertex( isdefined( self.shock_interval ), "trap_struct.shock_interval"+ generic_msg );
|
|
assertex( isdefined( self.shock_damage ), "trap_struct.shock_damage" + generic_msg );
|
|
assertex( isdefined( self.shock_trig ), "trap_struct.shock_trig" + generic_msg );
|
|
|
|
assertex( isdefined( self.capacity ), "trap_struct.capacity" + generic_msg );
|
|
assertex( isdefined( self.max_capacity ), "trap_struct.max_capacity" + generic_msg );
|
|
assertex( isdefined( self.trap_type ), "trap_struct.trap_type" + generic_msg );
|
|
}
|
|
|
|
//*****************************************************************
|
|
// ELECTRIC TRAPS: PUDDLE
|
|
//*****************************************************************
|
|
|
|
CONST_PUDDLE_COST = 750; // currency cost to enable trap
|
|
CONST_PUDDLE_COST_SMALL = 300;
|
|
CONST_PUDDLE_COST_MED = 500;
|
|
CONST_PUDDLE_COST_LARGE = 750;
|
|
|
|
CONST_PUDDLE_LIFE_SPAN_SMALL = 90; // time out
|
|
CONST_PUDDLE_LIFE_SPAN_MED = 120;
|
|
CONST_PUDDLE_LIFE_SPAN_LARGE = 150;
|
|
|
|
CONST_PUDDLE_SHOCK_DAMAGE = 200; // damage per shock
|
|
CONST_PUDDLE_PLAYER_DAMAGE = 3; // damage done to player
|
|
|
|
electric_puddle_init()
|
|
{
|
|
generators = getentarray( "puddle_generator", "targetname" );
|
|
if ( !isdefined( generators ) || generators.size == 0 )
|
|
return;
|
|
|
|
// wait before players are ready, we decide how to setup usage based on match type
|
|
while ( !isdefined( level.players ) || level.players.size < 1 )
|
|
wait 0.05;
|
|
|
|
level.electric_puddles = [];
|
|
foreach ( generator in generators )
|
|
{
|
|
generator.damagefeedback = false;
|
|
puddle = setup_electric_puddle( generator );
|
|
level.electric_puddles[ level.electric_puddles.size ] = puddle;
|
|
puddle thread run_electric_trap( ::play_puddle_on_fx, ::play_puddle_off_fx, ::ambient_puddle_shocks );
|
|
}
|
|
}
|
|
|
|
setup_electric_puddle( generator )
|
|
{
|
|
puddle = SpawnStruct();
|
|
puddle.trap_type = "traps_puddle";
|
|
|
|
// setup generator for use
|
|
puddle.generator = generator;
|
|
|
|
// setup damage trigger
|
|
puddle.shock_trig = getent( generator.target, "targetname" );
|
|
|
|
// setup electro contact point between wire and puddle
|
|
puddle.contact_points = [];
|
|
cur_contact_point = getstruct( puddle.shock_trig.target, "targetname" );
|
|
puddle.contact_points[ 0 ] = cur_contact_point;
|
|
while ( isdefined( cur_contact_point.target ) )
|
|
{
|
|
contact_point = getstruct( cur_contact_point.target, "targetname" );
|
|
puddle.contact_points[ puddle.contact_points.size ] = contact_point;
|
|
cur_contact_point = contact_point;
|
|
}
|
|
|
|
// setup shock fx
|
|
puddle.shock_fx[ "shock" ] = LoadFX( "vfx/moments/alien/fence_lightning_shock" );
|
|
puddle.shock_fx[ "ambient_flash" ] = LoadFX( "vfx/moments/alien/fence_lightning_turn_on" );
|
|
puddle.shock_fx[ "sparks" ] = loadfx( "fx/explosions/transformer_sparks_f_sound" );
|
|
puddle.shock_fx[ "sparks_sm" ] = loadfx( "fx/explosions/transformer_sparks_b_sound" );
|
|
|
|
// setup electric trap initial values
|
|
puddle.running = false;
|
|
puddle.capacity = CONST_GENERATOR_POWER;
|
|
puddle.max_capacity = CONST_GENERATOR_POWER;
|
|
|
|
puddle.player_damage = CONST_PUDDLE_PLAYER_DAMAGE;
|
|
puddle puddle_trap_setup_sizes();
|
|
|
|
puddle.generator SetCursorHint( "HINT_NOICON" );
|
|
puddle.generator SetHintString( puddle.hintString );
|
|
puddle.generator MakeUsable();
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list ( generator, puddle.cost );
|
|
|
|
return puddle;
|
|
}
|
|
|
|
puddle_trap_setup_sizes()
|
|
{
|
|
sizes = [ "small", "medium", "large" ];
|
|
custom_lifespan_array = [];
|
|
custom_lifespan_array["small"] = CONST_PUDDLE_LIFE_SPAN_SMALL;
|
|
custom_lifespan_array["medium"] = CONST_PUDDLE_LIFE_SPAN_MED;
|
|
custom_lifespan_array["large"] = CONST_PUDDLE_LIFE_SPAN_LARGE;
|
|
|
|
size_index = 1; // default medium
|
|
if ( isdefined( self.generator.script_noteworthy ) )
|
|
{
|
|
if ( self.generator.script_noteworthy == "small" )
|
|
size_index = 0;
|
|
if ( self.generator.script_noteworthy == "large" )
|
|
size_index = 2;
|
|
//ex: "custom small large" for small cost, large lifespan
|
|
if(IsSubStr( self.generator.script_noteworthy, "custom"))
|
|
{
|
|
tokens = strtok(self.generator.script_noteworthy, " ");
|
|
switch ( tokens[1] )
|
|
{
|
|
case "small":
|
|
size_index = 0;
|
|
break;
|
|
case "medium":
|
|
size_index = 1;
|
|
break;
|
|
case "large":
|
|
size_index = 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
self.custom = tokens[2];
|
|
}
|
|
}
|
|
|
|
if ( isPlayingSolo() )
|
|
size_index = int( max( 0, size_index - 1 ) );
|
|
|
|
self.shock_damage = CONST_PUDDLE_SHOCK_DAMAGE;
|
|
self.shock_interval = CONST_SHOCK_INTERVAL;
|
|
|
|
if ( sizes[ size_index ] == "small" )
|
|
{
|
|
self.cost = CONST_PUDDLE_COST_SMALL;
|
|
self.life_span = CONST_PUDDLE_LIFE_SPAN_SMALL;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_SMALL";
|
|
}
|
|
if ( sizes[ size_index ] == "medium" )
|
|
{
|
|
self.cost = CONST_PUDDLE_COST_MED;
|
|
self.life_span = CONST_PUDDLE_LIFE_SPAN_MED;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_MED";
|
|
}
|
|
if ( sizes[ size_index ] == "large" )
|
|
{
|
|
self.cost = CONST_PUDDLE_COST_LARGE;
|
|
self.life_span = CONST_PUDDLE_LIFE_SPAN_LARGE;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_LARGE";
|
|
}
|
|
|
|
if(IsDefined(self.custom))
|
|
{
|
|
self.life_span = custom_lifespan_array[self.custom];
|
|
}
|
|
}
|
|
|
|
play_puddle_off_fx()
|
|
{
|
|
//playfx( self.shock_fx[ "shock" ], self.contact_points[ 0 ].origin );
|
|
playfx( self.shock_fx[ "sparks" ], self.contact_points[ 0 ].origin );
|
|
|
|
count = 3;
|
|
while ( count > 0 )
|
|
{
|
|
//playfx( self.shock_fx[ "shock" ], self.generator.origin );
|
|
playfx( self.shock_fx[ "sparks" ], self.generator.origin );
|
|
|
|
count--;
|
|
wait 0.2;
|
|
}
|
|
}
|
|
|
|
play_puddle_on_fx()
|
|
{
|
|
//playfx( self.shock_fx[ "shock" ], self.contact_points[ 0 ].origin );
|
|
playfx( self.shock_fx[ "sparks" ], self.contact_points[ 0 ].origin );
|
|
}
|
|
|
|
ambient_puddle_shocks()
|
|
{
|
|
// self is fence struct
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
shock = SpawnFx( self.shock_fx[ "sparks" ], self.contact_points[ 0 ].origin );
|
|
|
|
sparks = [];
|
|
foreach ( contact_point in self.contact_points )
|
|
sparks[ sparks.size ] = SpawnFx( self.shock_fx[ "sparks" ], contact_point.origin );
|
|
|
|
ambient_on = false;
|
|
|
|
while ( 1 )
|
|
{
|
|
while ( !self.running )
|
|
{
|
|
ambient_on = false;
|
|
self waittill( "electric_trap_turned_on" );
|
|
}
|
|
|
|
if ( !ambient_on )
|
|
ambient_on = true;
|
|
|
|
triggerFx( shock );
|
|
wait 0.25;
|
|
triggerFx( shock );
|
|
|
|
foreach ( idx, spark in sparks )
|
|
{
|
|
// always play contact point spark, which is index 0
|
|
if ( idx == 0 )
|
|
{
|
|
triggerFx( spark );
|
|
continue;
|
|
}
|
|
|
|
// 1/4th chance of playing extra sparks
|
|
if( cointoss() )
|
|
triggerFx( spark );
|
|
}
|
|
|
|
if ( !is_true ( level.skip_radius_damage_on_puddles ) )
|
|
RadiusDamage( self.contact_points[ 0 ].origin, 80, 20, 5 );
|
|
waittill_any_timeout( RandomIntRange( 3, 5 ), "electric_trap_turned_off" );
|
|
}
|
|
}
|
|
|
|
//*****************************************************************
|
|
// ELECTRIC TRAPS: FENCE
|
|
//*****************************************************************
|
|
|
|
// <NOTE JC> These costs are likely to be replaced by some table lookup through some kind of init process that Colin is setting up
|
|
CONST_FENCE_COST = 750; // currency cost to enable fence
|
|
CONST_FENCE_COST_SMALL = 300;
|
|
CONST_FENCE_COST_MED = 500;
|
|
CONST_FENCE_COST_LARGE = 750;
|
|
|
|
CONST_FENCE_LIFE_SPAN = 120; // time out
|
|
CONST_FENCE_LIFE_SPAN_SMALL = 90;
|
|
CONST_FENCE_LIFE_SPAN_MED = 120;
|
|
CONST_FENCE_LIFE_SPAN_LARGE = 150;
|
|
|
|
CONST_FENCE_SHOCK_DAMAGE = 200; // damage per shock
|
|
CONST_FENCE_PLAYER_DAMAGE = 2;
|
|
|
|
electric_fence_init()
|
|
{
|
|
generators = getentarray( "fence_generator", "targetname" );
|
|
|
|
if ( !isdefined( generators ) || generators.size == 0 )
|
|
return;
|
|
|
|
// wait before players are ready, we decide how to setup usage based on match type
|
|
while ( !isdefined( level.players ) || level.players.size < 1 )
|
|
wait 0.05;
|
|
|
|
level.electric_fences = [];
|
|
foreach ( generator in generators )
|
|
{
|
|
generator.damagefeedback = false;
|
|
fence = setup_electric_fence( generator );
|
|
level.electric_fences[ level.electric_fences.size ] = fence;
|
|
|
|
fence thread run_electric_trap( ::play_fence_on_fx, ::play_fence_off_fx, ::ambient_fence_shocks );
|
|
}
|
|
}
|
|
|
|
setup_electric_fence( generator )
|
|
{
|
|
fence = SpawnStruct();
|
|
fence.trap_type = "traps_fence";
|
|
|
|
// setup generator for use
|
|
fence.generator = generator;
|
|
|
|
// setup fence corners
|
|
generator_targets = getstructarray( generator.target, "targetname" );
|
|
fence_sparks = [];
|
|
top_left = generator_targets[ 0 ];
|
|
foreach ( generator_target in generator_targets )
|
|
{
|
|
if ( isdefined( generator_target.script_noteworthy ) && generator_target.script_noteworthy == "fence_sparks" )
|
|
fence_sparks[ fence_sparks.size ] = generator_target;
|
|
|
|
if ( isdefined( generator_target.script_noteworthy ) && generator_target.script_noteworthy == "fence_area" )
|
|
top_left = generator_target;
|
|
}
|
|
bottom_left = getstruct( top_left.target, "targetname" );
|
|
bottom_right = getstruct( bottom_left.target, "targetname" );
|
|
top_right = getstruct( bottom_right.target, "targetname" );
|
|
|
|
fence.fence_top_left_angles = top_left.angles;
|
|
fence.fence_top_left = top_left.origin;
|
|
fence.fence_top_right = top_right.origin;
|
|
fence.fence_bottom_left = bottom_left.origin;
|
|
fence.fence_bottom_right = bottom_right.origin;
|
|
fence.fence_height = fence.fence_top_left[ 2 ] - fence.fence_bottom_left[ 2 ];
|
|
fence.fence_center = get_center( top_left.origin, top_right.origin, bottom_left.origin, bottom_right.origin );
|
|
fence.fence_sparks = fence_sparks;
|
|
|
|
// setup damage trigger
|
|
fence.shock_trig = getent( top_right.target, "targetname" );
|
|
fence.optimal_height = 100; // should be tweaked based on geo
|
|
|
|
// setup fence fx
|
|
fence.shock_fx[ "ambient" ] = LoadFX( "vfx/moments/alien/fence_lightning_ambient" );
|
|
fence.shock_fx[ "shock" ] = LoadFX( "vfx/moments/alien/fence_lightning_shock" );
|
|
fence.shock_fx[ "turn_on" ] = LoadFX( "vfx/moments/alien/fence_lightning_turn_on" );
|
|
|
|
fence.shock_fx[ "sparks" ] = loadfx( "fx/explosions/transformer_sparks_b_sound" );
|
|
fence.shock_fx[ "sparks_sm" ] = loadfx( "fx/explosions/transformer_sparks_f_sound" );
|
|
//fence.shock_fx[ "bar" ] = LoadFX( "vfx/gameplay/alien/vfx_alien_fence_bolt_horizontal" );
|
|
|
|
fence.shock_bar_fx_ent = spawn( "script_origin", fence.fence_center );
|
|
fence.shock_bar_fx_ent setmodel( "tag_origin" );
|
|
|
|
// setup fence initial values
|
|
fence.running = false;
|
|
fence.capacity = CONST_GENERATOR_POWER;
|
|
fence.max_capacity = CONST_GENERATOR_POWER;
|
|
|
|
fence.player_damage = CONST_FENCE_PLAYER_DAMAGE;
|
|
fence fence_trap_setup_sizes();
|
|
|
|
fence.generator SetCursorHint( "HINT_NOICON" );
|
|
fence.generator SetHintString( fence.hintString );
|
|
fence.generator MakeUsable();
|
|
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list ( generator, fence.cost );
|
|
|
|
return fence;
|
|
}
|
|
|
|
fence_trap_setup_sizes()
|
|
{
|
|
sizes = [ "small", "medium", "large" ];
|
|
custom_lifespan_array = [];
|
|
custom_lifespan_array["small"] = CONST_FENCE_LIFE_SPAN_SMALL;
|
|
custom_lifespan_array["medium"] = CONST_FENCE_LIFE_SPAN_MED;
|
|
custom_lifespan_array["large"] = CONST_FENCE_LIFE_SPAN_LARGE;
|
|
|
|
|
|
size_index = 1; // default medium
|
|
if ( isdefined( self.generator.script_noteworthy ) )
|
|
{
|
|
if ( self.generator.script_noteworthy == "small" )
|
|
size_index = 0;
|
|
if ( self.generator.script_noteworthy == "large" )
|
|
size_index = 2;
|
|
//ex: "custom small large" for small cost, large lifespan
|
|
if(IsSubStr( self.generator.script_noteworthy, "custom"))
|
|
{
|
|
tokens = strtok(self.generator.script_noteworthy, " ");
|
|
switch ( tokens[1] )
|
|
{
|
|
case "small":
|
|
size_index = 0;
|
|
break;
|
|
case "medium":
|
|
size_index = 1;
|
|
break;
|
|
case "large":
|
|
size_index = 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
|
|
self.custom = tokens[2];
|
|
}
|
|
}
|
|
|
|
if ( isPlayingSolo() )
|
|
size_index = int( max( 0, size_index - 1 ) );
|
|
|
|
// scaled based on height, shorter the fence the stronger the shock, sinse aliens touch it less
|
|
self.shock_damage = int( min( 800, CONST_FENCE_SHOCK_DAMAGE * max ( 1, ( self.optimal_height / self.fence_height ) ) ) );
|
|
self.shock_interval = CONST_SHOCK_INTERVAL;
|
|
|
|
if ( sizes[ size_index ] == "small" )
|
|
{
|
|
self.cost = CONST_FENCE_COST_SMALL;
|
|
self.life_span = CONST_FENCE_LIFE_SPAN_SMALL;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FENCE_SMALL";
|
|
|
|
if ( isDefined( level.generic_electric_trap_check ) && self [[level.generic_electric_trap_check]]() )
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_SMALL";
|
|
}
|
|
if ( sizes[ size_index ] == "medium" )
|
|
{
|
|
self.cost = CONST_FENCE_COST_MED;
|
|
self.life_span = CONST_FENCE_LIFE_SPAN_MED;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FENCE_MED";
|
|
|
|
if ( isDefined( level.generic_electric_trap_check ) && self [[level.generic_electric_trap_check]]() )
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_MED";
|
|
}
|
|
if ( sizes[ size_index ] == "large" )
|
|
{
|
|
self.cost = CONST_FENCE_COST_LARGE;
|
|
self.life_span = CONST_FENCE_LIFE_SPAN_LARGE;
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_FENCE_LARGE";
|
|
|
|
if ( isDefined( level.generic_electric_trap_check ) && self [[level.generic_electric_trap_check]]() )
|
|
self.hintString = &"ALIEN_COLLECTIBLES_ACTIVATE_PUDDLE_LARGE";
|
|
}
|
|
|
|
if(IsDefined(self.custom))
|
|
{
|
|
self.life_span = custom_lifespan_array[self.custom];
|
|
}
|
|
}
|
|
|
|
get_center( vec0, vec1, vec2, vec3 )
|
|
{
|
|
x = ( vec0[ 0 ] + vec1[ 0 ] + vec2[ 0 ] + vec3[ 0 ] ) / 4;
|
|
y = ( vec0[ 1 ] + vec1[ 1 ] + vec2[ 1 ] + vec3[ 1 ] ) / 4;
|
|
z = ( vec0[ 2 ] + vec1[ 2 ] + vec2[ 2 ] + vec3[ 2 ] ) / 4;
|
|
|
|
return ( x, y, z );
|
|
}
|
|
|
|
ambient_fence_shocks()
|
|
{
|
|
// self is fence struct
|
|
self endon( "death" );
|
|
level endon( "game_ended" );
|
|
|
|
ambient_on = false;
|
|
|
|
while ( 1 )
|
|
{
|
|
while ( !self.running )
|
|
{
|
|
ambient_on = false;
|
|
StopFXOnTag( self.shock_fx[ "ambient" ], self.shock_bar_fx_ent, "tag_origin" );
|
|
self waittill( "electric_trap_turned_on" );
|
|
}
|
|
|
|
if ( !ambient_on )
|
|
{
|
|
PlayFXOnTag( self.shock_fx[ "ambient" ], self.shock_bar_fx_ent, "tag_origin" );
|
|
ambient_on = true;
|
|
}
|
|
|
|
fence_hp_ratio = self.capacity / self.max_capacity;
|
|
height = self.fence_height * fence_hp_ratio;
|
|
end_pos_left = self.fence_bottom_left + ( 0, 0, height );
|
|
end_pos_right = self.fence_bottom_right + ( 0, 0, height );
|
|
end_pos_center = ( self.fence_center[ 0 ], self.fence_center[ 1 ], end_pos_right[ 2 ] );
|
|
|
|
playfx( self.shock_fx[ "sparks_sm" ], end_pos_left );
|
|
wait 0.3;
|
|
playfx( self.shock_fx[ "sparks_sm" ], end_pos_center );
|
|
wait 0.3;
|
|
playfx( self.shock_fx[ "sparks_sm" ], end_pos_right );
|
|
|
|
if ( isdefined( self.fence_sparks ) )
|
|
{
|
|
foreach ( spark in self.fence_sparks )
|
|
{
|
|
playfx( self.shock_fx[ "sparks_sm" ], spark.origin );
|
|
wait 0.3;
|
|
}
|
|
}
|
|
|
|
debug_electric_trap_print( self.generator.origin, "Power: " + fence_hp_ratio, ( 0.5, 0.5, 1 ), 0.75, 3, 3 );
|
|
waittill_any_timeout( RandomIntRange( 2, 3 ), "electric_trap_turned_off" );
|
|
}
|
|
}
|
|
|
|
play_fence_off_fx()
|
|
{
|
|
playfx( self.shock_fx[ "shock" ], self.fence_top_left );
|
|
playfx( self.shock_fx[ "sparks" ], self.fence_top_left );
|
|
|
|
playfx( self.shock_fx[ "shock" ], self.fence_top_right );
|
|
playfx( self.shock_fx[ "sparks" ], self.fence_top_right );
|
|
|
|
count = 3;
|
|
while ( count > 0 )
|
|
{
|
|
playfx( self.shock_fx[ "shock" ], self.generator.origin );
|
|
playfx( self.shock_fx[ "sparks" ], self.generator.origin );
|
|
|
|
count--;
|
|
wait 0.2;
|
|
}
|
|
}
|
|
|
|
play_fence_on_fx()
|
|
{
|
|
playfx( self.shock_fx[ "shock" ], self.fence_top_left );
|
|
playfx( self.shock_fx[ "sparks" ], self.fence_top_left );
|
|
|
|
playfx( self.shock_fx[ "shock" ], self.fence_top_right );
|
|
playfx( self.shock_fx[ "sparks" ], self.fence_top_right );
|
|
}
|
|
|
|
//*****************************************************************
|
|
// Turrets
|
|
//*****************************************************************
|
|
|
|
CONST_TURRET_COST = 750; // currency cost to use a turret
|
|
CONST_TURRET_BULLET_LIMIT = 300; // turret will be disabled after this many bullets fired
|
|
|
|
turret_monitorUse()
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
self SetCursorHint( "HINT_NOICON" );
|
|
self MakeUsable();
|
|
|
|
// wait till features are defined by level script
|
|
wait 0.05;
|
|
if ( alien_mode_has( "outline" ) )
|
|
maps\mp\alien\_outline_proto::add_to_outline_watch_list ( self, CONST_TURRET_COST );
|
|
|
|
disable_turret();
|
|
|
|
while ( true )
|
|
{
|
|
self waittill ( "trigger", player );
|
|
|
|
if ( !isPlayer ( player ) )
|
|
continue;
|
|
|
|
if ( player is_holding_deployable() )
|
|
{
|
|
player setLowerMessage( "cant_buy", &"ALIEN_COLLECTIBLES_PLAYER_HOLDING", 3 );
|
|
continue;
|
|
}
|
|
|
|
turret_cost = int( CONST_TURRET_COST * ( player perk_GetTrapCostScalar() ) );
|
|
|
|
if ( !is_turret_enabled() ) // player tries to buy the turret
|
|
{
|
|
if ( player can_activate_turret() )
|
|
{
|
|
player take_player_currency( turret_cost, false, "trap" );
|
|
enable_turret();
|
|
level thread maps\mp\alien\_music_and_dialog::playVOForSentry( player, "minigun" );
|
|
self thread monitor_player_use(); // handles the ammo counter hud
|
|
}
|
|
else
|
|
{
|
|
player setLowerMessage( "no_money", &"ALIEN_COLLECTIBLES_NO_MONEY", 3 );
|
|
//player iprintlnBold( "Turret costs $" + turret_cost );
|
|
}
|
|
}
|
|
else // player uses the turret
|
|
{
|
|
self.owner = player;
|
|
wait_for_disable_turret();
|
|
disable_turret();
|
|
}
|
|
}
|
|
}
|
|
|
|
wait_for_disable_turret()
|
|
{
|
|
thread watch_bullet_fired();
|
|
|
|
self waittill ( "disable_turret" );
|
|
}
|
|
|
|
watch_bullet_fired()
|
|
{
|
|
self endon ( "disable_turret" );
|
|
self notify( "stop_fire_monitor" );
|
|
self endon( "stop_fire_monitor" );
|
|
|
|
bullet_fired = 0;
|
|
turret_ammo = int( CONST_TURRET_BULLET_LIMIT * self.owner perk_GetTrapDurationScalar() );
|
|
self.turret_ammo = turret_ammo;
|
|
fireTime = weaponFireTime( self.weaponinfo );
|
|
while ( true )
|
|
{
|
|
self waittill ( "turret_fire" );
|
|
self getturretowner() notify( "turret_fire" );
|
|
self.heatLevel += fireTime;
|
|
bullet_fired++;
|
|
self.cooldownWaitTime = fireTime;
|
|
self.turret_ammo = ( turret_ammo - bullet_fired );
|
|
if ( bullet_fired > turret_ammo )
|
|
{
|
|
self.turret_ammo = 0;
|
|
break;
|
|
}
|
|
self.owner set_turret_ammocount( self.turret_ammo );
|
|
}
|
|
if ( isDefined( self.owner ) && isAlive ( self.owner ) )
|
|
self.owner thread wait_for_player_to_dismount_turret();
|
|
|
|
self notify ( "disable_turret" );
|
|
}
|
|
|
|
is_turret_enabled()
|
|
{
|
|
return self.enabled;
|
|
}
|
|
|
|
disable_turret()
|
|
{
|
|
self.enabled = false;
|
|
self SetHintString( &"ALIEN_COLLECTIBLES_ACTIVATE_TURRET" );
|
|
self TurretFireDisable();
|
|
self makeTurretInoperable();
|
|
}
|
|
|
|
enable_turret()
|
|
{
|
|
self.enabled = true;
|
|
self SetHintString( "" );
|
|
self TurretFireEnable();
|
|
self makeTurretOperable();
|
|
}
|
|
|
|
can_activate_turret()
|
|
{
|
|
// self is player
|
|
return self player_has_enough_currency( CONST_TURRET_COST );
|
|
}
|
|
|
|
//wait for players to jump on the turret and handle the ammo counter hud
|
|
monitor_player_use()
|
|
{
|
|
self endon ( "turret_disabled" );
|
|
while ( 1 )
|
|
{
|
|
self waittill( "trigger",user );
|
|
|
|
if ( !isDefined ( user ) || !isAlive ( user ) )
|
|
continue;
|
|
|
|
if ( user is_holding_deployable() )
|
|
{
|
|
user setLowerMessage( "cant_buy", &"ALIEN_COLLECTIBLES_PLAYER_HOLDING", 3 );
|
|
continue;
|
|
}
|
|
|
|
if ( user IsUsingTurret() )
|
|
{
|
|
self.owner = user;
|
|
while ( !isDefined ( self.turret_ammo ) ) //wait for the ammo to be set on the turret
|
|
{
|
|
wait ( 0.05 );
|
|
}
|
|
if( !is_chaos_mode() )
|
|
user disable_special_ammo();
|
|
user show_turret_icon( 2 );
|
|
user set_turret_ammocount( self.turret_ammo );
|
|
user SetClientOmnvar( "ui_alien_turret_overheat",0 );
|
|
self thread turret_overheat_monitor( user );
|
|
self thread turret_cooldown_monitor();
|
|
self thread clear_turret_ammo_counter_on_dismount( user );
|
|
self thread clear_turret_ammo_counter_on_death( user );
|
|
user setLowerMessage( "disengage_turret", &"ALIEN_COLLECTIBLES_DISENGAGE_TURRET", 4 );
|
|
}
|
|
else
|
|
{
|
|
user hide_turret_icon();
|
|
if( !is_chaos_mode() )
|
|
user enable_special_ammo();
|
|
self.owner = undefined;
|
|
user SetClientOmnvar( "ui_alien_turret_overheat",-1 );
|
|
user clearLowerMessage( "disengage_turret" );
|
|
}
|
|
}
|
|
}
|
|
|
|
//self = a turret
|
|
turret_cooldown_monitor()
|
|
{
|
|
self endon ( "death" );
|
|
self notify( "stop_cooldown_monitor" );
|
|
self endon( "stop_cooldown_monitor" );
|
|
self endon( "turret_disabled" );
|
|
|
|
while( true )
|
|
{
|
|
if( self.heatLevel > 0 )
|
|
{
|
|
if( self.cooldownWaitTime <= 0 )
|
|
{
|
|
self.heatLevel = max( 0, self.heatLevel - 0.05 );
|
|
}
|
|
else
|
|
{
|
|
self.cooldownWaitTime = max( 0, self.cooldownWaitTime - 0.05 );
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
}
|
|
|
|
|
|
//self = a turret
|
|
turret_overheat_monitor( player )
|
|
{
|
|
self notify( "overheat_monitor" );
|
|
self endon( "overheat_monitor" );
|
|
self endon( "turret_disabled" );
|
|
self.heatLevel = 0;
|
|
self.cooldownWaitTime = CONST_TURRET_COOLDOWN_TIME;
|
|
|
|
player endon( "disconnect" );
|
|
|
|
submitted_overheat_value = 0;
|
|
for( ;; )
|
|
{
|
|
// exceptions
|
|
if ( !isReallyAlive( player ) )
|
|
{ self.inUseBy = undefined;
|
|
player SetClientOmnvar( "ui_alien_turret_overheat",-1 );
|
|
break;
|
|
}
|
|
if ( !player IsUsingTurret() )
|
|
{
|
|
player SetClientOmnvar( "ui_alien_turret_overheat",-1 );
|
|
break;
|
|
}
|
|
|
|
if ( self.heatLevel >= CONST_TURRET_OVERHEAT_TIME )
|
|
barFrac = 1;
|
|
else
|
|
barFrac = self.heatLevel/CONST_TURRET_OVERHEAT_TIME;
|
|
|
|
// omnvar throttle
|
|
throttle = 5;
|
|
new_value = int( barFrac * 100 );
|
|
if ( submitted_overheat_value != new_value )
|
|
{
|
|
if ( new_value <= throttle || ( abs( abs( submitted_overheat_value ) - abs( new_value ) ) > throttle ) )
|
|
{
|
|
player SetClientOmnvar( "ui_alien_turret_overheat" , new_value );
|
|
submitted_overheat_value = new_value;
|
|
}
|
|
}
|
|
|
|
wait( 0.05 );
|
|
}
|
|
|
|
player SetClientOmnvar( "ui_alien_turret_overheat",-1 );
|
|
}
|
|
|
|
clear_turret_ammo_counter_on_death( user )
|
|
{
|
|
self notify ( "clearammocounterondeath" );
|
|
self endon( "clearammocounterondeath" );
|
|
|
|
user endon( "disconnect" );
|
|
|
|
self waittill( "turret_disabled" );
|
|
user hide_turret_icon();
|
|
user clearLowerMessage( "disengage_turret" );
|
|
}
|
|
|
|
clear_turret_ammo_counter_on_dismount( user )
|
|
{
|
|
self notify( "dimountammocounter" );
|
|
self endon ( "dismountammocounter" );
|
|
user endon( "disconnect" );
|
|
while ( user IsUsingTurret() ) //wait for the player to get off the turret before clearing it's owner
|
|
{
|
|
wait ( .1 );
|
|
}
|
|
user hide_turret_icon();
|
|
self.owner = undefined;
|
|
user clearLowerMessage( "disengage_turret" );
|
|
|
|
//restore his wepon if he happened to have a riotshield that got destroyed
|
|
if ( user GetCurrentWeapon() == "none" )
|
|
{
|
|
user thread restore_last_valid_weapon();
|
|
}
|
|
|
|
}
|
|
|
|
//self = a player
|
|
restore_last_valid_weapon()
|
|
{
|
|
weapons = self GetWeaponsListPrimaries();
|
|
foreach ( weapon in weapons )
|
|
{
|
|
if ( isDefined ( weapon ) && weapon != "none" )
|
|
{
|
|
self SwitchToWeapon ( weapon );
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
|
|
//*****************************************************************
|
|
// Flare Attractor
|
|
//*****************************************************************
|
|
//self = a player
|
|
monitor_flare_use()
|
|
{
|
|
self endon ( "death" );
|
|
self endon ( "disconnect" );
|
|
level endon ( "game_ended" );
|
|
self endon ( "end_monitor_flare_use" );
|
|
|
|
for ( ;; )
|
|
{
|
|
self waittill( "grenade_fire", flare, weapName );
|
|
|
|
if ( weapName == "iw6_aliendlc21_mp" ) //sticky flare
|
|
{
|
|
flare thread sticky_flare( self );
|
|
continue;
|
|
}
|
|
|
|
if ( weapName != "alienflare_mp" )
|
|
continue;
|
|
|
|
flare make_entity_sentient_mp("allies");
|
|
flare.threatbias = CONST_FLARE_THREATBIAS;
|
|
|
|
msg = flare waittill_notify_or_timeout_return("missile_stuck",7);
|
|
if ( IsDefined( msg ) && msg == "timeout" )
|
|
{
|
|
if ( isDefined( flare ) )
|
|
{
|
|
flare delete();
|
|
}
|
|
continue;
|
|
}
|
|
|
|
glowStick = spawn( "script_model", flare.origin );
|
|
|
|
flare delete();
|
|
glowstick setmodel("mil_emergency_flare_mp");
|
|
glowStick.angles = self.angles;
|
|
glowStick.owner = self;
|
|
if ( self maps\mp\alien\_persistence::is_upgrade_enabled( "master_scavenger_upgrade" ) )
|
|
glowStick thread create_flare(level.spawnGlow["enemy"] , self );
|
|
else
|
|
glowStick thread create_flare(level.spawnGlow["friendly"] , self );
|
|
self TakeWeapon( "alienflare_mp" );
|
|
|
|
}
|
|
}
|
|
|
|
//self is the thrown flare
|
|
sticky_flare( player )
|
|
{
|
|
self endon( "death" );
|
|
|
|
self make_entity_sentient_mp("allies");
|
|
self.threatbias = CONST_FLARE_THREATBIAS;
|
|
self.owner = player;
|
|
self thread flare_out_of_playable_monitor();
|
|
|
|
//play the fx while flying through the air
|
|
self thread create_flare( level._effect["sticky_flare"] , player );
|
|
|
|
// Play flare sfx
|
|
self thread sfx_flare_lp( player );
|
|
|
|
self waittill ("missile_stuck", stuckto );
|
|
|
|
if ( isDefined( stuckto ) && stuckto is_alien_agent() ) //TODO - how to handle when this happens to a pet , or stuck to a friendly ?
|
|
{
|
|
stuckto enable_alien_scripted();
|
|
stuckto.stuck_by_flare = true;
|
|
stuckto SetOrigin( stuckto.origin );
|
|
level thread wait_for_flare_finished( stuckto,self );
|
|
}
|
|
else//play sticky loop if on static surface, not a an alien agent
|
|
{
|
|
//self thread create_flare( level._effect["sticky_flare_loop"] , player );
|
|
alien = undefined;
|
|
level thread wait_for_flare_finished( alien ,self );
|
|
}
|
|
}
|
|
|
|
//self is a flare
|
|
flare_out_of_playable_monitor()
|
|
{
|
|
self endon( "death" );
|
|
self endon ( "missile_stuck" );
|
|
|
|
wait ( 7 );
|
|
|
|
if ( isDefined( self ) )
|
|
{
|
|
self delete();
|
|
}
|
|
}
|
|
//self is an alien
|
|
wait_for_flare_finished( alien, flare )
|
|
{
|
|
damageowner = flare.owner;
|
|
flare waittill( "deleting_flare" , org);
|
|
|
|
config = level.placeableConfigs[ "fuse_resin_tnt" ]; //sticky flare config
|
|
|
|
//do explosion
|
|
playfx ( level._effect[ "sticky_explode" ] ,org );
|
|
PlaySoundAtPos( org, "flare_explode_default" );
|
|
RadiusDamage( org,config.item_damage_radius,config.item_damage,config.item_damage_falloff,damageowner,"MOD_EXPLOSIVE","iw6_aliendlc22_mp" );
|
|
Earthquake( .35,.5,org,512 );
|
|
|
|
if ( isDefined( alien ) )
|
|
{
|
|
alien disable_alien_scripted();
|
|
alien.stuck_by_flare = false;
|
|
}
|
|
}
|
|
|
|
create_flare( showEffect, owner )
|
|
{
|
|
self endon ( "death" );
|
|
|
|
// PlayFXOnTag fails if run on the same frame the parent entity was created
|
|
wait ( 0.5 );
|
|
|
|
angles = self getTagAngles( "tag_fire_fx" );
|
|
PlayFXOnTag( showEffect, self, "tag_fire_fx" );
|
|
self playLoopSound( "emt_road_flare_burn" );
|
|
self.flareType = true;
|
|
|
|
if ( owner maps\mp\alien\_persistence::is_upgrade_enabled( "master_scavenger_upgrade" ) )
|
|
{
|
|
self thread flare_attract_aliens( CONST_UPGRADED_FLARE_TIME, owner );
|
|
wait( CONST_UPGRADED_FLARE_TIME );
|
|
}
|
|
else
|
|
{
|
|
self thread flare_attract_aliens( CONST_FLARE_TIME, owner );
|
|
wait( CONST_FLARE_TIME );
|
|
}
|
|
self notify( "deleting_flare", self.origin );
|
|
self delete();
|
|
}
|
|
|
|
sfx_flare_lp( player )
|
|
{
|
|
self endon( "death" );
|
|
|
|
beep_interval = 0.163;
|
|
|
|
if ( player maps\mp\alien\_persistence::is_upgrade_enabled( "master_scavenger_upgrade" ) )
|
|
FlareEndTime = GetTime() + ( (CONST_UPGRADED_FLARE_TIME ) - 1.4) * 1000;
|
|
else
|
|
FlareEndTime = GetTime() + ( (CONST_FLARE_TIME ) - 1.4) * 1000;
|
|
|
|
wait 0.2;
|
|
|
|
while ( GetTime() < FlareEndTime && IsDefined( self ) )
|
|
{
|
|
PlaySoundAtPos(self.origin, "flare_beep");
|
|
wait beep_interval;
|
|
}
|
|
|
|
// Final Beeps
|
|
PlaySoundAtPos(self.origin, "flare_beep_end");
|
|
}
|
|
|
|
FLARE_AFFECT_RADIUS = 512;
|
|
FLARE_MAX_NUM_ALIEN_AFFECTED = 6;
|
|
|
|
flare_attract_aliens( flare_time, owner )
|
|
{
|
|
attractEndTime = GetTime() + flare_time * 1000;
|
|
affectedAliens = [];
|
|
|
|
while ( GetTime() < attractEndTime && IsDefined( self ) )
|
|
{
|
|
currentAffectedAliens = [];
|
|
foreach( alien in affectedAliens )
|
|
{
|
|
if ( IsDefined( alien ) && IsAlive( alien ) )
|
|
currentAffectedAliens[currentAffectedAliens.size] = alien;
|
|
}
|
|
|
|
affectedAliens = currentAffectedAliens;
|
|
if ( IsDefined( owner ) )
|
|
focal_point = owner.origin;
|
|
else
|
|
focal_point = self.origin;
|
|
|
|
possibleFlareVictims = self get_possible_flare_victims( focal_point );
|
|
|
|
for ( alienIndex = 0; alienIndex < possibleFlareVictims.size && affectedAliens.size < FLARE_MAX_NUM_ALIEN_AFFECTED; alienIndex++ )
|
|
{
|
|
alien = possibleFlareVictims[alienIndex];
|
|
|
|
if ( !alien should_attract_alien() )
|
|
continue;
|
|
|
|
if ( IsDefined( alien.attractor_flare ) )
|
|
continue;
|
|
|
|
alien maps\mp\agents\alien\_alien_think::handle_attractor_flare( self, true );
|
|
affectedAliens[affectedAliens.size] = alien;
|
|
}
|
|
|
|
wait 0.2;
|
|
}
|
|
|
|
foreach ( alien in affectedAliens )
|
|
alien maps\mp\agents\alien\_alien_think::handle_attractor_flare( self, false );
|
|
}
|
|
|
|
get_possible_flare_victims( focal_point )
|
|
{
|
|
maxOwnerVictimsPriorityRangeSq = FLARE_AFFECT_RADIUS * FLARE_AFFECT_RADIUS * 0.5;
|
|
|
|
aliens = maps\mp\agents\_agent_utility::getActiveAgentsOfType( "alien" );
|
|
victimsAroundOwner = get_array_of_closest( focal_point, aliens, undefined, undefined, FLARE_AFFECT_RADIUS );
|
|
victimsAroundFlare = get_array_of_closest( self.origin, aliens, undefined, undefined, FLARE_AFFECT_RADIUS );
|
|
possibleVictims = [];
|
|
|
|
for ( ownerRangeIndex = 0; ownerRangeIndex < victimsAroundOwner.size; ownerRangeIndex++ )
|
|
{
|
|
victim = victimsAroundOwner[ownerRangeIndex];
|
|
if ( array_contains( possibleVictims, victim ) )
|
|
continue;
|
|
|
|
distanceToOwner = DistanceSquared( focal_point, victim.origin );
|
|
if ( distanceToOwner <= maxOwnerVictimsPriorityRangeSq )
|
|
possibleVictims[possibleVictims.size] = victim;
|
|
else
|
|
break;
|
|
}
|
|
|
|
flareRangeIndex = 0;
|
|
|
|
while ( ownerRangeIndex < victimsAroundOwner.size || flareRangeIndex < victimsAroundFlare.size )
|
|
{
|
|
ownerDistance = undefined;
|
|
ownerVictim = undefined;
|
|
while ( ownerRangeIndex < victimsAroundOwner.size )
|
|
{
|
|
ownerVictim = victimsAroundOwner[ownerRangeIndex];
|
|
if ( !array_contains( possibleVictims, ownerVictim ) )
|
|
{
|
|
ownerDistance = DistanceSquared( focal_point, ownerVictim.origin );
|
|
break;
|
|
}
|
|
|
|
ownerRangeIndex++;
|
|
}
|
|
|
|
while ( flareRangeIndex < victimsAroundFlare.size )
|
|
{
|
|
flareVictim = victimsAroundFlare[flareRangeIndex];
|
|
if ( array_contains( possibleVictims, flareVictim ) )
|
|
{
|
|
flareRangeIndex++;
|
|
continue;
|
|
}
|
|
|
|
flareDistance = DistanceSquared( self.origin, flareVictim.origin );
|
|
if ( !IsDefined( ownerDistance ) || flareDistance < ownerDistance )
|
|
possibleVictims[possibleVictims.size] = flareVictim;
|
|
else
|
|
break;
|
|
|
|
flareRangeIndex++;
|
|
}
|
|
|
|
if ( IsDefined( ownerDistance ) )
|
|
{
|
|
possibleVictims[possibleVictims.size] = ownerVictim;
|
|
ownerRangeIndex++;
|
|
}
|
|
}
|
|
|
|
return possibleVictims;
|
|
}
|
|
|
|
should_attract_alien()
|
|
{
|
|
if ( is_true ( self.stuck_by_flare ) ) //don't attract this guy to himself ( ! )
|
|
return false;
|
|
|
|
switch ( self maps\mp\alien\_utility::get_alien_type() )
|
|
{
|
|
case "elite":
|
|
case "mammoth":
|
|
case "gargoyle":
|
|
return false;
|
|
default:
|
|
return true;
|
|
}
|
|
}
|
|
|
|
deleteOnDeath( ent )
|
|
{
|
|
self waittill( "death" );
|
|
|
|
if ( IsDefined( ent ) )
|
|
ent delete();
|
|
}
|
|
|
|
// ========= EASTER EGG ACTIVATION =========
|
|
|
|
// via shooting the lodge sign spelling "lol" in under 3 seconds
|
|
|
|
CONST_EASTER_EGG_LODGE_SIGN_ACTIVE_TIME = 120; // seconds for easter egg to last
|
|
CONST_EASTER_EGG_LODGE_SIGN_TIMER = 5000; // miliseconds to complete the activation
|
|
CONST_EASTER_EGG_LODGE_SIGN_ACTIVATOR_WEAPON = "iw6_alienvks_mp_alienvksscope"; // weapon used to activate
|
|
|
|
easter_egg_lodge_sign()
|
|
{
|
|
level endon( "game_ended" );
|
|
|
|
level notify( "easter_egg_lodge_sign_reset" );
|
|
level endon( "easter_egg_lodge_sign_reset" );
|
|
|
|
letter_l = getent( "easter_egg_letter_l", "targetname" );
|
|
letter_o = getent( "easter_egg_letter_o", "targetname" );
|
|
letter_reset = getent( "easter_egg_letter_reset", "targetname" );
|
|
|
|
if ( !isdefined( letter_l ) || !isdefined( letter_o ) || !isdefined( letter_reset ) )
|
|
return;
|
|
|
|
letter_reset thread watch_for_letter_reset();
|
|
|
|
while ( 1 )
|
|
{
|
|
letter_l waittill( "damage", damage, attacker, direction_vec, point, type );
|
|
timer = gettime();
|
|
if ( !is_letter_valid_hit( attacker, type ) )
|
|
continue;
|
|
//iprintlnBold( "^5L" );
|
|
|
|
letter_o waittill( "damage", damage, attacker, direction_vec, point, type );
|
|
if ( !is_letter_valid_hit( attacker, type ) )
|
|
continue;
|
|
//iprintlnBold( "^6O" );
|
|
|
|
letter_l waittill( "damage", damage, attacker, direction_vec, point, type );
|
|
if ( !is_letter_valid_hit( attacker, type ) || ( gettime() - timer ) > CONST_EASTER_EGG_LODGE_SIGN_TIMER )
|
|
continue;
|
|
//iprintlnBold( "^5L" );
|
|
|
|
wait 1;
|
|
|
|
iprintlnBold( "^5L^6O^5L" );
|
|
|
|
level.easter_egg_lodge_sign_active = true;
|
|
wait CONST_EASTER_EGG_LODGE_SIGN_ACTIVE_TIME; // time for easter egg to stay active
|
|
level.easter_egg_lodge_sign_active = false;
|
|
}
|
|
}
|
|
|
|
is_letter_valid_hit( attacker, type )
|
|
{
|
|
// self is sign letter
|
|
if ( !isdefined( attacker ) || !isPlayer( attacker ) )
|
|
return false;
|
|
|
|
attacker_weapon = attacker GetCurrentWeapon();
|
|
if ( !isdefined( attacker_weapon ) || attacker_weapon != CONST_EASTER_EGG_LODGE_SIGN_ACTIVATOR_WEAPON )
|
|
return false;
|
|
|
|
type = ToLower( type );
|
|
if ( !isdefined( type ) || type != "mod_rifle_bullet" )
|
|
return false;
|
|
|
|
return true;
|
|
}
|
|
|
|
watch_for_letter_reset()
|
|
{
|
|
while ( 1 )
|
|
{
|
|
self waittill( "damage", damage, attacker, direction_vec, point, type, modelName, tagName, partName, iDFlags, weapon );
|
|
|
|
if ( !isdefined( attacker ) || !isPlayer( attacker ) )
|
|
continue;
|
|
|
|
if ( isdefined( level.easter_egg_lodge_sign_active ) && level.easter_egg_lodge_sign_active )
|
|
continue;
|
|
|
|
//iprintlnBold( "^5>_<" );
|
|
wait 1;
|
|
return easter_egg_lodge_sign();
|
|
}
|
|
}
|