diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm index 05d92a11ee1f..641fcc941d38 100644 --- a/code/__DEFINES/MC.dm +++ b/code/__DEFINES/MC.dm @@ -95,3 +95,12 @@ }\ /datum/controller/subsystem/timer/##X/fire() {..() /*just so it shows up on the profiler*/} \ /datum/controller/subsystem/timer/##X + +#define MOVEMENT_SUBSYSTEM_DEF(X) GLOBAL_REAL(SS##X, /datum/controller/subsystem/movement/##X);\ +/datum/controller/subsystem/movement/##X/New(){\ + NEW_SS_GLOBAL(SS##X);\ + PreInit();\ + ss_id="movement_[#X]";\ +}\ +/datum/controller/subsystem/movement/##X/fire() {..() /*just so it shows up on the profiler*/} \ +/datum/controller/subsystem/movement/##X diff --git a/code/__DEFINES/color_defines.dm b/code/__DEFINES/color_defines.dm index 0e24a46e279b..558dd7d88c9e 100644 --- a/code/__DEFINES/color_defines.dm +++ b/code/__DEFINES/color_defines.dm @@ -111,6 +111,7 @@ #define COLOR_BLOOD_BASE "#A10808" #define COLOR_BLOOD_MACHINE "#1F181F" +#define COLOR_BLOOD_XENO "#05EE05" // Pipe colours #define PIPE_COLOR_GREY "#ffffff" //yes white is grey diff --git a/code/__DEFINES/dcs/mob_signals.dm b/code/__DEFINES/dcs/mob_signals.dm index e9b71bdc58b7..bcbdc4fbf73c 100644 --- a/code/__DEFINES/dcs/mob_signals.dm +++ b/code/__DEFINES/dcs/mob_signals.dm @@ -218,3 +218,12 @@ /// from remove_ventcrawler(): (mob/living/crawler) #define COMSIG_LIVING_EXIT_VENTCRAWL "living_exit_ventcrawl" + +/// From base of /client/Move(): (new_loc, direction) +#define COMSIG_MOB_CLIENT_PRE_MOVE "mob_client_pre_move" + /// Should always match COMPONENT_MOVABLE_BLOCK_PRE_MOVE as these are interchangeable and used to block movement. + #define COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE COMPONENT_MOVABLE_BLOCK_PRE_MOVE + /// The argument of move_args which corresponds to the loc we're moving to + #define MOVE_ARG_NEW_LOC 1 + /// The arugment of move_args which dictates our movement direction + #define MOVE_ARG_DIRECTION 2 diff --git a/code/__DEFINES/dcs/movable_signals.dm b/code/__DEFINES/dcs/movable_signals.dm index 7aa2beca9330..59152fb06644 100644 --- a/code/__DEFINES/dcs/movable_signals.dm +++ b/code/__DEFINES/dcs/movable_signals.dm @@ -70,3 +70,19 @@ /// Called when blocking a teleport #define COMSIG_ATOM_INTERCEPT_TELEPORTED "intercept_teleported" #define COMPONENT_BLOCK_TELEPORT (1<<0) +///from base of atom/movable/newtonian_move(): (inertia_direction, start_delay) +#define COMSIG_MOVABLE_NEWTONIAN_MOVE "movable_newtonian_move" + #define COMPONENT_MOVABLE_NEWTONIAN_BLOCK (1<<0) + +///from datum/component/drift/apply_initial_visuals(): () +#define COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT "movable_drift_visual_attempt" + #define DRIFT_VISUAL_FAILED (1<<0) +///from datum/component/drift/allow_final_movement(): () +#define COMSIG_MOVABLE_DRIFT_BLOCK_INPUT "movable_drift_block_input" + #define DRIFT_ALLOW_INPUT (1<<0) + +///called when the movable's glide size is updated: (new_glide_size) +#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size" + +///signal sent out by an atom when it is no longer pulling something : (atom/pulling) +#define COMSIG_ATOM_NO_LONGER_PULLING "movable_no_longer_pulling" diff --git a/code/__DEFINES/dcs/moveloop_signals.dm b/code/__DEFINES/dcs/moveloop_signals.dm new file mode 100644 index 000000000000..c93d93251836 --- /dev/null +++ b/code/__DEFINES/dcs/moveloop_signals.dm @@ -0,0 +1,15 @@ +///from [/datum/move_loop/start_loop] (): +#define COMSIG_MOVELOOP_START "moveloop_start" +///from [/datum/move_loop/stop_loop] (): +#define COMSIG_MOVELOOP_STOP "moveloop_stop" +///from [/datum/move_loop/process] (): +#define COMSIG_MOVELOOP_PREPROCESS_CHECK "moveloop_preprocess_check" + #define MOVELOOP_SKIP_STEP (1<<0) +///from [/datum/move_loop/process] (succeeded, visual_delay): +#define COMSIG_MOVELOOP_POSTPROCESS "moveloop_postprocess" +//from [/datum/move_loop/has_target/jps/recalculate_path] (): +#define COMSIG_MOVELOOP_JPS_REPATH "moveloop_jps_repath" +///From base of /datum/move_loop/process() after attempting to move a movable: (datum/move_loop/loop, old_dir) +#define COMSIG_MOVABLE_MOVED_FROM_LOOP "movable_moved_from_loop" +///from [/datum/move_loop/has_target/jps/on_finish_pathing] +#define COMSIG_MOVELOOP_JPS_FINISHED_PATHING "moveloop_jps_finished_pathing" diff --git a/code/__DEFINES/dcs/obj_signals.dm b/code/__DEFINES/dcs/obj_signals.dm index 936667d709ff..4b138f7146a3 100644 --- a/code/__DEFINES/dcs/obj_signals.dm +++ b/code/__DEFINES/dcs/obj_signals.dm @@ -22,6 +22,14 @@ #define COMSIG_GLOB_CURSED_SLOT_MACHINE_WON "cursed_slot_machine_won" +// /obj/item/tank/jetpack + +/// from /obj/item/tank/jetpack/proc/turn_on() : () +#define COMSIG_JETPACK_ACTIVATED "jetpack_activated" + #define JETPACK_ACTIVATION_FAILED (1<<0) +/// from /obj/item/tank/jetpack/proc/turn_off() : () +#define COMSIG_JETPACK_DEACTIVATED "jetpack_deactivated" + // other subtypes /// from /datum/component/shelved/UnregisterFromParent(): (parent_uid) diff --git a/code/__DEFINES/directions.dm b/code/__DEFINES/directions.dm index 718ebeedf14f..702a6b9008bc 100644 --- a/code/__DEFINES/directions.dm +++ b/code/__DEFINES/directions.dm @@ -29,6 +29,11 @@ /// returns TRUE if direction is cardinal and false if not #define IS_DIR_CARDINAL(dir) (!IS_DIR_DIAGONAL(dir)) +///True if the dir is north or south, false therwise +#define NSCOMPONENT(d) (d&(NORTH|SOUTH)) +///True if the dir is east/west, false otherwise +#define EWCOMPONENT(d) (d&(EAST|WEST)) + /// Inverse direction, taking into account UP|DOWN if necessary. #define REVERSE_DIR(dir) ( ((dir & 85) << 1) | ((dir & 170) >> 1) ) /// returns TRUE if the direction is EAST or WEST @@ -36,4 +41,13 @@ /// returns TRUE if the direction is NORTH or SOUTH #define DIR_JUST_VERTICAL(dir) ((dir == NORTH) || (dir == SOUTH)) +/// North direction as a string "[1]" +#define TEXT_NORTH "[NORTH]" +/// South direction as a string "[2]" +#define TEXT_SOUTH "[SOUTH]" +/// East direction as a string "[4]" +#define TEXT_EAST "[EAST]" +/// West direction as a string "[8]" +#define TEXT_WEST "[WEST]" + #define EXCLUSIVE_OR(thing_one, thing_two) ((thing_one)^(thing_two)) diff --git a/code/__DEFINES/mob_defines.dm b/code/__DEFINES/mob_defines.dm index d3eb93078ea4..92fa20a22f80 100644 --- a/code/__DEFINES/mob_defines.dm +++ b/code/__DEFINES/mob_defines.dm @@ -383,3 +383,7 @@ #define BRAIN_DAMAGE_RATIO_MODERATE 6 / 12 #define BRAIN_DAMAGE_RATIO_SEVERE 8 / 12 #define BRAIN_DAMAGE_RATIO_CRITICAL 10 / 12 + +#define GRAB_PIXEL_SHIFT_PASSIVE 6 +#define GRAB_PIXEL_SHIFT_AGGRESSIVE 12 +#define GRAB_PIXEL_SHIFT_NECK 16 diff --git a/code/__DEFINES/movement_defines.dm b/code/__DEFINES/movement_defines.dm new file mode 100644 index 000000000000..cb76916dc0e8 --- /dev/null +++ b/code/__DEFINES/movement_defines.dm @@ -0,0 +1,71 @@ +/// The minimum for glide_size to be clamped to. +#define MIN_GLIDE_SIZE 1 +/// The maximum for glide_size to be clamped to. +/// This shouldn't be higher than the icon size, and generally you shouldn't be changing this, but it's here just in case. +#define MAX_GLIDE_SIZE 32 + +//Movement loop priority. Only one loop can run at a time, this dictates that +// Higher numbers beat lower numbers +///Standard, go lower then this if you want to override, higher otherwise +#define MOVEMENT_DEFAULT_PRIORITY 10 +///Very few things should override this +#define MOVEMENT_SPACE_PRIORITY 100 +///Higher then the heavens +#define MOVEMENT_ABOVE_SPACE_PRIORITY (MOVEMENT_SPACE_PRIORITY + 1) + +//Movement loop flags +///Should the loop act immediately following its addition? +#define MOVEMENT_LOOP_START_FAST (1<<0) +///Do we not use the priority system? +#define MOVEMENT_LOOP_IGNORE_PRIORITY (1<<1) +///Should we override the loop's glide? +#define MOVEMENT_LOOP_IGNORE_GLIDE (1<<2) +///Should we not update our movables dir on move? +#define MOVEMENT_LOOP_NO_DIR_UPDATE (1<<3) +///Is the loop moving the movable outside its control, like it's an external force? e.g. footsteps won't play if enabled. +#define MOVEMENT_LOOP_OUTSIDE_CONTROL (1<<4) + +// Movement loop status flags +/// Has the loop been paused, soon to be resumed? +#define MOVELOOP_STATUS_PAUSED (1<<0) +/// Is the loop running? (Is true even when paused) +#define MOVELOOP_STATUS_RUNNING (1<<1) +/// Is the loop queued in a subsystem? +#define MOVELOOP_STATUS_QUEUED (1<<2) + +/** + * Returns a bitfield containing flags both present in `flags` arg and the `processing_move_loop_flags` move_packet variable. + * Has no use outside of procs called within the movement proc chain. + */ +#define CHECK_MOVE_LOOP_FLAGS(movable, flags) (movable.move_packet ? (movable.move_packet.processing_move_loop_flags & (flags)) : NONE) + +//Index defines for movement bucket data packets +#define MOVEMENT_BUCKET_TIME 1 +#define MOVEMENT_BUCKET_LIST 2 + +///Return values for moveloop Move() +#define MOVELOOP_FAILURE 0 +#define MOVELOOP_SUCCESS 1 +#define MOVELOOP_NOT_READY 2 + +#define ACTIVE_MOVEMENT_OLDLOC 1 +#define ACTIVE_MOVEMENT_DIRECTION 2 +#define ACTIVE_MOVEMENT_FORCED 3 +#define ACTIVE_MOVEMENT_OLDLOCS 4 + +/// The arguments of this macro correspond directly to the argument order of /atom/movable/proc/Moved +#define SET_ACTIVE_MOVEMENT(_old_loc, _direction, _forced, _oldlocs) \ + active_movement = list( \ + _old_loc, \ + _direction, \ + _forced, \ + _oldlocs, \ + ) + +/// Finish any active movements +#define RESOLVE_ACTIVE_MOVEMENT \ + if(active_movement) { \ + var/__move_args = active_movement; \ + active_movement = null; \ + Moved(arglist(__move_args)); \ + } diff --git a/code/__DEFINES/movement_info.dm b/code/__DEFINES/movement_info.dm deleted file mode 100644 index 95c90f7a1fba..000000000000 --- a/code/__DEFINES/movement_info.dm +++ /dev/null @@ -1,16 +0,0 @@ -/// The arguments of this macro correspond directly to the argument order of /atom/movable/proc/Moved -#define SET_ACTIVE_MOVEMENT(_old_loc, _direction, _forced, _oldlocs) \ - active_movement = list( \ - _old_loc, \ - _direction, \ - _forced, \ - _oldlocs, \ - ) - -/// Finish any active movements -#define RESOLVE_ACTIVE_MOVEMENT \ - if(active_movement) { \ - var/__move_args = active_movement; \ - active_movement = null; \ - Moved(arglist(__move_args)); \ - } diff --git a/code/__DEFINES/vehicle_defines.dm b/code/__DEFINES/vehicle_defines.dm index 4cd27e39aa69..be45aea5fac7 100644 --- a/code/__DEFINES/vehicle_defines.dm +++ b/code/__DEFINES/vehicle_defines.dm @@ -30,10 +30,16 @@ /// The vehicle being ridden requires pixel offsets for all directions #define RIDING_OFFSET_ALL "ALL" +/// Compensating for time dilation +GLOBAL_VAR_INIT(glide_size_multiplier, 1.0) + ///Broken down, here's what this does: /// divides the world icon_size (32) by delay divided by ticklag to get the number of pixels something should be moving each tick. /// The division result is given a min value of 1 to prevent obscenely slow glide sizes from being set /// Then that's multiplied by the global glide size multiplier. 1.25 by default feels pretty close to spot on. This is just to try to get byond to behave. /// The whole result is then clamped to within the range above. /// Not very readable but it works -#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1))), 1, 32)) +#define DELAY_TO_GLIDE_SIZE(delay) (clamp(((world.icon_size / max((delay) / world.tick_lag, 1)) * GLOB.glide_size_multiplier), MIN_GLIDE_SIZE, MAX_GLIDE_SIZE)) + +///Similar to DELAY_TO_GLIDE_SIZE, except without the clamping, and it supports piping in an unrelated scalar +#define MOVEMENT_ADJUSTED_GLIDE_SIZE(delay, movement_disparity) (world.icon_size / ((delay) / world.tick_lag) * movement_disparity * GLOB.glide_size_multiplier) diff --git a/code/__HELPERS/lists.dm b/code/__HELPERS/lists.dm index 959af27e822a..47947cb245f1 100644 --- a/code/__HELPERS/lists.dm +++ b/code/__HELPERS/lists.dm @@ -49,6 +49,44 @@ };\ } while(FALSE) +#define SORT_FIRST_INDEX(list) (list[1]) +#define SORT_COMPARE_DIRECTLY(thing) (thing) +#define SORT_VAR_NO_TYPE(varname) var/varname + +/**** + * Even more custom binary search sorted insert, using defines instead of vars + * INPUT: Item to be inserted + * LIST: List to insert INPUT into + * TYPECONT: A define setting the var to the typepath of the contents of the list + * COMPARE: The item to compare against, usualy the same as INPUT + * COMPARISON: A define that takes an item to compare as input, and returns their comparable value + * COMPTYPE: How should the list be compared? Either COMPARE_KEY or COMPARE_VALUE. + */ +#define BINARY_INSERT_DEFINE(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \ + do {\ + var/list/__BIN_LIST = LIST;\ + var/__BIN_CTTL = length(__BIN_LIST);\ + if(!__BIN_CTTL) {\ + __BIN_LIST += INPUT;\ + } else {\ + var/__BIN_LEFT = 1;\ + var/__BIN_RIGHT = __BIN_CTTL;\ + var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + ##TYPECONT(__BIN_ITEM);\ + while(__BIN_LEFT < __BIN_RIGHT) {\ + __BIN_ITEM = COMPTYPE;\ + if(##COMPARISON(__BIN_ITEM) <= ##COMPARISON(COMPARE)) {\ + __BIN_LEFT = __BIN_MID + 1;\ + } else {\ + __BIN_RIGHT = __BIN_MID;\ + };\ + __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\ + };\ + __BIN_ITEM = COMPTYPE;\ + __BIN_MID = ##COMPARISON(__BIN_ITEM) > ##COMPARISON(COMPARE) ? __BIN_MID : __BIN_MID + 1;\ + __BIN_LIST.Insert(__BIN_MID, INPUT);\ + };\ + } while(FALSE) // Generic listoflist safe add and removal macros: ///If value is a list, wrap it in a list so it can be used with list add/remove operations diff --git a/code/__HELPERS/mob_helpers.dm b/code/__HELPERS/mob_helpers.dm index 23e6df82eedd..d3732086582f 100644 --- a/code/__HELPERS/mob_helpers.dm +++ b/code/__HELPERS/mob_helpers.dm @@ -336,7 +336,7 @@ var/user_loc = user.loc var/drifting = 0 - if(!user.Process_Spacemove(0) && user.inertia_dir) + if(GLOB.move_manager.processing_on(user, SSspacedrift)) drifting = 1 var/target_loc = target.loc @@ -367,7 +367,7 @@ break continue - if(drifting && !user.inertia_dir) + if(drifting && !GLOB.move_manager.processing_on(user, SSspacedrift)) drifting = 0 user_loc = user.loc @@ -398,7 +398,7 @@ var/atom/Uloc = user.loc var/drifting = FALSE - if(!allow_moving && !user.Process_Spacemove(0) && user.inertia_dir) + if(!allow_moving && GLOB.move_manager.processing_on(user, SSspacedrift)) drifting = TRUE var/holding = user.get_active_hand() @@ -426,7 +426,7 @@ if(progress) progbar.update(world.time - starttime) if(!allow_moving) - if(drifting && !user.inertia_dir) + if(drifting && !GLOB.move_manager.processing_on(user, SSspacedrift)) drifting = FALSE Uloc = user.loc if(!drifting && user.loc != Uloc) diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm index 7a339ec82835..2813c64425fd 100644 --- a/code/__HELPERS/unsorted.dm +++ b/code/__HELPERS/unsorted.dm @@ -1748,6 +1748,9 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new) matches[key] = value return matches +/proc/return_typenames(type) + return splittext("[type]", "/") + //Key thing that stops lag. Cornerstone of performance in ss13, Just sitting here, in unsorted.dm. //Increases delay as the server gets more overloaded, diff --git a/code/controllers/subsystem/SSspacedrift.dm b/code/controllers/subsystem/SSspacedrift.dm deleted file mode 100644 index 2707f12518fb..000000000000 --- a/code/controllers/subsystem/SSspacedrift.dm +++ /dev/null @@ -1,66 +0,0 @@ -SUBSYSTEM_DEF(spacedrift) - name = "Space Drift" - priority = FIRE_PRIORITY_SPACEDRIFT - wait = 5 - flags = SS_NO_INIT|SS_KEEP_TIMING - runlevels = RUNLEVEL_GAME | RUNLEVEL_POSTGAME - offline_implications = "Mobs will no longer respect a lack of gravity. No immediate action is needed." - cpu_display = SS_CPUDISPLAY_LOW - - var/list/currentrun = list() - var/list/processing = list() - -/datum/controller/subsystem/spacedrift/get_stat_details() - return "P:[length(processing)]" - -/datum/controller/subsystem/spacedrift/get_metrics() - . = ..() - var/list/cust = list() - cust["processing"] = length(processing) - .["custom"] = cust - -/datum/controller/subsystem/spacedrift/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[length(currentrun)] - currentrun.len-- - if(!AM) - processing -= AM - if(MC_TICK_CHECK) - return - continue - - if(AM.inertia_next_move > world.time) - if(MC_TICK_CHECK) - return - continue - - if(!AM.loc || AM.loc != AM.inertia_last_loc || AM.Process_Spacemove(0)) - AM.inertia_dir = 0 - - if(!AM.inertia_dir) - AM.inertia_last_loc = null - processing -= AM - if(MC_TICK_CHECK) - return - continue - - var/old_dir = AM.dir - var/old_loc = AM.loc - AM.inertia_moving = TRUE - AM.Move(get_step(AM, AM.inertia_dir), AM.inertia_dir, AM.inertia_move_delay) - AM.inertia_moving = FALSE - AM.inertia_next_move = world.time + AM.inertia_move_delay - if(AM.loc == old_loc) - AM.inertia_dir = 0 - - AM.setDir(old_dir) - AM.inertia_last_loc = AM.loc - if(MC_TICK_CHECK) - return - diff --git a/code/controllers/subsystem/SSthrowing.dm b/code/controllers/subsystem/SSthrowing.dm index 1cd7f739106d..3d570ef67f06 100644 --- a/code/controllers/subsystem/SSthrowing.dm +++ b/code/controllers/subsystem/SSthrowing.dm @@ -221,7 +221,7 @@ SUBSYSTEM_DEF(throwing) finalize() return - if(!AM.Move(step, get_dir(AM, step))) // we hit something during our move... + if(!AM.Move(step, get_dir(AM, step), DELAY_TO_GLIDE_SIZE(1 / speed))) // we hit something during our move... if(AM.throwing) // ...but finalize() wasn't called on Bump() because of a higher level definition that doesn't always call parent. finalize() return diff --git a/code/controllers/subsystem/SStime_track.dm b/code/controllers/subsystem/SStime_track.dm index 5f15a1bf9e42..e645077e8621 100644 --- a/code/controllers/subsystem/SStime_track.dm +++ b/code/controllers/subsystem/SStime_track.dm @@ -29,6 +29,7 @@ SUBSYSTEM_DEF(time_track) time_dilation_avg_fast = MC_AVERAGE_FAST(time_dilation_avg_fast, time_dilation_current) time_dilation_avg = MC_AVERAGE(time_dilation_avg, time_dilation_avg_fast) time_dilation_avg_slow = MC_AVERAGE_SLOW(time_dilation_avg_slow, time_dilation_avg) + GLOB.glide_size_multiplier = (current_byondtime - last_tick_byond_time) / (current_realtime - last_tick_realtime) else first_run = FALSE last_tick_realtime = current_realtime diff --git a/code/controllers/subsystem/movement/SSmovement.dm b/code/controllers/subsystem/movement/SSmovement.dm new file mode 100644 index 000000000000..d6cad4a734d3 --- /dev/null +++ b/code/controllers/subsystem/movement/SSmovement.dm @@ -0,0 +1,134 @@ +SUBSYSTEM_DEF(movement) + name = "Movement Loops" + flags = SS_NO_INIT|SS_BACKGROUND|SS_TICKER + wait = 1 //Fire each tick + /* + A breif aside about the bucketing system here + + The goal is to allow for higher loads of semi long delays while reducing cpu usage + Bucket insertion and management are much less complex then what you might see in SStimer + This is intentional, as we loop our delays much more often then that ss is designed for + We also have much shorter term timers, so we need to worry about redundant buckets much less + */ + ///Assoc list of "target time" -> list(things to process). Used for quick lookup + var/list/buckets = list() + ///Sorted list of list(target time, bucket to process) + var/list/sorted_buckets = list() + ///The time we started our last fire at + var/canonical_time = 0 + ///The visual delay of the subsystem + var/visual_delay = 1 + +/datum/controller/subsystem/movement/stat_entry(msg) + var/total_len = 0 + for(var/list/bucket as anything in sorted_buckets) + total_len += length(bucket[MOVEMENT_BUCKET_LIST]) + msg = "B:[length(sorted_buckets)] E:[total_len]" + return ..() + +/datum/controller/subsystem/movement/Recover() + //Get ready this is gonna be horrible + //We need to do this to support subtypes by the by + var/list/typenames = return_typenames(src.type) + var/our_name = typenames[length(typenames)] //Get the last name in the list, IE the subsystem identifier + + var/datum/controller/subsystem/movement/old_version = global.vars["SS[our_name]"] + buckets = old_version.buckets + sorted_buckets = old_version.sorted_buckets + +/datum/controller/subsystem/movement/fire(resumed) + if(!resumed) + canonical_time = world.time + + for(var/list/bucket_info as anything in sorted_buckets) + var/time = bucket_info[MOVEMENT_BUCKET_TIME] + if(time > canonical_time || MC_TICK_CHECK) + return + pour_bucket(bucket_info) + +/// Processes a bucket of movement loops (This should only ever be called by fire(), it exists to prevent runtime fuckery) +/datum/controller/subsystem/movement/proc/pour_bucket(list/bucket_info) + var/list/processing = bucket_info[MOVEMENT_BUCKET_LIST] // Cache for lookup speed + while(processing.len) + var/datum/move_loop/loop = processing[processing.len] + processing.len-- + // No longer queued since we just got removed from the loop + loop.queued_time = null + loop.process() //This shouldn't get nulls, if it does, runtime + if(!QDELETED(loop) && loop.status & MOVELOOP_STATUS_QUEUED) //Re-Insert the loop + loop.status &= ~MOVELOOP_STATUS_QUEUED + loop.timer = world.time + loop.delay + queue_loop(loop) + if(MC_TICK_CHECK) + break + + if(length(processing)) + return // Still work to be done + var/bucket_time = bucket_info[MOVEMENT_BUCKET_TIME] + smash_bucket(1, bucket_time) // We assume we're the first bucket in the queue right now + visual_delay = MC_AVERAGE_FAST(visual_delay, max((world.time - canonical_time) / wait, 1)) + +/// Removes a bucket from our system. You only need to pass in the time, but if you pass in the index of the list you save us some work +/datum/controller/subsystem/movement/proc/smash_bucket(index, bucket_time) + var/sorted_length = length(sorted_buckets) + if(!index) + index = sorted_length + 1 // let's setup the failure condition + for(var/i in 1 to sorted_length) + var/list/bucket_info = sorted_buckets[i] + if(bucket_info[MOVEMENT_BUCKET_TIME] != bucket_time) + continue + index = i + break + //This is technically possible, if our bucket is smashed inside the loop's process + //Let's be nice, the cost of doing it is cheap + if(index > sorted_length || !buckets["[bucket_time]"]) + return + + sorted_buckets.Cut(index, index + 1) //Removes just this list + //Removes the assoc lookup too + buckets -= "[bucket_time]" + +/datum/controller/subsystem/movement/proc/queue_loop(datum/move_loop/loop) + if(loop.status & MOVELOOP_STATUS_QUEUED) + stack_trace("A move loop attempted to queue while already queued") + return + loop.queued_time = loop.timer + loop.status |= MOVELOOP_STATUS_QUEUED + var/list/our_bucket = buckets["[loop.queued_time]"] + // If there's no bucket for this, lets set them up + if(!our_bucket) + buckets["[loop.queued_time]"] = list() + our_bucket = buckets["[loop.queued_time]"] + // This makes assoc buckets and sorted buckets point to the same place, allowing for quicker inserts + var/list/new_bucket = list(list(loop.queued_time, our_bucket)) + var/list/compare_item = list(loop.queued_time) + BINARY_INSERT_DEFINE(new_bucket, sorted_buckets, SORT_VAR_NO_TYPE, compare_item, SORT_FIRST_INDEX, COMPARE_KEY) + + our_bucket += loop + +/datum/controller/subsystem/movement/proc/dequeue_loop(datum/move_loop/loop) + // Go home, you're not here anyway + if(!(loop.status & MOVELOOP_STATUS_QUEUED)) + return + if(isnull(loop.queued_time)) // This happens if a moveloop is dequeued while handling process() + loop.status &= ~MOVELOOP_STATUS_QUEUED + return + var/list/our_entries = buckets["[loop.queued_time]"] + our_entries -= loop + if(!length(our_entries)) + smash_bucket(bucket_time = loop.queued_time) // We can't pass an index in for context because we don't know our position + loop.queued_time = null + loop.status &= ~MOVELOOP_STATUS_QUEUED + +/datum/controller/subsystem/movement/proc/add_loop(datum/move_loop/add) + if(add.status & MOVELOOP_STATUS_QUEUED) + CRASH("Loop being added that is already queued.") + add.loop_started() + if(QDELETED(add) || add.status & MOVELOOP_STATUS_QUEUED) + return + queue_loop(add) + +/datum/controller/subsystem/movement/proc/remove_loop(datum/move_loop/remove) + dequeue_loop(remove) + remove.loop_stopped() + diff --git a/code/controllers/subsystem/movement/SSspacedrift.dm b/code/controllers/subsystem/movement/SSspacedrift.dm new file mode 100644 index 000000000000..4002b5eb555f --- /dev/null +++ b/code/controllers/subsystem/movement/SSspacedrift.dm @@ -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 diff --git a/code/controllers/subsystem/movement/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm new file mode 100644 index 000000000000..2bf8212316b9 --- /dev/null +++ b/code/controllers/subsystem/movement/movement_types.dm @@ -0,0 +1,832 @@ +///Template class of the movement datums, handles the timing portion of the loops +/datum/move_loop + ///The movement packet that owns us + var/datum/movement_packet/owner + ///The subsystem we're processing on + var/datum/controller/subsystem/movement/controller + ///An extra reference we pass around + ///It is on occasion useful to have a reference to some datum without storing it on the moving object + ///Mostly comes up in high performance senarios where we care about things being singletons + ///This feels horrible, but constantly making components seems worse + var/datum/extra_info + ///The thing we're moving about + var/atom/movable/moving + ///Defines how different move loops override each other. Higher numbers beat lower numbers + var/priority = MOVEMENT_DEFAULT_PRIORITY + ///Bitfield of different things that affect how a loop operates, and other mechanics around it as well. + var/flags + ///Time till we stop processing in deci-seconds, defaults to forever + var/lifetime = INFINITY + ///Delay between each move in deci-seconds + var/delay = 1 + ///The next time we should process + ///Used primarially as a hint to be reasoned about by our [controller], and as the id of our bucket + var/timer = 0 + ///The time we are CURRENTLY queued for processing + ///Do not modify this directly + var/queued_time = -1 + /// Status bitfield for what state the move loop is currently in + var/status = NONE + +/datum/move_loop/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) + src.owner = owner + src.controller = controller + src.extra_info = extra_info + if(extra_info) + RegisterSignal(extra_info, COMSIG_PARENT_QDELETING, PROC_REF(info_deleted)) + src.moving = moving + src.priority = priority + src.flags = flags + +/datum/move_loop/proc/setup(delay = 1, timeout = INFINITY) + if(!ismovable(moving) || !owner) + return FALSE + + src.delay = max(delay, world.tick_lag) //Please... + src.lifetime = timeout + return TRUE + +///check if this exact moveloop datum already exists (in terms of vars) so we can avoid creating a new one to overwrite the old duplicate +/datum/move_loop/proc/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay = 1, timeout = INFINITY) + SHOULD_CALL_PARENT(TRUE) + if(loop_type == type && priority == src.priority && flags == src.flags && delay == src.delay && timeout == lifetime) + return TRUE + return FALSE + +///Called when a loop is starting by a movement subsystem +/datum/move_loop/proc/loop_started() + SHOULD_CALL_PARENT(TRUE) + SEND_SIGNAL(src, COMSIG_MOVELOOP_START) + status |= MOVELOOP_STATUS_RUNNING + //If this is our first time starting to move with this loop + //And we're meant to start instantly + if(!timer && flags & MOVEMENT_LOOP_START_FAST) + timer = world.time + return + timer = world.time + delay + +///Called when a loop is stopped, doesn't stop the loop itself +/datum/move_loop/proc/loop_stopped() + SHOULD_CALL_PARENT(TRUE) + status &= ~MOVELOOP_STATUS_RUNNING + SEND_SIGNAL(src, COMSIG_MOVELOOP_STOP) + +/datum/move_loop/proc/info_deleted(datum/source) + SIGNAL_HANDLER + extra_info = null + +/datum/move_loop/Destroy() + if(owner) + owner.remove_loop(controller, src) + owner = null + moving = null + controller = null + extra_info = null + return ..() + +///Exists as a helper so outside code can modify delay in a sane way +/datum/move_loop/proc/set_delay(new_delay) + delay = max(new_delay, world.tick_lag) + +///Pauses the move loop for some passed in period +///This functionally means shifting its timer up, and clearing it from its current bucket +/datum/move_loop/proc/pause_for(time) + if(!controller || !(status & MOVELOOP_STATUS_RUNNING)) //No controller or not running? go away + return + //Dequeue us from our current bucket + controller.dequeue_loop(src) + //Offset our timer + timer = world.time + time + //Now requeue us with our new target start time + controller.queue_loop(src) + +/datum/move_loop/process() + if(isnull(controller)) + qdel(src) + return + + var/old_delay = delay //The signal can sometimes change delay + + if(SEND_SIGNAL(src, COMSIG_MOVELOOP_PREPROCESS_CHECK) & MOVELOOP_SKIP_STEP) //Chance for the object to react + return + + lifetime -= old_delay //This needs to be based on work over time, not just time passed + + if(lifetime < 0) //Otherwise lag would make things look really weird + qdel(src) + return + + var/visual_delay = controller.visual_delay + var/old_dir = moving.dir + var/old_loc = moving.loc + + owner?.processing_move_loop_flags = flags + var/result = move() //Result is an enum value. Enums defined in __DEFINES/movement.dm + if(moving) + var/direction = get_dir(old_loc, moving.loc) + SEND_SIGNAL(moving, COMSIG_MOVABLE_MOVED_FROM_LOOP, src, old_dir, direction) + owner?.processing_move_loop_flags = NONE + + SEND_SIGNAL(src, COMSIG_MOVELOOP_POSTPROCESS, result, delay * visual_delay) + + if(QDELETED(src) || result != MOVELOOP_SUCCESS) //Can happen + return + + if(flags & MOVEMENT_LOOP_IGNORE_GLIDE) + return + + 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() + return MOVELOOP_FAILURE + + +///Pause our loop untill restarted with resume_loop() +/datum/move_loop/proc/pause_loop() + if(!controller || !(status & MOVELOOP_STATUS_RUNNING) || (status & MOVELOOP_STATUS_PAUSED)) //we dead + return + + //Dequeue us from our current bucket + controller.dequeue_loop(src) + status |= MOVELOOP_STATUS_PAUSED + +///Resume our loop after being paused by pause_loop() +/datum/move_loop/proc/resume_loop() + if(!controller || (status & MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED) != (MOVELOOP_STATUS_RUNNING|MOVELOOP_STATUS_PAUSED)) + return + + timer = world.time + controller.queue_loop(src) + status &= ~MOVELOOP_STATUS_PAUSED + +///Removes the atom from some movement subsystem. Defaults to 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 + return our_info.remove_subsystem(subsystem) + +/** + * Replacement for walk() + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * direction - The direction we want to move in + * 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/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() +/datum/move_loop/move + var/direction + +/datum/move_loop/move/setup(delay, timeout, dir) + . = ..() + if(!.) + return + direction = dir + +/datum/move_loop/move/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, dir) + if(..() && direction == dir) + return TRUE + return FALSE + +/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)) + // 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 + + +/** + * Like move(), but we don't care about collision at all + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * direction - The direction we want to move in + * 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/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 + +/datum/move_loop/move/force/move() + var/atom/old_loc = moving.loc + moving.forceMove(get_step(moving, direction)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/datum/move_loop/has_target + ///The thing we're moving in relation to, either at or away from + var/atom/target + +/datum/move_loop/has_target/setup(delay, timeout, atom/chasing) + . = ..() + if(!.) + return + if(!isatom(chasing)) + qdel(src) + return FALSE + + target = chasing + + if(!isturf(target)) + RegisterSignal(target, COMSIG_PARENT_QDELETING, PROC_REF(handle_no_target)) //Don't do this for turfs, because we don't care + +/datum/move_loop/has_target/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing) + if(..() && chasing == target) + return TRUE + return FALSE + +/datum/move_loop/has_target/Destroy() + target = null + return ..() + +/datum/move_loop/has_target/proc/handle_no_target() + SIGNAL_HANDLER + qdel(src) + + +/** + * Used for force-move loops, similar to move_towards_legacy() but not quite the same + * + * 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 + * 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/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 +/datum/move_loop/has_target/force_move + +/datum/move_loop/has_target/force_move/move() + var/atom/old_loc = moving.loc + moving.forceMove(get_step(moving, get_dir(moving, target))) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Used for following jps defined paths. The proc signature here's a bit long, I'm sorry + * + * 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 + * +**/ +/datum/move_manager/proc/jps_move(moving, + chasing, + delay, + timeout, + repath_delay, + max_path_length, + minimum_distance, + list/access, + simulated_only, + turf/avoid, + skip_first, + subsystem, + diagonal_handling, + priority, + flags, + datum/extra_info, + initial_path) + return add_to_loop(moving, + subsystem, + /datum/move_loop/has_target/jps, + priority, + flags, + extra_info, + delay, + timeout, + chasing, + repath_delay, + max_path_length, + minimum_distance, + access, + simulated_only, + avoid, + skip_first, + diagonal_handling, + initial_path) + +/datum/move_loop/has_target/jps + ///How often we're allowed to recalculate our path + var/repath_delay + ///Max amount of steps to search + var/max_path_length + ///Minimum distance to the target before path returns + var/minimum_distance + ///A list representing what access we have and what doors we can open. + var/list/access + ///Whether we consider turfs without atmos simulation (AKA do we want to ignore space) + var/simulated_only + ///A perticular turf to avoid + var/turf/avoid + ///Should we skip the first step? This is the tile we're currently on, which breaks some things + var/skip_first + ///Whether we replace diagonal movements with cardinal movements or follow through with them + var/diagonal_handling + ///A list for the path we're currently following + var/list/movement_path + ///Cooldown for repathing, prevents spam + COOLDOWN_DECLARE(repath_cooldown) + ///Bool used to determine if we're already making a path in JPS. this prevents us from re-pathing while we're already busy. + var/is_pathing = FALSE + ///Callbacks to invoke once we make a path + var/list/datum/callback/on_finish_callbacks = list() + +/datum/move_loop/has_target/jps/New(datum/movement_packet/owner, datum/controller/subsystem/movement/controller, atom/moving, priority, flags, datum/extra_info) + . = ..() + on_finish_callbacks += CALLBACK(src, PROC_REF(on_finish_pathing)) + +/datum/move_loop/has_target/jps/setup(delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, diagonal_handling, list/initial_path) + . = ..() + if(!.) + return + src.repath_delay = repath_delay + src.max_path_length = max_path_length + src.minimum_distance = minimum_distance + src.access = access + src.simulated_only = simulated_only + src.avoid = avoid + src.skip_first = skip_first + src.diagonal_handling = diagonal_handling + movement_path = initial_path?.Copy() + +/datum/move_loop/has_target/jps/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, repath_delay, max_path_length, minimum_distance, list/access, simulated_only, turf/avoid, skip_first, initial_path) + if(..() && repath_delay == src.repath_delay && max_path_length == src.max_path_length && minimum_distance == src.minimum_distance && access ~= src.access && simulated_only == src.simulated_only && avoid == src.avoid) + return TRUE + return FALSE + +/datum/move_loop/has_target/jps/loop_started() + . = ..() + if(!movement_path) + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + +/datum/move_loop/has_target/jps/loop_stopped() + . = ..() + movement_path = null + +/datum/move_loop/has_target/jps/Destroy() + avoid = null + on_finish_callbacks = null + return ..() + +///Tries to calculate a new path for this moveloop. +/datum/move_loop/has_target/jps/proc/recalculate_path() + if(!COOLDOWN_FINISHED(src, repath_cooldown)) + return + COOLDOWN_START(src, repath_cooldown, repath_delay) + if(SSpathfinder.pathfind(moving, target, max_path_length, minimum_distance, access, simulated_only, avoid, skip_first, diagonal_handling, on_finish = on_finish_callbacks)) + is_pathing = TRUE + SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_REPATH) + +///Called when a path has finished being created +/datum/move_loop/has_target/jps/proc/on_finish_pathing(list/path) + movement_path = path + is_pathing = FALSE + SEND_SIGNAL(src, COMSIG_MOVELOOP_JPS_FINISHED_PATHING, path) + +/datum/move_loop/has_target/jps/move() + if(!length(movement_path)) + if(is_pathing) + return MOVELOOP_NOT_READY + else + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + return MOVELOOP_FAILURE + + 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)) + . = (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 + // to the side while moving but could maybe still follow their path without needing a whole new path + if(get_turf(moving) == next_step) + if(length(movement_path)) + movement_path.Cut(1,2) + else + INVOKE_ASYNC(src, PROC_REF(recalculate_path)) + return MOVELOOP_FAILURE + + +///Base class of move_to and move_away, deals with the distance and target aspect of things +/datum/move_loop/has_target/dist_bound + var/distance = 0 + +/datum/move_loop/has_target/dist_bound/setup(delay, timeout, atom/chasing, dist = 0) + . = ..() + if(!.) + return + distance = dist + +/datum/move_loop/has_target/dist_bound/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, dist = 0) + if(..() && distance == dist) + return TRUE + return FALSE + +///Returns FALSE if the movement should pause, TRUE otherwise +/datum/move_loop/has_target/dist_bound/proc/check_dist() + return FALSE + +/datum/move_loop/has_target/dist_bound/move() + if(!check_dist()) //If we're too close don't do the move + return MOVELOOP_FAILURE + return MOVELOOP_SUCCESS + + +/** + * Wrapper around walk_to() + * + * 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 + * +**/ +/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() +/datum/move_loop/has_target/dist_bound/move_to + +/datum/move_loop/has_target/dist_bound/move_to/check_dist() + return (get_dist(moving, target) > distance) //If you get too close, stop moving closer + +/datum/move_loop/has_target/dist_bound/move_to/move() + . = ..() + if(!.) + return + var/atom/old_loc = moving.loc + var/turf/next = get_step_to(moving, target) + moving.Move(next, get_dir(moving, next), FALSE, !(flags & MOVEMENT_LOOP_NO_DIR_UPDATE)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/** + * Wrapper around GLOB.move_manager.move_away() + * + * 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 + * +**/ +/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 GLOB.move_manager.move_away() +/datum/move_loop/has_target/dist_bound/move_away + +/datum/move_loop/has_target/dist_bound/move_away/check_dist() + return (get_dist(moving, target) < distance) //If you get too far out, stop moving away + +/datum/move_loop/has_target/dist_bound/move_away/move() + . = ..() + if(!.) + 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)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + + +/** + * Helper proc for the move_towards datum + * + * 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 + * +**/ +/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) + +/** + * Helper proc for homing onto something with move_towards + * + * 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 + * +**/ +/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 GLOB.move_manager.home_onto +/datum/move_loop/has_target/move_towards + ///The turf we want to move into, used for course correction + var/turf/moving_towards + ///Should we try and stay on the path, or is deviation alright + var/home = FALSE + ///When this gets larger then 1 we move a turf + var/x_ticker = 0 + var/y_ticker = 0 + ///The rate at which we move, between 0 and 1 + var/x_rate = 1 + var/y_rate = 1 + //We store the signs of x and y seperately, because byond will round negative numbers down + //So doing all our operations with absolute values then multiplying them is easier + var/x_sign = 0 + var/y_sign = 0 + +/datum/move_loop/has_target/move_towards/setup(delay, timeout, atom/chasing, home = FALSE) + . = ..() + if(!.) + return FALSE + src.home = home + + if(home) + if(ismovable(target)) + RegisterSignal(target, COMSIG_MOVABLE_MOVED, PROC_REF(update_slope)) //If it can move, update your slope when it does + RegisterSignal(moving, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move)) + update_slope() + +/datum/move_loop/has_target/move_towards/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, atom/chasing, home = FALSE) + if(..() && home == src.home) + return TRUE + return FALSE + +/datum/move_loop/has_target/move_towards/Destroy() + if(home) + if(ismovable(target)) + UnregisterSignal(target, COMSIG_MOVABLE_MOVED) + if(moving) + UnregisterSignal(moving, COMSIG_MOVABLE_MOVED) + return ..() + +/datum/move_loop/has_target/move_towards/move() + //Move our tickers forward a step, we're guaranteed at least one step forward because of how the code is written + if(x_rate) //Did you know that rounding by 0 throws a divide by 0 error? + x_ticker = FLOOR(x_ticker + x_rate, x_rate) + if(y_rate) + y_ticker = FLOOR(y_ticker + y_rate, y_rate) + + var/x = moving.x + var/y = moving.y + var/z = moving.z + + moving_towards = locate(x + round(x_ticker) * x_sign, y + round(y_ticker) * y_sign, z) + //The tickers serve as good methods of tracking remainder + if(x_ticker >= 1) + x_ticker = MODULUS(x_ticker, 1) //I swear to god if you somehow go up by one then one in a tick I'm gonna go mad + 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)) + + //YOU FOUND THEM! GOOD JOB + if(home && get_turf(moving) == get_turf(target)) + x_rate = 0 + y_rate = 0 + return + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/datum/move_loop/has_target/move_towards/proc/handle_move(source, atom/OldLoc, Dir, Forced = FALSE) + SIGNAL_HANDLER + if(moving.loc != moving_towards && home) //If we didn't go where we should have, update slope to account for the deviation + update_slope() + +/datum/move_loop/has_target/move_towards/handle_no_target() + if(home) + return ..() + target = null + +/** + * Recalculates the slope between our object and the target, sets our rates to it + * + * The math below is reminiscent of something like y = mx + b + * Except we don't need to care about axis, since we do all our movement in steps of 1 + * Because of that all that matters is we only move one tile at a time + * So we take the smaller delta, divide it by the larger one, and get smaller step per large step + * Then we set the large step to 1, and we're done. This way we're guaranteed to never move more then a tile at once + * And we can have nice lines +**/ +/datum/move_loop/has_target/move_towards/proc/update_slope() + SIGNAL_HANDLER + + //You'll notice this is rise over run, except we flip the formula upside down depending on the larger number + //This is so we never move more then one tile at once + var/delta_y = target.y - moving.y + var/delta_x = target.x - moving.x + //It's more convienent to store delta x and y as absolute values + //and modify them right at the end then it is to deal with rounding errors + x_sign = (delta_x > 0) ? 1 : -1 + y_sign = (delta_y > 0) ? 1 : -1 + delta_x = abs(delta_x) + delta_y = abs(delta_y) + + if(delta_x >= delta_y) + if(delta_x == 0) //Just go up/down + x_rate = 0 + y_rate = 1 + return + x_rate = 1 + y_rate = delta_y / delta_x //rise over run, you know the deal + else + if(delta_y == 0) //Just go right/left + x_rate = 1 + y_rate = 0 + return + x_rate = delta_x / delta_y //Keep the larger step size at 1 + y_rate = 1 + +/** + * Wrapper for GLOB.move_manager.home_onto, not reccomended, as its movement ends up being a bit stilted + * + * 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 + * 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/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 GLOB.move_manager.home_onto() +/datum/move_loop/has_target/move_towards_budget + +/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)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE + +/** + * Assigns a target to a move loop that immediately freezes for a set duration of time. + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * halted_turf - The turf we want to freeze on. This should typically be the loc of moving. + * 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. This should be considered extremely non-optional as it will completely stun out the movement loop forever if unset. + * 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/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. +/datum/move_loop/freeze + +/datum/move_loop/freeze/move() + return MOVELOOP_SUCCESS // it's successful because it's not moving. we autoclear outselves when `timeout` is reached + +/** + * Helper proc for the move_rand datum + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * directions - A list of acceptable directions to try and move in. Defaults to GLOB.alldirs + * 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/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) + +/** + * This isn't actually the same as walk_rand + * Because walk_rand is really more like walk_to_rand + * It appears to pick a spot outside of range, and move towards it, then pick a new spot, etc. + * I can't actually replicate this on our side, because of how bad our pathfinding is, and cause I'm not totally sure I know what it's doing. + * I can just implement a random-walk though +**/ +/datum/move_loop/move_rand + var/list/potential_directions + +/datum/move_loop/move_rand/setup(delay, timeout, list/directions) + . = ..() + if(!.) + return + potential_directions = directions + +/datum/move_loop/move_rand/compare_loops(datum/move_loop/loop_type, priority, flags, extra_info, delay, timeout, list/directions) + if(..() && (length(potential_directions | directions) == length(potential_directions))) //i guess this could be useful if actually it really has yet to move + return MOVELOOP_SUCCESS + return MOVELOOP_FAILURE + +/datum/move_loop/move_rand/move() + var/list/potential_dirs = potential_directions.Copy() + while(potential_dirs.len) + 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)) + if(old_loc != moving?.loc) //If it worked, we're done + return MOVELOOP_SUCCESS + potential_dirs -= testdir + return MOVELOOP_FAILURE + +/** + * Wrapper around walk_rand(), doesn't actually result in a random walk, it's more like moving to random places in viewish + * + * Returns TRUE if the loop sucessfully started, or FALSE if it failed + * + * Arguments: + * moving - The atom we want to move + * 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/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 +/datum/move_loop/move_to_rand + +/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)) + return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm new file mode 100644 index 000000000000..6d6945ba28d1 --- /dev/null +++ b/code/datums/components/drift.dm @@ -0,0 +1,194 @@ +///Component that handles drifting +///Manages a movement loop that actually does the legwork of moving someone +///Alongside dealing with the post movement input blocking required to make things look nice +/datum/component/drift + var/atom/inertia_last_loc + var/old_dir + var/datum/move_loop/move/drifting_loop + ///Should we ignore the next glide rate input we get? + ///This is to some extent a hack around the order of operations + ///Around COMSIG_MOVELOOP_POSTPROCESS. I'm sorry lad + var/ignore_next_glide = FALSE + ///Have we been delayed? IE: active, but not working right this second? + var/delayed = FALSE + var/block_inputs_until + +/// Accepts three args. The direction to drift in, if the drift is instant or not, and if it's not instant, the delay on the start +/datum/component/drift/Initialize(direction, instant = FALSE, start_delay = 0) + if(!ismovable(parent)) + return COMPONENT_INCOMPATIBLE + . = ..() + + var/flags = MOVEMENT_LOOP_OUTSIDE_CONTROL + if(instant) + flags |= MOVEMENT_LOOP_START_FAST + var/atom/movable/movable_parent = parent + drifting_loop = GLOB.move_manager.move(moving = parent, direction = direction, delay = movable_parent.inertia_move_delay, subsystem = SSspacedrift, priority = MOVEMENT_SPACE_PRIORITY, flags = flags) + + if(!drifting_loop) //Really want to qdel here but can't + return COMPONENT_INCOMPATIBLE + + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_START, PROC_REF(drifting_start)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_STOP, PROC_REF(drifting_stop)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_PREPROCESS_CHECK, PROC_REF(before_move)) + RegisterSignal(drifting_loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(after_move)) + RegisterSignal(drifting_loop, COMSIG_PARENT_QDELETING, PROC_REF(loop_death)) + RegisterSignal(movable_parent, COMSIG_MOVABLE_NEWTONIAN_MOVE, PROC_REF(newtonian_impulse)) + if(drifting_loop.status & MOVELOOP_STATUS_RUNNING) + drifting_start(drifting_loop) // There's a good chance it'll autostart, gotta catch that + + var/visual_delay = movable_parent.inertia_move_delay + + // Start delay is essentially a more granular version of instant + // Isn't used in the standard case, just for things that have odd wants + if(!instant && start_delay) + drifting_loop.pause_for(start_delay) + visual_delay = start_delay + + apply_initial_visuals(visual_delay) + +/datum/component/drift/Destroy() + inertia_last_loc = null + if(!QDELETED(drifting_loop)) + qdel(drifting_loop) + drifting_loop = null + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = FALSE + return ..() + +/datum/component/drift/proc/apply_initial_visuals(visual_delay) + // If something "somewhere" doesn't want us to apply our glidesize delays, don't + if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) & DRIFT_VISUAL_FAILED) + return + + // Ignore the next glide because it's literally just us + ignore_next_glide = TRUE + var/atom/movable/movable_parent = parent + movable_parent.set_glide_size(MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay)) + if(ismob(parent)) + var/mob/mob_parent = parent + //Ok this is slightly weird, but basically, we need to force the client to glide at our rate + //Make sure moving into a space move looks like a space move essentially + //There is an inbuilt assumption that gliding will be added as a part of a move call, but eh + //It's ok if it's not, it's just important if it is. + mob_parent.client?.visual_delay = MOVEMENT_ADJUSTED_GLIDE_SIZE(visual_delay, SSspacedrift.visual_delay) + +/datum/component/drift/proc/newtonian_impulse(datum/source, inertia_direction) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + inertia_last_loc = movable_parent.loc + if(drifting_loop) + drifting_loop.direction = inertia_direction + if(!inertia_direction) + qdel(src) + return COMPONENT_MOVABLE_NEWTONIAN_BLOCK + +/datum/component/drift/proc/drifting_start() + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + inertia_last_loc = movable_parent.loc + RegisterSignal(movable_parent, COMSIG_MOVABLE_MOVED, PROC_REF(handle_move)) + // We will use glide size to intuit how long to delay our loop's next move for + // This way you can't ride two movements at once while drifting, since that'd be dumb as fuck + RegisterSignal(movable_parent, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(handle_glidesize_update)) + // If you stop pulling something mid drift, I want it to retain that momentum + RegisterSignal(movable_parent, COMSIG_ATOM_NO_LONGER_PULLING, PROC_REF(stopped_pulling)) + +/datum/component/drift/proc/drifting_stop() + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = FALSE + ignore_next_glide = FALSE + UnregisterSignal(movable_parent, list(COMSIG_MOVABLE_MOVED, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, COMSIG_ATOM_NO_LONGER_PULLING)) + +/datum/component/drift/proc/before_move(datum/source) + SIGNAL_HANDLER + var/atom/movable/movable_parent = parent + movable_parent.inertia_moving = TRUE + old_dir = movable_parent.dir + delayed = FALSE + +/datum/component/drift/proc/after_move(datum/source, result, visual_delay) + SIGNAL_HANDLER + if(result == MOVELOOP_FAILURE) + qdel(src) + return + + var/atom/movable/movable_parent = parent + movable_parent.setDir(old_dir) + movable_parent.inertia_moving = FALSE + if(movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) + glide_to_halt(visual_delay) + return + + inertia_last_loc = movable_parent.loc + ignore_next_glide = TRUE + +/datum/component/drift/proc/loop_death(datum/source) + SIGNAL_HANDLER + drifting_loop = null + UnregisterSignal(parent, COMSIG_MOVABLE_NEWTONIAN_MOVE) // We won't block a component from replacing us anymore + +/datum/component/drift/proc/handle_move(datum/source, old_loc) + SIGNAL_HANDLER + // This can happen, because signals once sent cannot be stopped + if(QDELETED(src)) + return + var/atom/movable/movable_parent = parent + if(!isturf(movable_parent.loc)) + qdel(src) + return + if(movable_parent.inertia_moving) + return + if(!movable_parent.Process_Spacemove(drifting_loop.direction, continuous_move = TRUE)) + return + qdel(src) + +/// We're going to take the passed in glide size +/// and use it to manually delay our loop for that period +/// to allow the other movement to complete +/datum/component/drift/proc/handle_glidesize_update(datum/source, glide_size) + SIGNAL_HANDLER + // If we aren't drifting, or this is us, fuck off + var/atom/movable/movable_parent = parent + if(!drifting_loop || movable_parent.inertia_moving) + return + // If we are drifting, but this set came from the moveloop itself, drop the input + // I'm sorry man + if(ignore_next_glide) + ignore_next_glide = FALSE + return + var/glide_delay = round(world.icon_size / glide_size, 1) * world.tick_lag + drifting_loop.pause_for(glide_delay) + delayed = TRUE + +/// If we're pulling something and stop, we want it to continue at our rate and such +/datum/component/drift/proc/stopped_pulling(datum/source, atom/movable/was_pulling) + SIGNAL_HANDLER + // This does mean it falls very slightly behind, but otherwise they'll potentially run into us + var/next_move_in = drifting_loop.timer - world.time + world.tick_lag + was_pulling.newtonian_move(drifting_loop.direction, start_delay = next_move_in) + +/datum/component/drift/proc/glide_to_halt(glide_for) + if(!ismob(parent)) + qdel(src) + return + + var/mob/mob_parent = parent + var/client/our_client = mob_parent.client + // If we're not active, don't do the glide because it'll look dumb as fuck + if(!our_client || delayed) + qdel(src) + return + + block_inputs_until = world.time + glide_for + QDEL_IN(src, glide_for + 1) + qdel(drifting_loop) + RegisterSignal(parent, COMSIG_MOB_CLIENT_PRE_MOVE, PROC_REF(allow_final_movement)) + +/datum/component/drift/proc/allow_final_movement(datum/source) + // Some things want to allow movement out of spacedrift, we should let them + if(SEND_SIGNAL(parent, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) & DRIFT_ALLOW_INPUT) + return + if(world.time < block_inputs_until) + return COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE diff --git a/code/datums/components/jetpack_component.dm b/code/datums/components/jetpack_component.dm new file mode 100644 index 000000000000..c82ce4fda72b --- /dev/null +++ b/code/datums/components/jetpack_component.dm @@ -0,0 +1,152 @@ +// Welcome to the jetpack component +// Apply this to something when you want it to be "like a jetpack" +// So propulsion through space on move, that sort of thing +/datum/component/jetpack + dupe_mode = COMPONENT_DUPE_UNIQUE_PASSARGS + /// Checks to ensure if we can move & if we can activate + var/datum/callback/check_on_move + /// If we should stabilize ourselves when not drifting + var/stabilize = FALSE + /// The signal we listen for as an activation + var/activation_signal + /// The signal we listen for as a de-activation + var/deactivation_signal + /// The return flag our parent expects for a failed activation + var/return_flag + /// The effect system for the jet pack trail + var/datum/effect_system/trail_follow/trail + /// The typepath to instansiate our trail as, when we need it + var/effect_type + +/** + * Arguments: + * * stabilize - If we should drift when we finish moving, or sit stable in space] + * * activation_signal - Signal we activate on + * * deactivation_signal - Signal we deactivate on + * * return_flag - Flag to return if activation fails + * * check_on_move - Callback we call each time we attempt a move, we expect it to retun true if the move is ok, false otherwise. It expects an arg, TRUE if fuel should be consumed, FALSE othewise + * * effect_type - Type of trail_follow to spawn + */ +/datum/component/jetpack/Initialize(stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type) + . = ..() + if(!isatom(parent)) + return COMPONENT_INCOMPATIBLE + if(!activation_signal) // Can't activate? go away + return COMPONENT_INCOMPATIBLE + + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.stabilize = stabilize + src.check_on_move = check_on_move + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.return_flag = return_flag + src.effect_type = effect_type + +/datum/component/jetpack/InheritComponent(datum/component/component, original, stabilize, activation_signal, deactivation_signal, return_flag, datum/callback/check_on_move, datum/effect_system/trail_follow/effect_type) + UnregisterSignal(parent, src.activation_signal) + if(src.deactivation_signal) + UnregisterSignal(parent, src.deactivation_signal) + RegisterSignal(parent, activation_signal, PROC_REF(activate)) + if(deactivation_signal) + RegisterSignal(parent, deactivation_signal, PROC_REF(deactivate)) + + src.stabilize = stabilize + src.check_on_move = check_on_move + src.activation_signal = activation_signal + src.deactivation_signal = deactivation_signal + src.return_flag = return_flag + src.effect_type = effect_type + + if(trail && trail.effect_type != effect_type) + setup_trail(trail.holder) + +/datum/component/jetpack/Destroy(force) + if(trail) + QDEL_NULL(trail) + check_on_move = null + return ..() + +/datum/component/jetpack/proc/setup_trail(mob/user) + if(trail) + QDEL_NULL(trail) + + trail = new effect_type + trail.auto_process = FALSE + trail.set_up(user) + trail.start() + +/datum/component/jetpack/proc/activate(datum/source, mob/user) + SIGNAL_HANDLER + + if(!check_on_move.Invoke(TRUE)) + return return_flag + + RegisterSignal(user, COMSIG_MOVABLE_MOVED, PROC_REF(move_react)) + RegisterSignal(user, COMSIG_MOVABLE_PRE_MOVE, PROC_REF(pre_move_react)) + RegisterSignal(user, COMSIG_MOVABLE_SPACEMOVE, PROC_REF(spacemove_react)) + RegisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT, PROC_REF(block_starting_visuals)) + RegisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT, PROC_REF(ignore_ending_block)) + + setup_trail(user) + +/datum/component/jetpack/proc/deactivate(datum/source, mob/user) + SIGNAL_HANDLER + + UnregisterSignal(user, COMSIG_MOVABLE_MOVED) + UnregisterSignal(user, COMSIG_MOVABLE_PRE_MOVE) + UnregisterSignal(user, COMSIG_MOVABLE_SPACEMOVE) + UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_VISUAL_ATTEMPT) + UnregisterSignal(user, COMSIG_MOVABLE_DRIFT_BLOCK_INPUT) + + if(trail) + QDEL_NULL(trail) + +/datum/component/jetpack/proc/move_react(mob/user) + SIGNAL_HANDLER + if(!user || !user.client)//Don't allow jet self using + return + if(!isturf(user.loc))//You can't use jet in nowhere or from mecha/closet + return + if(user.mob_has_gravity() || user.buckled)//You don't want use jet in gravity or while buckled. + return + if(user.pulledby)//You can't use jet if someone pull you + return + if(user.throwing)//You can't use jet if you thrown + return + if(user.client.calculate_move_dir())//You use jet when press keys. yes. + thrust() + +/datum/component/jetpack/proc/pre_move_react(mob/user) + SIGNAL_HANDLER + if(!trail) + return FALSE + trail.oldposition = get_turf(user) + +/datum/component/jetpack/proc/spacemove_react(mob/user, movement_dir, continuous_move) + SIGNAL_HANDLER + if(!continuous_move && movement_dir) + return COMSIG_MOVABLE_STOP_SPACEMOVE + // Check if we have the fuel to stop this. Do NOT cosume any fuel, just check + // This is done because things other then us can use our fuel + if(stabilize && check_on_move.Invoke(FALSE)) + return COMSIG_MOVABLE_STOP_SPACEMOVE + +/// Returns true if the thrust went well, false otherwise +/datum/component/jetpack/proc/thrust() + if(!check_on_move.Invoke(TRUE)) + return FALSE + trail.generate_effect() + return TRUE + +/// Basically, tell the drift component not to do its starting visuals, because they look dumb for us +/datum/component/jetpack/proc/block_starting_visuals(datum/source) + SIGNAL_HANDLER + return DRIFT_VISUAL_FAILED + +/// If we're on, don't let the drift component block movements at the end since we can speed +/datum/component/jetpack/proc/ignore_ending_block(datum/source) + SIGNAL_HANDLER + return DRIFT_ALLOW_INPUT diff --git a/code/datums/components/riding/riding_vehicle.dm b/code/datums/components/riding/riding_vehicle.dm index 0f5825f1b398..440ba225b267 100644 --- a/code/datums/components/riding/riding_vehicle.dm +++ b/code/datums/components/riding/riding_vehicle.dm @@ -70,7 +70,7 @@ if(!turf_check(next, current)) to_chat(user, "[movable_parent] cannot go onto [next]!") return - if(!Process_Spacemove(direction) || !isturf(movable_parent.loc)) + if(GLOB.move_manager.processing_on(user, SSspacedrift) || !isturf(movable_parent.loc)) return step(movable_parent, direction) diff --git a/code/datums/move_manager.dm b/code/datums/move_manager.dm new file mode 100644 index 000000000000..23ddcbecdb70 --- /dev/null +++ b/code/datums/move_manager.dm @@ -0,0 +1,173 @@ +/** + * 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/move_manager/proc/add_to_loop] helper procs that use them + * +**/ +/datum/move_manager + +GLOBAL_DATUM_INIT(move_manager, /datum/move_manager, new) + +///Adds a movable thing to a movement subsystem. Returns TRUE if it all worked, FALSE if it failed somehow +/datum/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/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 diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm index 2c102af007a7..d9f72df023c8 100644 --- a/code/game/atoms_movable.dm +++ b/code/game/atoms_movable.dm @@ -22,15 +22,21 @@ var/atom/movable/pulling /// Face towards the atom while pulling it var/face_while_pulling = FALSE - /// Whether this atom should have its dir automatically changed when it moves. Setting this to FALSE allows for things such as directional windows to retain dir on moving without snowflake code all of the place. + /// Whether this atom should have its dir automatically changed when it + /// moves. Setting this to FALSE allows for things such as directional windows + /// to retain dir on moving without snowflake code all of the place. + /// PARA: Doesn't set this currently to maintain current behavior for pulling items around, + /// because modifying direction on pull is expected. var/set_dir_on_move = TRUE var/throwforce = 0 - var/inertia_dir = 0 - var/atom/inertia_last_loc - var/inertia_moving = 0 - var/inertia_next_move = 0 + ///Are we moving with inertia? Mostly used as an optimization + var/inertia_moving = FALSE + ///Delay in deciseconds between inertia based movement var/inertia_move_delay = 5 + ///The last time we pushed off something + ///This is a hack to get around dumb him him me scenarios + var/last_pushoff var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move var/list/client_mobs_in_contents @@ -69,6 +75,10 @@ /// Used for icon smoothing. Won't smooth if it ain't anchored and can be unanchored. Only set to true on windows var/can_be_unanchored = FALSE + ///attempt to resume grab after moving instead of before. + var/atom/movable/moving_from_pull + ///Holds information about any movement loops currently running/waiting to run on the movable. Lazy, will be null if nothing's going on + var/datum/movement_packet/move_packet /// How far (in pixels) should this atom scatter when created/dropped/etc. Does not apply to mapped-in items. var/scatter_distance = 0 @@ -116,6 +126,10 @@ loc = null if(pulledby) pulledby.stop_pulling() + if(move_packet) + if(!QDELETED(move_packet)) + qdel(move_packet) + move_packet = null if(opacity && istype(T)) var/old_has_opaque_atom = T.has_opaque_atom @@ -296,17 +310,22 @@ RESOLVE_ACTIVE_MOVEMENT -/atom/movable/Move(atom/newloc, direct = 0, movetime) +/atom/movable/Move(atom/newloc, direct = 0, glide_size_override = 0, update_dir = TRUE) + var/atom/movable/pullee = pulling + var/turf/current_turf = loc if(!loc || !newloc) return FALSE var/atom/oldloc = loc + if(SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, newloc) & COMPONENT_MOVABLE_BLOCK_PRE_MOVE) + return FALSE + + if(glide_size_override && glide_size != glide_size_override) + set_glide_size(glide_size_override) + if(loc != newloc) - if(movetime > 0) - glide_for(movetime) - if(IS_DIR_CARDINAL(direct)) - . = ..(newloc, direct) // don't pass up movetime - setDir(direct) + if(!IS_DIR_DIAGONAL(direct)) //Cardinal move + . = ..(newloc, direct) else //Diagonal move, split it into cardinal moves moving_diagonally = FIRST_DIAG_STEP var/first_step_dir = 0 @@ -328,31 +347,60 @@ moving_diagonally = SECOND_DIAG_STEP . = step(src, direct_NS) if(first_step_dir != 0) - if(!.) + if(!. && set_dir_on_move && update_dir) setDir(first_step_dir) + Moved(oldloc, first_step_dir) else if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay newtonian_move(direct) + if(client_mobs_in_contents) + update_parallax_contents() moving_diagonally = 0 return if(!loc || (loc == oldloc && oldloc != newloc)) last_move = 0 + return + if(. && pulling && pulling == pullee && pulling != moving_from_pull) //we were pulling a thing and didn't lose it during our move. + if(pulling.anchored) + stop_pulling() + else + //puller and pullee more than one tile away or in diagonal position and whatever the pullee is pulling isn't already moving from a pull as it'll most likely result in an infinite loop a la ouroborus. + if(!pulling.pulling?.moving_from_pull) + var/pull_dir = get_dir(pulling, src) + var/target_turf = current_turf + + if(target_turf != current_turf || (moving_diagonally != SECOND_DIAG_STEP && IS_DIR_DIAGONAL(pull_dir)) || get_dist(src, pulling) > 1) + pulling.move_from_pull(src, target_turf, glide_size) + + // PARA: There was code here for handling multi-z, which isn't relevant to us. + check_pulling() + + //glide_size strangely enough can change mid movement animation and update correctly while the animation is playing + //This means that if you don't override it late like this, it will just be set back by the movement update that's called when you move turfs. + if(glide_size_override) + set_glide_size(glide_size_override) + last_move = direct - move_speed = world.time - l_move_time - l_move_time = world.time - if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, movetime)) //movement failed due to buckled mob + if(. && has_buckled_mobs() && !handle_buckled_mob_movement(loc, direct, glide_size_override)) //movement failed due to buckled mob . = FALSE -// Called after a successful Move(). By this point, we've already moved -/atom/movable/proc/Moved(atom/old_loc, Dir, Forced = FALSE) - SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, Dir, Forced) - if(!inertia_moving) - inertia_next_move = world.time + inertia_move_delay - newtonian_move(Dir) +/** + * Called after a successful Move(). By this point, we've already moved. + * Arguments: + * * old_loc is the location prior to the move. Can be null to indicate nullspace. + * * movement_dir is the direction the movement took place. Can be NONE if it was some sort of teleport. + * * The forced flag indicates whether this was a forced move, which skips many checks of regular movement. + * * The old_locs is an optional argument, in case the moved movable was present in multiple locations before the movement. + * * momentum_change represents whether this movement is due to a "new" force if TRUE or an already "existing" force if FALSE + **/ +/atom/movable/proc/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) + SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, old_loc, movement_dir, forced, old_locs, momentum_change) + + if(!inertia_moving && momentum_change) + newtonian_move(movement_dir) if(length(client_mobs_in_contents)) update_parallax_contents() @@ -369,6 +417,12 @@ L.source_atom.update_light() return TRUE +/// Called when src is being moved to a target turf because another movable (puller) is moving around. +/atom/movable/proc/move_from_pull(atom/movable/puller, turf/target_turf, glide_size_override) + moving_from_pull = puller + Move(target_turf, get_dir(src, target_turf), glide_size_override) + moving_from_pull = null + // Make sure you know what you're doing if you call this // You probably want CanPass() /atom/movable/Cross(atom/movable/crossed_atom) @@ -401,7 +455,7 @@ */ /atom/movable/Uncross() SHOULD_NOT_OVERRIDE(TRUE) - CRASH("Uncross() should not be being called, please read the doc-comment for it for why.") + CRASH("Unexpected atom/movable/Uncross() call") /** * default byond proc that is normally called on everything inside the previous turf @@ -554,19 +608,26 @@ update_runechat_msg_location() -//Called whenever an object moves and by mobs when they attempt to move themselves through space -//And when an object or action applies a force on src, see newtonian_move() below -//return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting -//Mobs should return TRUE if they should be able to move of their own volition, see client/Move() in mob_movement.dm -//movement_dir == 0 when stopping or any dir when trying to move -/atom/movable/proc/Process_Spacemove(movement_dir = 0) +/** + * Called whenever an object moves and by mobs when they attempt to move themselves through space + * And when an object or action applies a force on src, see [newtonian_move][/atom/movable/proc/newtonian_move] + * + * Return FALSE to have src start/keep drifting in a no-grav area and TRUE to stop/not start drifting + * + * Mobs should return TRUE if they should be able to move of their own volition, see [/client/proc/Move] + * + * Arguments: + * * movement_dir - 0 when stopping or any dir when trying to move + * * continuous_move - If this check is coming from something in the context of already drifting + */ +/atom/movable/proc/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(has_gravity(src)) return TRUE - if(pulledby && !pulledby.pulling) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE) return TRUE - if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir) & COMSIG_MOVABLE_STOP_SPACEMOVE) + if(pulledby && pulledby.pulledby != src) return TRUE if(throwing) @@ -577,17 +638,15 @@ return FALSE -/atom/movable/proc/newtonian_move(direction) //Only moves the object if it's under no gravity - if(!loc || Process_Spacemove(0)) - inertia_dir = 0 +/atom/movable/proc/newtonian_move(direction, instant = FALSE, start_delay = 0) + if(!isturf(loc) || Process_Spacemove(direction, continuous_move = TRUE)) return FALSE - inertia_dir = direction - if(!direction) + if(SEND_SIGNAL(src, COMSIG_MOVABLE_NEWTONIAN_MOVE, direction, start_delay) & COMPONENT_MOVABLE_NEWTONIAN_BLOCK) return TRUE - inertia_last_loc = loc - SSspacedrift.processing[src] = src + AddComponent(/datum/component/drift, direction, instant, start_delay) + return TRUE //called when src is thrown into hit_atom @@ -684,6 +743,7 @@ /// This proc is recursive, and calls itself to constantly set the glide size of an atom/movable /atom/movable/proc/set_glide_size(target = 8) + SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target) glide_size = target for(var/mob/buckled_mob as anything in buckled_mobs) @@ -708,14 +768,12 @@ if(master) return master.attack_hand(a, b, c) -/atom/movable/proc/handle_buckled_mob_movement(newloc,direct,movetime) +/atom/movable/proc/handle_buckled_mob_movement(newloc, direct, glide_size_override) for(var/m in buckled_mobs) var/mob/living/buckled_mob = m - if(!buckled_mob.Move(newloc, direct, movetime)) + if(!buckled_mob.Move(newloc, direct, glide_size_override)) forceMove(buckled_mob.loc) last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move return FALSE return TRUE diff --git a/code/game/gamemodes/miniantags/guardian/guardian.dm b/code/game/gamemodes/miniantags/guardian/guardian.dm index 83e47bff1c16..e566a3eac3a8 100644 --- a/code/game/gamemodes/miniantags/guardian/guardian.dm +++ b/code/game/gamemodes/miniantags/guardian/guardian.dm @@ -185,7 +185,7 @@ ghostize() qdel(src) -/mob/living/simple_animal/hostile/guardian/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/guardian/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE //Works better in zero G, and not useless in space //Manifest, Recall, Communicate diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm index 118340d2df83..cecc1cf7a4fe 100644 --- a/code/game/machinery/hologram.dm +++ b/code/game/machinery/hologram.dm @@ -555,7 +555,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/ HC.Disconnect(HC.calling_holopad) return ..() -/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0) +/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /obj/effect/overlay/holo_pad_hologram/examine(mob/user) diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm index 6d55f9a41ef3..9b3ff37da45c 100644 --- a/code/game/mecha/equipment/weapons/weapons.dm +++ b/code/game/mecha/equipment/weapons/weapons.dm @@ -233,10 +233,10 @@ if(isobj(H.shoes) && !(H.shoes.flags & NODROP)) var/thingy = H.shoes H.drop_item_to_ground(thingy) - walk_away(thingy,chassis,15,2) + GLOB.move_manager.move_away(thingy, chassis, 15, 2) spawn(20) if(thingy) - walk(thingy,0) + GLOB.move_manager.stop_looping(thingy) for(var/obj/mecha/combat/reticence/R in oview(6, chassis)) R.occupant_message("\The [R] has protected you from [chassis]'s HONK at the cost of some power.") R.use_power(R.get_charge() / 4) diff --git a/code/game/mecha/mecha.dm b/code/game/mecha/mecha.dm index df4cad6e0014..4ffdcdd9df4a 100644 --- a/code/game/mecha/mecha.dm +++ b/code/game/mecha/mecha.dm @@ -286,7 +286,7 @@ //////// MARK: Movement procs ////////////////////////////////// -/obj/mecha/Process_Spacemove(movement_dir = 0) +/obj/mecha/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) . = ..() if(.) return TRUE diff --git a/code/game/objects/effects/decals/Cleanable/alien_blood.dm b/code/game/objects/effects/decals/Cleanable/alien_blood.dm index 2e02edee4a22..1f062d451125 100644 --- a/code/game/objects/effects/decals/Cleanable/alien_blood.dm +++ b/code/game/objects/effects/decals/Cleanable/alien_blood.dm @@ -2,7 +2,7 @@ name = "xeno blood" desc = "It's green and acidic. It looks like... blood?" icon = 'icons/effects/blood.dmi' - basecolor = "#05EE05" + basecolor = COLOR_BLOOD_XENO bloodiness = BLOOD_AMOUNT_PER_DECAL blood_state = BLOOD_STATE_XENO @@ -15,11 +15,7 @@ desc = "Gnarly..." icon_state = "xgib1" random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6") - basecolor = "#05EE05" - -/obj/effect/decal/cleanable/blood/gibs/xeno/update_icon() - color = "#FFFFFF" - . = ..(NONE) + basecolor = COLOR_BLOOD_XENO /obj/effect/decal/cleanable/blood/gibs/xeno/up random_icon_states = list("xgib1", "xgib2", "xgib3", "xgib4", "xgib5", "xgib6", "xgibup1", "xgibup1", "xgibup1") @@ -37,7 +33,7 @@ random_icon_states = list("xgibmid1", "xgibmid2", "xgibmid3") /obj/effect/decal/cleanable/blood/xtracks - basecolor = "#05EE05" + basecolor = COLOR_BLOOD_XENO /// this is the alien blood file, slimes are aliens. /obj/effect/decal/cleanable/blood/slime diff --git a/code/game/objects/effects/decals/Cleanable/humans.dm b/code/game/objects/effects/decals/Cleanable/humans.dm index f9d8e1f4acba..b071cea77247 100644 --- a/code/game/objects/effects/decals/Cleanable/humans.dm +++ b/code/game/objects/effects/decals/Cleanable/humans.dm @@ -14,6 +14,7 @@ random_icon_states = list("mfloor1", "mfloor2", "mfloor3", "mfloor4", "mfloor5", "mfloor6", "mfloor7") blood_DNA = list() var/base_icon = 'icons/effects/blood.dmi' + base_icon_state = "mfloor1" var/blood_state = BLOOD_STATE_HUMAN bloodiness = BLOOD_AMOUNT_PER_DECAL var/basecolor = "#A10808" // Color when wet. @@ -21,7 +22,8 @@ var/dry_timer = 0 var/off_floor = FALSE var/image/weightless_image - inertia_move_delay = 1 // so they dont collide with who emitted them + var/weightless_icon = 'icons/effects/blood_weightless.dmi' + inertia_move_delay = 5 // so they dont collide with who emitted them /obj/effect/decal/cleanable/blood/replace_decal(obj/effect/decal/cleanable/blood/C) if(C == src) @@ -33,18 +35,32 @@ C.bloodiness += bloodiness return ..() -/obj/effect/decal/cleanable/blood/Initialize(mapload) +/obj/effect/decal/cleanable/blood/Initialize(mapload, decal_color) . = ..() - weightless_image = new() + if(decal_color) + basecolor = decal_color + else + if(basecolor == "rainbow") + basecolor = "#[pick("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")]" + + color = basecolor + base_icon_state = icon_state + + var/turf/T = get_turf(src) + check_gravity(T) update_icon() - if(!gravity_check) + if(gravity_check) + if(!. && !QDELETED(src)) + dry_timer = addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1), TIMER_STOPPABLE) + else + if(prob(50)) + animate_float(src, -1, rand(30,120)) + else + animate_levitate(src, -1, rand(30,120)) //weightless blood cannot dry return - if(!. && !QDELETED(src)) - dry_timer = addtimer(CALLBACK(src, PROC_REF(dry)), DRYING_TIME * (amount+1), TIMER_STOPPABLE) - var/static/list/loc_connections = list( COMSIG_ATOM_ENTERED = PROC_REF(on_atom_entered), ) @@ -56,38 +72,32 @@ QDEL_NULL(weightless_image) return ..() -/obj/effect/decal/cleanable/blood/update_icon() - var/turf/T = get_turf(src) - check_gravity(T) +/obj/effect/decal/cleanable/blood/update_overlays() + . = ..() + + if(gravity_check) + return + + if(!weightless_image) + color = COLOR_WHITE + weightless_image = image(weightless_icon, base_icon_state) + weightless_image.icon += basecolor + . += weightless_image + +/obj/effect/decal/cleanable/blood/update_icon() if(should_be_off_floor()) off_floor = TRUE layer = ABOVE_MOB_LAYER plane = GAME_PLANE - if(basecolor == "rainbow") - basecolor = "#[pick("FF0000","FF7F00","FFFF00","00FF00","0000FF","4B0082","8F00FF")]" - - color = basecolor - - if(!gravity_check) - if(prob(50)) - animate_float(src, -1, rand(30,120)) - else - animate_levitate(src, -1, rand(30,120)) - - if(weightless_image && weightless_image.icon_state) - icon_state = weightless_image.icon_state - - overlays -= weightless_image - color = "#FFFFFF" - icon = 'icons/effects/blood_weightless.dmi' - weightless_image = image(icon, icon_state) - icon_state = "empty" - weightless_image.icon += basecolor - overlays += weightless_image + if(gravity_check) + icon = initial(icon) + icon_state = base_icon_state + color = basecolor else - overlays.Cut() + icon_state = null + ..() /obj/effect/decal/cleanable/blood/proc/should_be_off_floor() @@ -116,32 +126,17 @@ return if(loc != T) forceMove(T) //move to the turf to splatter on - animate(src) //stop floating gravity_check = ALWAYS_IN_GRAVITY - icon = initial(icon) - icon_state = weightless_image.icon_state layer = initial(layer) plane = initial(plane) + animate(src) update_icon() - -/obj/effect/decal/cleanable/blood/Process_Spacemove(movement_dir) +/obj/effect/decal/cleanable/blood/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(gravity_check) return TRUE - if(has_gravity(src)) - if(!gravity_check) - splat(get_step(src, movement_dir)) - return TRUE - - if(pulledby && !pulledby.pulling) - return TRUE - - if(throwing) - return TRUE - - return FALSE - + return ..() /obj/effect/decal/cleanable/blood/Bump(atom/A) if(gravity_check) @@ -162,8 +157,11 @@ return ..() /obj/effect/decal/cleanable/blood/proc/bloodyify_human(mob/living/carbon/human/H) - if(inertia_dir && H.inertia_dir == inertia_dir) //if they are moving the same direction we are, no collison - return + // Originally this code would check to see if both us and the human + // we collided with had inertia in the same direction, and avoided collision + // if so. This might be possible with movement loops but, realistically, + // if we've gotten here, the objects have collided no matter what direction + // they were going in. var/list/obj/item/things_to_potentially_bloody = list() var/count = amount + 1 @@ -317,17 +315,21 @@ mergeable_decal = TRUE /obj/effect/decal/cleanable/blood/gibs/proc/streak(list/directions) - set waitfor = 0 + var/delay = 2 + var/range = pick(1, 200; 2, 150; 3, 50; 4) var/direction = pick(directions) - for(var/i = 0, i < pick(1, 200; 2, 150; 3, 50; 4), i++) - sleep(3) - if(i > 0) - var/obj/effect/decal/cleanable/blood/b = new /obj/effect/decal/cleanable/blood/splatter(loc) - b.basecolor = src.basecolor - b.update_icon() - if(step_to(src, get_step(src, direction), 0)) - break + var/datum/move_loop/loop = GLOB.move_manager.move_to(src, get_step(src, direction), delay = delay, timeout = range * delay, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(spread_movement_effects)) + +/obj/effect/decal/cleanable/blood/gibs/proc/spread_movement_effects(datum/move_loop/has_target/source) + SIGNAL_HANDLER // COMSIG_MOVELOOP_POSTPROCESS + var/obj/effect/decal/cleanable/blood/target = source.target + var/obj/effect/decal/cleanable/blood/splatter/splatter = new(loc, istype(target) ? target.basecolor : basecolor) + + if(istype(target)) + splatter.basecolor = target.basecolor + splatter.update_icon() /obj/effect/decal/cleanable/blood/old/Initialize(mapload) . = ..() diff --git a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm index 8048ab7e23d1..f0ddd75c6be5 100644 --- a/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm +++ b/code/game/objects/effects/decals/Cleanable/misc_cleanables.dm @@ -159,9 +159,8 @@ /obj/effect/decal/cleanable/vomit/Initialize(mapload) . = ..() var/turf/T = get_turf(src) - gravity_check = has_gravity(src, T) - if(loc != T) - forceMove(T) + check_gravity(T) + if(!gravity_check) layer = MOB_LAYER plane = GAME_PLANE @@ -177,9 +176,17 @@ AddElement(/datum/element/connect_loc, loc_connections) /obj/effect/decal/cleanable/vomit/Bump(atom/A) - . = ..() - if(A.density) + if(gravity_check) + return ..() + + if(iswallturf(A) || istype(A, /obj/structure/window)) splat(A) + return + else if(A.density) + splat(get_turf(A)) + return + + return ..() /obj/effect/decal/cleanable/vomit/proc/on_atom_entered(datum/source, atom/movable/entered) if(!gravity_check) @@ -204,22 +211,11 @@ plane = initial(plane) animate(src) -/obj/effect/decal/cleanable/vomit/Process_Spacemove(movement_dir) +/obj/effect/decal/cleanable/vomit/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(gravity_check) - return 1 - - if(has_gravity(src)) - if(!gravity_check) - splat(get_step(src, movement_dir)) - return 1 + return TRUE - if(pulledby && !pulledby.pulling) - return 1 - - if(throwing) - return 1 - - return 0 + return ..() /obj/effect/decal/cleanable/vomit/green name = "green vomit" diff --git a/code/game/objects/effects/decals/misc_decals.dm b/code/game/objects/effects/decals/misc_decals.dm index f8b34c230237..d287bbd31433 100644 --- a/code/game/objects/effects/decals/misc_decals.dm +++ b/code/game/objects/effects/decals/misc_decals.dm @@ -12,6 +12,19 @@ /obj/effect/decal/chempuff/blob_act(obj/structure/blob/B) return +/obj/effect/decal/chempuff/proc/loop_ended(datum/source) + SIGNAL_HANDLER // COMSIG_PARENT_QDELETING + if(QDELETED(src)) + return + qdel(src) + +/obj/effect/decal/chempuff/proc/check_move(datum/move_loop/source, succeeded) + SIGNAL_HANDLER // COMSIG_MOVELOOP_POSTPROCESS + var/turf/our_turf = get_turf(src) + reagents.reaction(our_turf) + for(var/atom/T in our_turf) + reagents.reaction(T) + /obj/effect/decal/snow name = "snow" density = FALSE diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm index ab4a464d0016..fa5464921bf0 100644 --- a/code/game/objects/effects/effect_system/effects_other.dm +++ b/code/game/objects/effects/effect_system/effects_other.dm @@ -1,3 +1,59 @@ +/datum/effect_system/trail_follow + var/turf/oldposition + var/active = FALSE + var/allow_overlap = FALSE + var/auto_process = TRUE + var/qdel_in_time = 10 + var/fadetype = "ion_fade" + var/fade = TRUE + var/nograv_required = FALSE + +/datum/effect_system/trail_follow/set_up(atom/atom) + attach(atom) + oldposition = get_turf(atom) + +/datum/effect_system/trail_follow/Destroy() + oldposition = null + stop() + return ..() + +/datum/effect_system/trail_follow/proc/stop() + oldposition = null + STOP_PROCESSING(SSfastprocess, src) + active = FALSE + return TRUE + +/datum/effect_system/trail_follow/start() + oldposition = get_turf(holder) + if(!check_conditions()) + return FALSE + if(auto_process) + START_PROCESSING(SSfastprocess, src) + active = TRUE + return TRUE + +/datum/effect_system/trail_follow/process() + generate_effect() + +/datum/effect_system/trail_follow/generate_effect() + if(!check_conditions()) + return stop() + if(oldposition && !(oldposition == get_turf(holder))) + if(!has_gravity(oldposition) || !nograv_required) + var/obj/effect/E = new effect_type(oldposition) + set_dir(E) + if(fade) + flick(fadetype, E) + E.icon_state = "" + if(qdel_in_time) + QDEL_IN(E, qdel_in_time) + oldposition = get_turf(holder) + +/datum/effect_system/trail_follow/proc/check_conditions() + if(!get_turf(holder)) + return FALSE + return TRUE + /// Ion trails for jetpacks, ion thrusters and other space-flying things /obj/effect/particle_effect/ion_trails name = "ion trails" @@ -8,6 +64,17 @@ dir = targetdir QDEL_IN(src, 0.6 SECONDS) +/datum/effect_system/trail_follow/ion + effect_type = /obj/effect/particle_effect/ion_trails + nograv_required = TRUE + qdel_in_time = 20 + +/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/I) + I.setDir(holder.dir) + +/datum/effect_system/trail_follow/ion/grav_allowed + nograv_required = FALSE + //Reagent-based explosion effect /datum/effect_system/reagents_explosion var/amount // TNT equivalent diff --git a/code/game/objects/effects/meteors.dm b/code/game/objects/effects/meteors.dm index 711985bf9da2..de1020d61ce1 100644 --- a/code/game/objects/effects/meteors.dm +++ b/code/game/objects/effects/meteors.dm @@ -97,7 +97,7 @@ GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/meaty = 5, /obj/effect/me if(timerid) deltimer(timerid) GLOB.meteor_list -= src - walk(src, 0) //this cancels the walk_towards() proc + GLOB.move_manager.stop_looping(src) //this cancels the GLOB.move_manager.home_onto() proc return ..() /obj/effect/meteor/Initialize(mapload, target) @@ -109,6 +109,9 @@ GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/meaty = 5, /obj/effect/me timerid = QDEL_IN(src, lifetime) chase_target(target) +/obj/effect/meteor/Process_Spacemove(movement_dir, continuous_move) + return TRUE + /obj/effect/meteor/Bump(atom/A) if(A) ram_turf(get_turf(A)) @@ -158,7 +161,7 @@ GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/meaty = 5, /obj/effect/me /obj/effect/meteor/proc/chase_target(atom/chasing, delay = 1) set waitfor = FALSE if(chasing) - walk_towards(src, chasing, delay) + GLOB.move_manager.home_onto(src, chasing, delay) /obj/effect/meteor/proc/meteor_effect() if(heavy) diff --git a/code/game/objects/effects/spawners/random/bluespace_tap_spawners.dm b/code/game/objects/effects/spawners/random/bluespace_tap_spawners.dm index 77f011d04ab9..32beefb70549 100644 --- a/code/game/objects/effects/spawners/random/bluespace_tap_spawners.dm +++ b/code/game/objects/effects/spawners/random/bluespace_tap_spawners.dm @@ -140,7 +140,7 @@ /obj/effect/spawner/random/bluespace_tap/cultural_rare name = "rare cultural artifacts" loot = list( - /obj/vehicle/space/speedbike/red, + /obj/tgvehicle/speedbike/red, /obj/item/gun/projectile/automatic/l6_saw/toy, /obj/item/gun/projectile/automatic/sniper_rifle/toy, /obj/item/bedsheet/centcom, diff --git a/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm b/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm index 9d1175f13dcd..7f781448d467 100644 --- a/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm +++ b/code/game/objects/effects/spawners/random/traders/trader_department_spawners.dm @@ -181,8 +181,8 @@ /obj/vehicle/motorcycle, /obj/vehicle/snowmobile, /obj/vehicle/snowmobile/blue, - /obj/vehicle/space/speedbike/red, - /obj/vehicle/space/speedbike, + /obj/tgvehicle/speedbike/red, + /obj/tgvehicle/speedbike, ) /obj/effect/spawner/random/traders/vehicle/make_item(spawn_loc, type_path_to_make) diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm index ce1e42e9181f..2a4a30a6b6c1 100644 --- a/code/game/objects/items/weapons/grenades/clusterbuster.dm +++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm @@ -47,7 +47,7 @@ icon_state = "clusterbang_segment_active" payload = payload_type active = TRUE - walk_away(src, loc, rand(1,4)) + GLOB.move_manager.move_away(src, loc, rand(1,4)) spawn(rand(15,60)) prime() @@ -69,7 +69,7 @@ var/obj/item/grenade/P = new type(loc) if(istype(P, /obj/item/grenade)) P.active = TRUE - walk_away(P,loc,rand(1,4)) + GLOB.move_manager.move_away(P,loc,rand(1,4)) spawn(rand(15,60)) if(!QDELETED(P)) diff --git a/code/game/objects/items/weapons/grenades/grenade.dm b/code/game/objects/items/weapons/grenades/grenade.dm index 27f0f27aec24..137c51bb545f 100644 --- a/code/game/objects/items/weapons/grenades/grenade.dm +++ b/code/game/objects/items/weapons/grenades/grenade.dm @@ -107,12 +107,11 @@ /obj/item/grenade/attack_hand() ///We need to clear the walk_to on grabbing a moving grenade to have it not leap straight out of your hand - walk(src, null, null) + GLOB.move_manager.stop_looping(src) ..() /obj/item/grenade/Destroy() - ///We need to clear the walk_to on destroy to allow a grenade which uses walk_to or related to properly GC - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) return ..() /obj/item/grenade/cmag_act(mob/user) diff --git a/code/game/objects/items/weapons/storage/bags.dm b/code/game/objects/items/weapons/storage/bags.dm index 724e387b6d1c..a4a11ba6442b 100644 --- a/code/game/objects/items/weapons/storage/bags.dm +++ b/code/game/objects/items/weapons/storage/bags.dm @@ -348,8 +348,7 @@ // Make each item scatter a bit for(var/obj/item/I in oldContents) - I.forceMove(M) - INVOKE_ASYNC(src, PROC_REF(scatter_tray_items), I) + do_scatter(I) if(prob(50)) playsound(M, 'sound/items/trayhit1.ogg', 50, 1) @@ -359,13 +358,18 @@ if(ishuman(M) && prob(10)) M.KnockDown(4 SECONDS) -/obj/item/storage/bag/tray/proc/scatter_tray_items(obj/item/I) - if(!I) - return +/obj/item/storage/bag/tray/proc/do_scatter(obj/item/tray_item) + var/delay = rand(2, 4) + var/datum/move_loop/loop = GLOB.move_manager.move_rand(tray_item, GLOB.cardinal, delay, timeout = rand(1, 2) * delay, flags = MOVEMENT_LOOP_START_FAST) + //This does mean scattering is tied to the tray. Not sure how better to handle it + RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(change_speed)) - for(var/i in 1 to rand(1, 2)) - step(I, pick(NORTH,SOUTH,EAST,WEST)) - sleep(rand(2, 4)) +/obj/item/storage/bag/tray/proc/change_speed(datum/move_loop/source) + SIGNAL_HANDLER // COMSIG_MOVELOOP_POSTPROCESS + var/new_delay = rand(2, 4) + var/count = source.lifetime / source.delay + source.lifetime = count * new_delay + source.delay = new_delay /obj/item/storage/bag/tray/update_icon_state() return @@ -415,7 +419,7 @@ I.forceMove(dropspot) // If there is no table, dump the contents of the tray at our feet like we're doing the service equivilent of a micdrop. if(!found_table && isturf(dropspot)) - INVOKE_ASYNC(src, PROC_REF(scatter_tray_items), I) + INVOKE_ASYNC(src, PROC_REF(do_scatter), I) if(found_table) user.visible_message("[user] unloads [user.p_their()] serving tray.") diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm index 3d8052094ceb..7808989a80e2 100644 --- a/code/game/objects/items/weapons/tanks/jetpack.dm +++ b/code/game/objects/items/weapons/tanks/jetpack.dm @@ -10,8 +10,37 @@ actions_types = list(/datum/action/item_action/set_internals, /datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization) var/gas_type = "oxygen" var/on = FALSE - var/stabilizers = FALSE var/volume_rate = 500 //Needed for borg jetpack transfer + var/stabilize = FALSE + var/thrust_callback + +/obj/item/tank/jetpack/Initialize(mapload) + . = ..() + thrust_callback = CALLBACK(src, PROC_REF(allow_thrust), 0.01) + configure_jetpack(stabilize) + +/obj/item/tank/jetpack/Destroy() + thrust_callback = null + return ..() + +/** + * configures/re-configures the jetpack component + * + * Arguments + * stabilize - Should this jetpack be stabalized + */ +/obj/item/tank/jetpack/proc/configure_jetpack(stabilize) + src.stabilize = stabilize + + AddComponent( \ + /datum/component/jetpack, \ + src.stabilize, \ + COMSIG_JETPACK_ACTIVATED, \ + COMSIG_JETPACK_DEACTIVATED, \ + JETPACK_ACTIVATION_FAILED, \ + thrust_callback, \ + /datum/effect_system/trail_follow/ion \ + ) /obj/item/tank/jetpack/populate_gas() if(gas_type) @@ -37,8 +66,8 @@ /obj/item/tank/jetpack/proc/toggle_stabilization(mob/user) if(on) - stabilizers = !stabilizers - to_chat(user, "You turn [src]'s stabilization [stabilizers ? "on" : "off"].") + configure_jetpack(!stabilize) + to_chat(user, "You turn [src]'s stabilization [stabilize ? "on" : "off"].") /obj/item/tank/jetpack/proc/cycle(mob/user) if(user.incapacitated()) @@ -54,31 +83,40 @@ var/datum/action/A = X A.UpdateButtons() - /obj/item/tank/jetpack/proc/turn_on(mob/user) + if(SEND_SIGNAL(src, COMSIG_JETPACK_ACTIVATED, user) & JETPACK_ACTIVATION_FAILED) + return FALSE + on = TRUE icon_state = "[initial(icon_state)]-on" /obj/item/tank/jetpack/proc/turn_off(mob/user) + SEND_SIGNAL(src, COMSIG_JETPACK_DEACTIVATED, user) on = FALSE - stabilizers = FALSE icon_state = initial(icon_state) -/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user) - if(!on) - return 0 +/obj/item/tank/jetpack/dropped(mob/user, silent) + . = ..() + if(on) + turn_off(user) + +/obj/item/tank/jetpack/proc/allow_thrust(num) + if(!ismob(loc)) + return FALSE + var/mob/user = loc + if((num < 0.005 || air_contents.total_moles() < num)) turn_off(user) - return 0 + return FALSE var/datum/gas_mixture/removed = air_contents.remove(num) if(removed.total_moles() < 0.005) turn_off(user) - return 0 + return FALSE var/turf/T = get_turf(user) T.blind_release_air(removed) - return 1 + return TRUE /obj/item/tank/jetpack/improvised name = "improvised jetpack" diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm index 35f8be4f3017..e1ac0156b718 100644 --- a/code/game/objects/structures.dm +++ b/code/game/objects/structures.dm @@ -43,7 +43,7 @@ REMOVE_TRAIT(loc, TRAIT_TURF_COVERED, UNIQUE_TRAIT_SOURCE(src)) return ..() -/obj/structure/Move() +/obj/structure/Move(atom/newloc, direct = 0, glide_size_override = 0, update_dir = TRUE) var/atom/old = loc if(!..()) return FALSE diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm index 12403792f164..ee9d664cff92 100644 --- a/code/game/objects/structures/morgue.dm +++ b/code/game/objects/structures/morgue.dm @@ -311,7 +311,7 @@ if(pass_info.is_movable) . = . || pass_info.pass_flags & PASSTABLE -/obj/structure/m_tray/Process_Spacemove(movement_dir) +/obj/structure/m_tray/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /* @@ -582,7 +582,7 @@ GLOBAL_LIST_EMPTY(crematoriums) connected = null return ..() -/obj/structure/c_tray/Process_Spacemove(movement_dir) +/obj/structure/c_tray/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE // Crematorium switch diff --git a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm index f7fc087084b9..cf0d6be4cab6 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm @@ -32,7 +32,7 @@ W.setDir(dir) qdel(src) -/obj/structure/chair/Move(atom/newloc, direct) +/obj/structure/chair/Move(atom/newloc, direct = 0, glide_size_override = 0, update_dir = TRUE) . = ..() handle_rotation() diff --git a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm index 067158533c98..20e24c5bf83f 100644 --- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm +++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm @@ -73,8 +73,6 @@ if(!buckled_mob.Move(get_step(buckled_mob, direction), direction)) loc = buckled_mob.loc //we gotta go back last_move = buckled_mob.last_move - inertia_dir = last_move - buckled_mob.inertia_dir = last_move . = 0 else diff --git a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm index cebaa4e5dc02..db2fdf551452 100644 --- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm +++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm @@ -50,7 +50,7 @@ stop_following() return ..() -/obj/structure/transit_tube_pod/Process_Spacemove() +/obj/structure/transit_tube_pod/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(moving) //No drifting while moving in the tubes return TRUE else return ..() diff --git a/code/game/turfs/simulated.dm b/code/game/turfs/simulated.dm index 5db6e2d37384..74bce5b10b0d 100644 --- a/code/game/turfs/simulated.dm +++ b/code/game/turfs/simulated.dm @@ -138,7 +138,6 @@ switch(src.wet) if(TURF_WET_WATER) if(!(M.slip("the wet floor", WATER_WEAKEN_TIME, tilesSlipped = 0, walkSafely = 1))) - M.inertia_dir = 0 return if(TURF_WET_LUBE) //lube @@ -147,7 +146,6 @@ if(TURF_WET_ICE) // Ice if(M.slip("the icy floor", 8 SECONDS, tilesSlipped = 0, walkSafely = 0)) - M.inertia_dir = 0 if(prob(5)) var/obj/item/organ/external/affected = M.get_organ("head") if(affected) diff --git a/code/modules/antagonists/changeling/powers/summon_spiders.dm b/code/modules/antagonists/changeling/powers/summon_spiders.dm index e535b7319f1d..d693bfd37217 100644 --- a/code/modules/antagonists/changeling/powers/summon_spiders.dm +++ b/code/modules/antagonists/changeling/powers/summon_spiders.dm @@ -114,7 +114,7 @@ switch(current_order) if(IDLE_AGGRESSIVE) Find_Enemies(around) - walk(src, 0) + GLOB.move_manager.stop_looping(src) if(FOLLOW_AGGRESSIVE) Find_Enemies(around) for(var/mob/living/carbon/C in around) @@ -131,7 +131,7 @@ return TRUE Goto(C, 0.5 SECONDS, 1) if(IDLE_RETALIATE) - walk(src, 0) + GLOB.move_manager.stop_looping(src) for(var/mob/living/simple_animal/hostile/poison/giant_spider/hunter/infestation_spider/H in around) if(faction_check_mob(H) && !attack_same && !H.attack_same) diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm index 687e79881db1..c1ecfb423eed 100644 --- a/code/modules/assembly/infrared.dm +++ b/code/modules/assembly/infrared.dm @@ -234,7 +234,7 @@ /obj/effect/beam/i_beam/update_icon_state() transform = turn(matrix(), dir2angle(dir)) -/obj/effect/beam/i_beam/Process_Spacemove(movement_dir) +/obj/effect/beam/i_beam/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /obj/effect/beam/i_beam/process() diff --git a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm index cf4f2f059591..d57ee7175bd8 100644 --- a/code/modules/awaymissions/mission_code/ruins/deepstorage.dm +++ b/code/modules/awaymissions/mission_code/ruins/deepstorage.dm @@ -175,7 +175,7 @@ if(target) playsound(loc, 'sound/voice/zombie_scream.ogg', 70, TRUE) -/mob/living/simple_animal/hostile/spaceinfected/Move(atom/newloc) +/mob/living/simple_animal/hostile/spaceinfected/Move(atom/newloc, direct = 0, glide_size_override = 0, update_dir = TRUE) if(ischasm(newloc)) // as this place filled with chasms, they shouldn't randomly fall in while wandering around return FALSE return ..() diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm index 82d1a6fab601..d1d5da742dcf 100644 --- a/code/modules/client/client_defines.dm +++ b/code/modules/client/client_defines.dm @@ -18,6 +18,8 @@ ///////// var/datum/preferences/prefs = null var/skip_antag = FALSE //TRUE when a player declines to be included for the selection process of game mode antagonists. + ///The visual delay to use for the current client.Move(), mostly used for making a client based move look like it came from some other slower source + var/visual_delay = 0 var/move_delay = 1 var/moving = null var/area = null diff --git a/code/modules/clothing/suits/misc_suits.dm b/code/modules/clothing/suits/misc_suits.dm index c08071b58812..a2079e453fb5 100644 --- a/code/modules/clothing/suits/misc_suits.dm +++ b/code/modules/clothing/suits/misc_suits.dm @@ -308,28 +308,13 @@ user.faction -= "carp" to_chat(user, "A sudden calm fills the gnashing void of your mind- you're alone now.") -/mob/living/carbon/human/Process_Spacemove(movement_dir = 0) - if(..()) - return TRUE - +/mob/living/carbon/human/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(istype(wear_suit, /obj/item/clothing/suit/hooded/carp_costume/dragon)) return TRUE - //Do we have a working jetpack? - var/obj/item/tank/jetpack/thrust - if(istype(back, /obj/item/tank/jetpack)) - thrust = back - else if(istype(wear_suit, /obj/item/clothing/suit/space/hardsuit)) - var/obj/item/clothing/suit/space/hardsuit/C = wear_suit - thrust = C.jetpack - else if(ismodcontrol(back)) - var/obj/item/mod/control/C = back - thrust = locate(/obj/item/mod/module/jetpack) in C - if(thrust) - if((movement_dir || thrust.stabilizers) && thrust.allow_thrust(0.01, src)) - return TRUE if(dna.species.spec_Process_Spacemove(src)) return TRUE - return FALSE + + return ..() /obj/item/clothing/head/hooded/carp_hood/dragon name = "space carp hood" diff --git a/code/modules/events/blob/blob_mobs.dm b/code/modules/events/blob/blob_mobs.dm index 14c3cc9db61e..dac32472ac74 100644 --- a/code/modules/events/blob/blob_mobs.dm +++ b/code/modules/events/blob/blob_mobs.dm @@ -44,7 +44,7 @@ H.color = COLOR_BLACK adjustHealth(-maxHealth * 0.0125) -/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/blob/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) // Use any nearby blob structures to allow space moves. for(var/obj/structure/blob/B in range(1, src)) return TRUE diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm index b0c11ae55b84..58c3f61be91e 100644 --- a/code/modules/events/dust.dm +++ b/code/modules/events/dust.dm @@ -42,7 +42,7 @@ var/turf/start = pick_edge_loc(startside, level_name_to_num(MAIN_STATION)) forceMove(start) goal = pick_edge_loc(REVERSE_DIR(startside), level_name_to_num(MAIN_STATION)) - walk_towards(src, goal, 1) + GLOB.move_manager.home_onto(src, goal, 1) /obj/effect/space_dust/Bump(atom/A) if(QDELETED(src)) @@ -64,7 +64,7 @@ life-- if(life <= 0) - walk(src, 0) + GLOB.move_manager.stop_looping(src) on_shatter(where) qdel(src) diff --git a/code/modules/events/meaty_ores.dm b/code/modules/events/meaty_ores.dm index c4431b23da03..16624b38ca9d 100644 --- a/code/modules/events/meaty_ores.dm +++ b/code/modules/events/meaty_ores.dm @@ -37,3 +37,6 @@ explosion(where, 0, pick(0,1), pick(2,3), 0) else new /mob/living/simple_animal/cow(where) + +/obj/effect/space_dust/meaty/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) + return TRUE diff --git a/code/modules/input/keybindings_procs.dm b/code/modules/input/keybindings_procs.dm index b5e95307f01d..f370a883e7fa 100644 --- a/code/modules/input/keybindings_procs.dm +++ b/code/modules/input/keybindings_procs.dm @@ -21,3 +21,9 @@ active_keybindings[linked_bind.binded_to] += list(linked_bind) return active_keybindings + +/client/proc/calculate_move_dir() + . = NONE + for(var/held_key in input_data.keys_held) + if(held_key in movement_kb_dirs) + . |= movement_kb_dirs[held_key] diff --git a/code/modules/mob/dead/observer/observer_base.dm b/code/modules/mob/dead/observer/observer_base.dm index d7d10ee753df..20eb899bb9b2 100644 --- a/code/modules/mob/dead/observer/observer_base.dm +++ b/code/modules/mob/dead/observer/observer_base.dm @@ -290,7 +290,7 @@ This is the proc mobs get to turn into a ghost. Forked from ghostize due to comp return // Ghosts have no momentum, being massless ectoplasm -/mob/dead/observer/Process_Spacemove(movement_dir) +/mob/dead/observer/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /mob/dead/observer/Move(NewLoc, direct) diff --git a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm index 6a43c6e32487..1177124863f9 100644 --- a/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm +++ b/code/modules/mob/living/carbon/alien/humanoid/humanoid.dm @@ -35,7 +35,7 @@ GLOBAL_LIST_INIT(strippable_alien_humanoid_items, create_strippable_list(list( AddComponent(/datum/component/footstep, FOOTSTEP_MOB_CLAW, 0.5, -11) AddElement(/datum/element/strippable, GLOB.strippable_alien_humanoid_items) -/mob/living/carbon/alien/humanoid/Process_Spacemove(check_drift = 0) +/mob/living/carbon/alien/humanoid/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(..()) return TRUE return FALSE diff --git a/code/modules/mob/living/carbon/human/human_defense.dm b/code/modules/mob/living/carbon/human/human_defense.dm index f50496e4ff57..cf4816db066f 100644 --- a/code/modules/mob/living/carbon/human/human_defense.dm +++ b/code/modules/mob/living/carbon/human/human_defense.dm @@ -532,7 +532,7 @@ emp_act bloody = TRUE var/turf/location = loc if(issimulatedturf(location)) - add_splatter_floor(location, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) + add_splatter_floor(location, emittor_intertia = last_movement_dir) if(ishuman(user)) var/mob/living/carbon/human/H = user if(get_dist(H, src) <= 1) //people with TK won't get smeared with blood diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm index eff680b8862c..61aa7959a246 100644 --- a/code/modules/mob/living/living.dm +++ b/code/modules/mob/living/living.dm @@ -213,8 +213,8 @@ var/current_dir if(isliving(AM)) current_dir = AM.dir - if(step(AM, t)) - step(src, t) + if(AM.Move(get_step(AM.loc, t), t, glide_size)) + Move(get_step(loc, t), t) if(current_dir) AM.setDir(current_dir) now_pushing = FALSE @@ -598,7 +598,7 @@ return return ..() -/mob/living/Move(atom/newloc, direct, movetime) +/mob/living/Move(atom/newloc, direct = 0, glide_size_override = 0, update_dir = TRUE) if(buckled && buckled.loc != newloc) //not updating position if(!buckled.anchored) return buckled.Move(newloc, direct) @@ -618,12 +618,12 @@ . = ..() if(.) step_count++ - pull_pulled(old_loc, pullee, movetime) + pull_pulled(old_loc, pullee, glide_size_override) if(s_active && !(s_active in contents) && get_turf(s_active) != get_turf(src)) //check !( s_active in contents) first so we hopefully don't have to call get_turf() so much. s_active.close(src) -/mob/living/proc/pull_pulled(turf/dest, atom/movable/pullee, movetime) +/mob/living/proc/pull_pulled(turf/dest, atom/movable/pullee, glide_size_override) if(pulling && pulling == pullee) // we were pulling a thing and didn't lose it during our move. if(pulling.anchored) stop_pulling() @@ -635,7 +635,7 @@ var/mob/living/M = pulling if(IS_HORIZONTAL(M) && !M.buckled && (prob(M.getBruteLoss() * 200 / M.maxHealth))) // So once you reach 50 brute damage you hit 100% chance to leave a blood trail for every tile you're pulled M.makeTrail(dest) - pulling.Move(dest, get_dir(pulling, dest), movetime) // the pullee tries to reach our previous position + pulling.Move(dest, get_dir(pulling, dest), glide_size_override) // the pullee tries to reach our previous position if(pulling && get_dist(src, pulling) > 1) // the pullee couldn't keep up stop_pulling() @@ -1173,10 +1173,10 @@ return return ..() -/mob/living/Moved(OldLoc, Dir, Forced = FALSE) +/mob/living/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() for(var/obj/O in src) - O.on_mob_move(Dir, src) + O.on_mob_move(movement_dir, src) /// Can a mob interact with the apc remotely like a pulse demon, cyborg, or AI? /mob/living/proc/can_remote_apc_interface(obj/machinery/power/apc/ourapc) diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm index 4f5a17da5088..96486d798949 100644 --- a/code/modules/mob/living/silicon/robot/robot_movement.dm +++ b/code/modules/mob/living/silicon/robot/robot_movement.dm @@ -1,4 +1,4 @@ -/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0) +/mob/living/silicon/robot/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(ionpulse()) return TRUE if(..()) diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm index f110db49ec45..2285c0fecc21 100644 --- a/code/modules/mob/living/simple_animal/bot/bot.dm +++ b/code/modules/mob/living/simple_animal/bot/bot.dm @@ -163,13 +163,13 @@ lost_target = FALSE last_target_location = get_turf(target) var/dist = get_dist(src, target) - walk_to(src, target, 1, 4) + GLOB.move_manager.move_to(src, target, 1, 4) if(get_dist(src, target) >= dist) frustration++ return if(!lost_target) - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) lost_target = TRUE frustration = 0 diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm index 7c3d29e71bb9..e3a63cf9dd92 100644 --- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm +++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm @@ -97,7 +97,7 @@ oldtarget_name = null anchored = FALSE currently_cuffing = FALSE - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) last_found = world.time set_weapon() @@ -216,7 +216,7 @@ if(lasertag_check) icon_state = "[lasercolor]ed2090" disabled = TRUE - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) target = null addtimer(CALLBACK(src, PROC_REF(unset_disabled)), 10 SECONDS) return TRUE @@ -260,7 +260,7 @@ switch(mode) if(BOT_IDLE) // idle - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) if(!lasercolor) //lasertag bots don't want to arrest anyone if(find_new_target()) @@ -271,7 +271,7 @@ if(BOT_HUNT) // hunting for perp // if can't reach perp for long enough, go idle if(frustration >= 8) - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -395,7 +395,7 @@ return 0 /mob/living/simple_animal/bot/ed209/explode() - walk_to(src,0) + GLOB.move_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm index 7f2ce26fd65d..380840b7e1ce 100644 --- a/code/modules/mob/living/simple_animal/bot/griefsky.dm +++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm @@ -110,7 +110,7 @@ switch(mode) if(BOT_IDLE) // idle icon_state = "griefsky1" - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) if(find_new_target()) return // see if any criminals are in range @@ -120,7 +120,7 @@ icon_state = spin_icon playsound(loc,'sound/effects/spinsabre.ogg',50, TRUE,-1) if(frustration >= frustration_number) // general beepsky doesn't give up so easily, jedi scum - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -182,7 +182,7 @@ return FALSE /mob/living/simple_animal/bot/secbot/griefsky/explode() - walk_to(src,0) + GLOB.move_manager.stop_looping(src) visible_message("[src] lets out a huge cough as it blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/assembly/prox_sensor(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/honkbot.dm b/code/modules/mob/living/simple_animal/bot/honkbot.dm index 9c243922bed3..c3476bc8ff6b 100644 --- a/code/modules/mob/living/simple_animal/bot/honkbot.dm +++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm @@ -61,7 +61,7 @@ target = null oldtarget_name = null anchored = FALSE - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) last_found = world.time spam_flag = FALSE @@ -266,7 +266,7 @@ return switch(mode) if(BOT_IDLE) // idle - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) if(find_new_target()) return if(!mode && auto_patrol) @@ -274,7 +274,7 @@ if(BOT_HUNT) // if can't reach perp for long enough, go idle if(frustration >= 5) //gives up easier than beepsky - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) playsound(loc, 'sound/misc/sadtrombone.ogg', 25, TRUE, -1) back_to_idle() return @@ -358,7 +358,7 @@ return FALSE /mob/living/simple_animal/bot/honkbot/explode() //doesn't drop cardboard nor its assembly, since its a very frail material. - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) new /obj/item/bikehorn(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm index c8909ba151ef..87b2aed467e7 100644 --- a/code/modules/mob/living/simple_animal/bot/secbot.dm +++ b/code/modules/mob/living/simple_animal/bot/secbot.dm @@ -120,7 +120,7 @@ target = null oldtarget_name = null anchored = FALSE - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) last_found = world.time @@ -315,7 +315,7 @@ switch(mode) if(BOT_IDLE) // idle - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) set_path(null) if(find_new_target()) // see if any criminals are in range return @@ -326,7 +326,7 @@ // if can't reach perp for long enough, go idle if(frustration >= 8) playsound(loc, 'sound/machines/buzz-two.ogg', 25, FALSE) - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -441,7 +441,7 @@ /mob/living/simple_animal/bot/secbot/explode() - walk_to(src,0) + GLOB.move_manager.stop_looping(src) visible_message("[src] blows apart!") var/turf/Tsec = get_turf(src) var/obj/item/secbot_assembly/Sa = new /obj/item/secbot_assembly(Tsec) diff --git a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm index 291690dda330..ca2bdaa20458 100644 --- a/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm +++ b/code/modules/mob/living/simple_animal/bot/syndicate_bots.dm @@ -89,7 +89,7 @@ saved_turf = current_turf switch(mode) if(BOT_IDLE) - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) if(find_new_target()) return @@ -98,7 +98,7 @@ if(BOT_HUNT) if(frustration >= 8) - walk_to(src,0) + GLOB.move_manager.stop_looping(src) set_path(null) back_to_idle() return @@ -165,7 +165,7 @@ if(!QDELETED(src)) if(depotarea) depotarea.list_remove(src, depotarea.guard_list) - walk_to(src,0) + GLOB.move_manager.stop_looping(src) visible_message("[src] blows apart!") do_sparks(3, 1, src) new /obj/effect/decal/cleanable/blood/oil(loc) @@ -195,7 +195,7 @@ /mob/living/simple_animal/bot/ed209/syndicate/speak() return -/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /mob/living/simple_animal/bot/ed209/syndicate/start_patrol() diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm index dbe50369e6f4..32838493cf25 100644 --- a/code/modules/mob/living/simple_animal/constructs.dm +++ b/code/modules/mob/living/simple_animal/constructs.dm @@ -318,7 +318,7 @@ Bring those who still cling to this world of illusion back to the master so they may know Truth." -/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE diff --git a/code/modules/mob/living/simple_animal/friendly/cat.dm b/code/modules/mob/living/simple_animal/friendly/cat.dm index 430ce7d223d3..b4ce532e5bd6 100644 --- a/code/modules/mob/living/simple_animal/friendly/cat.dm +++ b/code/modules/mob/living/simple_animal/friendly/cat.dm @@ -157,7 +157,7 @@ turns_since_scan++ if(turns_since_scan > 5) - walk(src, 0) + GLOB.move_manager.stop_looping(src) turns_since_scan = 0 if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc))) movement_target = null @@ -172,7 +172,7 @@ break if(movement_target) stop_automated_movement = TRUE - walk(src, movement_target, 0, 3) + GLOB.move_manager.move_to(src, movement_target, 0, 3) /mob/living/simple_animal/pet/cat/proc_cat name = "Proc" diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm index 6726f1193684..3abfbf56dc3f 100644 --- a/code/modules/mob/living/simple_animal/friendly/dog.dm +++ b/code/modules/mob/living/simple_animal/friendly/dog.dm @@ -534,7 +534,7 @@ minbodytemp = TCMB maxbodytemp = T0C + 40 -/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 //Void puppies can navigate space. //LISA! SQUEEEEEEEEE~ diff --git a/code/modules/mob/living/simple_animal/hostile/bat.dm b/code/modules/mob/living/simple_animal/hostile/bat.dm index b51af42343dd..ce9e955292ca 100644 --- a/code/modules/mob/living/simple_animal/hostile/bat.dm +++ b/code/modules/mob/living/simple_animal/hostile/bat.dm @@ -41,7 +41,7 @@ if(istype(L)) faction += "\ref[L]" -/mob/living/simple_animal/hostile/scarybat/Process_Spacemove(check_drift = 0) +/mob/living/simple_animal/hostile/scarybat/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return ..() //No drifting in space for space carp! //original comments do not steal /mob/living/simple_animal/hostile/scarybat/AttackingTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm index f87d7b54b5c2..82ddad0a60f0 100644 --- a/code/modules/mob/living/simple_animal/hostile/bear.dm +++ b/code/modules/mob/living/simple_animal/hostile/bear.dm @@ -36,7 +36,7 @@ gold_core_spawnable = HOSTILE_SPAWN footstep_type = FOOTSTEP_MOB_CLAW -/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE //No drifting in space for space bears! /mob/living/simple_animal/hostile/bear/black diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm index db719c53f01b..a9f90f65648a 100644 --- a/code/modules/mob/living/simple_animal/hostile/bees.dm +++ b/code/modules/mob/living/simple_animal/hostile/bees.dm @@ -56,7 +56,7 @@ var/static/beehometypecache = typecacheof(/obj/structure/beebox) var/static/hydroponicstypecache = typecacheof(/obj/machinery/hydroponics) -/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/poison/bees/Initialize(mapload) diff --git a/code/modules/mob/living/simple_animal/hostile/carp.dm b/code/modules/mob/living/simple_animal/hostile/carp.dm index a4ddaf327b03..849db0310eaa 100644 --- a/code/modules/mob/living/simple_animal/hostile/carp.dm +++ b/code/modules/mob/living/simple_animal/hostile/carp.dm @@ -92,7 +92,7 @@ base_dead_overlay.appearance_flags = RESET_COLOR add_overlay(base_dead_overlay) -/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE //No drifting in space for space carp! //original comments do not steal /mob/living/simple_animal/hostile/carp/AttackingTarget() diff --git a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm index f791a681b720..8366cd050716 100644 --- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm +++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm @@ -34,7 +34,7 @@ -/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 //copypasta from carp code /mob/living/simple_animal/hostile/deathsquid/ex_act(severity) diff --git a/code/modules/mob/living/simple_animal/hostile/drakehound.dm b/code/modules/mob/living/simple_animal/hostile/drakehound.dm index 61c8a2778c59..8d948385462e 100644 --- a/code/modules/mob/living/simple_animal/hostile/drakehound.dm +++ b/code/modules/mob/living/simple_animal/hostile/drakehound.dm @@ -45,7 +45,7 @@ /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic) -/mob/living/simple_animal/hostile/drakehound_breacher/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/drakehound_breacher/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/drakehound_breacher/ListTargetsLazy() diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm index d34557ef7199..22f4b014e19e 100644 --- a/code/modules/mob/living/simple_animal/hostile/faithless.dm +++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm @@ -30,5 +30,5 @@ gold_core_spawnable = HOSTILE_SPAWN footstep_type = FOOTSTEP_MOB_SHOE -/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm index ef5abf4224e9..d002d09a0933 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm @@ -422,7 +422,7 @@ Difficulty: Medium if(!swooping) ..() -/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /obj/effect/temp_visual/lava_warning diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm index 94e47e3e4392..809dc5eddc7e 100644 --- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm +++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm @@ -188,7 +188,7 @@ Difficulty: Medium var/armor = M.run_armor_check(limb_to_hit, LASER) M.apply_damage(70 - ((health / maxHealth) * 20), BURN, limb_to_hit, armor) -/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE) diff --git a/code/modules/mob/living/simple_animal/hostile/pirate.dm b/code/modules/mob/living/simple_animal/hostile/pirate.dm index 3e61c8e6a03e..8ac5a8f6b401 100644 --- a/code/modules/mob/living/simple_animal/hostile/pirate.dm +++ b/code/modules/mob/living/simple_animal/hostile/pirate.dm @@ -49,7 +49,7 @@ /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic) -/mob/living/simple_animal/hostile/pirate/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/pirate/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/pirate/ListTargetsLazy() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm index bc0df94d6242..aeb57abcae34 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/combat_drone.dm @@ -43,7 +43,7 @@ if(!has_gravity(T)) new /obj/effect/particle_effect/ion_trails(T, _dir) -/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(check_drift = 0) +/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /mob/living/simple_animal/hostile/malf_drone/ListTargets() diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm index c9696fd08792..edda4355d666 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/fish.dm @@ -85,7 +85,7 @@ icon_living = "koi[koinum]" icon_dead = "koi[koinum]-dead" -/mob/living/simple_animal/hostile/retaliate/carp/koi/Process_Spacemove(movement_dir) +/mob/living/simple_animal/hostile/retaliate/carp/koi/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/retaliate/carp/koi/honk diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm index da8df5e98d74..f67d27cdd211 100644 --- a/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm +++ b/code/modules/mob/living/simple_animal/hostile/retaliate/undead.dm @@ -37,7 +37,7 @@ initial_traits = list(TRAIT_FLYING) -/mob/living/simple_animal/hostile/retaliate/ghost/Process_Spacemove(check_drift = 0) +/mob/living/simple_animal/hostile/retaliate/ghost/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 /mob/living/simple_animal/hostile/retaliate/ghost/Life(seconds, times_fired) @@ -92,7 +92,7 @@ deathmessage = null gold_core_spawnable = NO_SPAWN -/mob/living/simple_animal/hostile/retaliate/skeleton/warden/Process_Spacemove(movement_dir) +/mob/living/simple_animal/hostile/retaliate/skeleton/warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/skeleton/angered_warden @@ -113,7 +113,7 @@ loot = list(/obj/effect/decal/remains/human, /obj/item/clothing/head/warden, /obj/item/card/sec_shuttle_ruin) gold_core_spawnable = NO_SPAWN -/mob/living/simple_animal/hostile/skeleton/angered_warden/Process_Spacemove(movement_dir) +/mob/living/simple_animal/hostile/skeleton/angered_warden/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/retaliate/zombie diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm b/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm index bef98567bab7..21233ac89e42 100644 --- a/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm +++ b/code/modules/mob/living/simple_animal/hostile/syndicate_mobs.dm @@ -283,7 +283,7 @@ alert_on_shield_breach = TRUE loot = list(/obj/effect/mob_spawn/human/corpse/syndicatequartermaster, /obj/effect/decal/cleanable/blood/innards, /obj/effect/decal/cleanable/blood, /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic) -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE // he should be able to chase us in space /mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/armory/Initialize(mapload) @@ -334,7 +334,7 @@ alert_on_timeout = FALSE // So random fauna doesn't make depot explode. loot = list() // Explodes, doesn't drop loot. -/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/death() @@ -357,7 +357,7 @@ /obj/effect/gibspawner/generic, /obj/effect/gibspawner/generic) -/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/syndicate/ranged @@ -384,7 +384,7 @@ /obj/effect/gibspawner/generic) -/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/hostile/syndicate/ranged/space/autogib diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm index 9f0bf777ef63..0eb0e4b3ad81 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/actions.dm @@ -257,7 +257,7 @@ busy = SPINNING_COCOON visible_message("[src] begins to secrete a sticky substance around [cocoon_target].") stop_automated_movement = TRUE - walk(src,0) + GLOB.move_manager.stop_looping(src) if(do_after(src, 40, target = cocoon_target.loc)) if(busy == SPINNING_COCOON) if(cocoon_target && isturf(cocoon_target.loc) && get_dist(src,cocoon_target) <= 1) diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm index a5474f7f6e1a..c242175fc0d0 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/reproduction.dm @@ -150,7 +150,7 @@ new_area.Entered(src) else frustration++ - walk_to(src, entry_vent, 1) + GLOB.move_manager.move_to(src, entry_vent, 1) if(frustration > 2) entry_vent = null else if(prob(33)) @@ -165,7 +165,7 @@ for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src)) if(!v.welded) entry_vent = v - walk_to(src, entry_vent, 1) + GLOB.move_manager.move_to(src, entry_vent, 1) break diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm index 5b55bdf28eb4..fcb808b41685 100644 --- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm +++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/white.dm @@ -48,7 +48,7 @@ new /obj/item/organ/internal/body_egg/terror_eggs(L) if(!ckey) LoseTarget() - walk_away(src,L,2,1) + GLOB.move_manager.move_away(src,L,2,1) /proc/IsTSInfected(mob/living/carbon/C) // Terror AI requires this if(C.get_int_organ(/obj/item/organ/internal/body_egg)) diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm index 260c85782487..89e82885dd5d 100644 --- a/code/modules/mob/living/simple_animal/shade.dm +++ b/code/modules/mob/living/simple_animal/shade.dm @@ -40,7 +40,7 @@ else ..() -/mob/living/simple_animal/shade/Process_Spacemove() +/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /mob/living/simple_animal/shade/holy diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm index 77f3a594b590..076f4c8ea7aa 100644 --- a/code/modules/mob/living/simple_animal/simple_animal.dm +++ b/code/modules/mob/living/simple_animal/simple_animal.dm @@ -167,7 +167,7 @@ /mob/living/simple_animal/Destroy() /// We need to clear the reference to where we're walking to properly GC - walk_to(src, 0) + GLOB.move_manager.stop_looping(src) QDEL_NULL(pcollar) for(var/datum/action/innate/hide/hide in actions) hide.Remove(src) @@ -627,7 +627,7 @@ /mob/living/simple_animal/Login() ..() - walk(src, 0) // if mob is moving under ai control, then stop AI movement + GLOB.move_manager.stop_looping(src) // if mob is moving under ai control, then stop AI movement /mob/living/simple_animal/proc/npc_safe(mob/user) return FALSE diff --git a/code/modules/mob/living/simple_animal/slime/slime_mob.dm b/code/modules/mob/living/simple_animal/slime/slime_mob.dm index 9ca33b23d694..a8d58bc31919 100644 --- a/code/modules/mob/living/simple_animal/slime/slime_mob.dm +++ b/code/modules/mob/living/simple_animal/slime/slime_mob.dm @@ -223,7 +223,7 @@ Atkcool = TRUE addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS) -/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0) +/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 2 /mob/living/simple_animal/slime/get_status_tab_items() diff --git a/code/modules/mob/mob_grab.dm b/code/modules/mob/mob_grab.dm index 99cf29d37794..31632e82cf61 100644 --- a/code/modules/mob/mob_grab.dm +++ b/code/modules/mob/mob_grab.dm @@ -47,6 +47,7 @@ affecting.grabbed_by += src RegisterSignal(affecting, COMSIG_MOVABLE_MOVED, PROC_REF(grab_moved)) RegisterSignal(assailant, COMSIG_MOVABLE_MOVED, PROC_REF(pull_grabbed)) + RegisterSignal(assailant, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(on_update_glide_size)) hud = new /atom/movable/screen/grab(src) hud.icon_state = "reinforce" @@ -74,13 +75,21 @@ affecting.grabbed_by -= src affecting = null if(assailant) - UnregisterSignal(assailant, COMSIG_MOVABLE_MOVED) + UnregisterSignal(assailant, list( + COMSIG_MOVABLE_MOVED, + COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, + )) if(assailant.client) assailant.client.screen -= hud assailant = null + QDEL_NULL(hud) return ..() +/obj/item/grab/proc/on_update_glide_size(mob/living/grabber, new_size) + if(affecting && grabber == assailant && affecting != assailant) + affecting.set_glide_size(new_size) + /obj/item/grab/proc/pull_grabbed(mob/user, turf/old_turf, direct, forced) SIGNAL_HANDLER if(assailant.moving_diagonally == FIRST_DIAG_STEP) //we dont want to do anything in the middle of diagonal step @@ -88,11 +97,8 @@ if(!assailant.Adjacent(old_turf)) qdel(src) return - var/list/grab_states_not_moving = list(GRAB_KILL, GRAB_NECK) //states of grab when we dont need affecting to be moved by himself - var/assailant_glide_speed = TICKS2DS(world.icon_size / assailant.glide_size) - if(state in grab_states_not_moving) - affecting.glide_for(assailant_glide_speed) - else if(get_turf(affecting) != old_turf) + + if(get_turf(affecting) != old_turf) var/possible_dest = list() var/list/mobs_do_not_move = list() // those are mobs we shouldnt move while we're going to new position var/list/dest_1_sort = list() // just better dest to be picked first @@ -131,7 +137,7 @@ if(get_turf(affecting) == dest) success_move = TRUE continue - if(affecting.Move(dest, get_dir(affecting, dest), assailant_glide_speed)) + if(affecting.Move(dest, get_dir(affecting, dest), glide_size)) success_move = TRUE break continue diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm index 80254530dca2..2f625649540c 100644 --- a/code/modules/mob/mob_movement.dm +++ b/code/modules/mob/mob_movement.dm @@ -47,7 +47,7 @@ #define CONFUSION_MAX 80 SECONDS -/client/Move(n, direct) +/client/Move(new_loc, direct) if(world.time < move_delay) return @@ -59,7 +59,7 @@ if(!mob || !mob.loc) return 0 - if(!n || !direct) // why did we never check this before? + if(!new_loc || !direct) // why did we never check this before? return FALSE if(mob.notransform) @@ -72,7 +72,7 @@ return Move_object(direct) if(!isliving(mob)) - return mob.Move(n, direct) + return mob.Move(new_loc, direct) if(mob.stat == DEAD) mob.ghostize() @@ -91,7 +91,7 @@ if(mob.remote_control) //we're controlling something, our movement is relayed to it return mob.remote_control.relaymove(mob, direct) - if(is_ai(mob)) + if(is_ai(mob)) var/mob/living/silicon/ai/ai = mob var/mob/camera/eye/ai/eye = ai.eyeobj if(istype(eye) && !istype(ai.remote_control)) @@ -122,6 +122,9 @@ if(!mob.Process_Spacemove(direct)) return 0 + if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_MOVE, args) & COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE) + return FALSE + if(mob.restrained()) // Why being pulled while cuffed prevents you from moving for(var/mob/M in orange(1, mob)) if(M.pulling == mob) @@ -132,20 +135,28 @@ else M.stop_pulling() + // We are now going to move + var/add_delay = mob.movement_delay() + + if(locate(/obj/item/grab, mob)) + if(!isalienhunter(mob)) // i hate grab code + add_delay += 7 + + var/new_glide_size = DELAY_TO_GLIDE_SIZE(add_delay * ((NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? sqrt(2) : 1)) + mob.set_glide_size(new_glide_size) // set it now in case of pulled objects - var/delay = mob.movement_delay() + //If the move was recent, count using old_move_delay + //We want fractional behavior and all if(old_move_delay + world.tick_lag > world.time) + //Yes this makes smooth movement stutter if add_delay is too fractional + //Yes this is better then the alternative move_delay = old_move_delay else move_delay = world.time - mob.last_movement = world.time - delay = TICKS2DS(-round(-(DS2TICKS(delay)))) //Rounded to the next tick in equivalent ds - - - if(locate(/obj/item/grab, mob)) - if(!isalienhunter(mob)) // i hate grab code - delay += 7 + //Basically an optional override for our glide size + //Sometimes you want to look like you're moving with a delay you don't actually have yet + visual_delay = 0 if(istype(living_mob)) var/newdir = NONE @@ -158,7 +169,7 @@ newdir = angle2dir(dir2angle(direct) + pick(45, -45)) if(newdir) direct = newdir - n = get_step(mob, direct) + new_loc = get_step(mob, direct) mob.last_movement_dir = direct @@ -166,18 +177,26 @@ if(mob.pulling) prev_pulling_loc = mob.pulling.loc - if(!(direct & (direct - 1))) // cardinal direction - . = mob.SelfMove(n, direct, delay) - else // diagonal movements take longer - var/diag_delay = delay * SQRT_2 - . = mob.SelfMove(n, direct, diag_delay) - if(mob.loc == n) + . = ..() + + if(mob.loc == new_loc) + mob.last_movement = world.time + if(IS_DIR_DIAGONAL(direct)) // only incur the extra delay if the move was *actually* diagonal // There would be a bit of visual jank if we try to walk diagonally next to a wall // and the move ends up being cardinal, rather than diagonal, // but that's better than it being jank on every *successful* diagonal move. - delay = diag_delay - move_delay += delay + add_delay *= sqrt(2) + + var/after_glide = 0 + if(visual_delay) + after_glide = visual_delay + else + after_glide = DELAY_TO_GLIDE_SIZE(add_delay) + + mob.set_glide_size(after_glide) + + move_delay += add_delay if(mob.pulledby) mob.pulledby.stop_pulling() @@ -193,9 +212,6 @@ #undef CONFUSION_MAX -/mob/proc/SelfMove(turf/n, direct, movetime) - return Move(n, direct, movetime) - ///Process_Grab() ///Called by client/Move() ///Checks to see if you are being grabbed and if so attemps to break it @@ -303,46 +319,76 @@ return TRUE -///Process_Spacemove -///Called by /client/Move() -///For moving in space -///Return 1 for movement 0 for none -/mob/Process_Spacemove(movement_dir = 0) +/** + * Handles mob/living movement in space (or no gravity) + * + * Called by /client/Move() + * + * return TRUE for movement or FALSE for none + */ +/mob/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(..()) - return 1 - var/atom/movable/backup = get_spacemove_backup(movement_dir) - if(backup) - if(istype(backup) && movement_dir && !backup.anchored) - var/opposite_dir = turn(movement_dir, 180) - if(backup.newtonian_move(opposite_dir)) //You're pushing off something movable, so it moves - to_chat(src, "You push off of [backup] to propel yourself.") - return 1 - return 0 + return TRUE + + // TODO: if(buckled) may belong here -/mob/get_spacemove_backup(movement_dir) - for(var/A in orange(1, get_turf(src))) - if(isarea(A)) + var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move) + if(!backup) + return FALSE + + if(continuous_move || !istype(backup) || !movement_dir || backup.anchored) + return TRUE + + var/opposite_dir = turn(movement_dir, 180) + if(backup.newtonian_move(opposite_dir)) //You're pushing off something movable, so it moves + to_chat(src, "You push off of [backup] to propel yourself.") + return TRUE + +/** + * Finds a target near a mob that is viable for pushing off when moving. + * Takes the intended movement direction as input, alongside if the context is checking if we're allowed to continue drifting + */ +/mob/get_spacemove_backup(moving_direction, continuous_move) + for(var/atom/pushover as anything in range(1, get_turf(src))) + if(pushover == src) continue - else if(isturf(A)) - var/turf/turf = A + if(isarea(pushover)) + continue + if(isturf(pushover)) + var/turf/turf = pushover if(isspaceturf(turf)) continue if(!turf.density && !mob_negates_gravity()) continue - return A - else - var/atom/movable/AM = A - if(AM == buckled) //Kind of unnecessary but let's just be sure + return pushover + + var/atom/movable/rebound = pushover + if(rebound == buckled) + continue + if(ismob(rebound)) + var/mob/lover = rebound + if(lover.buckled) continue - if(!AM.CanPass(src) || AM.density) - if(AM.anchored) - return AM - if(pulling == AM) - continue - if(get_turf(AM) == get_step(get_turf(src), movement_dir)) // No pushing off objects in front of you, while simultaneously pushing them fowards to go faster in space. - continue - . = AM + var/pass_allowed = rebound.CanPass(src, get_dir(rebound, src)) + if(!rebound.density && pass_allowed) + continue + //Sometime this tick, this pushed off something. Doesn't count as a valid pushoff target + if(rebound.last_pushoff == world.time) + continue + if(continuous_move && !pass_allowed) + var/datum/move_loop/move/rebound_engine = GLOB.move_manager.processing_on(rebound, SSspacedrift) + // If you're moving toward it and you're both going the same direction, stop + if(moving_direction == get_dir(src, pushover) && rebound_engine && moving_direction == rebound_engine.direction) + continue + else if(!pass_allowed) + if(moving_direction == get_dir(src, pushover)) // Can't push "off" of something that you're walking into + continue + if(rebound.anchored) + return rebound + if(pulling == rebound) + continue + return rebound /mob/proc/mob_has_gravity(turf/T) return has_gravity(src, T) @@ -350,35 +396,51 @@ /mob/proc/mob_negates_gravity() return 0 -/mob/proc/Move_Pulled(atom/A) - if(HAS_TRAIT(src, TRAIT_CANNOT_PULL) || restrained() || !pulling) - return - if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src)) +/atom/movable/proc/Move_Pulled(atom/moving_atom) + if(!pulling) + return FALSE + if(pulling.anchored || pulling.move_resist > move_force || !pulling.Adjacent(src, src, pulling)) stop_pulling() - return + return FALSE if(isliving(pulling)) - var/mob/living/L = pulling - if(L.buckled && L.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it + var/mob/living/pulling_mob = pulling + if(pulling_mob.buckled && pulling_mob.buckled.buckle_prevents_pull) //if they're buckled to something that disallows pulling, prevent it stop_pulling() - return - if(A == loc && pulling.density) + return FALSE + if(moving_atom == loc && pulling.density) + return FALSE + var/move_dir = get_dir(pulling.loc, moving_atom) + if(!Process_Spacemove(move_dir)) + return FALSE + if(!move_dir) + return FALSE + pulling.Move(get_step(pulling.loc, move_dir), move_dir, glide_size) + return TRUE + +/mob/living/Move_Pulled(atom/moving_atom) + . = ..() + if(!. || !isliving(moving_atom)) return - if(!Process_Spacemove(get_dir(pulling.loc, A))) + if(!Process_Spacemove(get_dir(pulling.loc, moving_atom))) return if(src in pulling.contents) return - var/target_turf = get_step(pulling, get_dir(pulling.loc, A)) + var/target_turf = get_step(pulling, get_dir(pulling.loc, moving_atom)) if(get_dist(target_turf, loc) > 1) // Make sure the turf we are trying to pull to is adjacent to the user. return // We do not use Adjacent() here because it checks if there are dense objects in the way, making it impossible to move an object to the side if we're blocked on both sides. + var/move_dir = get_dir(pulling.loc, moving_atom) if(ismob(pulling)) var/mob/M = pulling var/atom/movable/t = M.pulling M.stop_pulling() - . = step(pulling, get_dir(pulling.loc, A)) // we set the return value to step here, if we don't having someone buckled in to a chair and being pulled won't let them be unbuckeled + + // we set the return value to step here, if we don't having someone + // buckled in to a chair and being pulled won't let them be unbuckeled + . = pulling.Move(get_step(pulling.loc, moving_atom), move_dir, glide_size) if(M) M.start_pulling(t) else - . = step(pulling, get_dir(pulling.loc, A)) + . = pulling.Move(get_step(pulling.loc, moving_atom), move_dir, glide_size) /mob/proc/update_gravity(has_gravity) return diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index b907196b8807..efd50f678610 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -87,8 +87,6 @@ var/emp_proof = FALSE /// List of overlays the mod has. Needs to be cut onremoval / module deactivation var/list/mod_overlays = list() - /// Is the jetpack on so we should make ion effects? - var/jetpack_active = FALSE /// Cham option for when the cham module is installed. var/datum/action/item_action/chameleon_change/modsuit/chameleon_action /// Is the control unit disquised? @@ -241,13 +239,6 @@ if(slot == ITEM_SLOT_BACK) return TRUE -/obj/item/mod/control/on_mob_move(direction, mob/user) - if(!jetpack_active || !isturf(user.loc)) - return - var/turf/T = get_step(src, REVERSE_DIR(direction)) - if(!has_gravity(T)) - new /obj/effect/particle_effect/ion_trails(T, direction) - /obj/item/mod/control/Moved(atom/old_loc, movement_dir, forced, list/old_locs, momentum_change = TRUE) . = ..() if(!wearer || old_loc != wearer || loc == wearer) diff --git a/code/modules/mod/modules/_modules.dm b/code/modules/mod/modules/_modules.dm index 845c719aaa0d..7c2e568591ca 100644 --- a/code/modules/mod/modules/_modules.dm +++ b/code/modules/mod/modules/_modules.dm @@ -106,7 +106,7 @@ if(!mod.active || mod.activating || !mod.get_charge()) to_chat(mod.wearer, "Module is unpowered!") return FALSE - if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE) + if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED, mod.wearer) & MOD_ABORT_USE) return FALSE if(module_type == MODULE_ACTIVE) if(mod.selected_module && !mod.selected_module.on_deactivation(display_message = FALSE)) @@ -157,7 +157,7 @@ else if(display_message) to_chat(mod.wearer, "[src] deactivated.") //mod.wearer.update_clothing(mod.slot_flags) - SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED) + SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED, mod.wearer) mod.update_mod_overlays() return TRUE @@ -169,7 +169,7 @@ if(!check_power(use_power_cost)) to_chat(mod.wearer, "Module costs too much power to use!") return FALSE - if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED) & MOD_ABORT_USE) + if(SEND_SIGNAL(src, COMSIG_MODULE_TRIGGERED, mod.wearer) & MOD_ABORT_USE) return FALSE COOLDOWN_START(src, cooldown_timer, cooldown_time) //addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_clothing), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 3ef82f70ca50..dcc96abce8bf 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -128,40 +128,51 @@ overlay_state_inactive = "module_jetpack" overlay_state_active = "module_jetpack_on" /// Do we stop the wearer from gliding in space. - var/stabilizers = FALSE + var/stabilize = FALSE + var/thrust_callback -/obj/item/mod/module/jetpack/proc/set_stabilizers(new_stabilizers) - if(stabilizers == new_stabilizers) - return - stabilizers = new_stabilizers +/obj/item/mod/module/jetpack/Initialize(mapload) + . = ..() + thrust_callback = CALLBACK(src, PROC_REF(allow_thrust)) + configure_jetpack(stabilize) + +/obj/item/mod/module/jetpack/Destroy() + thrust_callback = null + return ..() + +/** + * configures/re-configures the jetpack component + * + * Arguments + * stabilize - Should this jetpack be stabalized + */ +/obj/item/mod/module/jetpack/proc/configure_jetpack(stabilize) + src.stabilize = stabilize + + AddComponent( \ + /datum/component/jetpack, \ + src.stabilize, \ + COMSIG_MODULE_TRIGGERED, \ + COMSIG_MODULE_DEACTIVATED, \ + MOD_ABORT_USE, \ + thrust_callback, \ + /datum/effect_system/trail_follow/ion/grav_allowed \ + ) /obj/item/mod/module/jetpack/get_configuration() . = ..() - .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilizers) + .["stabilizers"] = add_ui_configuration("Stabilizers", "bool", stabilize) /obj/item/mod/module/jetpack/configure_edit(key, value) switch(key) if("stabilizers") - set_stabilizers(value) + configure_jetpack(value) /obj/item/mod/module/jetpack/proc/allow_thrust() - if(!active) - return if(!drain_power(use_power_cost)) return FALSE return TRUE -/obj/item/mod/module/jetpack/proc/get_user() - return mod.wearer - -/obj/item/mod/module/jetpack/on_activation() - . = ..() - mod.jetpack_active = TRUE - -/obj/item/mod/module/jetpack/on_deactivation(display_message, deleting) - . = ..() - mod.jetpack_active = FALSE - /obj/item/mod/module/jetpack/advanced name = "MOD advanced ion jetpack module" desc = "An improvement on the previous model of electric thrusters. This one achieves better efficency through \ diff --git a/code/modules/power/engines/singularity/singularity.dm b/code/modules/power/engines/singularity/singularity.dm index 1d51905da0a8..a5931a20ae3f 100644 --- a/code/modules/power/engines/singularity/singularity.dm +++ b/code/modules/power/engines/singularity/singularity.dm @@ -107,7 +107,7 @@ GLOBAL_VAR_INIT(global_singulo_id, 1) B.remove(C) qdel(B) -/obj/singularity/Process_Spacemove() //The singularity stops drifting for no man! +/obj/singularity/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) //The singularity stops drifting for no man! return 0 /obj/singularity/blob_act(obj/structure/blob/B) diff --git a/code/modules/power/engines/tesla/energy_ball.dm b/code/modules/power/engines/tesla/energy_ball.dm index b7a012e9c8aa..e5d0c22bc8c4 100644 --- a/code/modules/power/engines/tesla/energy_ball.dm +++ b/code/modules/power/engines/tesla/energy_ball.dm @@ -137,7 +137,7 @@ // MORE POWER movement_beam(move_target, 1 SECONDS) sleep(0.5 SECONDS) - walk_towards(src, move_target, 0, 10) + GLOB.move_manager.home_onto(src, move_target, 0, 10) /obj/singularity/energy_ball/proc/on_atom_entered(datum/source, atom/movable/entered) var/mob/living/living_entered = entered diff --git a/code/modules/projectiles/projectile_base.dm b/code/modules/projectiles/projectile_base.dm index 8d8603f0dccf..57d2a23083ea 100644 --- a/code/modules/projectiles/projectile_base.dm +++ b/code/modules/projectiles/projectile_base.dm @@ -326,7 +326,7 @@ picked_mob.bullet_act(src, def_zone) qdel(src) -/obj/item/projectile/Process_Spacemove(movement_dir = 0) +/obj/item/projectile/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return 1 //Bullets don't drift in space /obj/item/projectile/process() diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm index b639acd9ad15..9ca16326b7fe 100644 --- a/code/modules/reagents/reagent_containers/spray.dm +++ b/code/modules/reagents/reagent_containers/spray.dm @@ -77,23 +77,14 @@ /obj/item/reagent_containers/spray/proc/spray(atom/A) - var/obj/effect/decal/chempuff/D = new /obj/effect/decal/chempuff(get_turf(src)) - D.create_reagents(amount_per_transfer_from_this) - reagents.trans_to(D, amount_per_transfer_from_this, 1/spray_currentrange) - D.icon += mix_color_from_reagents(D.reagents.reagent_list) - - for(var/i in 1 to spray_currentrange) - if(!step_towards(D, A) && i != 1) - qdel(D) - return - D.reagents.reaction(get_turf(D)) - for(var/atom/T in get_turf(D)) - D.reagents.reaction(T) - sleep(3) - if(QDELETED(D)) - return - qdel(D) - + var/obj/effect/decal/chempuff/chem_puff = new /obj/effect/decal/chempuff(get_turf(src)) + chem_puff.create_reagents(amount_per_transfer_from_this) + reagents.trans_to(chem_puff, amount_per_transfer_from_this, 1/spray_currentrange) + chem_puff.icon += mix_color_from_reagents(chem_puff.reagents.reagent_list) + + var/datum/move_loop/our_loop = GLOB.move_manager.move_towards_legacy(chem_puff, A, 3 DECISECONDS, timeout = spray_currentrange * 3 DECISECONDS, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY) + chem_puff.RegisterSignal(our_loop, COMSIG_PARENT_QDELETING, TYPE_PROC_REF(/obj/effect/decal/chempuff, loop_ended)) + chem_puff.RegisterSignal(our_loop, COMSIG_MOVELOOP_POSTPROCESS, TYPE_PROC_REF(/obj/effect/decal/chempuff, check_move)) /obj/item/reagent_containers/spray/attack_self__legacy__attackchain(mob/user) diff --git a/code/modules/surgery/organs/blood.dm b/code/modules/surgery/organs/blood.dm index e8983586475d..26c3f1fb32a7 100644 --- a/code/modules/surgery/organs/blood.dm +++ b/code/modules/surgery/organs/blood.dm @@ -87,9 +87,9 @@ blood_volume = max(blood_volume - amt, 0) if(isturf(loc)) //Blood loss still happens in locker, floor stays clean if(amt >= 10) - add_splatter_floor(loc, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) + add_splatter_floor(loc) else - add_splatter_floor(loc, 1, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) + add_splatter_floor(loc, 1) /mob/living/carbon/human/bleed(amt) amt *= physiology.bleed_mod @@ -108,7 +108,7 @@ blood_volume = max(blood_volume - amt, 0) if(prob(10 * amt)) // +5% chance per internal bleeding site that we'll cough up blood on a given tick. custom_emote(EMOTE_VISIBLE, "coughs up blood!") - add_splatter_floor(loc, 1, emittor_intertia = inertia_next_move > world.time ? last_movement_dir : null) + add_splatter_floor(loc, 1) return 1 else if(amt >= 1 && prob(5 * amt)) // +2.5% chance per internal bleeding site that we'll cough up blood on a given tick. Must be bleeding internally in more than one place to have a chance at this. vomit(0, 1) @@ -256,7 +256,7 @@ . += list("O-", "O+") //to add a splatter of blood or other mob liquid. -/mob/living/proc/add_splatter_floor(turf/T, small_drip, shift_x, shift_y, emittor_intertia) +/mob/living/proc/add_splatter_floor(turf/T, small_drip, shift_x, shift_y) if((get_blood_id() != "blood") && (get_blood_id() != "slimejelly"))//is it blood or welding fuel? return if(!T) @@ -264,13 +264,14 @@ var/list/temp_blood_DNA var/list/b_data = get_blood_data(get_blood_id()) + var/datum/move_loop/move/move_loop = GLOB.move_manager.processing_on(src, SSspacedrift) if(small_drip) // Only a certain number of drips (or one large splatter) can be on a given turf. var/obj/effect/decal/cleanable/blood/drip/drop = locate() in T if(drop) - if(emittor_intertia) - drop.newtonian_move(emittor_intertia) + if(move_loop) + drop.newtonian_move(move_loop.direction, instant = TRUE) if(drop.drips < 5) drop.drips++ var/image/I = image(drop.icon, drop.random_icon_states) @@ -295,8 +296,8 @@ else drop.basecolor = "#A10808" drop.update_icon() - if(emittor_intertia) - drop.newtonian_move(emittor_intertia) + if(move_loop) + drop.newtonian_move(move_loop.direction, instant = TRUE) return // Find a blood decal or create a new one. @@ -318,8 +319,8 @@ if(shift_x || shift_y) B.off_floor = TRUE B.layer = BELOW_MOB_LAYER //So the blood lands ontop of things like posters, windows, etc. - if(emittor_intertia) - B.newtonian_move(emittor_intertia) + if(move_loop) + B.newtonian_move(move_loop.direction, instant = TRUE) /mob/living/carbon/human/add_splatter_floor(turf/T, small_drip, shift_x, shift_y, emittor_intertia) if(!(NO_BLOOD in dna.species.species_traits)) diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm index e6cf100cf1f2..7bb1293a8d51 100644 --- a/code/modules/surgery/organs/organ_internal.dm +++ b/code/modules/surgery/organs/organ_internal.dm @@ -320,10 +320,10 @@ if(isobj(H.shoes)) var/thingy = H.shoes if(H.drop_item_to_ground(H.shoes)) - walk_away(thingy,H,15,2) + GLOB.move_manager.move_away(thingy, H, 15, 2) spawn(20) if(thingy) - walk(thingy,0) + GLOB.move_manager.stop_looping(thingy) /obj/item/organ/internal/honktumor/cursed unremovable = TRUE diff --git a/code/modules/vehicle/speedbike.dm b/code/modules/vehicle/speedbike.dm index 6de2b58d99ac..19a4582f79f6 100644 --- a/code/modules/vehicle/speedbike.dm +++ b/code/modules/vehicle/speedbike.dm @@ -1,50 +1,38 @@ -/obj/vehicle/space/speedbike +/obj/tgvehicle/speedbike name = "Speedbike" icon = 'icons/obj/bike.dmi' icon_state = "speedbike_blue" - layer = MOB_LAYER - 0.1 - vehicle_move_delay = 0 - var/overlay_state = "cover_blue" - var/mutable_appearance/overlay + layer = LYING_MOB_LAYER + var/cover_iconstate = "cover_blue" -/obj/vehicle/space/speedbike/Initialize(mapload) +/obj/tgvehicle/speedbike/Initialize(mapload) . = ..() - overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER) - add_overlay(overlay) + add_overlay(image(icon, cover_iconstate, ABOVE_MOB_LAYER)) + AddElement(/datum/element/ridable, /datum/component/riding/vehicle/speedbike) -/obj/vehicle/space/speedbike/Move(newloc,move_dir) +/obj/tgvehicle/speedbike/Move(newloc,move_dir) if(has_buckled_mobs()) new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc) . = ..() -/obj/vehicle/space/speedbike/handle_vehicle_layer() - switch(dir) - if(NORTH,SOUTH) - pixel_x = -16 - pixel_y = -16 - if(EAST,WEST) - pixel_x = -18 - pixel_y = 0 +/obj/tgvehicle/speedbike/red + icon_state = "speedbike_red" + cover_iconstate = "cover_red" -/obj/vehicle/space/speedbike/handle_vehicle_offsets() - if(has_buckled_mobs()) - for(var/m in buckled_mobs) - var/mob/living/buckled_mob = m - buckled_mob.setDir(dir) - switch(dir) - if(NORTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = -8 - if(SOUTH) - buckled_mob.pixel_x = 0 - buckled_mob.pixel_y = 4 - if(EAST) - buckled_mob.pixel_x = -10 - buckled_mob.pixel_y = 5 - if(WEST) - buckled_mob.pixel_x = 10 - buckled_mob.pixel_y = 5 +/datum/component/riding/vehicle/speedbike + vehicle_move_delay = 1 + override_allow_spacemove = TRUE + ride_check_flags = RIDER_NEEDS_LEGS | RIDER_NEEDS_ARMS | UNBUCKLE_DISABLED_RIDER -/obj/vehicle/space/speedbike/red - icon_state = "speedbike_red" - overlay_state = "cover_red" +/datum/component/riding/vehicle/speedbike/handle_specials() + . = ..() + set_vehicle_dir_layer(SOUTH, OBJ_LAYER) + set_vehicle_dir_layer(NORTH, ABOVE_MOB_LAYER) + set_vehicle_dir_layer(EAST, OBJ_LAYER) + set_vehicle_dir_layer(WEST, OBJ_LAYER) + + set_riding_offsets(RIDING_OFFSET_ALL, list(TEXT_NORTH = list(0, -8), TEXT_SOUTH = list(0, 4), TEXT_EAST = list(-10, 5), TEXT_WEST = list(10, 5))) + set_vehicle_dir_offsets(NORTH, -16, -16) + set_vehicle_dir_offsets(SOUTH, -16, -16) + set_vehicle_dir_offsets(EAST, -18, 0) + set_vehicle_dir_offsets(WEST, -18, 0) diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm index 396683813b16..e1e37ccaf046 100644 --- a/code/modules/vehicle/vehicle.dm +++ b/code/modules/vehicle/vehicle.dm @@ -229,7 +229,7 @@ return //write specifics for different vehicles -/obj/vehicle/Process_Spacemove(direction) +/obj/vehicle/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) if(has_gravity(src)) return TRUE @@ -245,7 +245,7 @@ pressure_resistance = INFINITY spaceworthy = TRUE -/obj/vehicle/space/Process_Spacemove(direction) +/obj/vehicle/space/Process_Spacemove(movement_dir = 0, continuous_move = FALSE) return TRUE /obj/vehicle/zap_act(power, zap_flags) diff --git a/paradise.dme b/paradise.dme index 47cf400490d6..5954dcf663ce 100644 --- a/paradise.dme +++ b/paradise.dme @@ -95,7 +95,7 @@ #include "code\__DEFINES\mob_defines.dm" #include "code\__DEFINES\mod.dm" #include "code\__DEFINES\move_force.dm" -#include "code\__DEFINES\movement_info.dm" +#include "code\__DEFINES\movement_defines.dm" #include "code\__DEFINES\muzzle_flash.dm" #include "code\__DEFINES\newscaster_defines.dm" #include "code\__DEFINES\particle_defines.dm" @@ -160,6 +160,7 @@ #include "code\__DEFINES\dcs\machinery_signals.dm" #include "code\__DEFINES\dcs\mob_signals.dm" #include "code\__DEFINES\dcs\movable_signals.dm" +#include "code\__DEFINES\dcs\moveloop_signals.dm" #include "code\__DEFINES\dcs\obj_signals.dm" #include "code\__HELPERS\_logging.dm" #include "code\__HELPERS\_string_lists.dm" @@ -336,7 +337,6 @@ #include "code\controllers\subsystem\SSrunechat.dm" #include "code\controllers\subsystem\SSsecurity_level.dm" #include "code\controllers\subsystem\SSshuttles.dm" -#include "code\controllers\subsystem\SSspacedrift.dm" #include "code\controllers\subsystem\SSstatpanel.dm" #include "code\controllers\subsystem\SSsun.dm" #include "code\controllers\subsystem\SStgui.dm" @@ -347,6 +347,9 @@ #include "code\controllers\subsystem\SSverb_manager.dm" #include "code\controllers\subsystem\SSvote.dm" #include "code\controllers\subsystem\SSweather.dm" +#include "code\controllers\subsystem\movement\movement_types.dm" +#include "code\controllers\subsystem\movement\SSmovement.dm" +#include "code\controllers\subsystem\movement\SSspacedrift.dm" #include "code\controllers\subsystem\non_firing\SSassets.dm" #include "code\controllers\subsystem\non_firing\SSatoms.dm" #include "code\controllers\subsystem\non_firing\SSchangelog.dm" @@ -402,6 +405,7 @@ #include "code\datums\logging.dm" #include "code\datums\mind.dm" #include "code\datums\mixed.dm" +#include "code\datums\move_manager.dm" #include "code\datums\movement_detector.dm" #include "code\datums\mutable_appearance.dm" #include "code\datums\ores.dm" @@ -442,12 +446,14 @@ #include "code\datums\components\deadchat_control.dm" #include "code\datums\components\debris.dm" #include "code\datums\components\defibrillator.dm" +#include "code\datums\components\drift.dm" #include "code\datums\components\ducttape.dm" #include "code\datums\components\edit_complainer.dm" #include "code\datums\components\emissive_blocker.dm" #include "code\datums\components\footstep.dm" #include "code\datums\components\forces_doors_open.dm" #include "code\datums\components\fullauto.dm" +#include "code\datums\components\jetpack_component.dm" #include "code\datums\components\ghost_direct_control.dm" #include "code\datums\components\label.dm" #include "code\datums\components\largeobjecttransparency.dm" diff --git a/tools/UpdatePaths/Scripts/27698_vehicle_paths.txt b/tools/UpdatePaths/Scripts/27698_vehicle_paths.txt new file mode 100644 index 000000000000..df3277271e7d --- /dev/null +++ b/tools/UpdatePaths/Scripts/27698_vehicle_paths.txt @@ -0,0 +1 @@ +/obj/vehicle/space/speedbike/@SUBTYPES : /obj/tgvehicle/speedbike/@SUBTYPE