#include maps\mp\_utility; #include common_scripts\utility; /*script_brushmodel_mover (1 0.25 0.5) ? script_brushmodel_movers will move and/or rotate to script_struct targets using the move parameters defined in script_parameters. Script_structs can target additional script structs to create paths and loops. Move parameters can be overridden with script_parameters on script structs. -------------------------------------------------------------------------------- Call level thread maps\mp\_movers::main() before _load::main(); in your level script. Include rawfile,maps/mp/_movers.gsc in the level csv --------------------------------------------------------------------------- Valid parameters that can be included in script_parameters: move_time: Time to move to target accel_time: Time spent accelerating. accel_time+decel_time must be less then move_time. decel_time: Time spent decelerating. accel_time+decel_time must be less then move_time. move_speed: Move speed of the mover. Overrides move_time. accel_frac: The faction of the total move time spent accelerating [0.0 to 1.0] decel_frac: The faction of the total move time spent decelerating [0.0 to 1.0] delay_time: Time the mover will wait before moving to the next position wait_time: Time the mover will wait after reaching to it's position delay_till: Wait till this level notify before moving to the next position wait_till: Wait till this level notify after reaching position usable: Sets the mover to usable, used in combination with delay_till_trigger delay_till_trigger: Will not move till target till mover receives "trigger" notify solid: Set the brush to solid or or not script_params: Allows parameters to be defined/added through script. Use maps\mp\_movers::script_mover_add_parameters("example_set", "move_speed=5;accel_frac=.1") in the level script to create a named set of parameters. The named parameters can be included by setting script_params=name_of_set -------------------------------------------------------------------------------- Advanced Stuff: -To set the "angles" of a mover have the mover target a script_struct with angles set and script_noteworthy=origin. This helps when rotating to angles on targeted structs -Script_models can be linked to the mover by having the mover target them and setting script_notewothy=link -Movers can target other movers. A mover will send "trigger" to its targeted movers when it started moving. Targeted movers should have delay_till_trigger. -Script_structs can also target movers and will trigger all targeted movers when a mover arrives at the script_struct -Move parameters can be randomized with this syntax: =,; -Move parameters can be set back to default with this syntax: =default -Script_struct 'A' can target script_struct 'B' with script_noteworthy=level_notify. When a mover is moving to 'A' it will send a level notify when its path crosses 'B'. The level notify is set by script_parameters on 'B' -If a mover targets a trigger with script_noteworthy=use_trigger_link or script_noteworthy=use_trigger that trigger will trigger the mover when triggered. use_trigger_link will link the trigger to the mover and use_trigger will not. -Entities targeted by the mover or mover goals can modified. The targeted entity needs script_noteworthy set to _on_. Example script_noteworthy=show_on_arrive Notifies: "arrive": Fires action when mover arrives at a goal "depart": Fires action when mover leaves a position Actions: "delete": Deletes the entity "hide": Hides the entity "show": Shows the entity (hides entity on map start) "triggerhide": Hides the entity by placing it under the world "triggershow": Shows the entity by moving it into the world (ent is moved under the world at startup) ----------------------------------------------------------------- default:"script_parameters" "move_time=5;accel_time=0;decel_time=0" */ /*script_model_mover (1 0.25 0.5) (-16 -16 -16) (16 16 16) script_model_movers will move and/or rotate to script_struct targets using the move parameters defined in script_parameters. Script_structs can target additional script structs to create paths and loops. Move parameters can be overridden with script_parameters on script structs. -------------------------------------------------------------------------------- Call level thread maps\mp\_movers::main() before _load::main(); in your level script. Include rawfile,maps/mp/_movers.gsc in the level csv --------------------------------------------------------------------------- Valid parameters that can be included in script_parameters: move_time: Time to move to target accel_time: Time spent accelerating. accel_time+decel_time must be less then move_time. decel_time: Time spent decelerating. accel_time+decel_time must be less then move_time. move_speed: Move speed of the mover. Overrides move_time. accel_frac: The faction of the total move time spent accelerating [0.0 to 1.0] decel_frac: The faction of the total move time spent decelerating [0.0 to 1.0] delay_time: Time the mover will wait before moving to the next position wait_time: Time the mover will wait after reaching to it's position delay_till: Wait till this level notify before moving to the next position wait_till: Wait till this level notify after reaching position usable: Sets the mover to usable, used in combination with delay_till_trigger delay_till_trigger: Will not move till target till mover receives "trigger" notify script_params: Allows parameters to be defined/added through script. Use maps\mp\_movers::script_mover_add_parameters("example_set", "move_speed=5;accel_frac=.1") in the level script to create a named set of parameters. The named parameters can be included by setting script_params=name_of_set -------------------------------------------------------------------------------- Advanced Stuff: -To set the "angles" of a mover have the mover target a script_struct with angles set and script_noteworthy=origin. This helps when rotating to angles on targeted structs -Script_models can be linked to the mover by having the mover target them and setting script_notewothy=link -Movers can target other movers. A mover will send "trigger" to its targeted movers when it started moving. Targeted movers should have delay_till_trigger. -Script_structs can also target movers and will trigger all targeted movers when a mover arrives at the script_struct -Move parameters can be randomized with this syntax: =,; -Move parameters can be set back to default with this syntax: =default -Script_struct 'A' can target script_struct 'B' with script_noteworthy=level_notify. When a mover is moving to 'A' it will send a level notify when its path crosses 'B'. The level notify is set by script_parameters on 'B' -If a mover targets a trigger with script_noteworthy=use_trigger_link or script_noteworthy=use_trigger that trigger will trigger the mover when triggered. use_trigger_link will link the trigger to the mover and use_trigger will not. -Entities targeted by the mover or mover goals can modified. The targeted entity needs script_noteworthy set to _on_. Example script_noteworthy=show_on_arrive Notifies: "arrive": Fires action when mover arrives at a goal "depart": Fires action when mover leaves a position Actions: "delete": Deletes the entity "hide": Hides the entity "show": Shows the entity (hides entity on map start) "triggerhide": Hides the entity by placing it under the world "triggershow": Shows the entity by moving it into the world (ent is moved under the world at startup) ----------------------------------------------------------------- default:"script_parameters" "move_time=5;accel_time=0;decel_time=0" */ /////// // Movers /////// main() { if( getDvar( "r_reflectionProbeGenerate" ) == "1" ) return; level.script_mover_defaults = []; level.script_mover_defaults["move_time"] = 5; level.script_mover_defaults["accel_time"] = 0; level.script_mover_defaults["decel_time"] = 0; level.script_mover_defaults["wait_time"] = 0; level.script_mover_defaults["delay_time"] = 0; level.script_mover_defaults["usable"] = 0; level.script_mover_defaults["hintstring"] = "activate"; script_mover_add_hintString("activate", &"MP_ACTIVATE_MOVER"); script_mover_add_parameters("none", ""); level.script_mover_named_goals = []; waitframe(); movers = []; classnames = script_mover_classnames(); foreach(class in classnames) { movers = array_combine(movers, GetEntArray(class, "classname")); } array_thread(movers, ::script_mover_int); } script_mover_classnames() { return ["script_model_mover", "script_brushmodel_mover"]; } script_mover_is_script_mover() { if(IsDefined(self.script_mover)) return self.script_mover; classnames = script_mover_classnames(); foreach(class in classnames) { if(self.classname == class) { self.script_mover = true; return true; } } return false; } script_mover_add_hintString(name, hintString) { if(!IsDefined(level.script_mover_hintstrings)) level.script_mover_hintstrings = []; level.script_mover_hintstrings[name] = hintString; } script_mover_add_parameters(name, parameters) { if(!IsDefined(level.script_mover_parameters)) level.script_mover_parameters = []; level.script_mover_parameters[name] = parameters; } script_mover_int() { if(!IsDefined(self.target)) return; self.script_mover = true; self.moving = false; self.origin_ent = self; self.use_triggers = []; self.linked_ents = []; structs = GetStructArray(self.target, "targetname"); foreach(target in structs) { if(!IsDefined(target.script_noteworthy)) continue; switch(target.script_noteworthy) { case "origin": if(!IsDefined(target.angles)) target.angles = (0,0,0); self.origin_ent = spawn("script_model", target.origin); self.origin_ent.angles = target.angles; self.origin_ent SetModel("tag_origin"); self.origin_ent LinkTo(self); break; default: break; } } ents = GetEntArray(self.target, "targetname"); foreach(target in ents) { if(!IsDefined(target.script_noteworthy)) continue; switch(target.script_noteworthy) { case "use_trigger_link": target EnableLinkTo(); target LinkTo(self); //Fallthrough case "use_trigger": target script_mover_parse_targets(); self thread script_mover_use_trigger(target); self.use_triggers[self.use_triggers.size] = target; break; case "link": target LinkTo(self); self.linked_ents[self.linked_ents.size] = target; break; default: break; } } self thread script_mover_parse_targets(); self thread script_mover_init_move_parameters(); self thread script_mover_save_default_move_parameters(); self thread script_mover_apply_move_parameters(self); self thread script_mover_move_to_target(); foreach(trigger in self.use_triggers) { self script_mover_set_usable(trigger, true); } } script_mover_use_trigger(trigger) { self endon("death"); while(1) { trigger waittill("trigger"); if(trigger.goals.size>0) { self notify("new_path"); self thread script_mover_move_to_target(trigger); } else { self notify("trigger"); } } } script_mover_move_to_named_goal(goal_name) { if(IsDefined(level.script_mover_named_goals[goal_name])) { self notify("new_path"); self.goals = [level.script_mover_named_goals[goal_name]]; self thread script_mover_move_to_target(); } } anglesClamp180(angles) { return (AngleClamp180(angles[0]),AngleClamp180(angles[1]),AngleClamp180(angles[2])); } script_mover_parse_targets() { if(IsDefined(self.parsed) && self.parsed) return; self.parsed = true; self.goals = []; self.movers = []; self.level_notify = []; structs = []; ents = []; if(IsDefined(self.target)) { structs = GetStructArray(self.target, "targetname"); ents = GetEntArray(self.target, "targetname"); } for(i=0;i0) wait mover.params["delay_time"]; move_time = mover.params["move_time"]; accel_time = mover.params["accel_time"]; decel_time = mover.params["decel_time"]; is_moveTo = false; is_rotateTo = false; trans = TransformMove(goal.origin, goal.angles, self.origin_ent.origin, self.origin_ent.angles, self.origin, self.angles); if(mover.origin != goal.origin) { if(IsDefined(mover.params["move_speed"])) { dist = distance(mover.origin, goal.origin); move_time = dist/mover.params["move_speed"]; } if(IsDefined(mover.params["accel_frac"])) { accel_time = mover.params["accel_frac"] * move_time; } if(IsDefined(mover.params["decel_frac"])) { decel_time = mover.params["decel_frac"] * move_time; } mover MoveTo(trans["origin"], move_time, accel_time, decel_time ); foreach(note in goal.level_notify) { self thread script_mover_run_notify(note.origin, note.script_parameters, self.origin, goal.origin); } is_moveTo = true; } if(anglesClamp180(trans["angles"]) != anglesClamp180(mover.angles)) { mover RotateTo(trans["angles"], move_time, accel_time, decel_time); is_rotateTo = true; } //Trigger movers targeted my the mover foreach ( targeted_mover in mover.movers ) { targeted_mover notify("trigger"); } current notify("depart"); mover script_mover_allow_usable(false); self.moving = true; if(IsDefined(mover.params["move_time_offset"]) && (mover.params["move_time_offset"]+move_time)>0) { wait mover.params["move_time_offset"]+move_time; } else if(is_moveTo) { self waittill("movedone"); } else if(is_rotateTo) { self waittill("rotatedone"); } else { wait move_time; } self.moving = false; self notify("move_end"); goal notify("arrive"); if(IsDefined(mover.params["solid"])) { if(mover.params["solid"]) mover solid(); else mover notsolid(); } //Trigger movers targeted my the goal foreach ( targeted_mover in goal.movers ) { targeted_mover notify("trigger"); } if(IsDefined(mover.params["wait_till"])) level waittill(mover.params["wait_till"]); if(mover.params["wait_time"]>0) wait mover.params["wait_time"]; mover script_mover_allow_usable(true); current = goal; } } script_mover_run_notify(notify_origin, level_notify, start, end) { self endon("move_end"); mover = self; move_dir = VectorNormalize(end-start); while(1) { notify_dir = VectorNormalize(notify_origin - mover.origin); if(VectorDot(move_dir,notify_dir)<=0) break; wait .05; } level notify(level_notify); } script_mover_init_move_parameters() { self.params = []; if(!IsDefined(self.angles)) self.angles = (0,0,0); self.angles = anglesClamp180(self.angles); script_mover_parse_move_parameters(self.script_parameters); } script_mover_parse_move_parameters(parameters) { if(!IsDefined(parameters)) parameters = ""; params = StrTok(parameters, ";"); foreach(param in params) { toks = strtok(param,"="); if(toks.size!=2) continue; if(toks[1]=="undefined" || toks[1]=="default") { //If default exist it will be set in script_mover_set_defaults self.params[toks[0]] = undefined; continue; } switch(toks[0]) { case "move_speed": case "accel_frac": case "decel_frac": case "move_time": case "accel_time": case "decel_time": case "wait_time": case "delay_time": case "move_time_offset": self.params[toks[0]] = script_mover_parse_range(toks[1]); break; case "wait_till": case "delay_till": case "name": case "hintstring": self.params[toks[0]] = toks[1]; break; case "usable": case "delay_till_trigger": case "solid": self.params[toks[0]] = int(toks[1]); break; case "script_params": param_name = toks[1]; additional_params = level.script_mover_parameters[param_name]; if(IsDefined(additional_params)) { script_mover_parse_move_parameters(additional_params); } break; default: break; } } } script_mover_parse_range(str) { value = 0; toks = strtok(str,","); if(toks.size==1) { value = float(toks[0]); } else if(toks.size==2) { minValue = float(toks[0]); maxValue = float(toks[1]); if(minValue>=maxValue) { value = minValue; } else { value = RandomFloatRange(minValue,maxValue); } } return value; } //to = self script_mover_apply_move_parameters(from) { foreach(key, value in from.params) { script_mover_set_param(key, value); } script_mover_set_defaults(); } script_mover_set_param(param_name, value) { if(!IsDefined(param_name)) return; if(param_name=="usable" && IsDefined(value)) { self script_mover_set_usable(self, value); } self.params[param_name] = value; } script_mover_allow_usable(usable) { if(self.params["usable"]) { self script_mover_set_usable(self, usable); } foreach(trigger in self.use_triggers) { self script_mover_set_usable(trigger, usable); } } script_mover_set_usable(use_ent,usable) { if(usable) { use_ent MakeUsable(); use_ent SetCursorHint("HINT_ACTIVATE"); use_ent SetHintString(level.script_mover_hintstrings[self.params["hintstring"]]); } else { use_ent MakeUnusable(); } } script_mover_save_default_move_parameters() { self.params_default = []; foreach(key, value in self.params) { self.params_default[key] = value; } } //Values in script_mover_defaults are assumed to always be defined //this checks that they are. script_mover_set_defaults() { foreach(key, value in level.script_mover_defaults) { if(!IsDefined(self.params[key])) script_mover_set_param(key, value); } if(IsDefined(self.params_default)) { foreach(key, value in self.params_default) { if(!IsDefined(self.params[key])) script_mover_set_param(key, value); } } } init() { level thread script_mover_connect_watch(); level thread script_mover_agent_spawn_watch(); } script_mover_connect_watch() { while ( 1 ) { level waittill( "connected", player ); player thread player_unresolved_collision_watch(); } } script_mover_agent_spawn_watch() { while ( 1 ) { level waittill( "spawned_agent", agent ); agent thread player_unresolved_collision_watch(); } } player_unresolved_collision_watch() { self endon("disconnect"); if ( IsAgent( self ) ) { self endon("death"); } self.unresolved_collision_count = 0; while(1) { self waittill( "unresolved_collision", mover ); self.unresolved_collision_count++; self thread clear_unresolved_collision_count_next_frame(); unresolved_collision_notify_min = 3; if(IsDefined(mover) && IsDefined(mover.unresolved_collision_notify_min)) unresolved_collision_notify_min = mover.unresolved_collision_notify_min; if(self.unresolved_collision_count>=unresolved_collision_notify_min) { if(IsDefined(mover)) { if(IsDefined( mover.unresolved_collision_func )) { mover [[mover.unresolved_collision_func]](self); } else if(IsDefined(mover.unresolved_collision_kill) && mover.unresolved_collision_kill) { mover unresolved_collision_owner_damage(self); } else { mover unresolved_collision_nearest_node(self); } } else { unresolved_collision_nearest_node(self); } self.unresolved_collision_count = 0; } } } clear_unresolved_collision_count_next_frame() { self endon("unresolved_collision"); waitframe(); if(IsDefined(self)) self.unresolved_collision_count = 0; } unresolved_collision_owner_damage(player) { inflictor = self; if(!IsDefined(inflictor.owner)) { player maps\mp\_movers::mover_suicide(); return; } canInflictorOwnerDamagePlayer = false; if( level.teambased ) { if( IsDefined( inflictor.owner.team ) && inflictor.owner.team != player.team ) canInflictorOwnerDamagePlayer = true; } else { if(player != inflictor.owner) canInflictorOwnerDamagePlayer = true; } if(!canInflictorOwnerDamagePlayer) { player maps\mp\_movers::mover_suicide(); return; } damage_ammount = 1000; //Kill if(IsDefined(inflictor.unresolved_collision_damage)) damage_ammount = inflictor.unresolved_collision_damage; player DoDamage( damage_ammount, inflictor.origin, inflictor.owner, inflictor, "MOD_CRUSH" ); } unresolved_collision_nearest_node(player, bAllowSuicide) { if ( IsDefined( level.override_unresolved_collision ) ) { self [[level.override_unresolved_collision]]( player, bAllowSuicide ); return; } nodes = self.unresolved_collision_nodes; if(IsDefined(nodes)) { nodes = SortByDistance(nodes, player.origin); } else { nodes = GetNodesInRadius( player.origin, 300, 0, 200 ); //Sorted version sorts by 2d dist, we need 3d nodes = SortByDistance(nodes, player.origin); } avoid_telefrag_offset = (0,0,-100); player CancelMantle(); player DontInterpolate(); player SetOrigin(player.origin + avoid_telefrag_offset); for(i=0; i= min_mph ) { self unresolved_collision_owner_damage(player); } } } } stop_player_pushed_kill() { self notify("stop_player_pushed_kill"); } //******************************************************************* // * // * //******************************************************************* script_mover_get_top_parent() { topParent = self GetLinkedParent(); parent = topParent; while ( IsDefined( parent ) ) { topParent = parent; parent = parent GetLinkedParent(); } return topParent; } script_mover_start_use( useEnt ) { // Backup the starting position of the use entity's parent. useParent = useEnt script_mover_get_top_parent(); if ( IsDefined( useParent ) ) { useParent.startUseOrigin = useParent.origin; } // Backup the player's mover and backup the start position. self.startUseMover = self GetMovingPlatformParent(); if ( IsDefined( self.startUseMover ) ) { // Check for the highest parent, else just use the mover as is. topParent = self.startUseMover script_mover_get_top_parent(); if ( IsDefined( topParent ) ) self.startUseMover = topParent; self.startUseMover.startUseOrigin = self.startUseMover.origin; } } script_mover_has_parent_moved( parent ) { if ( !IsDefined( parent ) ) return false; return LengthSquared( parent.origin - parent.startUseOrigin ) > 0.001; } script_mover_use_can_link( ent ) { if ( !IsPlayer( self ) ) return true; if ( !IsDefined( ent ) ) return false; topParent = ent script_mover_get_top_parent(); playerParent = self.startUseMover; // If neither parent is defined, we are definitely not moving. if ( !IsDefined( topParent ) && !IsDefined( playerParent ) ) return true; // If both parents exist and they are the same, we can stay linked. if ( IsDefined( topParent ) && IsDefined( playerParent ) && (topParent == playerParent) ) return true; // If a parent has moved and the parents are not the same, we cannot stay linked. if ( script_mover_has_parent_moved( topParent ) ) return false; if ( script_mover_has_parent_moved( playerParent ) ) return false; return true; } script_mover_link_to_use_object( player ) // self == object { // !!! FOR IW7: Request a function to disable player movement, but allow button input // which is what this linking is ultimately trying to achieve if ( IsPlayer(player) ) { player maps\mp\_movers::script_mover_start_use( self ); playerMover = player GetMovingPlatformParent(); linkToObject = undefined; // the player is a moving platform, link him to that because it doesn't matter what the object is on if ( IsDefined( playerMover ) ) { linkToObject = playerMover; } // the player is on the ground, and so is the object else if ( !IsDefined( self script_mover_get_top_parent() ) ) { linkToObject = self; } // the player is on the ground, but the object is on a mover else { // spawn a dummy entity and link the player to that linkToObject = Spawn( "script_model", player.origin ); linkToObject SetModel( "tag_origin" ); player.scriptMoverLinkDummy = linkToObject; // thread off a cleaup function player thread sciprt_mover_use_object_wait_for_disconnect( linkToObject ); } player PlayerLinkTo( linkToObject ); } else { // 2014-06-23 wallace: bots currently don't support GetMovingPlatformParent, which should be fixed in IW7 // as a result, they'll be subject to bug 184158 (getting stuck when trying to use a box on a platform) player LinkTo( self ); } player PlayerLinkedOffsetEnable(); } script_mover_unlink_from_use_object( player ) // self == object { player Unlink(); if ( IsDefined( player.scriptMoverLinkDummy ) ) { player notify( "removeMoverLinkDummy" ); player.scriptMoverLinkDummy Delete(); player.scriptMoverLinkDummy = undefined; } } sciprt_mover_use_object_wait_for_disconnect( linkDummy ) { self endon( "removeMoverLinkDummy" ); self waittill_any( "death", "disconnect" ); self.scriptMoverLinkDummy Delete(); self.scriptMoverLinkDummy = undefined; } //******************************************************************* // * // * //******************************************************************* notify_moving_platform_invalid() { children = self GetLinkedChildren( false ); if ( !IsDefined( children ) ) { return; } foreach ( child in children ) { if( IsDefined(child.no_moving_platfrom_unlink) && child.no_moving_platfrom_unlink ) continue; child unlink(); child notify( "invalid_parent", self ); } } process_moving_platform_death( data, platform ) { if(IsDefined(platform) && IsDefined(platform.no_moving_platfrom_death) && platform.no_moving_platfrom_death) return; if ( IsDefined( data.playDeathFx ) ) { PlayFX( getfx( "airdrop_crate_destroy" ), self.origin ); } if ( IsDefined( data.deathOverrideCallback ) ) { // we should be passing platform as an argument data.lastTouchedPlatform = platform; self thread [[data.deathOverrideCallback]]( data ); } else { self delete(); } } handle_moving_platform_touch( data ) { self notify( "handle_moving_platform_touch" ); self endon( "handle_moving_platform_touch" ); level endon( "game_ended" ); self endon( "death" ); self endon( "stop_handling_moving_platforms" ); // Special endon string. if ( IsDefined( data.endonString ) ) { self endon( data.endonString ); } while ( 1 ) { self waittill( "touching_platform", platform ); if ( IsDefined( data.validateAccurateTouching ) && data.validateAccurateTouching ) { if ( !self IsTouching( platform ) ) { wait( 0.05 ); continue; } } self thread process_moving_platform_death( data, platform ); break; } } handle_moving_platform_invalid( data ) { self notify( "handle_moving_platform_invalid" ); self endon( "handle_moving_platform_invalid" ); level endon( "game_ended" ); self endon( "death" ); self endon( "stop_handling_moving_platforms" ); // Special endon string. if ( IsDefined( data.endonString ) ) { self endon( data.endonString ); } self waittill( "invalid_parent", platform ); if ( IsDefined( data.invalidParentOverrideCallback ) ) { self thread [[data.invalidParentOverrideCallback]]( data ); } else { self thread process_moving_platform_death( data, platform ); } } // data: Spawned struct passed into handler. // data.linkParent: Parent to link this entity to. // data.endonString: String that will kill this handler when notified to entity. // data.playDeathFx: Flag to play a generic death effect. // data.deathOverrideCallback: Callback that will call instead of the automatic delete (ex. ims_override_death( platform ) ). // data.invalidParentOverrideCallback: Callback that will call instead of the automatic invalid parent handler. // data.validateAccurateTouching: If true, performs an extra IsTouching test on the touching platform before processing. handle_moving_platforms( data ) { self notify( "handle_moving_platforms" ); self endon( "handle_moving_platforms" ); level endon( "game_ended" ); self endon( "death" ); self endon( "stop_handling_moving_platforms" ); if ( !IsDefined( data ) ) { data = SpawnStruct(); } // Special endon string. if ( IsDefined( data.endonString ) ) { self endon( data.endonString ); } if ( IsDefined( data.linkParent ) ) { parent = self GetLinkedParent(); if ( !IsDefined( parent ) || parent != data.linkParent) self linkto( data.linkParent ); } thread handle_moving_platform_touch( data ); thread handle_moving_platform_invalid( data ); } // Force kill this process without using data.endonString. stop_handling_moving_platforms() { self notify( "stop_handling_moving_platforms" ); } moving_platform_empty_func( data ) { // Don't add anything. } //******************************************************************* // * // * //*******************************************************************