iw6-scripts-dev/maps/mp/_movers.gsc
2024-12-11 11:28:08 +01:00

1290 lines
32 KiB
Plaintext

#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: <param_name>=<min_value>,<max_value>;
-Move parameters can be set back to default with this syntax: <param_name>=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 <action_name>_on_<notify>. 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: <param_name>=<min_value>,<max_value>;
-Move parameters can be set back to default with this syntax: <param_name>=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 <action_name>_on_<notify>. 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;i<structs.size;i++)
{
target = structs[i];
if(!IsDefined(target.script_noteworthy))
target.script_noteworthy = "goal"; //Assume goal this is a mover goal pos if not set
switch(target.script_noteworthy)
{
case "ignore":
if(IsDefined(target.target))
{
add_structs = GetStructArray(target.target, "targetname");
foreach(add in add_structs)
{
structs[structs.size] = add;
}
}
break;
case "goal":
target script_mover_init_move_parameters();
target script_mover_parse_targets();
self.goals[self.goals.size] = target;
if(IsDefined(target.params["name"]))
{
level.script_mover_named_goals[target.params["name"]] = target;
}
break;
case "level_notify":
if(IsDefined(target.script_parameters))
{
self.level_notify[self.level_notify.size] = target;
}
break;
default:
break;
}
}
foreach(ent in ents)
{
if(ent script_mover_is_script_mover())
{
self.movers[self.movers.size] = ent;
continue;
}
if(!IsDefined(ent.script_noteworthy))
continue;
toks = StrTok(ent.script_noteworthy, "_");
if(toks.size!=3 || toks[1] != "on")
continue;
//toks[2] arrive or depart
switch(toks[0])
{
case "delete":
self thread script_mover_call_func_on_notify(ent, ::delete, toks[2]);
break;
case "hide":
self thread script_mover_call_func_on_notify(ent, ::Hide, toks[2]);
break;
case "show":
ent Hide();
self thread script_mover_call_func_on_notify(ent, ::Show, toks[2]);
break;
case "triggerhide":
case "triggerHide":
self thread script_mover_func_on_notify(ent, ::trigger_off, toks[2]);
break;
case "triggershow":
case "triggerShow":
ent trigger_off();
self thread script_mover_func_on_notify(ent, ::trigger_on, toks[2]);
break;
default:
break;
}
}
}
script_mover_func_on_notify(ent, func, note)
{
self endon("death");
ent endon("death");
while(1)
{
self waittill(note);
ent [[func]]();
}
}
script_mover_call_func_on_notify(ent, func, note)
{
self endon("death");
ent endon("death");
while(1)
{
self waittill(note);
ent call [[func]]();
}
}
script_mover_trigger_on()
{
self trigger_on();
}
script_mover_move_to_target(current)
{
self endon("death");
self endon("new_path");
if(!IsDefined(current))
current = self;
while(current.goals.size!=0)
{
goal = random(current.goals);
mover = self;
mover script_mover_apply_move_parameters( goal );
if(IsDefined(mover.params["delay_till"]))
level waittill(mover.params["delay_till"]);
if(IsDefined(mover.params["delay_till_trigger"]) && mover.params["delay_till_trigger"])
self waittill("trigger");
if(mover.params["delay_time"]>0)
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<nodes.size; i++)
{
check_node = nodes[i];
// if( check_node NodeIsDisconnected())
// continue;
org = check_node.origin;
if(!CanSpawn(org))
continue;
if(PositionWouldTelefrag(org))
continue;
if(player GetStance() == "prone")
player Setstance("crouch");
player SetOrigin( org );
return;
}
player SetOrigin(player.origin - avoid_telefrag_offset);
if( !IsDefined(bAllowSuicide) )
bAllowSuicide = true;
// no nodes found, fall back on suicide
if( bAllowSuicide )
{
player mover_suicide();
}
}
unresolved_collision_void(player)
{
}
mover_suicide()
{
if( IsDefined(level.isHorde) && !IsAgent( self ) )
{
return;
}
self _suicide();
}
//*******************************************************************
// *
// *
//*******************************************************************
player_pushed_kill( min_mph ) //self == mover
{
self endon("death");
self endon("stop_player_pushed_kill");
while( 1 )
{
self waittill( "player_pushed", player, platformMPH );
if ( isPlayer( player ) || isAgent( player ) )
{
mph = Length(platformMPH);
if ( mph >= 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.
}
//*******************************************************************
// *
// *
//*******************************************************************