boiii-scripts/shared/trigger_shared.gsc
2023-04-13 17:30:38 +02:00

1492 lines
40 KiB
Plaintext

#using scripts\codescripts\struct;
#using scripts\shared\array_shared;
#using scripts\shared\flag_shared;
#using scripts\shared\hud_util_shared;
#using scripts\shared\spawner_shared;
#using scripts\shared\system_shared;
#using scripts\shared\util_shared;
#namespace trigger;
function autoexec __init__sytem__() { system::register("trigger",&__init__,undefined,undefined); }
function __init__()
{
level.fog_trigger_current = undefined;
level.trigger_hint_string = [];
level.trigger_hint_func = [];
if ( !isdefined( level.trigger_flags ) )
{
init_flags();
}
trigger_funcs = [];
trigger_funcs[ "trigger_unlock" ] = &trigger_unlock;
trigger_funcs[ "flag_set" ] = &flag_set_trigger;
trigger_funcs[ "flag_clear" ] = &flag_clear_trigger;
trigger_funcs[ "flag_set_touching" ] = &flag_set_touching;
trigger_funcs[ "friendly_respawn_trigger" ] = &friendly_respawn_trigger;
trigger_funcs[ "friendly_respawn_clear" ] = &friendly_respawn_clear;
trigger_funcs[ "trigger_delete" ] = &trigger_turns_off;
trigger_funcs[ "trigger_delete_on_touch" ] = &trigger_delete_on_touch;
trigger_funcs[ "trigger_off" ] = &trigger_turns_off;
trigger_funcs[ "delete_link_chain" ] = &delete_link_chain;
trigger_funcs[ "no_crouch_or_prone" ] = &no_crouch_or_prone_think;
trigger_funcs[ "no_prone" ] = &no_prone_think;
trigger_funcs[ "flood_spawner" ] = &spawner::flood_trigger_think;
trigger_funcs[ "trigger_spawner" ] = &trigger_spawner;
trigger_funcs[ "trigger_hint" ] = &trigger_hint;
trigger_funcs[ "exploder" ] = &trigger_exploder;
foreach ( trig in get_all( "trigger_radius", "trigger_multiple", "trigger_once", "trigger_box" ) )
{
if ( (isdefined(trig.spawnflags)&&((trig.spawnflags & 256) == 256)) )
{
level thread trigger_look( trig );
}
}
foreach ( trig in get_all() )
{
/#
trig check_spawnflags();
#/
if ( trig.classname != "trigger_damage" )
{
if ( trig.classname != "trigger_hurt" )
{
if ( (isdefined(trig.spawnflags)&&((trig.spawnflags & 32) == 32)) )
{
level thread trigger_spawner( trig );
}
}
}
if ( ( trig.classname != "trigger_once" ) && is_trigger_once( trig ) )
{
level thread trigger_once( trig );
}
if ( isdefined( trig.script_flag_true ) )
{
level thread script_flag_true_trigger( trig );
}
if ( isdefined( trig.script_flag_set ) )
{
level thread flag_set_trigger( trig, trig.script_flag_set );
}
if ( isdefined( trig.script_flag_set_on_touching ) || isdefined( trig.script_flag_set_on_cleared ) )
{
level thread script_flag_set_touching( trig );
}
if ( isdefined( trig.script_flag_clear ) )
{
level thread flag_clear_trigger( trig, trig.script_flag_clear );
}
if ( isdefined( trig.script_flag_false ) )
{
level thread script_flag_false_trigger( trig );
}
if ( isdefined( trig.script_trigger_group ) )
{
trig thread trigger_group();
}
// MikeD( 06/26/07 ): Added script_notify, which will send out the value set to script_notify as a level notify once triggered
if ( isdefined( trig.script_notify ) )
{
level thread trigger_notify( trig, trig.script_notify );
}
if ( isdefined( trig.script_fallback ) )
{
level thread spawner::fallback_think( trig );
}
if ( isdefined( trig.script_killspawner ) )
{
level thread kill_spawner_trigger( trig );
}
if ( isdefined( trig.targetname ) )
{
// do targetname specific functions
if ( isdefined( trigger_funcs[ trig.targetname ] ) )
{
level thread [[ trigger_funcs[ trig.targetname ] ]]( trig );
}
}
}
}
function check_spawnflags()
{
if ( ( isdefined( self.script_trigger_allplayers ) && self.script_trigger_allplayers )
&& ( (isdefined(self.spawnflags)&&((self.spawnflags & 1) == 1))
|| (isdefined(self.spawnflags)&&((self.spawnflags & 2) == 2))
|| (isdefined(self.spawnflags)&&((self.spawnflags & 4) == 4))
|| (isdefined(self.spawnflags)&&((self.spawnflags & 8) == 8))
|| (isdefined(self.spawnflags)&&((self.spawnflags & 16) == 16)) ) )
{
//Assert( "Triggers using 'script_trigger_allplayers' are not compatible with other entity types. Please unset spawnflags for other types." ); // TODO: put this back in, just didn't want to check in an assert at the end of the day
}
}
function trigger_unlock( trigger )
{
// trigger unlocks unlock another trigger. When that trigger is hit, all unlocked triggers relock
// trigger_unlocks with the same script_noteworthy relock the same triggers
noteworthy = "not_set";
if( isdefined( trigger.script_noteworthy ) )
{
noteworthy = trigger.script_noteworthy;
}
target_triggers = GetEntArray( trigger.target, "targetname" );
trigger thread trigger_unlock_death( trigger.target );
while ( true )
{
array::run_all( target_triggers, &TriggerEnable, false );
trigger waittill( "trigger" );
array::run_all( target_triggers, &TriggerEnable, true );
wait_for_an_unlocked_trigger( target_triggers, noteworthy );
array::notify_all( target_triggers, "relock" );
}
}
function trigger_unlock_death( target )
{
self waittill( "death" );
target_triggers = GetEntArray( target, "targetname" );
array::run_all( target_triggers, &TriggerEnable, false );
}
function wait_for_an_unlocked_trigger( triggers, noteworthy )
{
level endon( "unlocked_trigger_hit" + noteworthy );
ent = SpawnStruct();
for( i = 0; i < triggers.size; i++ )
{
triggers[i] thread report_trigger( ent, noteworthy );
}
ent waittill( "trigger" );
level notify( "unlocked_trigger_hit" + noteworthy );
}
function report_trigger( ent, noteworthy )
{
self endon( "relock" );
level endon( "unlocked_trigger_hit" + noteworthy );
self waittill( "trigger" );
ent notify( "trigger" );
}
function get_trigger_look_target()
{
if ( isdefined( self.target ) )
{
a_potential_targets = GetEntArray( self.target, "targetname" );
a_targets = [];
foreach ( target in a_potential_targets )
{
if ( ( target.classname === "script_origin" ) )
{
if ( !isdefined( a_targets ) ) a_targets = []; else if ( !IsArray( a_targets ) ) a_targets = array( a_targets ); a_targets[a_targets.size]=target;;
}
}
a_potential_target_structs = struct::get_array( self.target );
a_targets = ArrayCombine( a_targets, a_potential_target_structs, true, false );
if ( a_targets.size > 0 )
{
Assert( a_targets.size == 1, "Look tigger at " + self.origin + " targets multiple origins/structs." );
e_target = a_targets[0];
}
}
if(!isdefined(e_target))e_target=self;
return e_target;
}
function trigger_look( trigger )
{
trigger endon( "death" );
e_target = trigger get_trigger_look_target();
if ( isdefined( trigger.script_flag ) && !isdefined( level.flag[trigger.script_flag] ) )
{
level flag::init( trigger.script_flag, undefined, true );
}
a_parameters = [];
if ( isdefined( trigger.script_parameters ) )
{
a_parameters = StrTok( trigger.script_parameters, ",; " );
}
b_ads_check = IsInArray( a_parameters, "check_ads" );
while ( true )
{
trigger waittill( "trigger", e_other );
if ( IsPlayer( e_other ) )
{
while ( isdefined( e_other ) && e_other IsTouching( trigger ) )
{
if ( e_other util::is_looking_at( e_target, trigger.script_dot, ( isdefined( trigger.script_trace ) && trigger.script_trace ) )
&& ( !b_ads_check || !e_other util::is_ads() ) )
{
trigger notify( "trigger_look", e_other );
if ( isdefined( trigger.script_flag ) )
{
level flag::set( trigger.script_flag );
}
}
else
{
if ( isdefined( trigger.script_flag ) )
{
level flag::clear( trigger.script_flag );
}
}
{wait(.05);};
}
if ( isdefined( trigger.script_flag ) )
{
level flag::clear( trigger.script_flag );
}
}
else
{
AssertMsg( "Look triggers only support players." );
}
}
}
function trigger_spawner( trigger )
{
a_spawners = GetSpawnerArray( trigger.target, "targetname" );
Assert( a_spawners.size > 0, "Triggers with flag TRIGGER_SPAWN at " + trigger.origin + " must target at least one spawner." );
trigger endon( "death" );
trigger wait_till();
foreach ( sp in a_spawners )
{
if ( isdefined( sp ) )
{
sp thread trigger_spawner_spawn();
}
}
}
function trigger_spawner_spawn()
{
self endon( "death" );
self flag::script_flag_wait();
self util::script_delay();
self spawner::spawn();
}
/@
"Name: trigger_notify()"
"Summary: Sends out a level notify of the trigger's script_notify once triggered"
"Module: Trigger"
"CallOn: "
"Example: trigger thread trigger_notify(); "
"SPMP: singleplayer"
@/
function trigger_notify( trigger, msg )
{
trigger endon( "death" );
other = trigger wait_till();
if( isdefined( trigger.target ) )
{
a_target_ents = GetEntArray( trigger.target, "targetname" );
foreach ( notify_ent in a_target_ents )
{
notify_ent notify( msg, other );
}
}
level notify( msg, other );
}
function flag_set_trigger( trigger, str_flag )
{
trigger endon( "death" );
if(!isdefined(str_flag))str_flag=trigger.script_flag;
if ( !level flag::exists( str_flag ) )
{
level flag::init( str_flag, undefined, true );
}
while ( true )
{
trigger wait_till();
if ( isdefined( trigger.targetname ) && ( trigger.targetname == "flag_set" ) )
{
// this is a "flag_set" trigger so support script_delay
// generic triggers don't use script_dealy for flag setting because the
// script_delay might be intended for something else
trigger util::script_delay();
}
level flag::set( str_flag );
}
}
function flag_clear_trigger( trigger, str_flag )
{
trigger endon( "death" );
if(!isdefined(str_flag))str_flag=trigger.script_flag;
if ( !level flag::exists( str_flag ) )
{
level flag::init( str_flag, undefined, true );
}
while ( true )
{
trigger wait_till();
if ( isdefined( trigger.targetname ) && ( trigger.targetname == "flag_clear" ) )
{
// this is a "level flag::clear" trigger so support script_delay
// generic triggers don't use script_dealy for flag clearing because the
// script_delay might be intended for something else
trigger util::script_delay();
}
level flag::clear( str_flag );
}
}
function add_tokens_to_trigger_flags( tokens )
{
for( i = 0; i < tokens.size; i++ )
{
flag = tokens[i];
if( !isdefined( level.trigger_flags[flag] ) )
{
level.trigger_flags[flag] = [];
}
level.trigger_flags[flag][level.trigger_flags[flag].size] = self;
}
}
function script_flag_false_trigger( trigger )
{
// all of these flags must be false for the trigger to be enabled
tokens = util::create_flags_and_return_tokens( trigger.script_flag_false );
trigger add_tokens_to_trigger_flags( tokens );
trigger update_based_on_flags();
}
function script_flag_true_trigger( trigger )
{
// all of these flags must be false for the trigger to be enabled
tokens = util::create_flags_and_return_tokens( trigger.script_flag_true );
trigger add_tokens_to_trigger_flags( tokens );
trigger update_based_on_flags();
}
function friendly_respawn_trigger( trigger )
{
trigger endon( "death" );
spawners = GetEntArray( trigger.target, "targetname" );
assert( spawners.size == 1, "friendly_respawn_trigger targets multiple spawner with targetname " + trigger.target + ". Should target just 1 spawner." );
spawner = spawners[0];
assert( !isdefined( spawner.script_forcecolor ), "targeted spawner at " + spawner.origin + " should not have script_forcecolor set!" );
spawners = undefined;
spawner endon( "death" );
while ( true )
{
trigger waittill( "trigger" );
// SRS 12/20/2007: updated to allow for multiple color chains to be reinforced from different areas
if( isdefined( trigger.script_forcecolor ) )
{
level.respawn_spawners_specific[trigger.script_forcecolor] = spawner;
}
else
{
level.respawn_spawner = spawner;
}
level flag::set( "respawn_friendlies" );
wait( 0.5 );
}
}
function friendly_respawn_clear( trigger )
{
trigger endon( "death" );
while ( true )
{
trigger waittill( "trigger" );
level flag::clear( "respawn_friendlies" );
wait 0.5;
}
}
function trigger_turns_off( trigger )
{
trigger wait_till();
trigger TriggerEnable( false );
if( !isdefined( trigger.script_linkTo ) )
{
return;
}
// also turn off all triggers this trigger links to
tokens = Strtok( trigger.script_linkto, " " );
for( i = 0; i < tokens.size; i++ )
{
array::run_all( GetEntArray( tokens[i], "script_linkname" ), &TriggerEnable, false );
}
}
function script_flag_set_touching( trigger )
{
trigger endon( "death" );
if ( isdefined( trigger.script_flag_set_on_touching ) )
{
level flag::init( trigger.script_flag_set_on_touching, undefined, true );
}
if ( isdefined( trigger.script_flag_set_on_cleared ) )
{
level flag::init( trigger.script_flag_set_on_cleared, undefined, true );
}
trigger thread _detect_touched();
while ( true )
{
trigger.script_touched = false;
{wait(.05);};
waittillframeend; // wait for touched variable to update for this frame
if ( !trigger.script_touched )
{
// HACK: wait one more frame if it isn't touched - saw a strange issue where
// the trigger thought it wasn't touched for a frame when another player joined
// even though the first player was still standing in the trigger
{wait(.05);};
waittillframeend;
}
if ( trigger.script_touched )
{
if ( isdefined( trigger.script_flag_set_on_touching ) )
{
level flag::set( trigger.script_flag_set_on_touching );
}
if ( isdefined( trigger.script_flag_set_on_cleared ) )
{
level flag::clear( trigger.script_flag_set_on_cleared );
}
}
else
{
if ( isdefined( trigger.script_flag_set_on_touching ) )
{
level flag::clear( trigger.script_flag_set_on_touching );
}
if ( isdefined( trigger.script_flag_set_on_cleared ) )
{
level flag::set( trigger.script_flag_set_on_cleared );
}
}
}
}
function _detect_touched()
{
self endon( "death" );
while ( true )
{
self waittill( "trigger" );
self.script_touched = true;
}
}
function trigger_delete_on_touch( trigger )
{
while ( true )
{
trigger waittill( "trigger", other );
if( isdefined( other ) )
{
// might've been removed before we got it
other Delete();
}
}
}
function flag_set_touching( trigger )
{
str_flag = trigger.script_flag;
if ( !isdefined( level.flag[ str_flag ] ) )
{
level flag::init( str_flag, undefined, true );
}
while ( true )
{
trigger waittill( "trigger", other );
level flag::set( str_flag );
while ( IsAlive( other ) && other IsTouching( trigger ) && isdefined( trigger ) )
{
wait( 0.25 );
}
level flag::clear( str_flag );
}
}
function trigger_once( trig )
{
trig endon( "death" );
if ( is_look_trigger( trig ) )
{
trig waittill( "trigger_look" );
}
else
{
trig waittill( "trigger" );
}
waittillframeend;
waittillframeend;
if ( isdefined( trig ) )
{
/#
println( "" );
println( "*** trigger debug: deleting trigger with ent#: " + trig getentitynumber() + " at origin: " + trig.origin );
println( "" );
#/
trig Delete();
}
}
function trigger_hint( trigger )
{
assert( isdefined( trigger.script_hint ), "Trigger_hint at " + trigger.origin + " has no .script_hint" );
trigger endon( "death" );
if ( !isdefined( level.displayed_hints ) )
{
level.displayed_hints = [];
}
waittillframeend; // give level script a chance to set the hint string and optional boolean functions on this hint
assert( isdefined( level.trigger_hint_string[ trigger.script_hint ] ), "Trigger_hint with hint " + trigger.script_hint + " had no hint string assigned to it. Define hint strings with util::add_hint_string()" );
trigger waittill( "trigger", other );
assert( IsPlayer( other ), "Tried to do a trigger_hint on a non player entity" );
if ( isdefined( level.displayed_hints[ trigger.script_hint ] ) )
{
return;
}
level.displayed_hints[ trigger.script_hint ] = true;
display_hint( trigger.script_hint );
}
function trigger_exploder( trigger )
{
trigger endon( "death" );
while (true)
{
trigger waittill( "trigger" );
if (isdefined(trigger.target ))
{
ActivateClientRadiantExploder( trigger.target );
}
}
}
/@
"Name: display_hint( <hint> )"
"Summary: Displays a hint created with add_hint_string."
"Module: Utility"
"MandatoryArg: <hint> : The hint reference created with add_hint_string."
"Example: display_hint( "huzzah" )"
"SPMP: singleplayer"
@/
function display_hint( hint )
{
if ( GetDvarString( "chaplincheat" ) == "1" )
{
return;
}
// hint triggers have an optional function they can boolean off of to determine if the hint will occur
// such as not doing the NVG hint if the player is using NVGs already
if( isdefined( level.trigger_hint_func[ hint ] ) )
{
if( [[ level.trigger_hint_func[ hint ] ]]() )
{
return;
}
_hint_print( level.trigger_hint_string[ hint ], level.trigger_hint_func[ hint ] );
}
else
{
_hint_print( level.trigger_hint_string[ hint ] );
}
}
function _hint_print( string, breakfunc )
{
const MYFADEINTIME = 1.0;
const MYFLASHTIME = 0.75;
const MYALPHAHIGH = 0.95;
const MYALPHALOW = 0.4;
level flag::wait_till_clear( "global_hint_in_use" );
level flag::set( "global_hint_in_use" );
Hint = hud::createFontString( "objective", 2 );
//Hint.color = ( 1, 1, .5 ); //remove color so that color highlighting on PC can show up.
Hint.alpha = 0.9;
Hint.x = 0;
Hint.y = -68;
Hint.alignx = "center";
Hint.aligny = "middle";
Hint.horzAlign = "center";
Hint.vertAlign = "middle";
Hint.foreground = false;
Hint.hidewhendead = true;
Hint setText( string );
Hint.alpha = 0;
Hint FadeOverTime( MYFADEINTIME );
Hint.alpha = MYALPHAHIGH;
_hint_print_wait( MYFADEINTIME );
if ( isdefined( breakfunc ) )
{
for ( ;; )
{
Hint FadeOverTime( MYFLASHTIME );
Hint.alpha = MYALPHALOW;
_hint_print_wait( MYFLASHTIME, breakfunc );
if ( [[ breakfunc ]]() )
{
break;
}
Hint FadeOverTime( MYFLASHTIME );
Hint.alpha = MYALPHAHIGH;
_hint_print_wait( MYFLASHTIME );
if ( [[ breakfunc ]]() )
{
break;
}
}
}
else
{
for ( i = 0; i < 5; i++ )
{
Hint FadeOverTime( MYFLASHTIME );
Hint.alpha = MYALPHALOW;
_hint_print_wait( MYFLASHTIME );
Hint FadeOverTime( MYFLASHTIME );
Hint.alpha = MYALPHAHIGH;
_hint_print_wait( MYFLASHTIME );
}
}
Hint Destroy();
level flag::clear( "global_hint_in_use" );
}
function _hint_print_wait( length, breakfunc )
{
if ( !isdefined( breakfunc ) )
{
wait( length );
return;
}
timer = length * 20;
for ( i = 0; i < timer; i++ )
{
if ( [[ breakfunc ]]() )
{
break;
}
{wait(.05);};
}
}
/@
"Name: get_all(type1, type2, type3, type4, type5, type6, type7, type8, type9)"
"Summary: Gets all triggers on the map. If only want certain types, can specify those"
"Module: Level"
"CallOn: N/A"
"OptionalArg: <type1-9> : Classname of Trigger to get."
"Example: trigs = get_all();"
"SPMP: singleplayer"
@/
function get_all( type1, type2, type3, type4, type5, type6, type7, type8, type9 )
{
if ( !isdefined( type1 ) )
{
type1 = "trigger_damage";
type2 = "trigger_hurt";
type3 = "trigger_lookat";
type4 = "trigger_once";
type5 = "trigger_radius";
type6 = "trigger_use";
type7 = "trigger_use_touch";
type8 = "trigger_box";
type9 = "trigger_multiple";
type10 = "trigger_out_of_bounds";
}
assert( _is_valid_trigger_type( type1 ) );
trigs = GetEntArray( type1, "classname" );
if ( isdefined( type2 ) )
{
assert( _is_valid_trigger_type( type2 ) );
trigs = ArrayCombine( trigs, GetEntArray( type2, "classname" ), true, false );
}
if ( isdefined( type3 ) )
{
assert( _is_valid_trigger_type( type3 ) );
trigs = ArrayCombine( trigs, GetEntArray( type3, "classname" ), true, false );
}
if ( isdefined( type4 ) )
{
assert( _is_valid_trigger_type( type4 ) );
trigs = ArrayCombine( trigs, GetEntArray( type4, "classname" ), true, false );
}
if ( isdefined( type5 ) )
{
assert( _is_valid_trigger_type( type5 ) );
trigs = ArrayCombine( trigs, GetEntArray( type5, "classname" ), true, false );
}
if ( isdefined( type6 ) )
{
assert( _is_valid_trigger_type( type6 ) );
trigs = ArrayCombine( trigs, GetEntArray( type6, "classname" ), true, false );
}
if ( isdefined( type7 ) )
{
assert( _is_valid_trigger_type( type7 ) );
trigs = ArrayCombine( trigs, GetEntArray( type7, "classname" ), true, false );
}
if ( isdefined( type8 ) )
{
assert( _is_valid_trigger_type( type8 ) );
trigs = ArrayCombine( trigs, GetEntArray( type8, "classname" ), true, false );
}
if ( isdefined( type9 ) )
{
assert( _is_valid_trigger_type( type9 ) );
trigs = ArrayCombine( trigs, GetEntArray( type9, "classname" ), true, false );
}
if ( isdefined( type10 ) )
{
assert( _is_valid_trigger_type( type9 ) );
trigs = ArrayCombine( trigs, GetEntArray( type10, "classname" ), true, false );
}
return trigs;
}
function _is_valid_trigger_type( type )
{
switch ( type )
{
case "trigger_damage":
case "trigger_hurt":
case "trigger_lookat":
case "trigger_once":
case "trigger_radius":
case "trigger_use":
case "trigger_use_touch":
case "trigger_box":
case "trigger_multiple":
case "trigger_out_of_bounds":
return true;
break;
default:
return false;
}
}
/@
function Name: wait_till( <str_name> , str_key = "targetname", e_entity )
Summary: Waits until a trigger with the specified key / value is triggered. Returns the trigger and assigns the entity that triggered it to "trig.who".
Module: Trigger
MandatoryArg: str_name: Key value.
OptionalArg: str_key: Key name on the trigger to use, example: "targetname" or "script_noteworthy".
OptionalArg: e_entity: Wait for a specific entity to trigger the trigger.
"OptionalArg: <b_assert> : Set to false to not assert if the trigger doesn't exist"
function Example: wait_till( "player_in_building1, "script_noteworthy" );
SPMP: both
@/
function wait_till( str_name, str_key = "targetname", e_entity, b_assert = true )
{
if ( isdefined( str_name ) )
{
triggers = GetEntArray( str_name, str_key );
if( ( SessionModeIsCampaignZombiesGame() ) )
{
// sjakatdar ( 09/21/2015 ) - A temporary fix which should be removed in T8 branch. In campaign zombies,
// players can double jump ahead and hit the trigger before trigger::wait_till is called, which can end up in this assert.
//Assert( !b_assert || ( triggers.size > 0 ), "trigger not found: " + str_name + " key: " + str_key );
// Its better to just return true if the trigger is not found instead.
if( triggers.size <= 0 )
return;
}
else
{
Assert( !b_assert || ( triggers.size > 0 ), "trigger not found: " + str_name + " key: " + str_key );
}
if ( triggers.size > 0 )
{
if ( triggers.size == 1 )
{
trigger_hit = triggers[0];
trigger_hit _trigger_wait( e_entity );
}
else
{
s_tracker = SpawnStruct();
array::thread_all( triggers,&_trigger_wait_think, s_tracker, e_entity );
s_tracker waittill( "trigger", e_other, trigger_hit );
trigger_hit.who = e_other;
}
return trigger_hit;
}
}
else
{
if( ( SessionModeIsCampaignZombiesGame() ) )
{
// sjakatdar ( 09/21/2015 ) - A temporary fix which should be removed in T8 branch. In campaign zombies,
// players can double jump ahead and hit the trigger before trigger::wait_till is called, which can end up in script errors
// Its better to just return true if the trigger is not found instead.
if( !IsDefined( self ) )
return;
}
return _trigger_wait( e_entity );
}
}
function _trigger_wait( e_entity )
{
self endon( "death" );
if ( isdefined( e_entity ) )
{
e_entity endon( "death" );
}
/#
if ( is_look_trigger( self ) )
{
Assert( !IsArray( e_entity ), "LOOK triggers can only wait on one entity." );
}
else if ( self.classname === "trigger_damage" )
{
Assert( !IsArray( e_entity ), "Damage triggers can only wait on one entity." );
}
#/
while ( true )
{
if ( is_look_trigger( self ) )
{
self waittill( "trigger_look", e_other );
if ( isdefined( e_entity ) )
{
if ( e_other !== e_entity )
{
continue;
}
}
}
else if ( self.classname === "trigger_damage" )
{
self waittill( "trigger", e_other );
if ( isdefined( e_entity ) )
{
if ( e_other !== e_entity )
{
continue;
}
}
}
else
{
self waittill( "trigger", e_other );
if ( isdefined( e_entity ) )
{
if ( IsArray( e_entity ) )
{
if ( !array::is_touching( e_entity, self ) )
{
continue;
}
}
else if ( !e_entity IsTouching( self ) && ( e_entity !== e_other ) )
{
continue;
}
}
}
break;
}
self.who = e_other;
return self;
}
function _trigger_wait_think( s_tracker, e_entity )
{
self endon( "death" );
s_tracker endon( "trigger" );
e_other = _trigger_wait( e_entity );
s_tracker notify( "trigger", e_other, self );
}
/@
"Name: use( <str_name> , [str_key], [ent], [b_assert] )"
"Summary: Activates a trigger with the specified key / value is triggered"
"Module: Trigger"
"CallOn: "
"MandatoryArg: <str_name> : Name of the key on this trigger. If name is not passed in, func will check against self"
"OptionalArg: [str_key] : Key on the trigger to use, example: "targetname" or "script_noteworthy""
"OptionalArg: [ent] : Entity that the trigger is used by"
"OptionalArg: [b_assert] : Set to false to not assert if the trigger doesn't exist"
"Example: use( "player_in_building1, "targetname", enemy, false );"
"SPMP: singleplayer"
@/
function use( str_name, str_key = "targetname", ent, b_assert = true )
{
if ( !isdefined( ent ) )
{
ent = GetPlayers()[0];
}
if ( isdefined( str_name ) )
{
e_trig = GetEnt( str_name, str_key );
if( !isdefined( e_trig ) )
{
if ( b_assert )
{
AssertMsg( "trigger not found: " + str_name + " key: " + str_key );
}
return;
}
}
else
{
e_trig = self;
str_name = self.targetname;
}
if( isdefined( ent ) )
{
e_trig UseBy( ent );
}
else
{
e_trig UseBy( e_trig );
}
level notify( str_name, ent ); // TODO: brianb - do we need this?
if ( is_look_trigger( e_trig ) )
{
e_trig notify( "trigger_look", ent );
}
return e_trig;
}
function set_flag_permissions( msg )
{
// turns triggers on or off depending on if they have the proper flags set, based on their shift-g menu settings
if ( !isdefined( level.trigger_flags ) || !isdefined( level.trigger_flags[ msg ] ) )
{
return;
}
// cheaper to do the upkeep at this time rather than with endons and waittills on the individual triggers
level.trigger_flags[ msg ] = array::remove_undefined( level.trigger_flags[ msg ] );
array::thread_all( level.trigger_flags[ msg ], &update_based_on_flags );
}
function update_based_on_flags()
{
true_on = true;
if ( isdefined( self.script_flag_true ) )
{
true_on = false;
tokens = util::create_flags_and_return_tokens( self.script_flag_true );
// stay off unless any of the flags are true
for( i=0; i < tokens.size; i++ )
{
if ( level flag::get( tokens[ i ] ) )
{
true_on = true;
break;
}
}
}
false_on = true;
if ( isdefined( self.script_flag_false ) )
{
tokens = util::create_flags_and_return_tokens( self.script_flag_false );
// stay off unless all flags are false
for( i=0; i < tokens.size; i++ )
{
if ( level flag::get( tokens[ i ] ) )
{
false_on = false;
break;
}
}
}
b_enable = true_on && false_on;
self TriggerEnable( b_enable );
}
function init_flags()
{
level.trigger_flags = [];
}
function is_look_trigger( trig )
{
return ( isdefined( trig ) ? (isdefined(trig.spawnflags)&&((trig.spawnflags & 256) == 256)) && !( trig.classname === "trigger_damage" ) : false );
}
function is_trigger_once( trig )
{
return ( isdefined( trig ) ? (isdefined(trig.spawnflags)&&((trig.spawnflags & 1024) == 1024)) || ( ( self.classname === "trigger_once" ) ) : false );
}
function wait_for_either( str_targetname1, str_targetname2 )
{
ent = SpawnStruct();
array = [];
array = ArrayCombine( array, GetEntArray( str_targetname1, "targetname" ), true, false );
array = ArrayCombine( array, GetEntArray( str_targetname2, "targetname" ), true, false );
for ( i = 0; i < array.size; i++ )
{
ent thread _ent_waits_for_trigger( array[ i ] );
}
ent waittill( "done", t_hit );
return t_hit;
}
function _ent_waits_for_trigger( trigger )
{
trigger wait_till();
self notify( "done", trigger );
}
/@
"Name: wait_or_timeout( n_time, str_name, str_key )"
"Summary: Wait for a trigger to trigger or time passes."
"MandatoryArg: n_time: the amount of time."
"OptionalArg: str_name: name of the trigger."
"OptionalArg: str_key: KVP key of the trigger."
"Example: trigger::wait_or_timeout( 5, "my_trigger", "targetname" );"
@/
function wait_or_timeout( n_time, str_name, str_key )
{
if ( isdefined( n_time ) ) { __s = SpawnStruct(); __s endon( "timeout" ); __s util::delay_notify( n_time, "timeout" ); };
wait_till( str_name, str_key );
}
/@
"Name: trigger_on_timeout( n_time, b_cancel_on_triggered, str_name, str_key )"
"Summary: Force a trigger to trigger when time passes."
"MandatoryArg: n_time: the amount of time."
"OptionalArg: b_cancel_on_triggered: don't trigger the trigger if it gets triggered before the time is up."
"OptionalArg: str_name: name of the trigger."
"OptionalArg: str_key: KVP key of the trigger."
"Example: trigger::trigger_on_timeout( 5, true, "my_trigger", "targetname" );"
@/
function trigger_on_timeout( n_time, b_cancel_on_triggered = true, str_name, str_key = "targetname" )
{
trig = self;
if ( isdefined( str_name ) )
{
trig = GetEnt( str_name, str_key );
}
if ( b_cancel_on_triggered )
{
if ( is_look_trigger( trig ) )
{
trig endon( "trigger_look" );
}
else
{
trig endon( "trigger" );
}
}
trig endon( "death" );
wait n_time;
trig use();
}
function multiple_waits( str_trigger_name, str_trigger_notify )
{
foreach ( trigger in GetEntArray( str_trigger_name, "targetname") )
{
trigger thread multiple_wait( str_trigger_notify );
}
}
function multiple_wait( str_trigger_notify )// self = trigger ent
{
level endon( str_trigger_notify );
self waittill( "trigger" );
level notify( str_trigger_notify );
}
/@
"Name: add_function( <str_trig_targetname>, [str_remove_on], <func>, [param_1], [param_2], [param_3], [param_4], [param_5], [param_6] )"
"Summary: Runs a function when a trigger is hit."
"Module: Utility"
"CallOn: level"
"MandatoryArg: <str_trig_targetname> : targetname of the trigger"
"OptionalArg: [str_remove_on] : remove the function on this notify to the trigger"
"MandatoryArg: <func> : pointer to a script function"
"OptionalArg: [param_1] : parameter 1 to pass to the func"
"OptionalArg: [param_2] : parameter 2 to pass to the func"
"OptionalArg: [param_3] : parameter 3 to pass to the func"
"OptionalArg: [param_4] : parameter 4 to pass to the func"
"OptionalArg: [param_5] : parameter 5 to pass to the func"
"OptionalArg: [param_6] : parameter 6 to pass to the func"
"Example: add_function( "my_trigger",&my_trigger_function );"
"SPMP: SP"
@/
function add_function( trigger, str_remove_on, func, param_1, param_2, param_3, param_4, param_5, param_6 )
{
self thread _do_trigger_function( trigger, str_remove_on, func, param_1, param_2, param_3, param_4, param_5, param_6 );
}
function _do_trigger_function( trigger, str_remove_on, func, param_1, param_2, param_3, param_4, param_5, param_6 )
{
self endon( "death" );
trigger endon( "death" );
if ( isdefined( str_remove_on ) )
{
trigger endon( str_remove_on );
}
while ( true )
{
if ( IsString( trigger ) )
{
wait_till( trigger );
}
else
{
trigger wait_till();
}
util::single_thread( self, func, param_1, param_2, param_3, param_4, param_5, param_6 );
}
}
function kill_spawner_trigger( trigger )
{
trigger trigger::wait_till();
a_spawners = GetSpawnerArray( trigger.script_killspawner, "script_killspawner" );
foreach ( sp in a_spawners )
{
sp Delete();
}
a_ents = GetEntArray( trigger.script_killspawner, "script_killspawner" );
foreach ( ent in a_ents )
{
if ( ( ent.classname === "spawn_manager" ) && ( ent != trigger ) )
{
ent Delete();
}
}
}
function get_script_linkto_targets()
{
targets = [];
if( !isdefined( self.script_linkto ) )
{
return targets;
}
tokens = Strtok( self.script_linkto, " " );
for( i = 0; i < tokens.size; i++ )
{
token = tokens[i];
target = GetEnt( token, "script_linkname" );
if( isdefined( target ) )
{
targets[targets.size] = target;
}
}
return targets;
}
function delete_link_chain( trigger )
{
// deletes all entities that it script_linkto's, and all entities that entity script linktos, etc.
trigger waittill( "trigger" );
targets = trigger get_script_linkto_targets();
array::thread_all( targets, &delete_links_then_self );
}
function delete_links_then_self()
{
targets = get_script_linkto_targets();
array::thread_all( targets, &delete_links_then_self );
self Delete();
}
function no_crouch_or_prone_think( trigger )
{
while ( true )
{
trigger waittill( "trigger", other );
if( !IsPlayer( other ) )
{
continue;
}
while( other IsTouching( trigger ) )
{
other AllowProne( false );
other AllowCrouch( false );
{wait(.05);};
}
other AllowProne( true );
other AllowCrouch( true );
}
}
function no_prone_think( trigger )
{
while ( true )
{
trigger waittill( "trigger", other );
if( !IsPlayer( other ) )
{
continue;
}
while( other IsTouching( trigger ) )
{
other AllowProne( false );
{wait(.05);};
}
other AllowProne( true );
}
}
function trigger_group()
{
self thread trigger_group_remove();
level endon( "trigger_group_" + self.script_trigger_group );
self waittill( "trigger" );
level notify( "trigger_group_" + self.script_trigger_group, self );
}
function trigger_group_remove()
{
level waittill( "trigger_group_" + self.script_trigger_group, trigger );
if( self != trigger )
{
self Delete();
}
}
//trigger_thread
function function_thread(ent, on_enter_payload, on_exit_payload)
{
ent endon("entityshutdown");
if(ent ent_already_in(self))
return;
add_to_ent(ent, self);
// iprintlnbold("Trigger " + self.targetname + " hit by ent " + ent getentitynumber());
if(isdefined(on_enter_payload))
{
[[on_enter_payload]](ent);
}
while(isdefined(ent) && ent istouching(self))
{
wait(0.01);
}
// iprintlnbold(ent getentitynumber() + " leaves trigger " + self.targetname + ".");
if(isdefined(ent) && isdefined(on_exit_payload))
{
[[on_exit_payload]](ent);
}
if(isdefined(ent))
{
remove_from_ent(ent, self);
}
}
function ent_already_in(trig)
{
if(!isdefined(self._triggers))
return false;
if(!isdefined(self._triggers[trig getentitynumber()]))
return false;
if(!self._triggers[trig getentitynumber()])
return false;
return true; // We're already in this trigger volume.
}
function add_to_ent(ent, trig)
{
if(!isdefined(ent._triggers))
{
ent._triggers = [];
}
ent._triggers[trig getentitynumber()] = 1;
}
function remove_from_ent(ent, trig)
{
if(!isdefined(ent._triggers))
return;
if(!isdefined(ent._triggers[trig getentitynumber()]))
return;
ent._triggers[trig getentitynumber()] = 0;
}