SSthrowing (#19421)

Ported SSThrowing from TG, to handle throwings.
Updated movement system to the latest iteration, made it a datum as per
latest iteration.
Updated pass/hit handling of atoms, introduced pass_flag_self to
determine what atoms allow to pass.
Moved procs and defines around to make them more organized.
This commit is contained in:
Fluffy
2024-06-18 19:32:06 +00:00
committed by GitHub
parent 56a7300903
commit 92c3ec6caf
96 changed files with 1125 additions and 496 deletions
+1 -1
View File
@@ -79,7 +79,7 @@ SUBSYSTEM_DEF(mob_ai)
/mob/proc/on_think_disabled()
walk_to(src, 0)
SSmove_manager.stop_looping(src)
QDEL_NULL(move_packet)
/mob/proc/on_think_enabled()
return
@@ -1,174 +0,0 @@
/**
* Acts as a namespace for movement packet/type related procs
*
* Exists to provide an in code implementation of movement looping
* Replaces things like walk() or walk_to(), among others
*
* Because we're doing things in engine, we have a lot more control over how different operations are performed
* We also get more say in when things happen, so we can subject movements to the whims of the master controller
* Rather then using a fuck ton of cpu just moving mobs or meteors
*
* The goal is to keep the loops themselves reasonably barebone, and implement more advanced behavior and control via the signals
*
* This may be bypassed in cases where snowflakes are nessesary, or where performance is important. S not a hard and fast thing
*
* Every atom can have a movement packet, which contains information and behavior about currently active loops, and queuing info
* Loops control how movement actually happens. So there's a "move in this direction" loop, a "move randomly" loop
*
* You can find the logic for this control in this file
*
* Specifics of how different loops operate can be found in the movement_types.dm file, alongside the [add to loop][/datum/controller/subsystem/move_manager/proc/add_to_loop] helper procs that use them
*
**/
SUBSYSTEM_DEF(move_manager)
name = "Movement Handler"
flags = SS_NO_INIT | SS_NO_FIRE
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
///Adds a movable thing to a movement subsystem. Returns TRUE if it all worked, FALSE if it failed somehow
/datum/controller/subsystem/move_manager/proc/add_to_loop(atom/movable/thing_to_add, datum/controller/subsystem/movement/subsystem = SSmovement, datum/move_loop/loop_type, priority = MOVEMENT_DEFAULT_PRIORITY, flags, datum/extra_info)
var/datum/movement_packet/our_data = thing_to_add.move_packet
if(!our_data)
our_data = new(thing_to_add)
var/list/arguments = args.Copy(2) //Drop the atom, since the movement packet already knows about it
return our_data.add_loop(arglist(arguments))
///Returns the subsystem's loop if we're processing on it, null otherwise
/datum/controller/subsystem/move_manager/proc/processing_on(atom/movable/packet_owner, datum/controller/subsystem/movement/subsystem)
var/datum/movement_packet/packet = packet_owner.move_packet
if(!packet)
return
var/datum/move_loop/linked_loop = packet.existing_loops[subsystem]
if(!linked_loop)
return
if(linked_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
return linked_loop
if(linked_loop != packet.running_loop)
return
return linked_loop
///A packet of information that describes the current state of a moving object
/datum/movement_packet
///Our parent atom
var/atom/movable/parent
///The move loop that's currently running, excluding those that ignore priority.
var/datum/move_loop/running_loop
/**
* Flags passed from the move loop before it calls move() and unset right after.
* Allows for properties of a move loop to be easily checked by mechanics outside of it.
* Having this a bitfield rather than a type var means we don't get screwed over
* if the move loop gets deleted mid-move, FYI.
*/
var/processing_move_loop_flags = NONE
///Assoc list of subsystems -> loop datum. Only one datum is allowed per subsystem
var/list/existing_loops = list()
/datum/movement_packet/New(atom/movable/parent)
src.parent = parent
parent.move_packet = src
/datum/movement_packet/Destroy(force)
parent.move_packet = null
parent = null
for(var/datum/controller/subsystem/processor as anything in existing_loops)
var/datum/move_loop/loop = existing_loops[processor]
if(QDELETED(loop))
continue
qdel(loop)
existing_loops.Cut()
existing_loops = null //Catch anyone modifying this post del
return ..()
///Adds a loop to our parent. Returns the created loop if a success, null otherwise
/datum/movement_packet/proc/add_loop(datum/controller/subsystem/movement/subsystem, datum/move_loop/loop_type, priority, flags, datum/extra_info)
var/datum/move_loop/existing_loop = existing_loops[subsystem]
if(existing_loop && existing_loop.priority > priority)
if(!(existing_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) && !(flags & MOVEMENT_LOOP_IGNORE_PRIORITY))
return //Give up
if(existing_loop?.compare_loops(arglist(args.Copy(2))))
return //it already exists stop trying to make the same moveloop
var/datum/move_loop/new_loop = new loop_type(src, subsystem, parent, priority, flags, extra_info) //Pass the mob to move and ourselves in via new
var/list/arguments = args.Copy(6) //Just send the args we've not already dealt with
var/worked_out = new_loop.setup(arglist(arguments)) //Here goes the rest
if(!worked_out)
qdel(new_loop)
return
existing_loops[subsystem] = new_loop
if(existing_loop)
qdel(existing_loop) //We need to do this here because otherwise the packet would think it was empty, and self destruct
contest_running_loop(new_loop)
return new_loop
///Attempts to contest the current running move loop. Returns TRUE if the loop is active, FALSE otherwise
/datum/movement_packet/proc/contest_running_loop(datum/move_loop/contestant)
var/datum/controller/subsystem/movement/contesting_subsystem = contestant.controller
if(contestant.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
contesting_subsystem.add_loop(contestant)
return TRUE
if(!running_loop)
running_loop = contestant
contesting_subsystem.add_loop(running_loop)
return TRUE
if(running_loop.priority > contestant.priority)
return FALSE
var/datum/controller/subsystem/movement/current_subsystem = running_loop.controller
var/current_running_loop = running_loop
running_loop = contestant
current_subsystem.remove_loop(current_running_loop)
if(running_loop != contestant) // A signal registrant could have messed with things
return FALSE
contesting_subsystem.add_loop(contestant)
return TRUE
///Tries to figure out the current favorite loop to run. More complex then just deciding between two different loops, assumes no running loop currently exists
/datum/movement_packet/proc/decide_on_running_loop()
if(running_loop)
return
if(!length(existing_loops)) //Die
qdel(src)
return
var/datum/move_loop/favorite
for(var/datum/controller/subsystem/movement/owner as anything in existing_loops)
var/datum/move_loop/checking = existing_loops[owner]
if(checking.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
continue
if(favorite && favorite.priority > checking.priority)
continue
favorite = checking
if(!favorite) //This isn't an error state, since some loops ignore the concept of a running loop
return
var/datum/controller/subsystem/movement/favorite_subsystem = favorite.controller
running_loop = favorite
favorite_subsystem.add_loop(running_loop)
/datum/movement_packet/proc/remove_loop(datum/controller/subsystem/movement/remove_from, datum/move_loop/loop_to_remove)
if(loop_to_remove == running_loop)
running_loop = null
remove_from.remove_loop(loop_to_remove)
if(loop_to_remove.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
remove_from.remove_loop(loop_to_remove)
if(QDELETED(src))
return
if(existing_loops[remove_from] == loop_to_remove)
existing_loops -= remove_from
decide_on_running_loop()
return
/datum/movement_packet/proc/remove_subsystem(datum/controller/subsystem/movement/remove)
var/datum/move_loop/our_loop = existing_loops[remove]
if(!our_loop)
return FALSE
qdel(our_loop)
return TRUE
@@ -135,9 +135,6 @@
if(flags & MOVEMENT_LOOP_IGNORE_GLIDE)
return
//No gliding adjustment, for now
//moving.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, visual_delay))
///Handles the actual move, overriden by children
///Returns FALSE if nothing happen, TRUE otherwise
/datum/move_loop/proc/move()
@@ -163,7 +160,7 @@
status &= ~MOVELOOP_STATUS_PAUSED
///Removes the atom from some movement subsystem. Defaults to SSmovement
/datum/controller/subsystem/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement)
/datum/move_manager/proc/stop_looping(atom/movable/moving, datum/controller/subsystem/movement/subsystem = SSmovement)
var/datum/movement_packet/our_info = moving.move_packet
if(!our_info)
return FALSE
@@ -184,7 +181,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/move, priority, flags, extra_info, delay, timeout, direction)
///Replacement for walk()
@@ -204,8 +201,7 @@
/datum/move_loop/move/move()
var/atom/old_loc = moving.loc
//moving.Move(get_step(moving, direction), direction, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(get_step(moving, direction), direction)
moving.Move(get_step(moving, direction), direction, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
// We cannot rely on the return value of Move(), we care about teleports and it doesn't
// Moving also can be null on occasion, if the move deleted it and therefor us
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
@@ -226,7 +222,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/force_move_dir(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/force_move_dir(moving, direction, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/move/force, priority, flags, extra_info, delay, timeout, direction)
/datum/move_loop/move/force
@@ -283,7 +279,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/force_move(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/force_move(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/force_move, priority, flags, extra_info, delay, timeout, chasing)
///Used for force-move loops
@@ -301,23 +297,23 @@
* Returns TRUE if the loop sucessfully started, or FALSE if it failed
*
* Arguments:
* * moving - The atom we want to move
* * chasing - The atom we want to move towards
* * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* * repath_delay - How often we're allowed to recalculate our path
* * max_path_length - The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite)
* * miminum_distance - Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example
* * access - A list representing what access we have and what doors we can open
* * simulated_only - Whether we consider turfs without atmos simulation (AKA do we want to ignore space)
* * avoid - If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf
* * skip_first - Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break things
* * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
* moving - The atom we want to move
* chasing - The atom we want to move towards
* delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* repath_delay - How often we're allowed to recalculate our path
* max_path_length - The maximum number of steps we can take in a given path to search (default: 30, 0 = infinite)
* miminum_distance - Minimum distance to the target before path returns, could be used to get near a target, but not right to it - for an AI mob with a gun, for example
* access - A list representing what access we have and what doors we can open
* simulated_only - Whether we consider turfs without atmos simulation (AKA do we want to ignore space)
* avoid - If we want to avoid a specific turf, like if we're a mulebot who already got blocked by some turf
* skip_first - Whether or not to delete the first item in the path. This would be done because the first item is the starting tile, which can break things
* timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/jps_move(moving,
/datum/move_manager/proc/jps_move(moving,
chasing,
delay,
timeout,
@@ -441,8 +437,7 @@
var/turf/next_step = movement_path[1]
var/atom/old_loc = moving.loc
//moving.Move(next_step, get_dir(moving, next_step), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(next_step, get_dir(moving, next_step))
moving.Move(next_step, get_dir(moving, next_step), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
. = (old_loc != moving?.loc) ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
// this check if we're on exactly the next tile may be overly brittle for dense objects who may get bumped slightly
@@ -486,17 +481,17 @@
* Returns TRUE if the loop sucessfully started, or FALSE if it failed
*
* Arguments:
* * moving - The atom we want to move
* * chasing - The atom we want to move towards
* * min_dist - the closest we're allower to get to the target
* * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
* moving - The atom we want to move
* chasing - The atom we want to move towards
* min_dist - the closest we're allower to get to the target
* delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_to(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_to(moving, chasing, min_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_to, priority, flags, extra_info, delay, timeout, chasing, min_dist)
///Wrapper around walk_to()
@@ -511,9 +506,7 @@
return
var/atom/old_loc = moving.loc
var/turf/next = get_step_to(moving, target)
//This used to be `moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))` but our system is different and
//putting a 4th or more parameters causes the gliding to catch fire, hence...
moving.Move(next, get_dir(moving, next))
moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
/**
@@ -522,17 +515,17 @@
* Returns TRUE if the loop sucessfully started, or FALSE if it failed
*
* Arguments:
* * moving - The atom we want to move
* * chasing - The atom we want to move towards
* * max_dist - the furthest away from the target we're allowed to get
* * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* * timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
* moving - The atom we want to move
* chasing - The atom we want to move towards
* max_dist - the furthest away from the target we're allowed to get
* delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* timeout - Time in deci-seconds until the moveloop self expires. Defaults to infinity
* subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_away, priority, flags, extra_info, delay, timeout, chasing, max_dist)
///Wrapper around walk_away()
@@ -547,8 +540,7 @@
return
var/atom/old_loc = moving.loc
var/turf/next = get_step_away(moving, target)
//moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(next, get_dir(moving, next))
moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
@@ -558,17 +550,17 @@
* Returns TRUE if the loop sucessfully started, or FALSE if it failed
*
* Arguments:
* * moving - The atom we want to move
* * chasing - The atom we want to move towards
* * delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* * home - Should we move towards the object at all times? Or launch towards them, but allow walls and such to take us off track. Defaults to FALSE
* * timeout - Time in deci-seconds until the moveloop self expires. Defaults to INFINITY
* * subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* * priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* * flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
* moving - The atom we want to move
* chasing - The atom we want to move towards
* delay - How many deci-seconds to wait between fires. Defaults to the lowest value, 0.1
* home - Should we move towards the object at all times? Or launch towards them, but allow walls and such to take us off track. Defaults to FALSE
* timeout - Time in deci-seconds until the moveloop self expires. Defaults to INFINITY
* subsystem - The movement subsystem to use. Defaults to SSmovement. Only one loop can exist for any one subsystem
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_towards(atom/movable/moving, atom/chasing, delay, home, timeout, datum/controller/subsystem/movement/subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_towards(moving, chasing, delay, home, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards, priority, flags, extra_info, delay, timeout, chasing, home)
/**
@@ -587,7 +579,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
return move_towards(moving, chasing, delay, TRUE, timeout, subsystem, priority, flags, extra_info)
///Used as a alternative to walk_towards
@@ -650,8 +642,7 @@
if(y_ticker >= 1)
y_ticker = MODULUS(x_ticker, 1)
var/atom/old_loc = moving.loc
//moving.Move(moving_towards, get_dir(moving, moving_towards), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(moving_towards, get_dir(moving, moving_towards))
moving.Move(moving_towards, get_dir(moving, moving_towards), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
//YOU FOUND THEM! GOOD JOB
if(home && get_turf(moving) == get_turf(target))
@@ -724,7 +715,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards_budget, priority, flags, extra_info, delay, timeout, chasing)
///The actual implementation of walk_towards()
@@ -733,8 +724,7 @@
/datum/move_loop/has_target/move_towards_budget/move()
var/turf/target_turf = get_step_towards(moving, target)
var/atom/old_loc = moving.loc
//moving.Move(target_turf, get_dir(moving, target_turf), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(target_turf, get_dir(moving, target_turf))
moving.Move(target_turf, get_dir(moving, target_turf), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
/**
@@ -751,7 +741,7 @@
* priority - Defines how different move loops override each other. Lower numbers beat higher numbers, equal defaults to what currently exists. Defaults to MOVEMENT_DEFAULT_PRIORITY
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*/
/datum/controller/subsystem/move_manager/proc/freeze(moving, halted_turf, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/freeze(moving, halted_turf, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/freeze, priority, flags, extra_info, delay, timeout, halted_turf)
/// As close as you can get to a "do-nothing" move loop, the pure intention of this is to absolutely resist all and any automated movement until the move loop times out.
@@ -775,7 +765,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_rand(moving, directions, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_rand(moving, directions, delay, timeout, subsystem, priority, flags, datum/extra_info)
if(!directions)
directions = GLOB.alldirs
return add_to_loop(moving, subsystem, /datum/move_loop/move_rand, priority, flags, extra_info, delay, timeout, directions)
@@ -807,8 +797,7 @@
var/testdir = pick(potential_dirs)
var/turf/moving_towards = get_step(moving, testdir)
var/atom/old_loc = moving.loc
//moving.Move(moving_towards, testdir, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(moving_towards, testdir)
moving.Move(moving_towards, testdir, FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
if(old_loc != moving?.loc) //If it worked, we're done
return MOVELOOP_SUCCESS
potential_dirs -= testdir
@@ -828,7 +817,7 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
/datum/controller/subsystem/move_manager/proc/move_to_rand(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
/datum/move_manager/proc/move_to_rand(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
return add_to_loop(moving, subsystem, /datum/move_loop/move_to_rand, priority, flags, extra_info, delay, timeout)
///Wrapper around step_rand
@@ -837,8 +826,7 @@
/datum/move_loop/move_to_rand/move()
var/atom/old_loc = moving.loc
var/turf/next = get_step_rand(moving)
//moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
moving.Move(next, get_dir(moving, next))
moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE))
return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
/**
@@ -855,13 +843,13 @@
* flags - Set of bitflags that effect move loop behavior in some way. Check _DEFINES/movement.dm
*
**/
// /datum/controller/subsystem/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
// /datum/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
// return add_to_loop(moving, subsystem, /datum/move_loop/disposal_holder, priority, flags, extra_info, delay, timeout)
// /// Disposal holders need to move through a chain of pipes
// /// Rather then through the world. This supports this
// /// If this ever changes, get rid of this, add drift component like logic to the holder
// /// And move them to move()
/// Disposal holders need to move through a chain of pipes
/// Rather then through the world. This supports this
/// If this ever changes, get rid of this, add drift component like logic to the holder
/// And move them to move()
// /datum/move_loop/disposal_holder
// /datum/move_loop/disposal_holder/setup(delay = 1, timeout = INFINITY)
@@ -0,0 +1,5 @@
MOVEMENT_SUBSYSTEM_DEF(spacedrift)
name = "Space Drift"
priority = FIRE_PRIORITY_SPACEDRIFT
flags = SS_NO_INIT|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
+242
View File
@@ -0,0 +1,242 @@
#define MAX_THROWING_DIST 1280 // 5 z-levels on default width
#define MAX_TICKS_TO_MAKE_UP 3 //how many missed ticks will we attempt to make up for this run.
SUBSYSTEM_DEF(throwing)
name = "Throwing"
priority = FIRE_PRIORITY_THROWING
wait = 1
flags = SS_NO_INIT|SS_KEEP_TIMING|SS_TICKER
runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME
var/list/currentrun
var/list/processing = list()
/datum/controller/subsystem/throwing/stat_entry(msg)
msg = "P:[length(processing)]"
return ..()
/datum/controller/subsystem/throwing/fire(resumed = 0)
if (!resumed)
src.currentrun = processing.Copy()
//cache for sanic speed (lists are references anyways)
var/list/currentrun = src.currentrun
while(length(currentrun))
var/atom/movable/AM = currentrun[currentrun.len]
var/datum/thrownthing/TT = currentrun[AM]
currentrun.len--
if (QDELETED(AM) || QDELETED(TT))
processing -= AM
if (MC_TICK_CHECK)
return
continue
TT.tick()
if (MC_TICK_CHECK)
return
currentrun = null
/datum/thrownthing
///Defines the atom that has been thrown (Objects and Mobs, mostly.)
var/atom/movable/thrownthing
///Weakref to the original intended target of the throw, to prevent hardDels
var/datum/weakref/initial_target
///The turf that the target was on, if it's not a turf itself.
var/turf/target_turf
///The turf that we were thrown from.
var/turf/starting_turf
///If the target happens to be a carbon and that carbon has a body zone aimed at, this is carried on here.
var/target_zone
///The initial direction of the thrower of the thrownthing for building the trajectory of the throw.
var/init_dir
///The maximum number of turfs that the thrownthing will travel to reach it's target.
var/maxrange
///Turfs to travel per tick
var/speed
///If a mob is the one who has thrown the object, then it's moved here. This can be null and must be null checked before trying to use it.
var/datum/weakref/thrower
///A variable that helps in describing objects thrown at an angle, if it should be moved diagonally first or last.
var/diagonals_first
///Set to TRUE if the throw is exclusively diagonal (45 Degree angle throws for example)
var/pure_diagonal
///Tracks how far a thrownthing has traveled mid-throw for the purposes of maxrange
var/dist_travelled = 0
///The start_time obtained via world.time for the purposes of tiles moved/tick.
var/start_time
///Distance to travel in the X axis/direction.
var/dist_x
///Distance to travel in the y axis/direction.
var/dist_y
///The Horizontal direction we're traveling (EAST or WEST)
var/dx
///The VERTICAL direction we're traveling (NORTH or SOUTH)
var/dy
///The movement force provided to a given object in transit. More info on these in move_force.dm
var/force = MOVE_FORCE_DEFAULT
///If the throw is gentle, then the thrownthing is harmless on impact.
var/gentle = FALSE
///How many tiles that need to be moved in order to travel to the target.
var/diagonal_error
///If a thrown thing has a callback, it can be invoked here within thrownthing.
var/datum/callback/callback
///Mainly exists for things that would freeze a thrown object in place, like a timestop'd tile. Or a Tractor Beam.
var/paused = FALSE
///How long an object has been paused for, to be added to the travel time.
var/delayed_time = 0
///The last world.time value stored when the thrownthing was moving.
var/last_move = 0
/datum/thrownthing/New(thrownthing, target, init_dir, maxrange, speed, thrower, diagonals_first, force, gentle, callback, target_zone)
. = ..()
src.thrownthing = thrownthing
RegisterSignal(thrownthing, COMSIG_QDELETING, PROC_REF(on_thrownthing_qdel))
src.starting_turf = get_turf(thrownthing)
src.target_turf = get_turf(target)
if(target_turf != target)
src.initial_target = WEAKREF(target)
src.init_dir = init_dir
src.maxrange = maxrange
src.speed = speed
if(thrower)
src.thrower = WEAKREF(thrower)
src.diagonals_first = diagonals_first
src.force = force
src.gentle = gentle
src.callback = callback
src.target_zone = target_zone
/datum/thrownthing/Destroy()
SSthrowing.processing -= thrownthing
SSthrowing.currentrun -= thrownthing
thrownthing.throwing = null
thrownthing = null
thrower = null
initial_target = null
callback = null
return ..()
///Defines the datum behavior on the thrownthing's qdeletion event.
/datum/thrownthing/proc/on_thrownthing_qdel(atom/movable/source, force)
SIGNAL_HANDLER
qdel(src)
/// Returns the mob thrower, or null
/datum/thrownthing/proc/get_thrower()
. = thrower?.resolve()
if(isnull(.))
thrower = null
/datum/thrownthing/proc/tick()
var/atom/movable/AM = thrownthing
if (!isturf(AM.loc) || !AM.throwing)
finalize()
return
if(paused)
delayed_time += world.time - last_move
return
var/atom/movable/actual_target = initial_target?.resolve()
var/mob/mob_thrower = get_thrower()
if(dist_travelled) //to catch sneaky things moving on our tile while we slept
for(var/atom/movable/obstacle as anything in get_turf(thrownthing))
if (obstacle == thrownthing || (obstacle == mob_thrower && !ismob(thrownthing)))
continue
if(ismob(obstacle) && thrownthing.pass_flags & PASSMOB && (obstacle != actual_target))
continue
if(obstacle.pass_flags_self & LETPASSTHROW)
continue
if (obstacle == actual_target || (obstacle.density)) //Different from TG
finalize(TRUE, obstacle)
return
var/atom/step
last_move = world.time
//calculate how many tiles to move, making up for any missed ticks.
var/tilestomove = CEILING(min(((((world.time+world.tick_lag) - start_time + delayed_time) * speed) - (dist_travelled ? dist_travelled : -1)), speed*MAX_TICKS_TO_MAKE_UP) * (world.tick_lag * SSthrowing.wait), 1)
while (tilestomove-- > 0)
if ((dist_travelled >= maxrange || AM.loc == target_turf) && AM.has_gravity(AM.loc))
finalize()
return
if (dist_travelled <= max(dist_x, dist_y)) //if we haven't reached the target yet we home in on it, otherwise we use the initial direction
step = get_step(AM, get_dir(AM, target_turf))
else
step = get_step(AM, init_dir)
if (!pure_diagonal && !diagonals_first) // not a purely diagonal trajectory and we don't want all diagonal moves to be done first
if (diagonal_error >= 0 && max(dist_x,dist_y) - dist_travelled != 1) //we do a step forward unless we're right before the target
step = get_step(AM, dx)
diagonal_error += (diagonal_error < 0) ? dist_x/2 : -dist_y
if (!step) // going off the edge of the map makes get_step return null, don't let things go off the edge
finalize()
return
if(!AM.Move(step, get_dir(AM, step))) // we hit something during our move... //Different from TG
if(AM.throwing) // ...but finalize() wasn't called on Bump() because of a higher level definition that doesn't always call parent.
finalize()
return
dist_travelled++
if(actual_target && !(actual_target.pass_flags_self & LETPASSTHROW) && actual_target.loc == AM.loc) // we crossed a movable with no density (e.g. a mouse or APC) we intend to hit anyway.
finalize(TRUE, actual_target)
return
if (dist_travelled > MAX_THROWING_DIST)
finalize()
return
/datum/thrownthing/proc/finalize(hit = FALSE, target=null)
set waitfor = FALSE
//done throwing, either because it hit something or it finished moving
if(!thrownthing)
return
thrownthing.throwing = null
if (!hit)
for (var/atom/movable/obstacle as anything in get_turf(thrownthing)) //looking for our target on the turf we land on.
if (obstacle == target)
hit = TRUE
thrownthing.throw_impact(obstacle, src)
if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
return //deletion should already be handled by on_thrownthing_qdel()
break
if (!hit)
thrownthing.throw_impact(get_turf(thrownthing), src) // we haven't hit something yet and we still must, let's hit the ground.
if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
return //deletion should already be handled by on_thrownthing_qdel()
thrownthing.newtonian_move(init_dir)
else
thrownthing.newtonian_move(init_dir)
if(target)
thrownthing.throw_impact(target, src)
if(QDELETED(thrownthing)) //throw_impact can delete things, such as glasses smashing
return //deletion should already be handled by on_thrownthing_qdel()
if (callback)
callback.Invoke()
// if(!thrownthing.currently_z_moving) // I don't think you can zfall while thrown but hey, just in case.
// var/turf/T = get_turf(thrownthing)
// T?.zFall(thrownthing)
if(thrownthing)
SEND_SIGNAL(thrownthing, COMSIG_MOVABLE_THROW_LANDED, src)
var/turf/landed_turf = get_turf(thrownthing)
if(landed_turf)
SEND_SIGNAL(landed_turf, COMSIG_TURF_MOVABLE_THROW_LANDED, thrownthing)
qdel(src)
#undef MAX_THROWING_DIST
#undef MAX_TICKS_TO_MAKE_UP