#include common_scripts\utility; /* Handles Old-School exploders. This is coupled with _createfx and _fx since a Radiant Created "exploder" can trigger a createfx created effect. This Script is legacy, tried and true, It eats up entites when using script_brushmodel swapping and a lot of script_models and the damage triggers. We should in a future game consider some entiity optimization in code. A way to Join models and brushes as one entity would be key. */ setup_individual_exploder( ent ) { exploder_num = ent.script_exploder; if ( !IsDefined( level.exploders[ exploder_num ] ) ) { level.exploders[ exploder_num ] = []; } targetname = ent.targetname; if ( !IsDefined( targetname ) ) targetname = ""; level.exploders[ exploder_num ][ level.exploders[ exploder_num ].size ] = ent; if ( exploder_model_starts_hidden( ent ) ) { ent Hide(); return; } if ( exploder_model_is_damaged_model( ent ) ) { ent Hide(); ent NotSolid(); if ( IsDefined( ent.spawnflags ) && ( ent.spawnflags & 1 ) ) { if ( IsDefined( ent.script_disconnectpaths ) ) { ent ConnectPaths(); } } return; } if ( exploder_model_is_chunk( ent ) ) { ent Hide(); ent NotSolid(); if ( IsDefined( ent.spawnflags ) && ( ent.spawnflags & 1 ) ) ent ConnectPaths(); return; } } setupExploders() { level.exploders = []; // Hide exploder models. ents = GetEntArray( "script_brushmodel", "classname" ); smodels = GetEntArray( "script_model", "classname" ); for ( i = 0; i < smodels.size; i++ ) ents[ ents.size ] = smodels[ i ]; foreach ( ent in ents ) { if ( IsDefined( ent.script_prefab_exploder ) ) ent.script_exploder = ent.script_prefab_exploder; if ( IsDefined( ent.masked_exploder ) ) continue; if ( IsDefined( ent.script_exploder ) ) { setup_individual_exploder( ent ); } } script_exploders = []; potentialExploders = GetEntArray( "script_brushmodel", "classname" ); for ( i = 0; i < potentialExploders.size; i++ ) { if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; if ( IsDefined( potentialExploders[ i ].script_exploder ) ) script_exploders[ script_exploders.size ] = potentialExploders[ i ]; } potentialExploders = GetEntArray( "script_model", "classname" ); for ( i = 0; i < potentialExploders.size; i++ ) { if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; if ( IsDefined( potentialExploders[ i ].script_exploder ) ) script_exploders[ script_exploders.size ] = potentialExploders[ i ]; } potentialExploders = GetEntArray( "item_health", "classname" ); for ( i = 0; i < potentialExploders.size; i++ ) { if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; if ( IsDefined( potentialExploders[ i ].script_exploder ) ) script_exploders[ script_exploders.size ] = potentialExploders[ i ]; } potentialExploders = level.struct; for ( i = 0; i < potentialExploders.size; i++ ) { if ( !IsDefined( potentialExploders[ i ] ) ) continue; // these must be getting deleted somewhere else? if ( IsDefined( potentialExploders[ i ].script_prefab_exploder ) ) potentialExploders[ i ].script_exploder = potentialExploders[ i ].script_prefab_exploder; if ( IsDefined( potentialExploders[ i ].script_exploder ) ) { if ( !IsDefined( potentialExploders[ i ].angles ) ) potentialExploders[ i ].angles = ( 0, 0, 0 ); script_exploders[ script_exploders.size ] = potentialExploders[ i ]; } } if ( !IsDefined( level.createFXent ) ) level.createFXent = []; acceptableTargetnames = []; acceptableTargetnames[ "exploderchunk visible" ] = true; acceptableTargetnames[ "exploderchunk" ] = true; acceptableTargetnames[ "exploder" ] = true; thread setup_flag_exploders(); for ( i = 0; i < script_exploders.size; i++ ) { exploder = script_exploders[ i ]; ent = createExploder( exploder.script_fxid ); ent.v = []; ent.v[ "origin" ] = exploder.origin; ent.v[ "angles" ] = exploder.angles; ent.v[ "delay" ] = exploder.script_delay; ent.v[ "delay_post" ] = exploder.script_delay_post; ent.v[ "firefx" ] = exploder.script_firefx; ent.v[ "firefxdelay" ] = exploder.script_firefxdelay; ent.v[ "firefxsound" ] = exploder.script_firefxsound; ent.v[ "earthquake" ] = exploder.script_earthquake; ent.v[ "rumble" ] = exploder.script_rumble; ent.v[ "damage" ] = exploder.script_damage; ent.v[ "damage_radius" ] = exploder.script_radius; ent.v[ "soundalias" ] = exploder.script_soundalias; ent.v[ "repeat" ] = exploder.script_repeat; ent.v[ "delay_min" ] = exploder.script_delay_min; ent.v[ "delay_max" ] = exploder.script_delay_max; ent.v[ "target" ] = exploder.target; ent.v[ "ender" ] = exploder.script_ender; ent.v[ "physics" ] = exploder.script_physics; ent.v[ "type" ] = "exploder"; // ent.v[ "worldfx" ] = true; if ( !IsDefined( exploder.script_fxid ) ) ent.v[ "fxid" ] = "No FX"; else ent.v[ "fxid" ] = exploder.script_fxid; ent.v [ "exploder" ] = exploder.script_exploder; AssertEx( IsDefined( exploder.script_exploder ), "Exploder at origin " + exploder.origin + " has no script_exploder" ); if ( IsDefined( level.createFXexploders ) ) { // if we're using the optimized lookup, add it in the proper place ary = level.createFXexploders[ ent.v[ "exploder" ] ]; if ( !IsDefined( ary ) ) ary = []; ary[ ary.size ] = ent; level.createFXexploders[ ent.v[ "exploder" ] ] = ary; } if ( !IsDefined( ent.v[ "delay" ] ) ) ent.v[ "delay" ] = 0; if ( IsDefined( exploder.target ) ) { get_ent = GetEntArray( ent.v[ "target" ], "targetname" )[ 0 ]; if ( IsDefined( get_ent ) ) { org = get_ent.origin; ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] ); } else { get_ent = get_target_ent( ent.v[ "target" ] ); if ( IsDefined( get_ent ) ) { org = get_ent.origin; ent.v[ "angles" ] = VectorToAngles( org - ent.v[ "origin" ] ); } } } // this basically determines if its a brush / model exploder or not if ( !IsDefined( exploder.code_classname ) ) { //I assume everything that doesn't have a code_classname is a struct that needs a script_modelname to make its way into the game ent.model = exploder; if ( IsDefined( ent.model.script_modelname ) ) { PreCacheModel( ent.model.script_modelname ); } } else if ( exploder.code_classname == "script_brushmodel" || IsDefined( exploder.model ) ) { ent.model = exploder; ent.model.disconnect_paths = exploder.script_disconnectpaths; } if ( IsDefined( exploder.targetname ) && IsDefined( acceptableTargetnames[ exploder.targetname ] ) ) ent.v[ "exploder_type" ] = exploder.targetname; else ent.v[ "exploder_type" ] = "normal"; if ( IsDefined( exploder.masked_exploder ) ) { ent.v[ "masked_exploder" ] = exploder.model; ent.v[ "masked_exploder_spawnflags" ] = exploder.spawnflags; ent.v[ "masked_exploder_script_disconnectpaths" ] = exploder.script_disconnectpaths; exploder Delete(); } ent common_scripts\_createfx::post_entity_creation_function(); } } setup_flag_exploders() { // createfx has to do 2 waittillframeends so we have to do 3 to make sure this comes after // createfx is all done setting up. Who will raise the gambit to 4? waittillframeend; waittillframeend; waittillframeend; exploder_flags = []; foreach ( ent in level.createFXent ) { if ( ent.v[ "type" ] != "exploder" ) continue; theFlag = ent.v[ "flag" ]; if ( !IsDefined( theFlag ) ) { continue; } if ( theFlag == "nil" ) { ent.v[ "flag" ] = undefined; } exploder_flags[ theFlag ] = true; } foreach ( msg, _ in exploder_flags ) { thread exploder_flag_wait( msg ); } } exploder_flag_wait( msg ) { if ( !flag_exist( msg ) ) flag_init( msg ); flag_wait( msg ); foreach ( ent in level.createFXent ) { if ( ent.v[ "type" ] != "exploder" ) continue; theFlag = ent.v[ "flag" ]; if ( !IsDefined( theFlag ) ) { continue; } if ( theFlag != msg ) continue; ent activate_individual_exploder(); } } exploder_model_is_damaged_model( ent ) { return( IsDefined( ent.targetname ) ) && ( ent.targetname == "exploder" ); } exploder_model_starts_hidden( ent ) { return( ent.model == "fx" ) && ( ( !IsDefined( ent.targetname ) ) || ( ent.targetname != "exploderchunk" ) ); } exploder_model_is_chunk( ent ) { return( IsDefined( ent.targetname ) ) && ( ent.targetname == "exploderchunk" ); } show_exploder_models_proc( num ) { num += ""; //prof_begin( "hide_exploder" ); if ( IsDefined( level.createFXexploders ) ) { // do optimized flavor if available exploders = level.createFXexploders[ num ]; if ( IsDefined( exploders ) ) { foreach ( ent in exploders ) { //pre exploded geo. don't worry about deleted exploder geo.. if ( ! exploder_model_starts_hidden( ent.model ) && ! exploder_model_is_damaged_model( ent.model ) && !exploder_model_is_chunk( ent.model ) ) { ent.model Show(); } //exploded geo and should be shown if ( IsDefined( ent.brush_shown ) ) ent.model Show(); } } } else { for ( i = 0; i < level.createFXent.size; i++ ) { ent = level.createFXent[ i ]; if ( !IsDefined( ent ) ) continue; if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != num ) continue; if ( IsDefined( ent.model ) ) { //pre exploded geo. don't worry about deleted exploder geo.. if ( ! exploder_model_starts_hidden( ent.model ) && ! exploder_model_is_damaged_model( ent.model ) && !exploder_model_is_chunk( ent.model ) ) { ent.model Show(); } //exploded geo and should be shown if ( IsDefined( ent.brush_shown ) ) ent.model Show(); } } } //prof_end( "hide_exploder" ); } stop_exploder_proc( num ) { num += ""; if ( IsDefined( level.createFXexploders ) ) { // do optimized flavor if available exploders = level.createFXexploders[ num ]; if ( IsDefined( exploders ) ) { foreach ( ent in exploders ) { if ( !IsDefined( ent.looper ) ) continue; ent.looper Delete(); } } } else { for ( i = 0; i < level.createFXent.size; i++ ) { ent = level.createFXent[ i ]; if ( !IsDefined( ent ) ) continue; if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != num ) continue; if ( !IsDefined( ent.looper ) ) continue; ent.looper Delete(); } } } get_exploder_array_proc( msg ) { msg += ""; array = []; if ( IsDefined( level.createFXexploders ) ) { // do optimized flavor if available exploders = level.createFXexploders[ msg ]; if ( IsDefined( exploders ) ) { array = exploders; } } else { foreach ( ent in level.createFXent ) { if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != msg ) continue; array[ array.size ] = ent; } } return array; } hide_exploder_models_proc( num ) { num += ""; //prof_begin( "hide_exploder" ); if ( IsDefined( level.createFXexploders ) ) { // do optimized flavor if available exploders = level.createFXexploders[ num ]; if ( IsDefined( exploders ) ) { foreach ( ent in exploders ) { if ( IsDefined( ent.model ) ) ent.model Hide(); } } } else { for ( i = 0; i < level.createFXent.size; i++ ) { ent = level.createFXent[ i ]; if ( !IsDefined( ent ) ) continue; if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != num ) continue; if ( IsDefined( ent.model ) ) ent.model Hide(); } } //prof_end( "hide_exploder" ); } delete_exploder_proc( num ) { num += ""; //prof_begin( "delete_exploder" ); if ( IsDefined( level.createFXexploders ) ) { // do optimized flavor if available exploders = level.createFXexploders[ num ]; if ( IsDefined( exploders ) ) { foreach ( ent in exploders ) { if ( IsDefined( ent.model ) ) ent.model Delete(); } } } else { for ( i = 0; i < level.createFXent.size; i++ ) { ent = level.createFXent[ i ]; if ( !IsDefined( ent ) ) continue; if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != num ) continue; if ( IsDefined( ent.model ) ) ent.model Delete(); } } //ends trigger threads. level notify( "killexplodertridgers" + num ); //prof_end( "delete_exploder" ); } exploder_damage() { if ( IsDefined( self.v[ "delay" ] ) ) delay = self.v[ "delay" ]; else delay = 0; if ( IsDefined( self.v[ "damage_radius" ] ) ) radius = self.v[ "damage_radius" ]; else radius = 128; damage = self.v[ "damage" ]; origin = self.v[ "origin" ]; wait( delay ); if ( IsDefined( level.custom_radius_damage_for_exploders ) ) [[ level.custom_radius_damage_for_exploders ]]( origin, radius, damage ); else // Range, max damage, min damage RadiusDamage( origin, radius, damage, damage ); } activate_individual_exploder_proc() { if ( IsDefined( self.v[ "firefx" ] ) ) self thread fire_effect(); if ( IsDefined( self.v[ "fxid" ] ) && self.v[ "fxid" ] != "No FX" ) self thread cannon_effect(); else if ( IsDefined( self.v[ "soundalias" ] ) && self.v[ "soundalias" ] != "nil" ) self thread sound_effect(); if ( IsDefined( self.v[ "loopsound" ] ) && self.v[ "loopsound" ] != "nil" ) self thread effect_loopsound(); if ( IsDefined( self.v[ "damage" ] ) ) self thread exploder_damage(); if ( IsDefined( self.v[ "earthquake" ] ) ) self thread exploder_earthquake(); if ( IsDefined( self.v[ "rumble" ] ) ) self thread exploder_rumble(); if ( self.v[ "exploder_type" ] == "exploder" ) self thread brush_show(); else if ( ( self.v[ "exploder_type" ] == "exploderchunk" ) || ( self.v[ "exploder_type" ] == "exploderchunk visible" ) ) self thread brush_throw(); else self thread brush_delete(); } brush_delete() { // if( ent.v[ "exploder_type" ] != "normal" && !isdefined( ent.v[ "fxid" ] ) && !isdefined( ent.v[ "soundalias" ] ) ) // if( !isdefined( ent.script_fxid ) ) num = self.v[ "exploder" ]; if ( IsDefined( self.v[ "delay" ] ) ) wait( self.v[ "delay" ] ); else wait( 0.05 ); // so it disappears after the replacement appears if ( !IsDefined( self.model ) ) return; Assert( IsDefined( self.model ) ); if ( IsDefined( self.model.classname ) ) // script_struct support - Nate -- self is always a struct so it will never have a classname, using self.model instead -Carlos if ( isSP() && ( self.model.spawnflags & 1 ) ) self.model call [[ level.connectPathsFunction ]](); if ( level.createFX_enabled ) { if ( IsDefined( self.exploded ) ) return; self.exploded = true; self.model Hide(); self.model NotSolid(); wait( 3 ); self.exploded = undefined; self.model Show(); self.model Solid(); return; } if ( !IsDefined( self.v[ "fxid" ] ) || self.v[ "fxid" ] == "No FX" ) self.v[ "exploder" ] = undefined; waittillframeend;// so it hides stuff after it shows the new stuff // if ( IsDefined( self.classname ) ) // script_struct support nate --- self is always a struct so it will never have a classname, using self.model instead -Carlos if ( IsDefined( self.model ) && IsDefined( self.model.classname ) ) { self.model Delete(); } } brush_throw() { if ( IsDefined( self.v[ "delay" ] ) ) wait( self.v[ "delay" ] ); ent = undefined; if ( IsDefined( self.v[ "target" ] ) ) ent = get_target_ent( self.v[ "target" ] ); if ( !IsDefined( ent ) ) { self.model Delete(); return; } self.model Show(); if ( IsDefined( self.v[ "delay_post" ] ) ) wait( self.v[ "delay_post" ] ); startorg = self.v[ "origin" ]; startang = self.v[ "angles" ]; org = ent.origin; temp_vec = ( org - self.v[ "origin" ] ); x = temp_vec[ 0 ]; y = temp_vec[ 1 ]; z = temp_vec[ 2 ]; physics = IsDefined( self.v[ "physics" ] ); if ( physics ) { target = undefined; if ( IsDefined( ent.target ) ) target = ent get_target_ent(); if ( !IsDefined( target ) ) { contact_point = startorg; // no spin just push it. throw_vec = ent.origin; } else { contact_point = ent.origin; throw_vec = ( ( target.origin - ent.origin ) * self.v[ "physics" ] ); } self.model PhysicsLaunchClient( contact_point, throw_vec ); return; } else { self.model RotateVelocity( ( x, y, z ), 12 ); self.model MoveGravity( ( x, y, z ), 12 ); } if ( level.createFX_enabled ) { if ( IsDefined( self.exploded ) ) return; self.exploded = true; wait( 3 ); self.exploded = undefined; self.v[ "origin" ] = startorg; self.v[ "angles" ] = startang; self.model Hide(); return; } self.v[ "exploder" ] = undefined; wait( 6 ); self.model Delete(); } brush_show() { if ( IsDefined( self.v[ "delay" ] ) ) wait( self.v[ "delay" ] ); Assert( IsDefined( self.model ) ); if ( !IsDefined( self.model.script_modelname ) ) { self.model Show(); self.model Solid(); } else // script_structs don't pass in their .model value SO use script_modelname on them saves an entity. { model = self.model spawn_tag_origin(); if ( IsDefined( self.model.script_linkname ) ) model.script_linkname = self.model.script_linkname; model SetModel( self.model.script_modelname ); model Show(); } self.brush_shown = true; // used for hiding an exploder. if ( isSP() && !IsDefined( self.model.script_modelname ) && ( self.model.spawnflags & 1 ) ) { if ( !IsDefined( self.model.disconnect_paths ) ) self.model call [[ level.connectPathsFunction ]](); else self.model call [[ level.disconnectPathsFunction ]](); } if ( level.createFX_enabled ) { if ( IsDefined( self.exploded ) ) return; self.exploded = true; wait( 3 ); self.exploded = undefined; if ( !IsDefined( self.model.script_modelname ) ) { self.model Hide(); self.model NotSolid(); } } } exploder_rumble() { if ( !isSP() ) return; self exploder_delay(); level.player PlayRumbleOnEntity( self.v[ "rumble" ] ); } exploder_delay() { if ( !IsDefined( self.v[ "delay" ] ) ) self.v[ "delay" ] = 0; min_delay = self.v[ "delay" ]; max_delay = self.v[ "delay" ] + 0.001; // cant randomfloatrange on the same # if ( IsDefined( self.v[ "delay_min" ] ) ) min_delay = self.v[ "delay_min" ]; if ( IsDefined( self.v[ "delay_max" ] ) ) max_delay = self.v[ "delay_max" ]; if ( min_delay > 0 ) wait( RandomFloatRange( min_delay, max_delay ) ); } effect_loopsound() { if ( IsDefined( self.loopsound_ent ) ) { self.loopsound_ent Delete(); } // save off this info in case we delete the effect origin = self.v[ "origin" ]; alias = self.v[ "loopsound" ]; self exploder_delay(); self.loopsound_ent = play_loopsound_in_space( alias, origin ); } sound_effect() { self effect_soundalias(); } effect_soundalias() { // save off this info in case we delete the effect origin = self.v[ "origin" ]; alias = self.v[ "soundalias" ]; self exploder_delay(); play_sound_in_space( alias, origin ); } exploder_earthquake() { self exploder_delay(); do_earthquake( self.v[ "earthquake" ], self.v[ "origin" ] ); } exploder_playSound() { if ( !IsDefined( self.v[ "soundalias" ] ) || self.v[ "soundalias" ] == "nil" ) return; play_sound_in_space( self.v[ "soundalias" ], self.v[ "origin" ] ); } fire_effect() { forward = self.v[ "forward" ]; up = self.v[ "up" ]; org = undefined; firefxSound = self.v[ "firefxsound" ]; origin = self.v[ "origin" ]; firefx = self.v[ "firefx" ]; ender = self.v[ "ender" ]; if ( !IsDefined( ender ) ) ender = "createfx_effectStopper"; fireFxDelay = 0.5; if ( IsDefined( self.v[ "firefxdelay" ] ) ) fireFxDelay = self.v[ "firefxdelay" ]; self exploder_delay(); if ( IsDefined( firefxSound ) ) loop_fx_sound( firefxSound, origin, 1, ender ); PlayFX( level._effect[ firefx ], self.v[ "origin" ], forward, up ); } cannon_effect() { if ( IsDefined( self.v[ "repeat" ] ) ) { thread exploder_playSound(); for ( i = 0; i < self.v[ "repeat" ]; i++ ) { PlayFX( level._effect[ self.v[ "fxid" ] ], self.v[ "origin" ], self.v[ "forward" ], self.v[ "up" ] ); self exploder_delay(); } return; } self exploder_delay(); if ( IsDefined( self.looper ) ) self.looper Delete(); self.looper = SpawnFx( getfx( self.v[ "fxid" ] ), self.v[ "origin" ], self.v[ "forward" ], self.v[ "up" ] ); TriggerFX( self.looper ); exploder_playSound(); } activate_exploder( num, players, startTime ) { num += ""; //prof_begin( "activate_exploder" ); //here's a hook so you can know when a certain number of an exploder is going off level notify( "exploding_" + num ); found_server_exploder = false; if ( IsDefined( level.createFXexploders ) && !level.createFX_enabled ) { // do optimized form if available exploders = level.createFXexploders[ num ]; if ( IsDefined( exploders ) ) { foreach ( ent in exploders ) { ent activate_individual_exploder(); found_server_exploder = true; } } } else { for ( i = 0;i < level.createFXent.size;i++ ) { ent = level.createFXent[ i ]; if ( !IsDefined( ent ) ) continue; if ( ent.v[ "type" ] != "exploder" ) continue; // make the exploder actually removed the array instead? if ( !IsDefined( ent.v[ "exploder" ] ) ) continue; if ( ent.v[ "exploder" ] + "" != num ) continue; ent activate_individual_exploder(); found_server_exploder = true; } } if ( !shouldRunServerSideEffects() && !found_server_exploder ) activate_clientside_exploder( num, players, startTime ); //prof_end( "activate_exploder" ); } activate_clientside_exploder( exploderName, players, startTime ) { if ( !is_valid_clientside_exploder_name( exploderName ) ) { PrintLn( "^1ERROR: Exploder Index '" + exploderName + "' is not a valid exploder index >= 0" ); return; } exploder_num = Int( exploderName ); ActivateClientExploder( exploder_num, players, startTime ); } is_valid_clientside_exploder_name( exploderName ) { if ( !IsDefined( exploderName ) ) return false; exploder_num = exploderName; if ( IsString( exploderName ) ) { exploder_num = Int( exploderName ); if ( exploder_num == 0 && exploderName != "0" ) return false; } return exploder_num >= 0; } shouldRunServerSideEffects() { if ( isSP() ) return true; if ( !IsDefined( level.createFX_enabled ) ) level.createFX_enabled = ( GetDvar( "createfx" ) != "" ); if ( level.createFX_enabled ) return true; else return GetDvar( "clientSideEffects" ) != "1"; } exploder_before_load( num, players, startTime ) { // gotta wait twice because the createfx_init function waits once then inits all exploders. This guarentees // that if an exploder is run on the first frame, it happens after the fx are init. waittillframeend; waittillframeend; activate_exploder( num, players, startTime ); } exploder_after_load( num, players, startTime ) { activate_exploder( num, players, startTime ); }