diff --git a/_maps/map_files/generic/CentComm.dmm b/_maps/map_files/generic/CentComm.dmm
index f75b4f36459..dc3d3e31e5b 100644
--- a/_maps/map_files/generic/CentComm.dmm
+++ b/_maps/map_files/generic/CentComm.dmm
@@ -1765,7 +1765,7 @@
/turf/simulated/wall/indestructible/fakeglass,
/area/centcom/specops)
"aQh" = (
-/obj/structure/chair/wheelchair/bike{
+/obj/vehicle/motorcycle{
dir = 8
},
/obj/structure/sign/poster/contraband/random{
@@ -6811,7 +6811,7 @@
},
/area/centcom/zone3)
"dxs" = (
-/obj/structure/chair/wheelchair/bike{
+/obj/vehicle/motorcycle{
dir = 1
},
/turf/simulated/floor/plasteel{
@@ -44128,7 +44128,7 @@
/area/centcom/supply)
"vwk" = (
/obj/effect/decal/cleanable/dirt,
-/obj/structure/chair/wheelchair/bike{
+/obj/vehicle/motorcycle{
dir = 1
},
/turf/simulated/floor/plasteel{
@@ -44637,7 +44637,7 @@
/turf/simulated/floor/plasteel,
/area/shuttle/escape)
"vLc" = (
-/obj/structure/chair/wheelchair/bike{
+/obj/vehicle/motorcycle{
dir = 8
},
/obj/effect/decal/cleanable/dirt,
@@ -46609,7 +46609,7 @@
/turf/simulated/floor/mech_bay_recharge_floor,
/area/centcom/specops)
"wGg" = (
-/obj/structure/chair/wheelchair/bike{
+/obj/vehicle/motorcycle{
dir = 8
},
/turf/simulated/floor/wood{
diff --git a/code/__DEFINES/MC.dm b/code/__DEFINES/MC.dm
index 2bc7d6e1a18..b642392b7ea 100644
--- a/code/__DEFINES/MC.dm
+++ b/code/__DEFINES/MC.dm
@@ -93,3 +93,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();\
+}\
+/datum/controller/subsystem/movement/##X/fire() {..() /*just so it shows up on the profiler*/} \
+/datum/controller/subsystem/movement/##X
+
diff --git a/code/__DEFINES/cooldowns.dm b/code/__DEFINES/cooldowns.dm
index ebdc4666244..67c94710d71 100644
--- a/code/__DEFINES/cooldowns.dm
+++ b/code/__DEFINES/cooldowns.dm
@@ -70,14 +70,18 @@
#define TIMER_COOLDOWN_START(cd_source, cd_index, cd_time) LAZYSET(cd_source.cooldowns, cd_index, addtimer(CALLBACK(GLOBAL_PROC, GLOBAL_PROC_REF(end_cooldown), cd_source, cd_index), cd_time))
-#define TIMER_COOLDOWN_CHECK(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index)
+/// Checks if a timer based cooldown is NOT finished.
+#define TIMER_COOLDOWN_RUNNING(cd_source, cd_index) LAZYACCESS(cd_source.cooldowns, cd_index)
+
+/// Checks if a timer based cooldown is finished.
+#define TIMER_COOLDOWN_FINISHED(cd_source, cd_index) (!TIMER_COOLDOWN_RUNNING(cd_source, cd_index))
#define TIMER_COOLDOWN_END(cd_source, cd_index) LAZYREMOVE(cd_source.cooldowns, cd_index)
/*
* Stoppable timer cooldowns.
* Use indexes the same as the regular tiemr cooldowns.
- * They make use of the TIMER_COOLDOWN_CHECK() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one.
+ * They make use of the TIMER_COOLDOWN_RUNNING() and TIMER_COOLDOWN_END() macros the same, just not the TIMER_COOLDOWN_START() one.
* A bit more expensive than the regular timers, but can be reset before they end and the time left can be checked.
*/
@@ -85,7 +89,7 @@
#define S_TIMER_COOLDOWN_RESET(cd_source, cd_index) reset_cooldown(cd_source, cd_index)
-#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_CHECK(cd_source, cd_index)))
+#define S_TIMER_COOLDOWN_TIMELEFT(cd_source, cd_index) (timeleft(TIMER_COOLDOWN_RUNNING(cd_source, cd_index)))
/*
@@ -100,8 +104,10 @@
#define COOLDOWN_START(cd_source, cd_index, cd_time) (cd_source.cd_index = world.time + (cd_time))
//Returns true if the cooldown has run its course, false otherwise
-#define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index < world.time)
+#define COOLDOWN_FINISHED(cd_source, cd_index) (cd_source.cd_index <= world.time)
#define COOLDOWN_RESET(cd_source, cd_index) cd_source.cd_index = 0
+#define COOLDOWN_STARTED(cd_source, cd_index) (cd_source.cd_index != 0)
+
#define COOLDOWN_TIMELEFT(cd_source, cd_index) (max(0, cd_source.cd_index - world.time))
diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index f48196e3ab8..202be3d606f 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -61,6 +61,17 @@
#define COMSIG_ATOM_ATTACK "atom_attack"
///called when the atom sucessfully has it's density var changed, from base atom/set_density(): (value)
#define COMSIG_ATOM_SET_DENSITY "atom_set_density"
+///from base of atom/experience_pressure_difference(): (pressure_difference, direction, pressure_resistance_prob_delta)
+#define COMSIG_ATOM_PRE_PRESSURE_PUSH "atom_pre_pressure_push"
+ ///prevents pressure movement
+ #define COMSIG_ATOM_BLOCKS_PRESSURE (1<<0)
+///signal sent out by an atom when it checks if it can be pulled, for additional checks
+#define COMSIG_ATOM_CAN_BE_PULLED "movable_can_be_pulled"
+ #define COMSIG_ATOM_CANT_PULL (1 << 0)
+///signal sent out by an atom when it is no longer being pulled by something else : (atom/puller)
+#define COMSIG_ATOM_NO_LONGER_PULLED "movable_no_longer_pulled"
+///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"
///from base of atom/attackby(): (/obj/item, /mob/living, params)
#define COMSIG_PARENT_ATTACKBY "atom_attackby"
@@ -220,10 +231,7 @@
#define ZIMPACT_NO_MESSAGE (1<<1)
/// Do not do the spin animation when landing
#define ZIMPACT_NO_SPIN (1<<2)
-///from base of atom/movable/experience_pressure_difference(): (pressure_difference, direction, pressure_resistance_prob_delta)
-#define COMSIG_ATOM_PRE_PRESSURE_PUSH "atom_pre_pressure_push"
- ///prevents pressure movement
- #define COMSIG_ATOM_BLOCKS_PRESSURE (1<<0)
+
/////////////////
///from base of area/Entered(): (/area)
@@ -327,6 +335,22 @@
#define COMSIG_MOVABLE_DISPOSING "movable_disposing"
///called when the movable is removed from a disposal holder object: /obj/structure/disposalpipe/proc/expel(): (obj/structure/disposalholder/H, turf/T, direction)
#define COMSIG_MOVABLE_EXIT_DISPOSALS "movable_exit_disposals"
+///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"
+///called when the movable's glide size is updated: (new_glide_size)
+#define COMSIG_MOVABLE_UPDATE_GLIDE_SIZE "movable_glide_size"
+/// from base of atom/movable/Process_Spacemove(): (movement_dir, continuous_move)
+#define COMSIG_MOVABLE_SPACEMOVE "spacemove"
+ #define COMSIG_MOVABLE_STOP_SPACEMOVE (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)
// /datum/mind signals
@@ -551,6 +575,8 @@
#define MOVE_ARG_NEW_LOC 1
/// The arugment of move_args which dictates our movement direction
#define MOVE_ARG_DIRECTION 2
+/// From base of /client/Move(): (direction, old_dir)
+#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved"
/// From base of /client/Move(): (list/move_args)
#define COMSIG_MOB_CLIENT_PRE_LIVING_MOVE "mob_client_pre_living_move"
@@ -1058,6 +1084,21 @@
/// Sent from /proc/do_after once a do_after action completes, whether via the bar filling or via interruption.
#define COMSIG_DO_AFTER_ENDED "mob_do_after_ended"
+
// HUD:
/// Sent from /datum/hud/proc/eye_z_changed() : (old_offset, new_offset)
#define COMSIG_HUD_OFFSET_CHANGED "hud_offset_changed"
+
+
+///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"
+
diff --git a/code/__DEFINES/dcs/signals_object.dm b/code/__DEFINES/dcs/signals_object.dm
new file mode 100644
index 00000000000..2f403818a11
--- /dev/null
+++ b/code/__DEFINES/dcs/signals_object.dm
@@ -0,0 +1,8 @@
+// Jetpack things
+
+//called in /obj/item/tank/jetpack/proc/turn_on() : ()
+#define COMSIG_JETPACK_ACTIVATED "jetpack_activated"
+ #define JETPACK_ACTIVATION_FAILED (1<<0)
+//called in /obj/item/tank/jetpack/proc/turn_off() : ()
+#define COMSIG_JETPACK_DEACTIVATED "jetpack_deactivated"
+
diff --git a/code/__DEFINES/math.dm b/code/__DEFINES/math.dm
index e273f72e9f8..f1322a4b7d3 100644
--- a/code/__DEFINES/math.dm
+++ b/code/__DEFINES/math.dm
@@ -2,7 +2,6 @@
#define PI 3.1415
#define INFINITY 1e31 //closer than enough
-#define SQRT_2 1.41421356237
#define SHORT_REAL_LIMIT 16777216
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index f5e8a30b70c..95fa74ce865 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -393,3 +393,4 @@
// Return values for [/mob/living/proc/handle_ventcrawl()]
#define VENTCRAWL_IN_SUCCESS 1
#define VENTCRAWL_OUT_SUCCESS 2
+
diff --git a/code/__DEFINES/movement.dm b/code/__DEFINES/movement.dm
index 651a7be5df3..4b5dbabb7ba 100644
--- a/code/__DEFINES/movement.dm
+++ b/code/__DEFINES/movement.dm
@@ -1,3 +1,74 @@
+/// 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
+
+/// 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)) * 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)
+
+/// Highest value of var/move_to_delay to allow gliding, looks silly otherwise.
+/// This number is arbitary and a subject to change.
+#define MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE 16
+
+/// Above this multiplicative slowdown simplemobs will stop gliding, cause it looks pretty bad otherwise..
+#define END_GLIDE_SPEED 10
+
+//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
+
/**
* currently_z_moving defines. Higher numbers mean higher priority.
diff --git a/code/__DEFINES/traits/declarations.dm b/code/__DEFINES/traits/declarations.dm
index f43284cba78..122da71d7cd 100644
--- a/code/__DEFINES/traits/declarations.dm
+++ b/code/__DEFINES/traits/declarations.dm
@@ -80,6 +80,9 @@ Remember to update _globalvars/traits.dm if you're adding/removing/renaming trai
/// Give us unsafe_unwrenching protection
#define TRAIT_GUSTPROTECTION "gustprotection"
+/// Unlinks gliding from movement speed, meaning that there will be a delay between movements rather than a single move movement between tiles
+#define TRAIT_NO_GLIDE "no_glide"
+
/// Apply this to make a mob not dense, and remove it when you want it to no longer make them undense, other sorces of undesity will still apply. Always define a unique source when adding a new instance of this!
#define TRAIT_UNDENSE "undense"
diff --git a/code/__HELPERS/_lists.dm b/code/__HELPERS/_lists.dm
index 52521378e8a..578ae4d3383 100644
--- a/code/__HELPERS/_lists.dm
+++ b/code/__HELPERS/_lists.dm
@@ -5,38 +5,6 @@
* Sorting
*/
-/*
- * Misc
- */
-
- // binary search sorted insert
-// IN: Object to be inserted
-// LIST: List to insert object into
-// TYPECONT: The typepath of the contents of the list
-// COMPARE: The variable on the objects to compare
-#define BINARY_INSERT(IN, LIST, TYPECONT, COMPARE) \
- var/__BIN_CTTL = length(LIST);\
- if(!__BIN_CTTL) {\
- LIST += IN;\
- } else {\
- var/__BIN_LEFT = 1;\
- var/__BIN_RIGHT = __BIN_CTTL;\
- var/__BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
- var/##TYPECONT/__BIN_ITEM;\
- while(__BIN_LEFT < __BIN_RIGHT) {\
- __BIN_ITEM = LIST[__BIN_MID];\
- if(__BIN_ITEM.##COMPARE <= IN.##COMPARE) {\
- __BIN_LEFT = __BIN_MID + 1;\
- } else {\
- __BIN_RIGHT = __BIN_MID;\
- };\
- __BIN_MID = (__BIN_LEFT + __BIN_RIGHT) >> 1;\
- };\
- __BIN_ITEM = LIST[__BIN_MID];\
- __BIN_MID = __BIN_ITEM.##COMPARE > IN.##COMPARE ? __BIN_MID : __BIN_MID + 1;\
- LIST.Insert(__BIN_MID, IN);\
- }
-
/// Passed into BINARY_INSERT to compare keys
#define COMPARE_KEY __BIN_LIST[__BIN_MID]
/// Passed into BINARY_INSERT to compare values
@@ -51,7 +19,7 @@
* COMPARISON: The variable on the objects to compare
* COMPTYPE: How should the values be compared? Either COMPARE_KEY or COMPARE_VALUE.
*/
-#define BINARY_INSERT_TG(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
+#define BINARY_INSERT(INPUT, LIST, TYPECONT, COMPARE, COMPARISON, COMPTYPE) \
do {\
var/list/__BIN_LIST = LIST;\
var/__BIN_CTTL = length(__BIN_LIST);\
@@ -78,6 +46,45 @@
} 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)
+
+
//Returns a list in plain english as a string
/proc/english_list(var/list/input, nothing_text = "nothing", and_text = " and ", comma_text = ", ", final_comma_text = "" )
var/total = input.len
diff --git a/code/__HELPERS/mobs.dm b/code/__HELPERS/mobs.dm
index c588dbd6087..19b41109a3c 100644
--- a/code/__HELPERS/mobs.dm
+++ b/code/__HELPERS/mobs.dm
@@ -320,7 +320,7 @@
var/atom/target_loc = target?.loc
var/drifting = FALSE
- if(!user.Process_Spacemove(NONE) && user.inertia_dir)
+ if(SSmove_manager.processing_on(user, SSspacedrift))
drifting = TRUE
var/holding = user.get_active_hand()
@@ -351,7 +351,7 @@
. = FALSE
break
- if(drifting && (!(timed_action_flags & DA_IGNORE_SPACE_DRIFT) || !user.inertia_dir))
+ if(drifting && (!(timed_action_flags & DA_IGNORE_SPACE_DRIFT) || !SSmove_manager.processing_on(user, SSspacedrift)))
drifting = FALSE
user_loc = user.loc
diff --git a/code/__HELPERS/unsorted.dm b/code/__HELPERS/unsorted.dm
index fb685eeafe3..d847275dde3 100644
--- a/code/__HELPERS/unsorted.dm
+++ b/code/__HELPERS/unsorted.dm
@@ -2145,3 +2145,7 @@ GLOBAL_DATUM_INIT(dview_mob, /mob/dview, new)
return ITEM_SLOT_LEGCUFFED_STRING
if(ITEM_SLOT_ACCESSORY)
return ITEM_SLOT_ACCESSORY_STRING
+
+
+/proc/return_typenames(type)
+ return splittext("[type]", "/")
diff --git a/code/_globalvars/traits.dm b/code/_globalvars/traits.dm
index f3239a4fe72..4f4bfda99cb 100644
--- a/code/_globalvars/traits.dm
+++ b/code/_globalvars/traits.dm
@@ -48,6 +48,7 @@ GLOBAL_LIST_INIT(traits_by_type, list(
"TRAIT_LASEREYES" = TRAIT_LASEREYES,
"TRAIT_MUTE" = TRAIT_MUTE,
"TRAIT_NEGATES_GRAVITY" = TRAIT_NEGATES_GRAVITY,
+ "TRAIT_NO_GLIDE" = TRAIT_NO_GLIDE,
"TRAIT_NO_SLIP_ALL" = TRAIT_NO_SLIP_ALL,
"TRAIT_NO_SLIP_ICE" = TRAIT_NO_SLIP_ICE,
"TRAIT_NO_SLIP_SLIDE" = TRAIT_NO_SLIP_SLIDE,
diff --git a/code/controllers/subsystem/movement/move_handler.dm b/code/controllers/subsystem/movement/move_handler.dm
new file mode 100644
index 00000000000..13c1225c47b
--- /dev/null
+++ b/code/controllers/subsystem/movement/move_handler.dm
@@ -0,0 +1,187 @@
+/**
+ * Acts as a namespace for movement packet/type related procs
+ *
+ * Exists to provide an in code implementation of movement looping
+ * Replaces things like walk() or walk_to(), among others
+ *
+ * Because we're doing things in engine, we have a lot more control over how different operations are performed
+ * We also get more say in when things happen, so we can subject movements to the whims of the master controller
+ * Rather then using a fuck ton of cpu just moving mobs or meteors
+ *
+ * The goal is to keep the loops themselves reasonably barebone, and implement more advanced behavior and control via the signals
+ *
+ * This may be bypassed in cases where snowflakes are nessesary, or where performance is important. S not a hard and fast thing
+ *
+ * Every atom can have a movement packet, which contains information and behavior about currently active loops, and queuing info
+ * Loops control how movement actually happens. So there's a "move in this direction" loop, a "move randomly" loop
+ *
+ * You can find the logic for this control in this file
+ *
+ * Specifics of how different loops operate can be found in the movement_types.dm file, alongside the [add to loop][/datum/controller/subsystem/move_manager/proc/add_to_loop] helper procs that use them
+ *
+**/
+SUBSYSTEM_DEF(move_manager)
+ name = "Movement Handler"
+ flags = SS_NO_INIT|SS_NO_FIRE
+ runlevels = RUNLEVEL_GAME|RUNLEVEL_POSTGAME
+ offline_implications = "Move loops for movables are no longer available. No immediate action is needed."
+ ss_id = "move_manager"
+
+
+///Adds a movable thing to a movement subsystem. Returns TRUE if it all worked, FALSE if it failed somehow
+/datum/controller/subsystem/move_manager/proc/add_to_loop(atom/movable/thing_to_add, datum/controller/subsystem/movement/subsystem = SSmovement, datum/move_loop/loop_type, priority = MOVEMENT_DEFAULT_PRIORITY, flags, datum/extra_info)
+ var/datum/movement_packet/our_data = thing_to_add.move_packet
+ if(!our_data)
+ our_data = new(thing_to_add)
+
+ var/list/arguments = args.Copy(2) //Drop the atom, since the movement packet already knows about it
+ return our_data.add_loop(arglist(arguments))
+
+
+///Returns the subsystem's loop if we're processing on it, null otherwise
+/datum/controller/subsystem/move_manager/proc/processing_on(atom/movable/packet_owner, datum/controller/subsystem/movement/subsystem)
+ var/datum/movement_packet/packet = packet_owner.move_packet
+ if(!packet)
+ return
+ var/datum/move_loop/linked_loop = packet.existing_loops[subsystem]
+ if(!linked_loop)
+ return
+ if(linked_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
+ return linked_loop
+ if(linked_loop != packet.running_loop)
+ return
+ return linked_loop
+
+
+///A packet of information that describes the current state of a moving object
+/datum/movement_packet
+ ///Our parent atom
+ var/atom/movable/parent
+ ///The move loop that's currently running, excluding those that ignore priority.
+ var/datum/move_loop/running_loop
+ /**
+ * Flags passed from the move loop before it calls move() and unset right after.
+ * Allows for properties of a move loop to be easily checked by mechanics outside of it.
+ * Having this a bitfield rather than a type var means we don't get screwed over
+ * if the move loop gets deleted mid-move, FYI.
+ */
+ var/processing_move_loop_flags = NONE
+ ///Assoc list of subsystems -> loop datum. Only one datum is allowed per subsystem
+ var/list/existing_loops = list()
+
+
+/datum/movement_packet/New(atom/movable/parent)
+ src.parent = parent
+ parent.move_packet = src
+
+
+/datum/movement_packet/Destroy(force)
+ parent.move_packet = null
+ parent = null
+ for(var/datum/controller/subsystem/processor as anything in existing_loops)
+ var/datum/move_loop/loop = existing_loops[processor]
+ if(QDELETED(loop))
+ continue
+ qdel(loop)
+ existing_loops.Cut()
+ existing_loops = null //Catch anyone modifying this post del
+ return ..()
+
+
+///Adds a loop to our parent. Returns the created loop if a success, null otherwise
+/datum/movement_packet/proc/add_loop(datum/controller/subsystem/movement/subsystem, datum/move_loop/loop_type, priority, flags, datum/extra_info)
+ var/datum/move_loop/existing_loop = existing_loops[subsystem]
+
+ if(existing_loop && existing_loop.priority > priority)
+ if(!(existing_loop.flags & MOVEMENT_LOOP_IGNORE_PRIORITY) && !(flags & MOVEMENT_LOOP_IGNORE_PRIORITY))
+ return //Give up
+
+ if(existing_loop?.compare_loops(arglist(args.Copy(2))))
+ return //it already exists stop trying to make the same moveloop
+
+ var/datum/move_loop/new_loop = new loop_type(src, subsystem, parent, priority, flags, extra_info) //Pass the mob to move and ourselves in via new
+ var/list/arguments = args.Copy(6) //Just send the args we've not already dealt with
+
+ var/worked_out = new_loop.setup(arglist(arguments)) //Here goes the rest
+ if(!worked_out)
+ qdel(new_loop)
+ return
+
+ existing_loops[subsystem] = new_loop
+ if(existing_loop)
+ qdel(existing_loop) //We need to do this here because otherwise the packet would think it was empty, and self destruct
+ contest_running_loop(new_loop)
+ return new_loop
+
+
+///Attempts to contest the current running move loop. Returns TRUE if the loop is active, FALSE otherwise
+/datum/movement_packet/proc/contest_running_loop(datum/move_loop/contestant)
+ var/datum/controller/subsystem/movement/contesting_subsystem = contestant.controller
+
+ if(contestant.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
+ contesting_subsystem.add_loop(contestant)
+ return TRUE
+ if(!running_loop)
+ running_loop = contestant
+ contesting_subsystem.add_loop(running_loop)
+ return TRUE
+ if(running_loop.priority > contestant.priority)
+ return FALSE
+
+ var/datum/controller/subsystem/movement/current_subsystem = running_loop.controller
+
+ var/current_running_loop = running_loop
+ running_loop = contestant
+ current_subsystem.remove_loop(current_running_loop)
+ if(running_loop != contestant) // A signal registrant could have messed with things
+ return FALSE
+ contesting_subsystem.add_loop(contestant)
+ return TRUE
+
+
+///Tries to figure out the current favorite loop to run. More complex then just deciding between two different loops, assumes no running loop currently exists
+/datum/movement_packet/proc/decide_on_running_loop()
+ if(running_loop)
+ return
+ if(!length(existing_loops)) //Die
+ qdel(src)
+ return
+ var/datum/move_loop/favorite
+ for(var/datum/controller/subsystem/movement/owner as anything in existing_loops)
+ var/datum/move_loop/checking = existing_loops[owner]
+ if(checking.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
+ continue
+ if(favorite && favorite.priority > checking.priority)
+ continue
+ favorite = checking
+
+ if(!favorite) //This isn't an error state, since some loops ignore the concept of a running loop
+ return
+
+ var/datum/controller/subsystem/movement/favorite_subsystem = favorite.controller
+
+ running_loop = favorite
+ favorite_subsystem.add_loop(running_loop)
+
+
+/datum/movement_packet/proc/remove_loop(datum/controller/subsystem/movement/remove_from, datum/move_loop/loop_to_remove)
+ if(loop_to_remove == running_loop)
+ running_loop = null
+ remove_from.remove_loop(loop_to_remove)
+ if(loop_to_remove.flags & MOVEMENT_LOOP_IGNORE_PRIORITY)
+ remove_from.remove_loop(loop_to_remove)
+ if(QDELETED(src))
+ return
+ if(existing_loops[remove_from] == loop_to_remove)
+ existing_loops -= remove_from
+ decide_on_running_loop()
+ return
+
+
+/datum/movement_packet/proc/remove_subsystem(datum/controller/subsystem/movement/remove)
+ var/datum/move_loop/our_loop = existing_loops[remove]
+ if(!our_loop)
+ return FALSE
+ qdel(our_loop)
+ return TRUE
+
diff --git a/code/controllers/subsystem/movement/movement.dm b/code/controllers/subsystem/movement/movement.dm
new file mode 100644
index 00000000000..6363d74cf66
--- /dev/null
+++ b/code/controllers/subsystem/movement/movement.dm
@@ -0,0 +1,145 @@
+SUBSYSTEM_DEF(movement)
+ name = "Movement Loops"
+ flags = SS_NO_INIT|SS_BACKGROUND|SS_TICKER
+ wait = 1 //Fire each tick
+ offline_implications = "Move loops for movables are no longer available. No immediate action is needed."
+ cpu_display = SS_CPUDISPLAY_HIGH
+ ss_id = "move_manager"
+ /*
+ 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/get_stat_details()
+ var/total_len = 0
+ for(var/list/bucket as anything in sorted_buckets)
+ total_len += length(bucket[MOVEMENT_BUCKET_LIST])
+ return "B:[length(sorted_buckets)] E:[total_len]"
+
+
+/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/movement_types.dm b/code/controllers/subsystem/movement/movement_types.dm
new file mode 100644
index 00000000000..726f32557d8
--- /dev/null
+++ b/code/controllers/subsystem/movement/movement_types.dm
@@ -0,0 +1,894 @@
+///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/controller/subsystem/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/controller/subsystem/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/controller/subsystem/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/controller/subsystem/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/controller/subsystem/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/controller/subsystem/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 walk_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/controller/subsystem/move_manager/proc/move_away(moving, chasing, max_dist, delay, timeout, subsystem, priority, flags, datum/extra_info)
+ return add_to_loop(moving, subsystem, /datum/move_loop/has_target/dist_bound/move_away, priority, flags, extra_info, delay, timeout, chasing, max_dist)
+
+///Wrapper around walk_away()
+/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/controller/subsystem/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/controller/subsystem/move_manager/proc/home_onto(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
+ return move_towards(moving, chasing, delay, TRUE, timeout, subsystem, priority, flags, extra_info)
+
+///Used as a alternative to walk_towards
+/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 walk_towards, not reccomended, as it's 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/controller/subsystem/move_manager/proc/move_towards_legacy(moving, chasing, delay, timeout, subsystem, priority, flags, datum/extra_info)
+ return add_to_loop(moving, subsystem, /datum/move_loop/has_target/move_towards_budget, priority, flags, extra_info, delay, timeout, chasing)
+
+///The actual implementation of walk_towards()
+/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/controller/subsystem/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/controller/subsystem/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/controller/subsystem/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
+
+/*
+/**
+ * Snowflake disposal movement. Moves a disposal holder along a chain of disposal pipes
+ *
+ * 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/controller/subsystem/move_manager/proc/move_disposals(moving, delay, timeout, subsystem, priority, flags, datum/extra_info)
+ return add_to_loop(moving, subsystem, /datum/move_loop/disposal_holder, priority, flags, extra_info, delay, timeout)
+
+/// Disposal holders need to move through a chain of pipes
+/// Rather then through the world. This supports this
+/// If this ever changes, get rid of this, add drift component like logic to the holder
+/// And move them to move()
+/datum/move_loop/disposal_holder
+
+/datum/move_loop/disposal_holder/setup(delay = 1, timeout = INFINITY)
+ // This is a horrible pattern.
+ // Move loops should almost never need to be one offs. Please don't do this if you can help it
+ if(!istype(moving, /obj/structure/disposalholder))
+ stack_trace("You tried to make a [moving.type] object move like a disposals holder, stop that!")
+ return FALSE
+ return ..()
+
+/datum/move_loop/disposal_holder/move()
+ var/obj/structure/disposalholder/holder = moving
+ if(!holder.current_pipe)
+ return FALSE
+ var/atom/old_loc = moving.loc
+ holder.current_pipe = holder.current_pipe.transfer(holder)
+ return old_loc != moving?.loc ? MOVELOOP_SUCCESS : MOVELOOP_FAILURE
+*/
diff --git a/code/controllers/subsystem/movement/spacedrift.dm b/code/controllers/subsystem/movement/spacedrift.dm
new file mode 100644
index 00000000000..fb3098c8020
--- /dev/null
+++ b/code/controllers/subsystem/movement/spacedrift.dm
@@ -0,0 +1,9 @@
+MOVEMENT_SUBSYSTEM_DEF(spacedrift)
+ name = "Space Drift"
+ priority = FIRE_PRIORITY_SPACEDRIFT
+ flags = SS_NO_INIT|SS_TICKER
+ 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
+ ss_id = "space_drift"
+
diff --git a/code/controllers/subsystem/spacedrift.dm b/code/controllers/subsystem/spacedrift.dm
deleted file mode 100644
index c8e71dce9c2..00000000000
--- a/code/controllers/subsystem/spacedrift.dm
+++ /dev/null
@@ -1,62 +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
- ss_id = "space_drift"
- var/list/currentrun = list()
- var/list/processing = list()
-
-
-/datum/controller/subsystem/spacedrift/get_stat_details()
- return "P:[length(processing)]"
-
-
-/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(currentrun.len)
- var/atom/movable/AM = currentrun[currentrun.len]
- 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(NONE))
- AM.inertia_dir = NONE
-
- 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
- step(AM, AM.inertia_dir)
- AM.inertia_moving = FALSE
- AM.inertia_next_move = world.time + AM.inertia_move_delay
- if(AM.loc == old_loc)
- AM.inertia_dir = NONE
-
- AM.setDir(old_dir)
- AM.inertia_last_loc = AM.loc
- if(MC_TICK_CHECK)
- return
-
diff --git a/code/controllers/subsystem/throwing.dm b/code/controllers/subsystem/throwing.dm
index 759c924319b..de814a867c8 100644
--- a/code/controllers/subsystem/throwing.dm
+++ b/code/controllers/subsystem/throwing.dm
@@ -176,7 +176,7 @@ SUBSYSTEM_DEF(throwing)
finalize()
return
- if(!AM.Move(step, get_dir(AM, step), speed)) // 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
@@ -218,8 +218,7 @@ SUBSYSTEM_DEF(throwing)
if(QDELETED(thrownthing))
return
- if(isturf(thrownthing.loc))
- thrownthing.newtonian_move(REVERSE_DIR(init_dir))
+ thrownthing.newtonian_move(REVERSE_DIR(init_dir))
qdel(src)
diff --git a/code/controllers/subsystem/time_track.dm b/code/controllers/subsystem/time_track.dm
index 08930eb5a8c..7e948aa9980 100644
--- a/code/controllers/subsystem/time_track.dm
+++ b/code/controllers/subsystem/time_track.dm
@@ -31,6 +31,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/timer.dm b/code/controllers/subsystem/timer.dm
index 1f5ba549dbd..e7e93a2ec20 100644
--- a/code/controllers/subsystem/timer.dm
+++ b/code/controllers/subsystem/timer.dm
@@ -138,7 +138,7 @@ SUBSYSTEM_DEF(timer)
continue
ctime_timer.spent = 0
ctime_timer.timeToRun = REALTIMEOFDAY + ctime_timer.wait
- BINARY_INSERT_TG(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY)
+ BINARY_INSERT(ctime_timer, clienttime_timers, /datum/timedevent, ctime_timer, timeToRun, COMPARE_KEY)
else
qdel(ctime_timer)
@@ -526,7 +526,7 @@ SUBSYSTEM_DEF(timer)
else if (timeToRun >= TIMER_MAX(timer_subsystem))
L = timer_subsystem.second_queue
if(L)
- BINARY_INSERT_TG(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY)
+ BINARY_INSERT(src, L, /datum/timedevent, src, timeToRun, COMPARE_KEY)
return
// Get a local reference to the bucket list, this is faster than referencing the datum
diff --git a/code/datums/action.dm b/code/datums/action.dm
index 5107abf6575..2ae545aeba5 100644
--- a/code/datums/action.dm
+++ b/code/datums/action.dm
@@ -219,7 +219,7 @@
return FALSE
if(target && attack_self)
var/obj/item/I = target
- I.ui_action_click(owner, type, left_click)
+ I.ui_action_click(owner, src, left_click)
return TRUE
/datum/action/item_action/ApplyIcon(atom/movable/screen/movable/action_button/current_button)
@@ -496,7 +496,7 @@
/datum/action/item_action/jetpack_stabilization/ninja/UpdateButtonIcon()
. = ..()
var/obj/item/tank/jetpack/J = target
- if(!istype(J) || !J.stabilizers)
+ if(!istype(J) || !J.stabilize)
button.icon_state = "[background_icon_state]"
else
button.icon_state = "[background_icon_state]_active"
diff --git a/code/datums/components/drift.dm b/code/datums/components/drift.dm
new file mode 100644
index 00000000000..520d0793aea
--- /dev/null
+++ b/code/datums/components/drift.dm
@@ -0,0 +1,209 @@
+///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 = SSmove_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.dm b/code/datums/components/jetpack.dm
new file mode 100644
index 00000000000..604e9027ff8
--- /dev/null
+++ b/code/datums/components/jetpack.dm
@@ -0,0 +1,167 @@
+// 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
+ /// Whether we will skip trails effect
+ var/skip_trails
+ /// 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 return `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, skip_trails)
+ . = ..()
+ 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
+ src.skip_trails = skip_trails
+
+
+/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, skip_trails)
+ 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
+ src.skip_trails = skip_trails
+
+ if(trail && trail.effect_type != effect_type)
+ setup_trail(trail.holder)
+
+
+/datum/component/jetpack/Destroy(force)
+ QDEL_NULL(trail)
+ check_on_move = null
+ return ..()
+
+
+/datum/component/jetpack/proc/setup_trail(mob/user)
+ 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)
+
+ 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 cannot use jet in nowhere or from mecha/closet
+ return
+ if(!(user.movement_type & FLOATING) || user.buckled)//You cannot use jet in gravity or while buckled.
+ return
+ if(user.pulledby)//You cannot use jet if someone pull you
+ return
+ if(user.throwing)//You cannot use jet if you thrown
+ return
+ if(user.client.input_data.desired_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
+ if(!skip_trails)
+ 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/wet_floor.dm b/code/datums/components/wet_floor.dm
index 174a0be27a4..0ae8ff1cbc8 100644
--- a/code/datums/components/wet_floor.dm
+++ b/code/datums/components/wet_floor.dm
@@ -126,7 +126,7 @@
var/turf/simulated/T = parent
var/diff = world.time - last_process
var/decrease = 0
- var/t = T.air.temperature
+ var/t = T.air ? T.air.temperature : T20C
switch(t)
if(-INFINITY to T0C)
add_wet(TURF_WET_ICE, max_time_left()) //Water freezes into ice!
diff --git a/code/datums/spells/rod_form.dm b/code/datums/spells/rod_form.dm
index 3864c01bfa6..9fdf4873111 100644
--- a/code/datums/spells/rod_form.dm
+++ b/code/datums/spells/rod_form.dm
@@ -3,7 +3,7 @@
desc = "Take on the form of an immovable rod, destroying all in your path."
clothes_req = TRUE
human_req = FALSE
- base_cooldown = 60 SECONDS
+ base_cooldown = 1 MINUTES
cooldown_min = 20 SECONDS
invocation = "CLANG!"
invocation_type = "shout"
@@ -27,10 +27,10 @@
for(var/mob/living/M in targets)
var/turf/start = get_turf(M)
var/obj/effect/immovablerod/wizard/W = new(start, get_ranged_target_turf(M, M.dir, (15 + spell_level * 3)), rod_delay)
+ W.setDir(M.dir)
W.wizard = M
W.spell = src
W.max_distance += spell_level * 3 //You travel farther when you upgrade the spell
- W.start_turf = start
M.forceMove(W)
ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
M.status_flags |= GODMODE
@@ -40,17 +40,17 @@
* Wizard Version of the Immovable Rod
*/
/obj/effect/immovablerod/wizard
+ notify = FALSE
var/max_distance = 13
var/mob/living/wizard
- var/turf/start_turf
var/obj/effect/proc_holder/spell/spell
- notify = FALSE
-/obj/effect/immovablerod/wizard/Move(atom/newloc, direct = NONE, movetime)
- if(get_dist(start_turf, get_turf(src)) >= max_distance)
+/obj/effect/immovablerod/wizard/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
+ . = ..()
+ max_distance--
+ if((!loc || max_distance <= 0) && !QDELETED(src))
qdel(src)
- ..()
/obj/effect/immovablerod/wizard/Destroy()
@@ -60,6 +60,5 @@
wizard.forceMove(get_turf(src))
wizard = null
spell = null
- start_turf = null
return ..()
diff --git a/code/game/atoms.dm b/code/game/atoms.dm
index 1e2a419df8f..374e43f2552 100644
--- a/code/game/atoms.dm
+++ b/code/game/atoms.dm
@@ -9,7 +9,7 @@
/atom
layer = TURF_LAYER
plane = GAME_PLANE
- appearance_flags = TILE_BOUND
+ appearance_flags = TILE_BOUND|LONG_GLIDE
var/level = 2
var/flags = NONE
var/flags_2 = NONE
@@ -305,9 +305,11 @@
/atom/proc/on_reagent_change()
return
+
/atom/proc/Bumped(atom/movable/moving_atom)
SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom)
- return
+ return FALSE
+
/// Convenience proc to see if a container is open for chemistry handling
/atom/proc/is_open_container()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index a76762a1353..2d29738409c 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -1,6 +1,6 @@
/atom/movable
layer = OBJ_LAYER
- appearance_flags = TILE_BOUND|PIXEL_SCALE
+ appearance_flags = TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
glide_size = 8 // Default, adjusted when mobs move based on their movement delays
var/last_move = null
var/anchored = FALSE
@@ -18,20 +18,20 @@
var/throwforce = 0
var/pull_push_speed_modifier = 1
- ///The last time we pushed off something
- ///This is a hack to get around dumb him him me scenarios
- var/last_pushoff
-
/// If false makes [CanPass][/atom/proc/CanPass] call [CanPassThrough][/atom/movable/proc/CanPassThrough] on this type instead of using default behaviour
var/generic_canpass = TRUE
- var/inertia_dir = NONE
- var/atom/inertia_last_loc
+ /// 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
+ /// Are we moving with inertia? Mostly used as an optimization
var/inertia_moving = FALSE
- var/inertia_next_move = 0
+ /// 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
- ///Used for the calculate_adjacencies proc for icon smoothing.
+ /// Used for the calculate_adjacencies proc for icon smoothing.
var/can_be_unanchored = FALSE
/// Whether the atom allows mobs to be buckled to it. Can be ignored in [/atom/movable/proc/buckle_mob()] if force = TRUE
@@ -57,6 +57,8 @@
/// NONE:0 not doing a diagonal move. FIRST_DIAG_STEP:1 and SECOND_DIAG_STEP:2 doing the first/second step of the diagonal move.
var/moving_diagonally = NONE
+ ///contains every client mob corresponding to every client eye in this container. lazily updated by SSparallax and is sparse:
+ ///only the last container of a client eye has this list assuming no movement since SSparallax's last fire
var/list/client_mobs_in_contents
/// Either FALSE, [EMISSIVE_BLOCK_GENERIC], or [EMISSIVE_BLOCK_UNIQUE]
@@ -75,6 +77,7 @@
///is the mob currently ascending or descending through z levels?
var/currently_z_moving
+
/atom/movable/attempt_init(loc, ...)
var/turf/T = get_turf(src)
if(T && SSatoms.initialized != INITIALIZATION_INSSATOMS && GLOB.space_manager.is_zlevel_dirty(T.z))
@@ -127,7 +130,7 @@
AddComponent(/datum/component/overlay_lighting, is_directional = TRUE)
-/atom/movable/Destroy()
+/atom/movable/Destroy(force)
unbuckle_all_mobs(force = TRUE)
QDEL_NULL(em_block)
@@ -137,11 +140,16 @@
for(var/atom/movable/AM in contents)
qdel(AM)
LAZYCLEARLIST(client_mobs_in_contents)
- move_to_null_space()
if(pulledby)
pulledby.stop_pulling()
if(orbiting)
stop_orbit()
+ if(move_packet)
+ if(!QDELETED(move_packet))
+ qdel(move_packet)
+ move_packet = null
+
+ move_to_null_space()
/atom/movable/get_emissive_block()
@@ -253,9 +261,11 @@
/atom/movable/proc/stop_pulling()
if(!pulling)
return
-
pulling.pulledby = null
+ var/atom/movable/old_pulling = pulling
pulling = null
+ SEND_SIGNAL(old_pulling, COMSIG_ATOM_NO_LONGER_PULLED, src)
+ SEND_SIGNAL(src, COMSIG_ATOM_NO_LONGER_PULLING, old_pulling)
/**
@@ -296,6 +306,17 @@
/atom/movable/proc/setLoc(var/T, var/teleported=0)
loc = T
+
+/atom/movable/proc/set_glide_size(target = 8)
+ if(HAS_TRAIT(src, TRAIT_NO_GLIDE))
+ return
+ SEND_SIGNAL(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, target)
+ glide_size = target
+
+ for(var/mob/buckled_mob as anything in buckled_mobs)
+ buckled_mob.set_glide_size(target)
+
+
/**
* meant for movement with zero side effects. only use for objects that are supposed to move "invisibly" (like camera mobs or ghosts)
* if you want something to move onto a tile with a beartrap or recycler or tripmine or mouse without that object knowing about it at all, use this
@@ -305,23 +326,23 @@
var/atom/old_loc = loc
var/direction = get_dir(old_loc, new_loc)
loc = new_loc
- Moved(old_loc, direction, TRUE)
+ Moved(old_loc, direction, TRUE, FALSE)
-/atom/movable/Move(atom/newloc, direct = NONE, movetime)
+/atom/movable/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
+ //Early override for some cases like diagonal movement
+ if(glide_size_override && glide_size != glide_size_override)
+ set_glide_size(glide_size_override)
if(loc != newloc)
SEND_SIGNAL(src, COMSIG_MOVABLE_PRE_MOVE, oldloc)
- if(movetime > 0)
- glide_for(movetime)
-
- if(!(direct & (direct - 1))) //Cardinal move
- . = ..(newloc, direct) // don't pass up movetime
+ if(!ISDIAGONALDIR(direct)) //Cardinal move
+ . = ..(newloc, direct)
else //Diagonal move, split it into cardinal moves
moving_diagonally = FIRST_DIAG_STEP
var/first_step_dir
@@ -375,8 +396,9 @@
if(!.)
setDir(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 = NONE
return
@@ -385,6 +407,11 @@
set_currently_z_moving(FALSE, TRUE)
return
+ //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)
+
if(.)
Moved(oldloc, direct, FALSE)
@@ -392,7 +419,7 @@
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(s)
. = FALSE
if(currently_z_moving)
@@ -402,13 +429,14 @@
else
set_currently_z_moving(FALSE, TRUE)
+
// Called after a successful Move(). By this point, we've already moved
-/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE)
+/atom/movable/proc/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
+ SHOULD_CALL_PARENT(TRUE)
- if(!inertia_moving)
- inertia_next_move = world.time + inertia_move_delay
+ if(!inertia_moving && momentum_change)
newtonian_move(Dir)
- if(length(client_mobs_in_contents))
+ if(!moving_diagonally && client_mobs_in_contents)
update_parallax_contents()
SEND_SIGNAL(src, COMSIG_MOVABLE_MOVED, OldLoc, Dir, Forced)
@@ -424,14 +452,6 @@
light.source_atom.update_light()
return TRUE
-// Change glide size for the duration of one movement
-/atom/movable/proc/glide_for(movetime)
- if(movetime)
- glide_size = world.icon_size/max(DS2TICKS(movetime), 1)
-// spawn(movetime)
-// glide_size = initial(glide_size)
-// else
-// glide_size = initial(glide_size)
// Previously known as HasEntered()
// This is automatically called when something enters your square
@@ -442,15 +462,25 @@
/atom/movable/Uncrossed(atom/movable/AM)
SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM)
-/atom/movable/Bump(atom/A, yes) //the "yes" arg is to differentiate our Bump proc from byond's, without it every Bump() call would become a double Bump().
- if(A && yes)
- SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, A)
- if(throwing)
- throwing.finalize(A)
- . = TRUE
- if(QDELETED(A))
- return
- A.Bumped(src)
+
+/// The "custom_bump" arg is to differentiate our Bump proc in [/turf/Enter()] from byond's.
+/// Without it every Bump() call would become a double Bump().
+/// Return `TRUE` if you want to skip bump chain.
+/// Always check for null return in children, otherwise you will end with double bump.
+/atom/movable/Bump(atom/bumped_atom, custom_bump)
+ if(!custom_bump)
+ return null
+ if(!bumped_atom)
+ CRASH("Bump was called with no argument.")
+ . = FALSE
+ SEND_SIGNAL(src, COMSIG_MOVABLE_BUMP, bumped_atom)
+ if(throwing)
+ throwing.finalize(bumped_atom)
+ . = TRUE
+ if(QDELETED(bumped_atom))
+ return .
+ bumped_atom.Bumped(src)
+
/// Sets the currently_z_moving variable to a new value. Used to allow some zMovement sources to have precedence over others.
/atom/movable/proc/set_currently_z_moving(new_z_moving_value, forced = FALSE)
@@ -694,15 +724,19 @@
*
* 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]
+ * Mobs should return `TRUE` if they should be able to move of their own volition, see [/client/proc/Move]
*
* Arguments:
* * movement_dir - NONE 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 = NONE)
+/atom/movable/proc/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
if(has_gravity())
return TRUE
+ if(SEND_SIGNAL(src, COMSIG_MOVABLE_SPACEMOVE, movement_dir, continuous_move) & COMSIG_MOVABLE_STOP_SPACEMOVE)
+ return TRUE
+
if(pulledby && pulledby.pulledby != src)
return TRUE
@@ -719,17 +753,16 @@
/// Only moves the object if it's under no gravity
-/atom/movable/proc/newtonian_move(direction)
- if(!isturf(loc) || Process_Spacemove(NONE))
- inertia_dir = NONE
+/// Accepts the direction to move, if the push should be instant, and an optional parameter to fine tune the start delay
+/atom/movable/proc/newtonian_move(direction, instant = FALSE, start_delay = 0)
+ if(QDELETED(src) || !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
@@ -839,18 +872,16 @@
if(master)
return master.attack_hand(a, b, c)
-/atom/movable/proc/handle_buckled_mob_movement(newloc,direct,movetime)
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.glide_size = glide_size
- if(!buckled_mob.Move(newloc, direct, movetime))
- forceMove(buckled_mob.loc)
+
+/atom/movable/proc/handle_buckled_mob_movement(newloc, direct, glide_size_override)
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ if(!buckled_mob.Move(newloc, direct, glide_size_override)) //If a mob buckled to us can't make the same move as us
+ Move(buckled_mob.loc, direct) //Move back to its location
last_move = buckled_mob.last_move
- inertia_dir = last_move
- buckled_mob.inertia_dir = last_move
return FALSE
return TRUE
+
/atom/movable/proc/force_pushed(atom/movable/pusher, force = MOVE_FORCE_DEFAULT, direction)
return FALSE
@@ -874,7 +905,7 @@
return TRUE
-/atom/movable/proc/get_spacemove_backup(moving_direction)
+/atom/movable/proc/get_spacemove_backup(moving_direction, continuous_move)
for(var/checked_range in orange(1, get_turf(src)))
if(isarea(checked_range))
continue
diff --git a/code/game/gamemodes/blob/overmind.dm b/code/game/gamemodes/blob/overmind.dm
index 68ea63eda06..4a194f55c04 100644
--- a/code/game/gamemodes/blob/overmind.dm
+++ b/code/game/gamemodes/blob/overmind.dm
@@ -106,14 +106,14 @@
stat(null, "Core Health: [blob_core.obj_integrity]")
stat(null, "Power Stored: [blob_points]/[max_blob_points]")
-/mob/camera/blob/Move(var/NewLoc, var/Dir = 0)
+/mob/camera/blob/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(world.time < last_movement)
return
last_movement = world.time + 0.5 // cap to 20fps
- var/obj/structure/blob/B = locate() in range("3x3", NewLoc)
+ var/obj/structure/blob/B = locate() in range("3x3", newloc)
if(B)
- loc = NewLoc
+ loc = newloc
else
return 0
diff --git a/code/game/gamemodes/clockwork/clockwork_items.dm b/code/game/gamemodes/clockwork/clockwork_items.dm
index d838fd687f3..56b26d04b41 100644
--- a/code/game/gamemodes/clockwork/clockwork_items.dm
+++ b/code/game/gamemodes/clockwork/clockwork_items.dm
@@ -691,8 +691,8 @@
if(enchant_type)
. += "clockwork_robe_overlay_[enchant_type]"
-/obj/item/clothing/suit/hooded/clockrobe/ui_action_click(mob/user, actiontype)
- if(actiontype == /datum/action/item_action/activate/enchant)
+/obj/item/clothing/suit/hooded/clockrobe/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/activate/enchant))
if(!iscarbon(user))
return
var/mob/living/carbon/carbon = user
diff --git a/code/game/gamemodes/clockwork/clockwork_misc.dm b/code/game/gamemodes/clockwork/clockwork_misc.dm
index 9460f2f7f00..c76c66d41ce 100644
--- a/code/game/gamemodes/clockwork/clockwork_misc.dm
+++ b/code/game/gamemodes/clockwork/clockwork_misc.dm
@@ -5,7 +5,7 @@
icon = 'icons/obj/lavaland/dead_ratvar.dmi'
icon_state = "dead_ratvar"
flags = ON_BORDER
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
layer = FLY_LAYER
anchored = TRUE
density = TRUE
diff --git a/code/game/gamemodes/clockwork/cogscarab.dm b/code/game/gamemodes/clockwork/cogscarab.dm
index b3490a63842..a3f15934b07 100644
--- a/code/game/gamemodes/clockwork/cogscarab.dm
+++ b/code/game/gamemodes/clockwork/cogscarab.dm
@@ -216,8 +216,8 @@
SSticker.mode.remove_clocker(mind, FALSE)
adjustBruteLoss(health)
-/mob/living/silicon/robot/cogscarab/Bump(atom/movable/AM, yes)
- if(is_type_in_list(AM, allowed_bumpable_objects))
+/mob/living/silicon/robot/cogscarab/Bump(atom/bumped_atom, custom_bump)
+ if(custom_bump && is_type_in_list(bumped_atom, allowed_bumpable_objects))
return ..()
/mob/living/silicon/robot/cogscarab/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE)
diff --git a/code/game/gamemodes/devil/true_devil/_true_devil.dm b/code/game/gamemodes/devil/true_devil/_true_devil.dm
index d11dbe04ad8..201899cb7f8 100644
--- a/code/game/gamemodes/devil/true_devil/_true_devil.dm
+++ b/code/game/gamemodes/devil/true_devil/_true_devil.dm
@@ -133,7 +133,7 @@
// If the devil wants to actually attack, they have the pitchfork.
-/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE)
+/mob/living/carbon/true_devil/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/game/gamemodes/meteor/meteor.dm b/code/game/gamemodes/meteor/meteor.dm
index 52347ab0ebb..d3c3eb0fb39 100644
--- a/code/game/gamemodes/meteor/meteor.dm
+++ b/code/game/gamemodes/meteor/meteor.dm
@@ -25,7 +25,7 @@
var/waitduration = rand(3000,6000)
while(waveduration - world.time > 0)
sleep(max(65 - text2num("[wave]0") / 2, 40))
- spawn() spawn_meteors(6, GLOB.meteors_normal)
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 6, GLOB.meteors_normal)
wave++
sleep(waitduration)
sendmeteors()
diff --git a/code/game/gamemodes/meteor/meteors.dm b/code/game/gamemodes/meteor/meteors.dm
index 1a1e2dc7414..baace6c32cb 100644
--- a/code/game/gamemodes/meteor/meteors.dm
+++ b/code/game/gamemodes/meteor/meteors.dm
@@ -1,84 +1,112 @@
//Meteors probability of spawning during a given wave
-GLOBAL_LIST_INIT(meteors_normal, list(/obj/effect/meteor/dust=3, /obj/effect/meteor/medium=8, /obj/effect/meteor/big=3, \
- /obj/effect/meteor/flaming=1, /obj/effect/meteor/irradiated=3)) //for normal meteor event
-
-GLOBAL_LIST_INIT(meteors_threatening, list(/obj/effect/meteor/medium=4, /obj/effect/meteor/big=8, \
- /obj/effect/meteor/flaming=3, /obj/effect/meteor/irradiated=3)) //for threatening meteor event
-
-GLOBAL_LIST_INIT(meteors_catastrophic, list(/obj/effect/meteor/medium=5, /obj/effect/meteor/big=75, \
- /obj/effect/meteor/flaming=10, /obj/effect/meteor/irradiated=10, /obj/effect/meteor/tunguska = 1)) //for catastrophic meteor event
+GLOBAL_LIST_INIT(meteors_normal, list( //for normal meteor event
+ /obj/effect/meteor/dust = 3,
+ /obj/effect/meteor/medium = 8,
+ /obj/effect/meteor/big = 3,
+ /obj/effect/meteor/flaming = 1,
+ /obj/effect/meteor/irradiated = 3,
+))
+
+GLOBAL_LIST_INIT(meteors_threatening, list( //for threatening meteor event
+ /obj/effect/meteor/medium = 4,
+ /obj/effect/meteor/big = 8,
+ /obj/effect/meteor/flaming = 3,
+ /obj/effect/meteor/irradiated = 3,
+))
+
+GLOBAL_LIST_INIT(meteors_catastrophic, list( //for catastrophic meteor event
+ /obj/effect/meteor/medium = 5,
+ /obj/effect/meteor/big = 75,
+ /obj/effect/meteor/flaming = 10,
+ /obj/effect/meteor/irradiated = 10,
+ /obj/effect/meteor/tunguska = 1,
+))
GLOBAL_LIST_INIT(meteors_dust, list(/obj/effect/meteor/dust)) //for space dust event
GLOBAL_LIST_INIT(meteors_gore, list(/obj/effect/meteor/gore)) //Meaty Gore
-GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops
+GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/gore/ops)) //Meaty Ops
+
+GLOBAL_LIST_INIT(meteors_pigs, list(/obj/effect/meteor/gore/pigops)) // pigOps
+
+GLOBAL_LIST_INIT(meteors_space_dust, list(/obj/effect/meteor/space_dust/weak)) //for another space dust event
///////////////////////////////
//Meteor spawning global procs
///////////////////////////////
-/proc/spawn_meteors(var/number = 10, var/list/meteortypes)
- for(var/i = 0; i < number; i++)
- spawn_meteor(meteortypes)
+/proc/spawn_meteors(number = 10, list/meteor_types, direction)
+ for(var/i in 1 to number)
+ spawn_meteor(meteor_types, direction)
-/proc/spawn_meteor(var/list/meteortypes)
- var/turf/pickedstart
- var/turf/pickedgoal
+
+/proc/spawn_meteor(list/meteor_types, direction, atom/target)
+ var/turf/picked_start
+ var/turf/picked_goal
var/max_i = 10//number of tries to spawn meteor.
- while(!isspaceturf(pickedstart))
- var/startSide = pick(GLOB.cardinal)
- var/level = pick(levels_by_trait(STATION_LEVEL))
- pickedstart = spaceDebrisStartLoc(startSide, level)
- pickedgoal = spaceDebrisFinishLoc(startSide, level)
+ while(!isspaceturf(picked_start))
+ var/start_side
+ if(direction) //If a direction has been specified, we set start_side to it. Otherwise, pick randomly
+ start_side = direction
+ else
+ start_side = pick(GLOB.cardinal)
+ var/start_Z = pick(levels_by_trait(STATION_LEVEL))
+ picked_start = spaceDebrisStartLoc(start_side, start_Z)
+ if(target)
+ if(!isturf(target))
+ target = get_turf(target)
+ picked_goal = target
+ else
+ picked_goal = spaceDebrisFinishLoc(start_side, start_Z)
max_i--
- if(max_i<=0)
+ if(max_i <= 0)
return
- var/Me = pickweight(meteortypes)
- var/obj/effect/meteor/M = new Me(pickedstart)
- M.dest = pickedgoal
- M.z_original = pick(levels_by_trait(STATION_LEVEL))
- spawn(0)
- walk_towards(M, M.dest, 1)
- return
+ var/new_meteor = pickweight(meteor_types)
+ new new_meteor(picked_start, picked_goal)
+
+
+#define MAP_EDGE_PAD 1
-/proc/spaceDebrisStartLoc(startSide, Z)
+/proc/spaceDebrisStartLoc(start_side, Z)
var/starty
var/startx
- switch(startSide)
+ switch(start_side)
if(NORTH)
- starty = world.maxy-(TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
+ starty = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)
+ startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(EAST)
- starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
- startx = world.maxx-(TRANSITIONEDGE+1)
+ starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
+ startx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)
if(SOUTH)
- starty = (TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
+ starty = (TRANSITIONEDGE + MAP_EDGE_PAD)
+ startx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(WEST)
- starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
- startx = (TRANSITIONEDGE+1)
- var/turf/T = locate(startx, starty, Z)
- return T
+ starty = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
+ startx = (TRANSITIONEDGE + MAP_EDGE_PAD)
+ . = locate(startx, starty, Z)
+
/proc/spaceDebrisFinishLoc(startSide, Z)
var/endy
var/endx
switch(startSide)
if(NORTH)
- endy = TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
+ endy = (TRANSITIONEDGE + MAP_EDGE_PAD)
+ endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(EAST)
- endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
- endx = TRANSITIONEDGE
+ endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
+ endx = (TRANSITIONEDGE + MAP_EDGE_PAD)
if(SOUTH)
- endy = world.maxy-TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
+ endy = world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD)
+ endx = rand((TRANSITIONEDGE + MAP_EDGE_PAD), world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD))
if(WEST)
- endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
- endx = world.maxx-TRANSITIONEDGE
- var/turf/T = locate(endx, endy, Z)
- return T
+ endy = rand((TRANSITIONEDGE + MAP_EDGE_PAD),world.maxy-(TRANSITIONEDGE + MAP_EDGE_PAD))
+ endx = world.maxx-(TRANSITIONEDGE + MAP_EDGE_PAD)
+ . = locate(endx, endy, Z)
+
+#undef MAP_EDGE_PAD
+
///////////////////////
//The meteor effect
@@ -91,52 +119,73 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops
icon_state = "small"
density = TRUE
anchored = TRUE
- var/hits = 4
- var/hitpwr = 2 //Level of ex_act to be called on hit.
- var/dest
pass_flags = PASSTABLE
- var/heavy = 0
+
+ ///The resilience of our meteor
+ var/hits = 4
+ ///Level of ex_act to be called on hit.
+ var/hitpwr = EXPLODE_HEAVY
+ //Should we shake people's screens on impact
+ var/heavy = FALSE
+ ///Sound to play when we hit something
var/meteorsound = 'sound/effects/meteorimpact.ogg'
- var/z_original = 1
+ ///Our starting z level, prevents infinite meteors
+ var/z_original
- var/meteordrop = /obj/item/stack/ore/iron
+ //Potential items to spawn when we die. Can be list.
+ var/list/meteordrop = /obj/item/stack/ore/iron
+ ///How much stuff to spawn when we die
var/dropamt = 2
-/obj/effect/meteor/Initialize(mapload)
+ ///The thing we're moving towards, usually a turf
+ var/atom/dest
+ ///Lifetime in seconds
+ var/lifetime = 180 SECONDS
+ /// Chance to shake everyone screen on impact.
+ var/shake_chance = 50
+
+
+/obj/effect/meteor/Initialize(mapload, turf/target)
. = ..()
z_original = z
+ GLOB.meteor_list += src
+ SpinAnimation()
+ chase_target(target)
-/obj/effect/meteor/Move()
- if(z != z_original || loc == dest)
- qdel(src)
- return
- . = ..() //process movement...
+/obj/effect/meteor/Destroy()
+ GLOB.meteor_list -= src
+ return ..()
+
- if(.)//.. if did move, ram the turf we get in
- var/turf/T = get_turf(loc)
- ram_turf(T)
+/obj/effect/meteor/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
+ . = ..()
+ if(QDELETED(src))
+ return
+
+ if(OldLoc != loc)//If did move, ram the turf we get in
+ var/turf/ram_turf = get_turf(loc)
+ ram_turf(ram_turf)
- if(prob(10) && !isspaceturf(T))//randomly takes a 'hit' from ramming
+ if(prob(10) && !isspaceturf(ram_turf))//randomly takes a 'hit' from ramming
get_hit()
- return .
+ if(z != z_original || loc == get_turf(dest))
+ qdel(src)
-/obj/effect/meteor/Destroy()
- GLOB.meteor_list -= src
- walk(src,0) //this cancels the walk_towards() proc
- return ..()
-/obj/effect/meteor/New()
- ..()
- GLOB.meteor_list += src
- SpinAnimation()
+/obj/effect/meteor/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ return TRUE //Keeps us from drifting for no reason
+
+
+/obj/effect/meteor/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
-/obj/effect/meteor/Bump(atom/A)
- if(A)
- ram_turf(get_turf(A))
- playsound(src.loc, meteorsound, 40, 1)
- get_hit()
+ ram_turf(get_turf(bumped_atom))
+ playsound(loc, meteorsound, 40, TRUE)
+ get_hit()
/obj/effect/meteor/CanAllowThrough(atom/movable/mover, border_dir)
@@ -145,15 +194,32 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops
return TRUE
-/obj/effect/meteor/proc/ram_turf(var/turf/T)
+/obj/effect/meteor/proc/chase_target(atom/chasing, delay, home)
+ if(!isatom(chasing))
+ return
+ var/datum/move_loop/new_loop = SSmove_manager.move_towards(src, chasing, delay, home, lifetime)
+ if(!new_loop)
+ return
+
+ RegisterSignal(new_loop, COMSIG_PARENT_QDELETING, PROC_REF(handle_stopping))
+
+
+///Deals with what happens when we stop moving, IE we die
+/obj/effect/meteor/proc/handle_stopping()
+ SIGNAL_HANDLER
+ if(!QDELETED(src))
+ qdel(src)
+
+
+/obj/effect/meteor/proc/ram_turf(turf/target_turf)
//first bust whatever is in the turf
- for(var/atom/A in T)
- if(A != src)
- A.ex_act(hitpwr)
+ for(var/atom/thing as anything in (target_turf.contents - src))
+ thing.ex_act(hitpwr)
//then, ram the turf if it still exists
- if(T)
- T.ex_act(hitpwr)
+ if(!QDELETED(target_turf))
+ target_turf.ex_act(hitpwr)
+
//process getting 'hit' by colliding with a dense object
//or randomly when ramming turfs
@@ -161,167 +227,234 @@ GLOBAL_LIST_INIT(meteors_ops, list(/obj/effect/meteor/goreops)) //Meaty Ops
hits--
if(hits <= 0)
make_debris()
- meteor_effect(heavy)
+ meteor_effect()
qdel(src)
+
/obj/effect/meteor/ex_act()
return
-/obj/effect/meteor/attackby(obj/item/W as obj, mob/user as mob, params)
- if(istype(W, /obj/item/pickaxe))
+
+/obj/effect/meteor/attackby(obj/item/I, mob/user, params)
+ if(istype(I, /obj/item/pickaxe))
make_debris()
qdel(src)
return
return ..()
+
/obj/effect/meteor/proc/make_debris()
+ if(!meteordrop)
+ return
for(var/throws = dropamt, throws > 0, throws--)
- var/obj/item/O = new meteordrop(get_turf(src))
- O.throw_at(dest, 5, 10)
-
-/obj/effect/meteor/proc/meteor_effect(var/sound=1)
- if(sound)
- var/sound/meteor_sound = sound(meteorsound)
- var/random_frequency = get_rand_frequency()
-
- for(var/P in GLOB.player_list)
- var/mob/M = P
- var/turf/T = get_turf(M)
- if(!T || T.z != src.z)
- continue
- var/dist = get_dist(M.loc, src.loc)
- if(prob(50))
- shake_camera(M, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
- M.playsound_local(src.loc, null, 50, 1, random_frequency, 10, S = meteor_sound)
+ var/spawn_type = meteordrop
+ if(islist(meteordrop))
+ spawn_type = pick(meteordrop)
+ var/obj/item/thing_to_spawn = new spawn_type(get_turf(src))
+ if(dest)
+ thing_to_spawn.throw_at(dest, 5, 10)
+
+
+/obj/effect/meteor/proc/meteor_effect()
+ if(!heavy)
+ return
+
+ var/sound/meteor_sound = sound(meteorsound)
+ var/random_frequency = get_rand_frequency()
+
+ for(var/mob/mob as anything in GLOB.player_list)
+ var/turf/mob_turf = get_turf(mob)
+ if(!mob_turf || mob_turf.z != z)
+ continue
+ var/dist = get_dist(mob.loc, loc)
+ if(prob(shake_chance))
+ shake_camera(mob, dist > 20 ? 3 : 5, dist > 20 ? 1 : 3)
+ mob.playsound_local(loc, null, 50, TRUE, random_frequency, 10, S = meteor_sound)
+
+
+/**
+ * Handles the meteor's interaction with meteor shields.
+ *
+ * Returns TRUE if the meteor should be destroyed. Overridable for custom shield interaction.
+ * Return FALSE if a meteor's interaction with meteor shields should NOT destroy it.
+ *
+ * Arguments:
+ * * defender - The meteor shield that is vaporizing us.
+ */
+/obj/effect/meteor/proc/shield_defense(obj/machinery/satellite/meteor_shield/defender)
+ return TRUE
+
///////////////////////
//Meteor types
///////////////////////
-//Dust
-/obj/effect/meteor/dust
- name = "space dust"
- icon_state = "dust"
- pass_flags = PASSTABLE | PASSGRILLE
- hits = 1
- hitpwr = 3
- meteorsound = 'sound/weapons/tap.ogg'
- meteordrop = /obj/item/stack/ore/glass
-
//Medium-sized
/obj/effect/meteor/medium
name = "meteor"
dropamt = 3
+
/obj/effect/meteor/medium/meteor_effect()
- ..(heavy)
- explosion(src.loc, 0, 1, 2, 3, 0, cause = src)
+ . = ..()
+ explosion(loc, 0, 1, 2, 3, adminlog = FALSE, cause = src)
+
//Large-sized
/obj/effect/meteor/big
name = "large meteor"
icon_state = "large"
+ heavy = TRUE
hits = 6
- heavy = 1
dropamt = 4
+
/obj/effect/meteor/big/meteor_effect()
- ..(heavy)
- explosion(src.loc, 1, 2, 3, 4, 0, cause = src)
+ . = ..()
+ explosion(loc, 1, 2, 3, 4, adminlog = FALSE, cause = src)
+
//Flaming meteor
/obj/effect/meteor/flaming
name = "flaming meteor"
icon_state = "flaming"
hits = 5
- heavy = 1
+ heavy = TRUE
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = /obj/item/stack/ore/plasma
+
/obj/effect/meteor/flaming/meteor_effect()
- ..(heavy)
- explosion(src.loc, 1, 2, 3, 4, 0, 0, flame_range = 5, cause = src)
+ . = ..()
+ explosion(loc, 1, 2, 3, 4, adminlog = FALSE, flame_range = 5, cause = src)
+
//Radiation meteor
/obj/effect/meteor/irradiated
name = "glowing meteor"
icon_state = "glowing"
- heavy = 1
+ heavy = TRUE
meteordrop = /obj/item/stack/ore/uranium
/obj/effect/meteor/irradiated/meteor_effect()
- ..(heavy)
- explosion(src.loc, 0, 0, 4, 3, 0, cause = src)
+ . = ..()
+ explosion(loc, 0, 0, 4, 3, adminlog = FALSE, cause = src)
new /obj/effect/decal/cleanable/greenglow(get_turf(src))
for(var/mob/living/L in view(5, src))
L.apply_effect(40, IRRADIATE)
+
//Station buster Tunguska
/obj/effect/meteor/tunguska
name = "tunguska meteor"
icon_state = "flaming"
desc = "Your life briefly passes before your eyes the moment you lay them on this monstruosity."
hits = 30
- hitpwr = 1
- heavy = 1
+ hitpwr = EXPLODE_DEVASTATE
+ heavy = TRUE
meteorsound = 'sound/effects/bamf.ogg'
meteordrop = /obj/item/stack/ore/plasma
+
/obj/effect/meteor/tunguska/meteor_effect()
- ..(heavy)
- explosion(src.loc, 5, 10, 15, 20, 0, cause = src)
+ . = ..()
+ explosion(loc, 5, 10, 15, 20, adminlog = FALSE, cause = src)
-/obj/effect/meteor/tunguska/Bump()
- ..()
- if(prob(20))
- explosion(src.loc,2,4,6,8, cause = src)
+
+/obj/effect/meteor/tunguska/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.) || !prob(20))
+ return .
+ explosion(loc, 2, 4, 6, 8, cause = src)
//Gore
/obj/effect/meteor/gore
- name = "Oraganic Debris"
+ name = "organic debris"
icon = 'icons/mob/human.dmi'
- icon_state = "body_m_s"
+ icon_state = "fatbody_s"
hits = 1
- hitpwr = 0
+ hitpwr = EXPLODE_NONE
meteorsound = 'sound/effects/blobattack.ogg'
meteordrop = /obj/item/reagent_containers/food/snacks/meat
var/meteorgibs = /obj/effect/gibspawner/generic
+
/obj/effect/meteor/gore/make_debris()
- ..()
+ . = ..()
new meteorgibs(get_turf(src))
-/obj/effect/meteor/gore/ram_turf(turf/T)
- if(!isspaceturf(T))
- new /obj/effect/decal/cleanable/blood(T)
+/obj/effect/meteor/gore/ram_turf(turf/target_turf)
+ if(!isspaceturf(target_turf))
+ new /obj/effect/decal/cleanable/blood(target_turf)
+
-/obj/effect/meteor/gore/Bump(atom/A)
- A.ex_act(hitpwr)
+/obj/effect/meteor/gore/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.) || QDELETED(bumped_atom))
+ return .
+ bumped_atom.ex_act(hitpwr)
get_hit()
+
//Meteor Ops
-/obj/effect/meteor/goreops
- name = "MeteorOps"
+/obj/effect/meteor/gore/ops
+ name = "meteorOps"
icon = 'icons/mob/animal.dmi'
icon_state = "syndicaterangedpsace"
hits = 10
- hitpwr = 1
- meteorsound = 'sound/effects/blobattack.ogg'
- meteordrop = /obj/item/reagent_containers/food/snacks/meat
- var/meteorgibs = /obj/effect/gibspawner/generic
+ hitpwr = EXPLODE_DEVASTATE
-/obj/effect/meteor/goreops/make_debris()
- ..()
- new meteorgibs(get_turf(src))
+/obj/effect/meteor/gore/pigops
+ name = "pigOps"
+ icon = 'icons/mob/animal.dmi'
+ icon_state = "pig"
+ hitpwr = EXPLODE_DEVASTATE
+ hits = 3
+ shake_chance = 20
-/obj/effect/meteor/goreops/ram_turf(turf/T)
- if(!isspaceturf(T))
- new /obj/effect/decal/cleanable/blood(T)
-/obj/effect/meteor/goreops/Bump(atom/A)
- A.ex_act(hitpwr)
- get_hit()
+//Dust
+/obj/effect/meteor/dust
+ name = "dust"
+ desc = "Dust in space."
+ icon_state = "dust"
+ pass_flags = PASSTABLE|PASSGRILLE
+ hits = 1
+ hitpwr = EXPLODE_LIGHT
+ meteorsound = 'sound/weapons/tap.ogg'
+ meteordrop = /obj/item/stack/ore/glass
+
+
+// Space Dust
+/obj/effect/meteor/space_dust
+ name = "space dust"
+ desc = "Dust in space."
+ icon_state = "space_dust"
+ heavy = TRUE
+ hitpwr = EXPLODE_HEAVY
+ hits = 2
+ meteordrop = null
+
+
+/obj/effect/meteor/space_dust/ex_act(severity)
+ qdel(src)
+
+
+/obj/effect/meteor/space_dust/weak
+ hitpwr = EXPLODE_LIGHT
+ hits = 1
+
+
+/obj/effect/meteor/space_dust/strong
+ hitpwr = EXPLODE_DEVASTATE
+ hits = 6
+
+
+/obj/effect/meteor/space_dust/super
+ hitpwr = EXPLODE_DEVASTATE
+ hits = 40
+
diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm
index 40c0d7a6766..77580c83bbc 100644
--- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm
+++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon.dm
@@ -345,7 +345,7 @@
/mob/living/simple_animal/demon/pulse_demon/proc/is_valid_apc(obj/machinery/power/apc/A)
return istype(A) && !(A.stat & BROKEN) && !A.shorted
-/mob/living/simple_animal/demon/pulse_demon/Move(newloc)
+/mob/living/simple_animal/demon/pulse_demon/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/obj/machinery/power/new_power = locate(/obj/machinery/power) in newloc
var/obj/structure/cable/new_cable = locate(/obj/structure/cable) in newloc
diff --git a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm
index 9a04ac70cdf..4c9d3329f29 100644
--- a/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm
+++ b/code/game/gamemodes/miniantags/demons/pulse_demon/pulse_demon_interactions.dm
@@ -124,7 +124,7 @@
var/mob/living/simple_animal/demon/pulse_demon/demon = user
if(demon.bot_movedelay <= world.time && dir)
Move(get_step(get_turf(src), dir))
- demon.bot_movedelay = world.time + (BOT_STEP_DELAY * (base_speed - 1)) * ((dir in GLOB.diagonals) ? SQRT_2 : 1)
+ demon.bot_movedelay = world.time + (BOT_STEP_DELAY * (base_speed - 1)) * ((dir in GLOB.diagonals) ? sqrt(2) : 1)
/obj/machinery/recharger/attack_pulsedemon(mob/living/simple_animal/demon/pulse_demon/user)
user.forceMove(src)
diff --git a/code/game/gamemodes/miniantags/guardian/types/fire.dm b/code/game/gamemodes/miniantags/guardian/types/fire.dm
index d060bd60782..86f72a645d0 100644
--- a/code/game/gamemodes/miniantags/guardian/types/fire.dm
+++ b/code/game/gamemodes/miniantags/guardian/types/fire.dm
@@ -36,29 +36,24 @@
new /obj/effect/temp_visual/guardian/phase/out(get_turf(M))
summoner.AdjustHallucinate(10 SECONDS)
-/mob/living/simple_animal/hostile/guardian/fire/Crossed(AM as mob|obj, oldloc)
- ..()
+
+/mob/living/simple_animal/hostile/guardian/fire/Crossed(atom/movable/AM, oldloc)
+ . = ..()
collision_ignite(AM)
-/mob/living/simple_animal/hostile/guardian/fire/Bumped(atom/movable/moving_atom)
- ..()
- collision_ignite(moving_atom)
-/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj)
- ..()
- collision_ignite(AM)
+/mob/living/simple_animal/hostile/guardian/fire/MobBump(mob/bumped_mob)
+ . = ..()
+ collision_ignite(bumped_mob)
+
-/mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(AM as mob|obj)
+/mob/living/simple_animal/hostile/guardian/fire/proc/collision_ignite(atom/movable/AM)
if(isliving(AM))
var/mob/living/M = AM
if(AM != summoner && M.fire_stacks < 7)
M.fire_stacks = 7
M.IgniteMob()
-/mob/living/simple_animal/hostile/guardian/fire/Bump(AM as mob|obj)
- ..()
- collision_ignite(AM)
-
/obj/effect/proc_holder/spell/aoe/guardian_hallucination
name = "Волна галлюцинаций"
diff --git a/code/game/gamemodes/shadowling/ascendant_shadowling.dm b/code/game/gamemodes/shadowling/ascendant_shadowling.dm
index 552360dff47..45e01eb5e94 100644
--- a/code/game/gamemodes/shadowling/ascendant_shadowling.dm
+++ b/code/game/gamemodes/shadowling/ascendant_shadowling.dm
@@ -43,7 +43,7 @@
icon_living = "NurnKal"
update_icon(UPDATE_OVERLAYS)
-/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/ascendant_shadowling/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //copypasta from carp code
/mob/living/simple_animal/ascendant_shadowling/ex_act(severity)
diff --git a/code/game/machinery/computer/pod.dm b/code/game/machinery/computer/pod.dm
index 934e2ad24f1..a0cb80dd7be 100644
--- a/code/game/machinery/computer/pod.dm
+++ b/code/game/machinery/computer/pod.dm
@@ -388,9 +388,9 @@ GLOBAL_LIST_EMPTY(deathsquad_teles)
/obj/structure/deathsquad_tele/Bumped(atom/movable/moving_atom)
+ . = ..()
if(!ztarget || !working)
- return ..()
-
+ return .
INVOKE_ASYNC(src, PROC_REF(async_bump_effect), moving_atom)
diff --git a/code/game/machinery/doors/airlock_control.dm b/code/game/machinery/doors/airlock_control.dm
index 409f107a9bc..4b16a47717e 100644
--- a/code/game/machinery/doors/airlock_control.dm
+++ b/code/game/machinery/doors/airlock_control.dm
@@ -116,12 +116,12 @@
if(!surpress_send) send_status()
/obj/machinery/door/airlock/Bumped(atom/movable/moving_atom)
- ..(moving_atom)
+ . = ..()
if(ismecha(moving_atom))
var/obj/mecha/mecha = moving_atom
if(density && radio_connection && mecha.occupant && (allowed(mecha.occupant) || check_access_list(mecha.operation_req_access)))
send_status(1)
- return
+
/obj/machinery/door/airlock/set_frequency(new_frequency)
SSradio.remove_object(src, frequency)
diff --git a/code/game/machinery/doors/door.dm b/code/game/machinery/doors/door.dm
index 9c2e3cde763..c2dc48f8067 100644
--- a/code/game/machinery/doors/door.dm
+++ b/code/game/machinery/doors/door.dm
@@ -94,11 +94,11 @@
QDEL_NULL(spark_system)
return ..()
-/obj/machinery/door/Bumped(atom/movable/moving_atom)
- ..()
+/obj/machinery/door/Bumped(atom/movable/moving_atom, skip_effects = FALSE)
+ . = ..()
- if(operating || emagged)
- return
+ if(skip_effects || operating || emagged)
+ return .
if(ismob(moving_atom))
var/mob/B = moving_atom
if((isrobot(B)) && B.stat)
@@ -130,9 +130,9 @@
cmag_switch(TRUE, mecha.occupant)
return
do_animate("deny")
- return
-/obj/machinery/door/Move(new_loc, new_dir)
+
+/obj/machinery/door/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/turf/T = loc
. = ..()
move_update_air(T)
diff --git a/code/game/machinery/doors/firedoor.dm b/code/game/machinery/doors/firedoor.dm
index ae7735d96f7..d4b9ef9842c 100644
--- a/code/game/machinery/doors/firedoor.dm
+++ b/code/game/machinery/doors/firedoor.dm
@@ -78,13 +78,11 @@
affecting_areas.Cut()
return ..()
-/obj/machinery/door/firedoor/Bumped(atom/movable/moving_atom)
+
+/obj/machinery/door/firedoor/Bumped(atom/movable/moving_atom, skip_effects = FALSE)
if(panel_open || operating)
- SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom)
- return
- if(!density)
- return ..()
- return 0
+ return ..(moving_atom, TRUE)
+ return ..(moving_atom, density)
/obj/machinery/door/firedoor/proc/adjust_light()
diff --git a/code/game/machinery/doors/poddoor.dm b/code/game/machinery/doors/poddoor.dm
index 35d93b4d3fd..56487fd03cc 100644
--- a/code/game/machinery/doors/poddoor.dm
+++ b/code/game/machinery/doors/poddoor.dm
@@ -26,12 +26,9 @@
resistance_flags = INDESTRUCTIBLE | LAVA_PROOF | FIRE_PROOF | UNACIDABLE | ACID_PROOF
hackable = FALSE
-/obj/machinery/door/poddoor/Bumped(atom/movable/moving_atom)
- SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom)
- if(density)
- return
- else
- return 0
+
+/obj/machinery/door/poddoor/Bumped(atom/movable/moving_atom, skip_effects = TRUE)
+ . = ..()
/obj/machinery/door/poddoor/impassable/preopen
icon_state = "open"
diff --git a/code/game/machinery/doors/unpowered.dm b/code/game/machinery/doors/unpowered.dm
index bc885f22037..1ac89d39d0d 100644
--- a/code/game/machinery/doors/unpowered.dm
+++ b/code/game/machinery/doors/unpowered.dm
@@ -1,11 +1,8 @@
/obj/machinery/door/unpowered
explosion_block = 1
-/obj/machinery/door/unpowered/Bumped(atom/movable/moving_atom)
- if(locked)
- SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom)
- return
- ..()
+/obj/machinery/door/unpowered/Bumped(atom/movable/moving_atom, skip_effects = TRUE)
+ . = ..()
/obj/machinery/door/unpowered/attackby(obj/item/I, mob/user, params)
if(locked)
diff --git a/code/game/machinery/doors/windowdoor.dm b/code/game/machinery/doors/windowdoor.dm
index 38cd8a945a4..d9b8990b5c1 100644
--- a/code/game/machinery/doors/windowdoor.dm
+++ b/code/game/machinery/doors/windowdoor.dm
@@ -69,10 +69,10 @@
sleep(20)
close()
-/obj/machinery/door/window/Bumped(atom/movable/moving_atom)
- SEND_SIGNAL(src, COMSIG_ATOM_BUMPED, moving_atom)
+/obj/machinery/door/window/Bumped(atom/movable/moving_atom, skip_effects = TRUE)
+ . = ..()
if(operating || !density)
- return
+ return .
if(!ismob(moving_atom))
if(ismecha(moving_atom))
var/obj/mecha/mecha = moving_atom
diff --git a/code/game/machinery/hologram.dm b/code/game/machinery/hologram.dm
index a398b59e972..256b97d3170 100644
--- a/code/game/machinery/hologram.dm
+++ b/code/game/machinery/hologram.dm
@@ -504,7 +504,7 @@ For the other part of the code, check silicon say.dm. Particularly robot talk.*/
return ..()
-/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = NONE)
+/obj/effect/overlay/holo_pad_hologram/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/game/machinery/iv_drip.dm b/code/game/machinery/iv_drip.dm
index f466b97cc43..ceb17841630 100644
--- a/code/game/machinery/iv_drip.dm
+++ b/code/game/machinery/iv_drip.dm
@@ -78,7 +78,7 @@
if(bag)
. += bag.examine(user)
-/obj/machinery/iv_drip/Move(NewLoc, direct)
+/obj/machinery/iv_drip/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(!.) // ..() will return 0 if we didn't actually move anywhere.
return .
diff --git a/code/game/machinery/mass_driver.dm b/code/game/machinery/mass_driver.dm
index 4d98f81c249..6fe6387af9c 100644
--- a/code/game/machinery/mass_driver.dm
+++ b/code/game/machinery/mass_driver.dm
@@ -72,15 +72,14 @@
desc = "Now you're here, now you're over there."
density = TRUE
-/obj/machinery/mass_driver/bumper/Bumped(atom/movable/moving_atom)
- ..()
+/obj/machinery/mass_driver/bumper/Bumped(atom/movable/moving_atom)
+ . = ..()
set_density(FALSE)
step(moving_atom, get_dir(moving_atom, src))
spawn(1)
set_density(TRUE)
drive()
- return
////////////////MASS DRIVER FRAME///////////////////
diff --git a/code/game/machinery/rechargestation.dm b/code/game/machinery/rechargestation.dm
index c36adb4cfff..7c75ebfdfa6 100644
--- a/code/game/machinery/rechargestation.dm
+++ b/code/game/machinery/rechargestation.dm
@@ -99,8 +99,7 @@
qdel(src)
/obj/machinery/recharge_station/Bumped(atom/movable/moving_atom)
- ..()
-
+ . = ..()
if(ismob(moving_atom))
move_inside(moving_atom)
diff --git a/code/game/machinery/recycler.dm b/code/game/machinery/recycler.dm
index eda89339c75..1121d772068 100644
--- a/code/game/machinery/recycler.dm
+++ b/code/game/machinery/recycler.dm
@@ -88,26 +88,15 @@
icon_state = icon_name + "[is_powered]" + "[(blood ? "bld" : "")]" // add the blood tag at the end
-// This is purely for admin possession !FUN!.
-/obj/machinery/recycler/Bump(atom/movable/AM)
- ..()
- if(AM)
- Bumped(AM)
-
/obj/machinery/recycler/Bumped(atom/movable/moving_atom)
- ..()
-
- if(stat & (BROKEN|NOPOWER))
- return
- if(!anchored)
- return
- if(emergency_mode)
- return
-
+ . = ..()
+ if((stat & (BROKEN|NOPOWER)) || !anchored || emergency_mode)
+ return .
var/move_dir = get_dir(loc, moving_atom.loc)
if(move_dir == eat_dir)
eat(moving_atom)
+
/obj/machinery/recycler/proc/eat(atom/AM0, sound = 1)
var/list/to_eat = list(AM0)
if(isitem(AM0))
diff --git a/code/game/machinery/teleporter.dm b/code/game/machinery/teleporter.dm
index b23abf4d210..d074e71fc20 100644
--- a/code/game/machinery/teleporter.dm
+++ b/code/game/machinery/teleporter.dm
@@ -375,20 +375,20 @@
break
return power_station
-/obj/machinery/teleport/hub/Bumped(atom/movable/moving_atom)
- ..()
+/obj/machinery/teleport/hub/Bumped(atom/movable/moving_atom)
+ . = ..()
if(!is_teleport_allowed(z) && !admin_usage)
if(ismob(moving_atom))
to_chat(moving_atom, "You can't use this here.")
- return
+ return .
if(power_station && power_station.engaged && !panel_open && !blockAI(moving_atom) && !isspacepod(moving_atom))
if(!teleport(moving_atom) && isliving(moving_atom)) // the isliving(M) is needed to avoid triggering errors if a spark bumps the telehub
visible_message(span_warning("[src] emits a loud buzz, as its teleport portal flickers and fails!"))
playsound(loc, 'sound/machines/buzz-sigh.ogg', 50, FALSE)
power_station.toggle() // turn off the portal.
use_power(5000)
- return
+
/obj/machinery/teleport/hub/attackby(obj/item/I, mob/user, params)
if(exchange_parts(user, I))
@@ -497,22 +497,21 @@
return TRUE
return FALSE
-/obj/machinery/teleport/perma/Bumped(atom/movable/moving_atom)
- ..()
- if(stat & (BROKEN|NOPOWER))
- return
+/obj/machinery/teleport/perma/Bumped(atom/movable/moving_atom)
+ . = ..()
+ if((stat & (BROKEN|NOPOWER)) || !target || recalibrating || panel_open || blockAI(moving_atom))
+ return .
if(!is_teleport_allowed(z))
to_chat(moving_atom, "You can't use this here.")
- return
+ return .
+ do_teleport(moving_atom, target)
+ use_power(5000)
+ if(tele_delay)
+ recalibrating = TRUE
+ update_icon()
+ addtimer(CALLBACK(src, PROC_REF(BumpedCallback)), tele_delay)
- if(target && !recalibrating && !panel_open && !blockAI(moving_atom))
- do_teleport(moving_atom, target)
- use_power(5000)
- if(tele_delay)
- recalibrating = TRUE
- update_icon()
- addtimer(CALLBACK(src, PROC_REF(BumpedCallback)), tele_delay)
/obj/machinery/teleport/perma/proc/BumpedCallback()
recalibrating = FALSE
diff --git a/code/game/machinery/transformer.dm b/code/game/machinery/transformer.dm
index 964859cc967..9779618c9e2 100644
--- a/code/game/machinery/transformer.dm
+++ b/code/game/machinery/transformer.dm
@@ -70,11 +70,11 @@
update_icon(UPDATE_ICON_STATE)
/obj/machinery/transformer/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
// They have to be human to be transformed.
if(is_on_cooldown || !ishuman(moving_atom))
- return
+ return .
var/mob/living/carbon/human/H = moving_atom
var/move_dir = get_dir(loc, H.loc)
@@ -126,10 +126,10 @@
desc = "Turns anything placed inside black and white."
/obj/machinery/transformer/mime/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(is_on_cooldown)
- return
+ return .
// Crossed didn't like people lying down.
if(istype(moving_atom))
@@ -137,7 +137,7 @@
do_transform_mime(moving_atom)
else
to_chat(moving_atom, "Only items can be greyscaled.")
- return
+
/obj/machinery/transformer/proc/do_transform_mime(obj/item/I)
if(is_on_cooldown || stat & (BROKEN|NOPOWER))
@@ -188,10 +188,10 @@
icon_state = initial(icon_state)
/obj/machinery/transformer/xray/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(is_on_cooldown)
- return
+ return .
// Crossed didn't like people lying down.
if(ishuman(moving_atom))
diff --git a/code/game/mecha/equipment/weapons/weapons.dm b/code/game/mecha/equipment/weapons/weapons.dm
index 6744321c2a2..bb48133a3f0 100644
--- a/code/game/mecha/equipment/weapons/weapons.dm
+++ b/code/game/mecha/equipment/weapons/weapons.dm
@@ -169,20 +169,6 @@
/obj/item/projectile/beam/pulse/heavy
name = "heavy pulse laser"
icon_state = "pulse1_bl"
- var/life = 20
-
-/obj/item/projectile/beam/pulse/heavy/Bump(atom/A)
- A.bullet_act(src, def_zone)
- life -= 10
- if(ismob(A))
- var/mob/M = A
- if(istype(firer, /mob))
- add_attack_logs(firer, M, "Mecha-shot with [src]")
- else
- add_attack_logs(null, M, "Mecha-shot with [src]")
- if(life <= 0)
- qdel(src)
- return
/obj/item/mecha_parts/mecha_equipment/weapon/energy/taser
@@ -239,10 +225,10 @@
if(isobj(H.shoes) && !HAS_TRAIT(H.shoes, TRAIT_NODROP))
var/thingy = H.shoes
H.drop_item_ground(H.shoes)
- walk_away(thingy,chassis,15,2)
+ SSmove_manager.move_away(thingy, chassis, 15, 2)
spawn(20)
if(thingy)
- walk(thingy,0)
+ SSmove_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 a757e765d96..9bc60f0af19 100644
--- a/code/game/mecha/mecha.dm
+++ b/code/game/mecha/mecha.dm
@@ -334,31 +334,30 @@
//////////////////////////////////
//////// Movement procs ////////
//////////////////////////////////
-/obj/mecha/Process_Spacemove(movement_dir = NONE)
+/obj/mecha/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
. = ..()
if(.)
return TRUE
- if(thrusters_active && movement_dir && use_power(step_energy_drain))
- return TRUE
+
//Turns strafe OFF if not enough energy to step (with actuator module only)
if(strafe && actuator && !has_charge(actuator.energy_per_step))
toggle_strafe(silent = TRUE)
- var/atom/movable/backup = get_spacemove_backup(movement_dir)
- if(!backup)
- return FALSE
-
- //get_spacemove_backup() already checks if a returned turf is solid, so we can just go
- if(!istype(backup) || !movement_dir || backup.anchored)
+ var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move)
+ if(backup)
+ if(!istype(backup) || !movement_dir || backup.anchored || continuous_move)
+ return TRUE //get_spacemove_backup() already checks if a returned turf is solid, so we can just go
+ last_pushoff = world.time
+ if(backup.newtonian_move(REVERSE_DIR(movement_dir), instant = TRUE))
+ backup.last_pushoff = world.time
+ if(occupant)
+ to_chat(occupant, span_info("You push off of [backup] to propel yourself."))
return TRUE
- last_pushoff = world.time
- if(backup.newtonian_move(REVERSE_DIR(movement_dir)))
- backup.last_pushoff = world.time
- if(occupant)
- to_chat(occupant, span_info("You push off of [backup] to propel yourself."))
+ if(thrusters_active && movement_dir && use_power(step_energy_drain))
+ return TRUE
- return TRUE
+ return FALSE
/obj/mecha/relaymove(mob/user, direction)
@@ -418,10 +417,6 @@
if(internal_damage & MECHA_INT_CONTROL_LOST)
if(strafe) //No strafe while controls are malfunctioning
toggle_strafe(silent = TRUE)
- if(direction & (direction - 1)) //Trick to check for diagonal direction
- glide_for(step_in * 1.41)
- else
- glide_for(step_in)
move_result = mechsteprand()
move_type = MECHAMOVE_RAND
else if(direction & (UP|DOWN))
@@ -440,28 +435,25 @@
if(direction & (direction - 1)) //Trick to check for diagonal direction
if(strafe)
if(strafe_diagonal) //Diagonal strafe is overpowered, disabled by default on all mechas
- glide_for(step_in * 1.41)
step_in_final *= STRAFE_DIAGONAL_FACTOR //Applies speed multiplier if mecha moved diagonally
move_result = mechstep(direction, old_direction, step_in_final)
move_type = MECHAMOVE_STEP
else
- glide_for(step_in)
strafed_backwards = is_opposite_dir(convert_diagonal_dir(direction))
step_in_final *= strafed_backwards ? STRAFE_BACKWARDS_FACTOR : 1 //Applies speed multiplier if mecha moved backwards
move_result = mechstep(convert_diagonal_dir(direction), old_direction, step_in_final) //Any diagonal movement will be converted to cardinal via "convert_diagonal_dir" proc
move_type = MECHAMOVE_STEP
else
- glide_for(step_in * 1.41)
move_result = mechstep(direction)
move_type = MECHAMOVE_STEP
else
- glide_for(step_in)
strafed_backwards = is_opposite_dir(direction)
step_in_final *= strafed_backwards ? STRAFE_BACKWARDS_FACTOR : 1 //Applies speed multiplier if mecha moved backwards
move_result = mechstep(direction, old_direction, step_in_final)
move_type = MECHAMOVE_STEP
if(move_result && move_type)
+ set_glide_size(DELAY_TO_GLIDE_SIZE(step_in_final))
if(strafe && actuator) //Energy drain mechanics for actuator module
use_power(strafed_backwards ? (actuator.energy_per_step * STRAFE_BACKWARDS_FACTOR) : actuator.energy_per_step)
aftermove(move_type)
@@ -515,10 +507,6 @@
if(can_move < world.time)
. = FALSE // We lie to mech code and say we didn't get to move, because we want to handle power usage + cooldown ourself
flick("[initial_icon]-phase", src)
- if(direction & (direction - 1)) //moved diagonally
- glide_for(step_in * 4.23)
- else
- glide_for(step_in * 3)
forceMove(get_step(src, direction))
use_power(phasing_energy_drain)
playsound(src, stepsound, 40, 1)
@@ -531,70 +519,72 @@
if(. && stepsound)
playsound(src, stepsound, 40, 1)
-/obj/mecha/Bump(var/atom/obstacle, bump_allowed)
- if(throwing) //high velocity mechas in your face!
- var/breakthrough = 0
- if(istype(obstacle, /obj/structure/window))
- qdel(obstacle)
- breakthrough = 1
-
- else if(istype(obstacle, /obj/structure/grille/))
- var/obj/structure/grille/G = obstacle
- G.obj_break()
- breakthrough = 1
-
- else if(istype(obstacle, /obj/structure/table))
- var/obj/structure/table/T = obstacle
- qdel(T)
- breakthrough = 1
-
- else if(istype(obstacle, /obj/structure/rack))
- new /obj/item/rack_parts(obstacle.loc)
- qdel(obstacle)
- breakthrough = 1
-
- else if(istype(obstacle, /obj/structure/reagent_dispensers/fueltank))
- obstacle.ex_act(1)
-
- else if(isliving(obstacle))
- var/mob/living/L = obstacle
- var/hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg')
- if(L.flags & GODMODE)
- return
- L.take_overall_damage(5,0)
- if(L.buckled)
- L.buckled = 0
- L.Weaken(10 SECONDS)
- L.apply_effect(STUTTER, 10 SECONDS)
- playsound(src, pick(hit_sound), 50, 0, 0)
- breakthrough = 1
- else
- if(throwing)
- throwing.finalize()
- crashing = null
+/obj/mecha/Bump(atom/bumped_atom, custom_bump)
+ if(!custom_bump)
+ return null
+
+ if(!throwing)
+ . = ..()
+ if(.)
+ return .
+ if(isobj(bumped_atom))
+ var/obj/bumped_object = bumped_atom
+ if(!bumped_object.anchored)
+ step(bumped_atom, dir)
+ else if(ismob(bumped_atom))
+ step(bumped_atom, dir)
+ return .
+
+ //high velocity mechas in your face!
+ var/breakthrough = FALSE
+ if(istype(bumped_atom, /obj/structure/window))
+ qdel(bumped_atom)
+ breakthrough = TRUE
+
+ else if(istype(bumped_atom, /obj/structure/grille))
+ var/obj/structure/grille/grille = bumped_atom
+ grille.obj_break()
+ breakthrough = TRUE
+
+ else if(istype(bumped_atom, /obj/structure/table))
+ qdel(bumped_atom)
+ breakthrough = TRUE
+
+ else if(istype(bumped_atom, /obj/structure/rack))
+ new /obj/item/rack_parts(bumped_atom.loc)
+ qdel(bumped_atom)
+ breakthrough = TRUE
+
+ else if(istype(bumped_atom, /obj/structure/reagent_dispensers/fueltank))
+ bumped_atom.ex_act(EXPLODE_DEVASTATE)
+
+ else if(isliving(bumped_atom))
+ var/mob/living/bumped_living = bumped_atom
+ if(bumped_living.flags & GODMODE)
+ return
+ var/static/list/mecha_hit_sound = list('sound/weapons/genhit1.ogg','sound/weapons/genhit2.ogg','sound/weapons/genhit3.ogg')
+ bumped_living.take_overall_damage(5)
+ bumped_living.unbuckle_mob(force = TRUE)
+ bumped_living.Weaken(10 SECONDS)
+ bumped_living.apply_effect(STUTTER, 10 SECONDS)
+ playsound(src, pick(mecha_hit_sound), 50, FALSE)
+ breakthrough = TRUE
+ else
+ throwing.finalize()
+ crashing = null
- ..()
+ . = ..()
- if(breakthrough)
- if(crashing)
- spawn(1)
- throw_at(crashing, 50, throw_speed)
- else
- spawn(1)
- crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile.
- throw_at(crashing, 50, throw_speed)
+ if(breakthrough)
+ if(crashing)
+ spawn(1)
+ throw_at(crashing, 50, throw_speed)
+ else
+ spawn(1)
+ crashing = get_distant_turf(get_turf(src), dir, 3)//don't use get_dir(src, obstacle) or the mech will stop if he bumps into a one-direction window on his tile.
+ throw_at(crashing, 50, throw_speed)
- else
- if(bump_allowed)
- if(..())
- return
- if(isobj(obstacle))
- var/obj/O = obstacle
- if(!O.anchored)
- step(obstacle, dir)
- else if(ismob(obstacle))
- step(obstacle, dir)
///////////////////////////////////
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 71151494b9a..92800b5551e 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -84,7 +84,7 @@
target.set_buckled(src)
buckled_mobs |= target
target.throw_alert(ALERT_BUCKLED, /atom/movable/screen/alert/restrained/buckled)
- //target.set_glide_size(glide_size)
+ target.set_glide_size(glide_size)
target.Move(loc)
target.setDir(dir)
@@ -136,7 +136,7 @@
buckled_mob.set_buckled(null)
buckled_mob.set_anchored(initial(buckled_mob.anchored))
buckled_mob.clear_alert(ALERT_BUCKLED)
- //buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.cached_multiplicative_slowdown))
+ buckled_mob.set_glide_size(DELAY_TO_GLIDE_SIZE(buckled_mob.cached_multiplicative_slowdown))
buckled_mobs -= buckled_mob
if(anchored)
REMOVE_TRAIT(buckled_mob, TRAIT_NO_FLOATING_ANIM, BUCKLED_TRAIT)
diff --git a/code/game/objects/effects/anomalies.dm b/code/game/objects/effects/anomalies.dm
index 1fe78d5ea00..359b91ff17c 100644
--- a/code/game/objects/effects/anomalies.dm
+++ b/code/game/objects/effects/anomalies.dm
@@ -122,12 +122,19 @@
. = ..()
gravShock(AM)
-/obj/effect/anomaly/grav/Bump(atom/A)
- gravShock(A)
+
+/obj/effect/anomaly/grav/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
+ gravShock(bumped_atom)
+
/obj/effect/anomaly/grav/Bumped(atom/movable/moving_atom)
+ . = ..()
gravShock(moving_atom)
+
/obj/effect/anomaly/grav/proc/gravShock(mob/living/A)
if(boing && isliving(A) && !A.stat)
if(!knockdown) // no hardstuns with megafauna
@@ -161,10 +168,14 @@
. = ..()
mobShock(AM)
-/obj/effect/anomaly/flux/Bump(atom/A)
- mobShock(A)
+/obj/effect/anomaly/flux/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
+ mobShock(bumped_atom)
/obj/effect/anomaly/flux/Bumped(atom/movable/moving_atom)
+ . = ..()
mobShock(moving_atom)
/obj/effect/anomaly/flux/proc/mobShock(mob/living/M)
@@ -199,6 +210,7 @@
investigate_log("teleported [key_name_log(M)] to [COORD(M)]", INVESTIGATE_TELEPORTATION)
/obj/effect/anomaly/bluespace/Bumped(atom/movable/moving_atom)
+ . = ..()
if(isliving(moving_atom))
do_teleport(moving_atom, moving_atom, 8)
investigate_log("teleported [key_name_log(moving_atom)] to [COORD(moving_atom)]", INVESTIGATE_TELEPORTATION)
diff --git a/code/game/objects/effects/bump_teleporter.dm b/code/game/objects/effects/bump_teleporter.dm
index a6a3621d48f..e8c4a2b62df 100644
--- a/code/game/objects/effects/bump_teleporter.dm
+++ b/code/game/objects/effects/bump_teleporter.dm
@@ -25,20 +25,18 @@ GLOBAL_LIST_EMPTY(bump_teleporters)
/obj/effect/bump_teleporter/singularity_pull()
return
+
/obj/effect/bump_teleporter/Bumped(atom/movable/moving_atom)
- if (!ismob(moving_atom))
- // user.loc = src.loc // Stop at teleporter location
- return
-
- if (!id_target)
- // user.loc = src.loc // Stop at teleporter location, there is nowhere to teleport to.
- return
- for(var/bt in GLOB.bump_teleporters)
- var/obj/effect/bump_teleporter/teleporter = bt
+ . = ..()
+ if(!id_target || !ismob(moving_atom))
+ return .
+
+ for(var/obj/effect/bump_teleporter/teleporter as anything in GLOB.bump_teleporters)
if(teleporter.id == id_target)
moving_atom.forceMove(teleporter.loc)
process_special_effects(moving_atom)
- return
+ break
+
///Special effects for teleporter. Supposed to be overriden.
/obj/effect/bump_teleporter/proc/process_special_effects(mob/living/target)
diff --git a/code/game/objects/effects/effect_system/effect_system.dm b/code/game/objects/effects/effect_system/effect_system.dm
index 204b341d4d0..6cde8347aaa 100644
--- a/code/game/objects/effects/effect_system/effect_system.dm
+++ b/code/game/objects/effects/effect_system/effect_system.dm
@@ -11,69 +11,78 @@ would spawn and follow the beaker, even if it is carried or thrown.
pass_flags = PASSTABLE | PASSGRILLE
anchored = TRUE
-/obj/effect/particle_effect/New()
- ..()
- if(SSticker)
- GLOB.cameranet.updateVisibility(src)
+
+/obj/effect/particle_effect/Initialize(mapload)
+ . = ..()
+ GLOB.cameranet.updateVisibility(src)
+
/obj/effect/particle_effect/Destroy()
- if(SSticker)
- GLOB.cameranet.updateVisibility(src)
+ GLOB.cameranet.updateVisibility(src)
return ..()
+
+// Prevents effects from getting registered for SSspacedrift
+/obj/effect/particle_effect/newtonian_move(direction, instant = FALSE, start_delay = 0)
+ return TRUE
+
+
/datum/effect_system
var/number = 3
- var/cardinals = 0
+ var/cardinals = FALSE
var/turf/location
var/atom/holder
var/effect_type
var/total_effects = 0
var/autocleanup = FALSE //will delete itself after use
+
/datum/effect_system/Destroy()
holder = null
location = null
return ..()
-/datum/effect_system/proc/set_up(n = 3, c = 0, loca)
- if(n > 10)
- n = 10
- number = n
- cardinals = c
- if(isturf(loca))
- location = loca
- else
- location = get_turf(loca)
+
+/datum/effect_system/proc/set_up(number = 3, cardinals = FALSE, location)
+ src.number = min(number, 10)
+ src.cardinals = cardinals
+ src.location = get_turf(location)
+
/datum/effect_system/proc/attach(atom/atom)
holder = atom
+
/datum/effect_system/proc/start()
if(QDELETED(src))
return
for(var/i in 1 to number)
if(total_effects > 20)
return
- INVOKE_ASYNC(src, PROC_REF(generate_effect))
+ generate_effect()
+
/datum/effect_system/proc/generate_effect()
if(holder)
location = get_turf(holder)
- var/obj/effect/E = new effect_type(location)
+ var/obj/effect/effect = new effect_type(location)
total_effects++
var/direction
if(cardinals)
direction = pick(GLOB.cardinal)
else
direction = pick(GLOB.alldirs)
- var/steps_amt = pick(1,2,3)
- for(var/j in 1 to steps_amt)
- sleep(5)
- step(E,direction)
- if(!QDELETED(src))
- addtimer(CALLBACK(src, PROC_REF(decrement_total_effect)), 20)
-
-/datum/effect_system/proc/decrement_total_effect()
+ var/step_amt = pick(1,2,3)
+ var/step_delay = 5
+
+ var/datum/move_loop/loop = SSmove_manager.move(effect, direction, step_delay, timeout = step_delay * step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(decrement_total_effect))
+
+
+/datum/effect_system/proc/decrement_total_effect(datum/source)
+ SIGNAL_HANDLER
total_effects--
- if(autocleanup && total_effects <= 0)
- qdel(src)
+ if(!autocleanup || total_effects > 0)
+ return
+ QDEL_IN(src, 2 SECONDS)
+
diff --git a/code/game/objects/effects/effect_system/effects_explosion.dm b/code/game/objects/effects/effect_system/effects_explosion.dm
index 9276465daed..c6d1b3ce437 100644
--- a/code/game/objects/effects/effect_system/effects_explosion.dm
+++ b/code/game/objects/effects/effect_system/effects_explosion.dm
@@ -1,51 +1,62 @@
/obj/effect/particle_effect/expl_particles
- name = "explosive particles"
+ name = "fire"
icon_state = "explosion_particle"
- opacity = 1
+ opacity = TRUE
anchored = TRUE
-/obj/effect/particle_effect/expl_particles/New()
+
+/obj/effect/particle_effect/expl_particles/Initialize(mapload)
..()
- QDEL_IN(src, 15)
+ return INITIALIZE_HINT_LATELOAD
+
+
+/obj/effect/particle_effect/expl_particles/LateInitialize()
+ var/step_amt = pick(25;1,50;2,100;3,200;4)
+
+ var/datum/move_loop/loop = SSmove_manager.move(src, pick(GLOB.alldirs), 1, timeout = step_amt, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(end_particle))
+
+
+/obj/effect/particle_effect/expl_particles/proc/end_particle(datum/source)
+ SIGNAL_HANDLER
+ if(QDELETED(src))
+ return
+ qdel(src)
+
/datum/effect_system/expl_particles
number = 10
+
/datum/effect_system/expl_particles/start()
for(var/i in 1 to number)
- spawn(0)
- var/obj/effect/particle_effect/expl_particles/expl = new /obj/effect/particle_effect/expl_particles(location)
- var/direct = pick(GLOB.alldirs)
- var/steps_amt = pick(1;25,2;50,3,4;200)
- for(var/j in 1 to steps_amt)
- sleep(1)
- step(expl,direct)
+ new /obj/effect/particle_effect/expl_particles(location)
+
/obj/effect/explosion
name = "explosive particles"
icon = 'icons/effects/96x96.dmi'
icon_state = "explosion"
- opacity = 1
+ opacity = TRUE
anchored = TRUE
+ layer = ABOVE_ALL_MOB_LAYER
mouse_opacity = MOUSE_OPACITY_TRANSPARENT
pixel_x = -32
pixel_y = -32
-/obj/effect/explosion/New()
- ..()
- QDEL_IN(src, 10)
+
+/obj/effect/explosion/Initialize(mapload)
+ . = ..()
+ QDEL_IN(src, 1 SECONDS)
/datum/effect_system/explosion
-/datum/effect_system/explosion/set_up(loca)
- if(isturf(loca))
- location = loca
- else
- location = get_turf(loca)
+/datum/effect_system/explosion/set_up(location)
+ src.location = get_turf(location)
/datum/effect_system/explosion/start()
new/obj/effect/explosion(location)
- var/datum/effect_system/expl_particles/P = new/datum/effect_system/expl_particles()
+ var/datum/effect_system/expl_particles/P = new
P.set_up(10, 0, location)
P.start()
@@ -58,4 +69,4 @@
/datum/effect_system/explosion/smoke/start()
..()
- addtimer(CALLBACK(src, PROC_REF(create_smoke)), 5)
+ addtimer(CALLBACK(src, PROC_REF(create_smoke)), 0.5 SECONDS)
diff --git a/code/game/objects/effects/effect_system/effects_other.dm b/code/game/objects/effects/effect_system/effects_other.dm
index e0aaef0445a..9e0f6c3eb40 100644
--- a/code/game/objects/effects/effect_system/effects_other.dm
+++ b/code/game/objects/effects/effect_system/effects_other.dm
@@ -1,15 +1,141 @@
-/// Ion trails for spacepods and other space-flying things
+/////////////////////////////////////////////
+//////// Attach a trail to any object, that spawns when it moves (like for the jetpack)
+/// just pass in the object to attach it to in set_up
+/// Then do start() to start it and stop() to stop it, obviously
+/// and don't call start() in a loop that will be repeated otherwise it'll get spammed!
+/////////////////////////////////////////////
+
+/datum/effect_system/trail_follow
+ var/turf/oldposition
+ var/active = FALSE
+ var/allow_overlap = FALSE
+ var/auto_process = TRUE
+ var/qdel_in_time = 1 SECONDS
+ 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)) && (!oldposition.has_gravity() || !nograv_required))
+ var/obj/effect/new_effect = new effect_type(oldposition)
+ set_dir(new_effect)
+ if(fade && fadetype)
+ flick(fadetype, new_effect)
+ new_effect.icon_state = ""
+ if(qdel_in_time)
+ QDEL_IN(new_effect, qdel_in_time)
+ oldposition = get_turf(holder)
+
+
+/datum/effect_system/trail_follow/proc/set_dir(obj/effect/particle_effect/ion_trails/trails)
+ trails.setDir(holder.dir)
+
+
+/datum/effect_system/trail_follow/proc/check_conditions()
+ if(!get_turf(holder))
+ return FALSE
+ return TRUE
+
+
+/datum/effect_system/trail_follow/ion
+ effect_type = /obj/effect/particle_effect/ion_trails
+ nograv_required = TRUE
+ qdel_in_time = 2 SECONDS
+
+
+/datum/effect_system/trail_follow/ion/grav_allowed
+ nograv_required = FALSE
+
+
+/datum/effect_system/trail_follow/spacepod
+ effect_type = /obj/effect/particle_effect/ion_trails
+ nograv_required = TRUE
+ qdel_in_time = 2 SECONDS
+
+
+/datum/effect_system/trail_follow/spacepod/set_dir(obj/effect/particle_effect/ion_trails/trails1, obj/effect/particle_effect/ion_trails/trails2)
+ trails1.setDir(holder.dir)
+ trails2.setDir(holder.dir)
+
+
+/datum/effect_system/trail_follow/spacepod/generate_effect()
+ if(!check_conditions())
+ return stop()
+ if(oldposition && !(oldposition == get_turf(holder)) && (!oldposition.has_gravity() || !nograv_required))
+ // spacepod loc is always southwest corner of 4x4 space
+ var/turf/our_turf = holder.loc
+ var/loc1
+ var/loc2
+ switch(holder.dir)
+ if(NORTH)
+ loc1 = get_step(our_turf, SOUTH)
+ loc2 = get_step(loc1, EAST)
+ if(SOUTH) // More difficult, offset to the north!
+ loc1 = get_step(get_step(our_turf, NORTH), NORTH)
+ loc2 = get_step(loc1, EAST)
+ if(EAST) // Just one to the north should suffice
+ loc1 = get_step(our_turf , WEST)
+ loc2 = get_step(loc1, NORTH)
+ if(WEST) // One to the east and north from there
+ loc1 = get_step(get_step(our_turf, EAST), EAST)
+ loc2 = get_step(loc1, NORTH)
+ var/obj/effect/effect1 = new effect_type(loc1)
+ var/obj/effect/effect2 = new effect_type(loc2)
+ set_dir(effect1, effect2)
+ if(fade && fadetype)
+ flick(fadetype, effect1)
+ flick(fadetype, effect2)
+ effect1.icon_state = ""
+ effect2.icon_state = ""
+ if(qdel_in_time)
+ QDEL_IN(effect1, qdel_in_time)
+ QDEL_IN(effect2, qdel_in_time)
+ oldposition = get_turf(holder)
+
+
/obj/effect/particle_effect/ion_trails
name = "ion trails"
icon_state = "ion_trails"
- anchored = TRUE
-
-/obj/effect/particle_effect/ion_trails/Initialize(mapload, targetdir)
- . = ..()
- dir = targetdir
- flick("ion_fade", src)
- icon_state = null
- QDEL_IN(src, 2 SECONDS)
+
+
+/obj/effect/particle_effect/ion_trails/flight
+ icon_state = "ion_trails_flight"
+
//Reagent-based explosion effect
/datum/effect_system/reagents_explosion
diff --git a/code/game/objects/effects/effect_system/effects_water.dm b/code/game/objects/effects/effect_system/effects_water.dm
index 272cbebe3b2..1ef5d957809 100644
--- a/code/game/objects/effects/effect_system/effects_water.dm
+++ b/code/game/objects/effects/effect_system/effects_water.dm
@@ -2,27 +2,70 @@
/obj/effect/particle_effect/water
name = "water"
icon_state = "extinguish"
+ mouse_opacity = MOUSE_OPACITY_TRANSPARENT
+ pass_flags = PASSTABLE|PASSMACHINE|PASSSTRUCTURE|PASSGRILLE|PASSBLOB|PASSVEHICLE
var/life = 15
-/obj/effect/particle_effect/water/New()
- ..()
- QDEL_IN(src, 70)
+/obj/effect/particle_effect/water/Initialize(mapload)
+ . = ..()
+ QDEL_IN(src, 7 SECONDS)
+
-/obj/effect/particle_effect/water/Move(turf/newloc)
+/obj/effect/particle_effect/water/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(--life < 1)
- qdel()
- return 0
- if(newloc.density)
- return 0
- . = ..()
+ qdel(src)
+ return FALSE
+ return ..()
+
-/obj/effect/particle_effect/water/Bump(atom/A)
+/obj/effect/particle_effect/water/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
if(reagents)
- reagents.reaction(A)
- if(istype(A,/atom/movable))
- var/atom/movable/AM = A
- AM.water_act(life, COLD_WATER_TEMPERATURE, src)
- return ..()
+ reagents.reaction(bumped_atom)
+ bumped_atom.water_act(life, COLD_WATER_TEMPERATURE, src)
+
+
+///Extinguisher snowflake
+/obj/effect/particle_effect/water/extinguisher
+
+
+/obj/effect/particle_effect/water/extinguisher/Move(atom/newloc, direct = NONE, glide_size_override = 0)
+ . = ..()
+ if(!reagents)
+ return
+ var/turf/source_turf = get_turf(src)
+ reagents.reaction(source_turf)
+ for(var/atom/thing as anything in source_turf)
+ reagents.reaction(source_turf)
+
+
+/// Starts the effect moving at a target with a delay in deciseconds, and a lifetime in moves
+/// Returns the created loop
+/obj/effect/particle_effect/water/extinguisher/proc/move_at(atom/target, delay, lifetime)
+ var/datum/move_loop/loop = SSmove_manager.move_towards_legacy(src, target, delay, timeout = delay * lifetime, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(post_forcemove))
+ RegisterSignal(loop, COMSIG_PARENT_QDELETING, PROC_REF(movement_stopped))
+ return loop
+
+
+/obj/effect/particle_effect/water/extinguisher/proc/post_forcemove(datum/move_loop/source, success)
+ SIGNAL_HANDLER
+ if(!success)
+ end_life(source)
+
+
+/obj/effect/particle_effect/water/extinguisher/proc/movement_stopped(datum/move_loop/source)
+ SIGNAL_HANDLER
+ if(!QDELETED(src))
+ end_life(source)
+
+
+/obj/effect/particle_effect/water/extinguisher/proc/end_life(datum/move_loop/engine)
+ QDEL_IN(src, engine.delay) //Gotta let it stop drifting
+ animate(src, alpha = 0, time = engine.delay)
+
/////////////////////////////////////////////
// GENERIC STEAM SPREAD SYSTEM
diff --git a/code/game/objects/effects/spiders.dm b/code/game/objects/effects/spiders.dm
index ebcd597c409..b731d65179f 100644
--- a/code/game/objects/effects/spiders.dm
+++ b/code/game/objects/effects/spiders.dm
@@ -129,11 +129,6 @@
playsound(src.loc, user.dna.species.unarmed.attack_sound, 25, 1, -1)
attack_generic(user, max_integrity/3)
-/obj/structure/spider/spiderling/Bump(atom/user)
- if(istype(user, /obj/structure/table))
- loc = user.loc
- else
- ..()
/obj/structure/spider/spiderling/process()
if(travelling_in_vent)
@@ -186,8 +181,7 @@
for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
if(!v.welded)
entry_vent = v
- glide_for(3)
- walk_to(src, entry_vent, 1)
+ SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4))
break
if(isturf(loc))
amount_grown += rand(0,2)
@@ -227,8 +221,7 @@
available_turfs += S
if(!length(available_turfs))
return FALSE
- glide_for(3)
- walk_to(src, pick(available_turfs))
+ SSmove_manager.move_to(src, pick(available_turfs), 1, rand(2, 4))
return TRUE
/obj/structure/spider/spiderling/decompile_act(obj/item/matter_decompiler/C, mob/user)
diff --git a/code/game/objects/items.dm b/code/game/objects/items.dm
index 6a615a761be..151f103f920 100644
--- a/code/game/objects/items.dm
+++ b/code/game/objects/items.dm
@@ -764,7 +764,7 @@ GLOBAL_DATUM_INIT(fire_overlay, /mutable_appearance, mutable_appearance('icons/g
* The default action is attack_self().
* Checks before we get to here are: mob is alive, mob is not restrained, paralyzed, asleep, resting, laying, item is on the mob.
*/
-/obj/item/proc/ui_action_click(mob/user, actiontype, leftclick)
+/obj/item/proc/ui_action_click(mob/user, action, leftclick)
attack_self(user)
diff --git a/code/game/objects/items/weapons/extinguisher.dm b/code/game/objects/items/weapons/extinguisher.dm
index 602e834f465..440920b1a93 100644
--- a/code/game/objects/items/weapons/extinguisher.dm
+++ b/code/game/objects/items/weapons/extinguisher.dm
@@ -16,14 +16,25 @@
attack_verb = list("slammed", "whacked", "bashed", "thunked", "battered", "bludgeoned", "thrashed")
dog_fashion = /datum/dog_fashion/back
resistance_flags = FIRE_PROOF
+ /// The max amount of water this extinguisher can hold.
var/max_water = 50
- var/last_use = 1.0
- var/safety = 1
+ /// Does the welder extinguisher start with water.
+ var/starting_water = TRUE
+ /// Cooldown between uses.
+ var/last_use = 1
+ /// Can we refill this at a water tank?
var/refilling = FALSE
+ /// Can we actually fire currently?
+ var/safety = TRUE
+ /// something that should be replaced with base_icon_state
var/sprite_name = "fire_extinguisher"
- var/power = 5 //Maximum distance launched water will travel
- var/precision = 0 //By default, turfs picked from a spray are random, set to 1 to make it always have at least one water effect per row
- var/cooling_power = 2 //Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled
+ /// Maximum distance launched water will travel.
+ var/power = 5
+ /// By default, turfs picked from a spray are random, set to TRUE to make it always have at least one water effect per row.
+ var/precision = FALSE
+ /// Sets the cooling_temperature of the water reagent datum inside of the extinguisher when it is refilled.
+ var/cooling_power = 2
+
/obj/item/extinguisher/mini
name = "pocket fire extinguisher"
@@ -50,7 +61,7 @@
/obj/item/extinguisher/examine(mob/user)
. = ..()
- . += "The safety is [safety ? "on" : "off"]."
+ . += span_info("The safety is [safety ? "on" : "off"].")
/obj/item/extinguisher/update_icon_state()
@@ -62,8 +73,7 @@
desc = "The safety is [safety ? "on" : "off"]."
-
-/obj/item/extinguisher/attack_self(mob/user as mob)
+/obj/item/extinguisher/attack_self(mob/user)
safety = !safety
update_appearance(UPDATE_ICON_STATE|UPDATE_DESC)
to_chat(user, "The safety is [safety ? "on" : "off"].")
@@ -76,27 +86,28 @@
else
return ..()
+
/obj/item/extinguisher/proc/AttemptRefill(atom/target, mob/user)
if(istype(target, /obj/structure/reagent_dispensers/watertank) && target.Adjacent(user))
var/safety_save = safety
- safety = 1
+ safety = TRUE
if(reagents.total_volume == reagents.maximum_volume)
- to_chat(user, "\The [src] is already full!")
+ to_chat(user, span_notice("[src] is already full!"))
safety = safety_save
- return 1
- var/obj/structure/reagent_dispensers/watertank/W = target
- var/transferred = W.reagents.trans_to(src, max_water)
+ return TRUE
+ var/obj/structure/reagent_dispensers/watertank/watertank = target
+ var/transferred = watertank.reagents.trans_to(src, max_water)
if(transferred > 0)
- to_chat(user, "\The [src] has been refilled by [transferred] units")
- playsound(src.loc, 'sound/effects/refill.ogg', 50, 1, -6)
- for(var/datum/reagent/water/R in reagents.reagent_list)
- R.cooling_temperature = cooling_power
+ to_chat(user, span_notice("[src] has been refilled by [transferred] units."))
+ playsound(loc, 'sound/effects/refill.ogg', 50, TRUE, SHORT_RANGE_SOUND_EXTRARANGE)
+ for(var/datum/reagent/water/reagent as anything in reagents.reagent_list)
+ reagent.cooling_temperature = cooling_power
else
- to_chat(user, "\The [W] is empty!")
+ to_chat(user, span_notice("[watertank] is empty!"))
safety = safety_save
- return 1
- else
- return 0
+ return TRUE
+ return FALSE
+
/obj/item/extinguisher/afterattack(atom/target, mob/user , flag)
. = ..()
@@ -108,93 +119,77 @@
refilling = FALSE
return
- if(!safety)
- if(src.reagents.total_volume < 1)
- to_chat(usr, "\The [src] is empty.")
- return
-
- if(world.time < src.last_use + 20)
- return
-
- src.last_use = world.time
-
- if(reagents.chem_temp > 300 || reagents.chem_temp < 280)
- add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
- playsound(src.loc, 'sound/effects/extinguish.ogg', 75, 1, -3)
-
- var/direction = get_dir(src,target)
-
- if(usr.buckled && isobj(usr.buckled) && !usr.buckled.anchored )
- spawn(0)
- var/obj/structure/chair/C = null
- if(istype(usr.buckled, /obj/structure/chair))
- C = usr.buckled
- var/obj/B = usr.buckled
- var/movementdirection = turn(direction,180)
- if(C) C.propelled = 4
- B.glide_for(1)
- usr.glide_for(1)
- step(B, movementdirection)
- sleep(1)
- step(B, movementdirection)
- if(C) C.propelled = 3
- sleep(1)
- step(B, movementdirection)
- sleep(1)
- step(B, movementdirection)
- if(C) C.propelled = 2
- B.glide_for(2)
- usr.glide_for(2)
- sleep(2)
- step(B, movementdirection)
- if(C) C.propelled = 1
- sleep(2)
- step(B, movementdirection)
- if(C) C.propelled = 0
- B.glide_for(3)
- usr.glide_for(3)
- sleep(3)
- step(B, movementdirection)
- sleep(3)
- step(B, movementdirection)
- sleep(3)
- step(B, movementdirection)
-
- else user.newtonian_move(turn(direction, 180))
-
- var/turf/T = get_turf(target)
- var/turf/T1 = get_step(T,turn(direction, 90))
- var/turf/T2 = get_step(T,turn(direction, -90))
- var/list/the_targets = list(T,T1,T2)
- if(precision)
- var/turf/T3 = get_step(T1, turn(direction, 90))
- var/turf/T4 = get_step(T2,turn(direction, -90))
- the_targets = list(T,T1,T2,T3,T4)
-
- for(var/a=0, a<5, a++)
- spawn(0)
- var/obj/effect/particle_effect/water/W = new /obj/effect/particle_effect/water( get_turf(src) )
- var/turf/my_target = pick(the_targets)
- if(precision)
- the_targets -= my_target
- var/datum/reagents/R = new/datum/reagents(5)
- if(!W) return
- W.reagents = R
- R.my_atom = W
- if(!W || !src) return
- src.reagents.trans_to(W,1)
- for(var/b=0, b<5, b++)
- step_towards(W,my_target)
- if(!W || !W.reagents) return
- W.reagents.reaction(get_turf(W))
- for(var/atom/atm in get_turf(W))
- if(!W) return
- W.reagents.reaction(atm)
- if(isliving(atm)) //For extinguishing mobs on fire
- var/mob/living/M = atm
- M.ExtinguishMob()
-
- if(W.loc == my_target) break
- sleep(2)
+ if(safety)
+ return
+
+ if(reagents.total_volume < 1)
+ to_chat(user, span_danger("[src] is empty."))
+ return
+
+ if(world.time < last_use + 2 SECONDS)
+ return
+ last_use = world.time
+
+ if(reagents.chem_temp > 300 || reagents.chem_temp < 280)
+ add_attack_logs(user, target, "Sprayed with superheated or cooled fire extinguisher at Temperature [reagents.chem_temp]K")
+ playsound(loc, 'sound/effects/extinguish.ogg', 75, TRUE, -3)
+
+ var/direction = get_dir(src,target)
+
+ if(user.buckled && isobj(user.buckled) && !user.buckled.anchored)
+ var/movementdirection = REVERSE_DIR(direction)
+ addtimer(CALLBACK(src, PROC_REF(move_chair), user.buckled, movementdirection), 0.1 SECONDS)
else
- return ..()
+ user.newtonian_move(REVERSE_DIR(direction))
+
+ //Get all the turfs that can be shot at
+ var/turf/T = get_turf(target)
+ var/turf/T1 = get_step(T,turn(direction, 90))
+ var/turf/T2 = get_step(T,turn(direction, -90))
+ var/list/the_targets = list(T,T1,T2)
+ if(precision)
+ var/turf/T3 = get_step(T1, turn(direction, 90))
+ var/turf/T4 = get_step(T2,turn(direction, -90))
+ the_targets.Add(T3,T4)
+
+ var/list/water_particles = list()
+ for(var/a in 1 to 5)
+ var/obj/effect/particle_effect/water/extinguisher/water = new (get_turf(src))
+ var/my_target = pick(the_targets)
+ water_particles[water] = my_target
+ // If precise, remove turf from targets so it won't be picked more than once
+ if(precision)
+ the_targets -= my_target
+ var/datum/reagents/water_reagents = new(5)
+ water.reagents = water_reagents
+ water_reagents.my_atom = water
+ reagents.trans_to(water, 1)
+
+ //Make em move dat ass, hun
+ move_particles(water_particles)
+
+
+//Particle movement loop
+/obj/item/extinguisher/proc/move_particles(list/particles)
+ var/delay = 2
+ // Second loop: Get all the water particles and make them move to their target
+ for(var/obj/effect/particle_effect/water/extinguisher/water as anything in particles)
+ water.move_at(particles[water], delay, power)
+
+
+//Chair movement loop
+/obj/item/extinguisher/proc/move_chair(obj/buckled_object, movementdirection)
+ var/datum/move_loop/loop = SSmove_manager.move(buckled_object, movementdirection, 1, timeout = 9, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+ //This means the chair slowing down is dependant on the extinguisher existing, which is weird
+ //Couldn't figure out a better way though
+ RegisterSignal(loop, COMSIG_MOVELOOP_POSTPROCESS, PROC_REF(manage_chair_speed))
+
+
+/obj/item/extinguisher/proc/manage_chair_speed(datum/move_loop/move/source)
+ SIGNAL_HANDLER
+ switch(source.lifetime)
+ if(4 to 5)
+ source.delay = 2
+ if(1 to 3)
+ source.delay = 3
+
diff --git a/code/game/objects/items/weapons/grenades/chem_grenade.dm b/code/game/objects/items/weapons/grenades/chem_grenade.dm
index f1b1427cfd1..d4a8d44a53f 100644
--- a/code/game/objects/items/weapons/grenades/chem_grenade.dm
+++ b/code/game/objects/items/weapons/grenades/chem_grenade.dm
@@ -268,10 +268,11 @@
if(nadeassembly)
nadeassembly.hear_message(M, msg)
-/obj/item/grenade/chem_grenade/Bump()
- ..()
- if(nadeassembly)
- nadeassembly.process_movement()
+/obj/item/grenade/chem_grenade/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.) || !nadeassembly)
+ return .
+ nadeassembly.process_movement()
/obj/item/grenade/chem_grenade/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // called when a throw stops
..()
diff --git a/code/game/objects/items/weapons/grenades/clusterbuster.dm b/code/game/objects/items/weapons/grenades/clusterbuster.dm
index 44af94e16d3..fd4195a4093 100644
--- a/code/game/objects/items/weapons/grenades/clusterbuster.dm
+++ b/code/game/objects/items/weapons/grenades/clusterbuster.dm
@@ -42,7 +42,7 @@
icon_state = "clusterbang_segment_active"
payload = payload_type
active = 1
- walk_away(src,loc,rand(1,4))
+ SSmove_manager.move_away(src, loc, rand(1,4), 1)
spawn(rand(15,60))
prime()
@@ -64,7 +64,7 @@
var/obj/item/grenade/P = new type(loc)
if(istype(P, /obj/item/grenade))
P.active = 1
- walk_away(P,loc,rand(1,4))
+ SSmove_manager.move_away(P, loc, rand(1,4), 1)
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 7ab3942f399..e440961a233 100644
--- a/code/game/objects/items/weapons/grenades/grenade.dm
+++ b/code/game/objects/items/weapons/grenades/grenade.dm
@@ -17,12 +17,6 @@
var/display_timer = TRUE
-/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)
- return ..()
-
-
/obj/item/grenade/deconstruct(disassembled = TRUE)
if(!disassembled)
prime()
@@ -104,7 +98,7 @@
return TRUE
-/obj/item/grenade/attack_hand()
- walk(src, null, null)
- ..()
+/obj/item/grenade/attack_hand(mob/user)
+ SSmove_manager.stop_looping(src)
+ . = ..()
diff --git a/code/game/objects/items/weapons/implants/implant_stealth.dm b/code/game/objects/items/weapons/implants/implant_stealth.dm
index a87c96dfb8a..d02b1204165 100644
--- a/code/game/objects/items/weapons/implants/implant_stealth.dm
+++ b/code/game/objects/items/weapons/implants/implant_stealth.dm
@@ -195,14 +195,15 @@
addtimer(CALLBACK(src, PROC_REF(go_invisible)), 1 SECONDS, TIMER_OVERRIDE|TIMER_UNIQUE)
-/obj/structure/closet/cardboard/agent/Bump(atom/A, yes)
+/obj/structure/closet/cardboard/agent/Bump(atom/bumped_atom, custom_bump)
. = ..()
- if(isliving(A))
- reveal()
+ if(. || isnull(.) || !isliving(bumped_atom))
+ return .
+ reveal()
-/obj/structure/closet/cardboard/agent/Bumped(atom/movable/A)
+/obj/structure/closet/cardboard/agent/Bumped(atom/movable/moving_atom)
. = ..()
- if(isliving(A))
+ if(isliving(moving_atom))
reveal()
diff --git a/code/game/objects/items/weapons/tanks/jetpack.dm b/code/game/objects/items/weapons/tanks/jetpack.dm
index 77f9571f448..8f65466256b 100644
--- a/code/game/objects/items/weapons/tanks/jetpack.dm
+++ b/code/game/objects/items/weapons/tanks/jetpack.dm
@@ -7,51 +7,103 @@
distribute_pressure = ONE_ATMOSPHERE * O2STANDARD
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 = 0
- var/stabilizers = 0
- var/volume_rate = 500 //Needed for borg jetpack transfer
+ var/on = FALSE
+ var/stabilize = FALSE
+ var/skip_trails = FALSE
+ var/thrust_callback
+
+
+/obj/item/tank/jetpack/Initialize(mapload)
+ . = ..()
+ thrust_callback = CALLBACK(src, PROC_REF(allow_thrust), 0.01)
+ configure_jetpack(stabilize, skip_trails)
+
+
+/obj/item/tank/jetpack/Destroy()
+ thrust_callback = null
+ return ..()
+
+
+/**
+ * Configures/re-configures the jetpack component
+ *
+ * Arguments:
+ * * stabilize - if `TRUE` jetpack owner will not be affected by newtonian movement
+ * * skip_trails - if `TRUE` skips ion trails visualization
+ */
+/obj/item/tank/jetpack/proc/configure_jetpack(stabilize, skip_trails)
+ if(!isnull(stabilize))
+ src.stabilize = stabilize
+ if(!isnull(skip_trails))
+ src.skip_trails = skip_trails
+ AddComponent(
+ /datum/component/jetpack, \
+ src.stabilize, \
+ COMSIG_JETPACK_ACTIVATED, \
+ COMSIG_JETPACK_DEACTIVATED, \
+ JETPACK_ACTIVATION_FAILED, \
+ thrust_callback, \
+ /datum/effect_system/trail_follow/ion, \
+ src.skip_trails \
+ )
+
/obj/item/tank/jetpack/populate_gas()
- if(gas_type)
- switch(gas_type)
- if("oxygen")
- air_contents.oxygen = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
- if("carbon dioxide")
- air_contents.carbon_dioxide = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
-
-/obj/item/tank/jetpack/ui_action_click(mob/user, actiontype)
- if(actiontype == /datum/action/item_action/toggle_jetpack || actiontype == /datum/action/item_action/toggle_jetpack/ninja)
+ if(!gas_type)
+ return
+ switch(gas_type)
+ if("oxygen")
+ air_contents.oxygen = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
+ if("carbon dioxide")
+ air_contents.carbon_dioxide = ((6 * ONE_ATMOSPHERE) * volume / (R_IDEAL_GAS_EQUATION * T20C))
+
+
+/obj/item/tank/jetpack/item_action_slot_check(slot, mob/user)
+ if(slot & ITEM_SLOT_BACK)
+ return TRUE
+
+
+/obj/item/tank/jetpack/equipped(mob/user, slot, initial = FALSE)
+ . = ..()
+ if(on && !(slot & ITEM_SLOT_BACK))
+ turn_off(user)
+
+
+/obj/item/tank/jetpack/dropped(mob/user, slot, silent = FALSE)
+ . = ..()
+ if(on)
+ turn_off(user)
+
+
+/obj/item/tank/jetpack/ui_action_click(mob/user, action, leftclick)
+ if(istype(action, /datum/action/item_action/toggle_jetpack))
cycle(user)
- else if(actiontype == /datum/action/item_action/jetpack_stabilization || actiontype == /datum/action/item_action/jetpack_stabilization/ninja)
- toggle_stabilization(user)
+ else if(istype(action, /datum/action/item_action/jetpack_stabilization))
+ if(on)
+ configure_jetpack(!stabilize)
+ to_chat(user, span_notice("You turn the jetpack stabilization [stabilize ? "on" : "off"]."))
+ for(var/datum/action/existing as anything in actions)
+ existing.UpdateButtonIcon()
else
toggle_internals(user)
-/obj/item/tank/jetpack/proc/toggle_stabilization(mob/user)
- if(on)
- stabilizers = !stabilizers
- to_chat(user, "You turn [src]'s stabilization [stabilizers ? "on" : "off"].")
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
-/obj/item/tank/jetpack/proc/cycle(mob/user, must_be_on_back = TRUE)
+/obj/item/tank/jetpack/proc/cycle(mob/user)
if(user.incapacitated())
return
- if(must_be_on_back && src != user.back)
- to_chat(user, "You need [src] to be on your back!")
- return
-
if(!on)
- turn_on(user)
- to_chat(user, "You turn the jetpack on.")
+ if(turn_on(user))
+ to_chat(user, span_notice("You turn the jetpack on."))
+ else
+ to_chat(user, span_notice("You fail to turn the jetpack on."))
+ return
else
turn_off(user)
- to_chat(user, "You turn the jetpack off.")
- for(var/X in actions)
- var/datum/action/A = X
- A.UpdateButtonIcon()
+ to_chat(user, span_notice("You turn the jetpack off."))
+
+ for(var/datum/action/action as anything in actions)
+ action.UpdateButtonIcon()
/obj/item/tank/jetpack/update_icon_state()
@@ -59,39 +111,48 @@
/obj/item/tank/jetpack/proc/turn_on(mob/user)
+ if(SEND_SIGNAL(src, COMSIG_JETPACK_ACTIVATED, user) & JETPACK_ACTIVATION_FAILED)
+ return FALSE
on = TRUE
update_icon(UPDATE_ICON_STATE)
+ return TRUE
+
/obj/item/tank/jetpack/proc/turn_off(mob/user)
+ SEND_SIGNAL(src, COMSIG_JETPACK_DEACTIVATED, user)
on = FALSE
- stabilizers = FALSE
update_icon(UPDATE_ICON_STATE)
-/obj/item/tank/jetpack/proc/allow_thrust(num, mob/living/user, should_leave_trail)
- if(!on)
- return 0
- if((num < 0.005 || air_contents.total_moles() < num))
+
+/// num argument is set on jetpack init, in a CALLBACK
+/// use_fuel argument comes from an attached component (used to check if we can start and skips fuel usage)
+/obj/item/tank/jetpack/proc/allow_thrust(num, use_fuel = TRUE)
+ var/mob/user = get_owner()
+ if(!user)
+ return FALSE
+
+ 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)
+ // We've got the gas, it's chill
+ if(!use_fuel)
+ return TRUE
+
+ var/datum/gas_mixture/removed = remove_air(num)
if(removed.total_moles() < 0.005)
turn_off(user)
- return 0
+ return FALSE
- var/turf/T = get_turf(user)
+ var/turf/T = get_turf(src)
T.assume_air(removed)
+ return TRUE
- if(!user.has_gravity(T) && should_leave_trail)
- new /obj/effect/particle_effect/ion_trails(T)
- return 1
+/obj/item/tank/jetpack/proc/get_owner()
+ if(ishuman(loc))
+ return loc
-/obj/item/tank/jetpack/Moved(OldLoc, Dir, Forced)
- var/mob/living/carbon/human/holder = loc
- if(on && !(istype(holder) && holder.back == src))
- turn_off()
- ..()
/obj/item/tank/jetpack/improvised
name = "improvised jetpack"
@@ -101,13 +162,20 @@
volume = 20 //normal jetpacks have 70 volume
gas_type = null //it starts empty
-/obj/item/tank/jetpack/improvised/allow_thrust(num, mob/living/user, should_leave_trail)
+
+/obj/item/tank/jetpack/improvised/allow_thrust(num, use_fuel = TRUE)
+ var/mob/user = get_owner()
+ if(!user)
+ return FALSE
+
if(rand(0, 250) == 0)
- to_chat(user, "You feel your jetpack's engines cut out.")
+ to_chat(user, span_notice("You feel your jetpack's engines cut out."))
turn_off(user)
- return
+ return FALSE
+
return ..()
+
/obj/item/tank/jetpack/void
name = "Void Jetpack (Oxygen)"
desc = "It works well in a void."
@@ -169,60 +237,73 @@
w_class = WEIGHT_CLASS_NORMAL
actions_types = list(/datum/action/item_action/toggle_jetpack, /datum/action/item_action/jetpack_stabilization)
volume = 1
- slot_flags = null
+ slot_flags = NONE
gas_type = null
fillable = FALSE
var/datum/gas_mixture/temp_air_contents
- var/obj/item/tank/internals/tank = null
- var/mob/living/carbon/human/cur_user
+ var/obj/item/tank/internals/tank
+ var/obj/item/clothing/suit/space/our_suit
-/obj/item/tank/jetpack/suit/New()
- ..()
+
+/obj/item/tank/jetpack/suit/Initialize(mapload)
+ . = ..()
STOP_PROCESSING(SSobj, src)
temp_air_contents = air_contents
+
+/obj/item/tank/jetpack/suit/Destroy()
+ our_suit = null
+ tank = null
+ temp_air_contents = null
+ return ..()
+
+
+/obj/item/tank/jetpack/suit/item_action_slot_check(slot, mob/user)
+ return TRUE
+
+
+/obj/item/tank/jetpack/suit/get_owner()
+ if(our_suit && ishuman(our_suit.loc))
+ return our_suit.loc
+
+
/obj/item/tank/jetpack/suit/attack_self()
return
+
/obj/item/tank/jetpack/suit/examine(mob/user)
. = ..(user, show_contents_info = FALSE)
-/obj/item/tank/jetpack/suit/cycle(mob/user)
- if(!istype(loc, /obj/item/clothing/suit/space))
- to_chat(user, "[src] must be connected to your suit!")
- return
- var/mob/living/carbon/human/H = user
- if(!istype(H.s_store, /obj/item/tank))
- to_chat(user, "You need a tank in your suit storage!")
- return
- ..(user, must_be_on_back = FALSE)
-/obj/item/tank/jetpack/suit/turn_on(mob/user)
- if(!ishuman(loc.loc) || loc.loc != user)
- return
- var/mob/living/carbon/human/H = user
- tank = H.s_store
+/obj/item/tank/jetpack/suit/allow_thrust(num, use_fuel = TRUE)
+ if(!our_suit)
+ return FALSE
+ if(!istype(tank, /obj/item/tank))
+ return FALSE
+ return ..()
+
+
+/obj/item/tank/jetpack/suit/turn_on(mob/living/carbon/human/user)
+ if(!ishuman(user))
+ return FALSE
+ if(!our_suit)
+ to_chat(user, span_warning("[src] must be connected to your suit!"))
+ return FALSE
+ if(!istype(user.s_store, /obj/item/tank))
+ to_chat(user, span_warning("You need a tank in your suit storage!"))
+ return FALSE
+ tank = user.s_store
air_contents = tank.air_contents
START_PROCESSING(SSobj, src)
- cur_user = user
- ..()
+ return ..()
+
-/obj/item/tank/jetpack/suit/turn_off(mob/user)
+/obj/item/tank/jetpack/suit/turn_off(mob/living/carbon/human/user)
tank = null
air_contents = temp_air_contents
STOP_PROCESSING(SSobj, src)
- cur_user = null
- ..()
+ return ..()
-/obj/item/tank/jetpack/suit/process()
- if(!ishuman(loc.loc))
- turn_off(cur_user)
- return
- var/mob/living/carbon/human/H = loc.loc
- if(!tank || tank != H.s_store)
- turn_off(cur_user)
- return
- ..()
/obj/item/tank/jetpack/suit/ninja
name = "ninja jetpack upgrade"
@@ -231,12 +312,23 @@
icon_state = "ninja_jetpack"
actions_types = list(/datum/action/item_action/toggle_jetpack/ninja, /datum/action/item_action/jetpack_stabilization/ninja)
-/obj/item/tank/jetpack/suit/ninja/New()
+
+/obj/item/tank/jetpack/suit/ninja/Initialize(mapload)
. = ..()
var/datum/action/item_action/jetpack_action
for(jetpack_action in actions)
jetpack_action.button_icon = 'icons/mob/actions/actions_ninja.dmi'
jetpack_action.background_icon_state = "background_green"
-/obj/item/tank/jetpack/suit/ninja/allow_thrust(num, mob/living/user, should_leave_trail)
- . = ..(num, user, cur_user?.alpha != NINJA_ALPHA_INVISIBILITY && should_leave_trail)
+
+/obj/item/tank/jetpack/suit/ninja/allow_thrust(num, use_fuel = TRUE)
+ var/mob/user = get_owner()
+ if(!user)
+ return FALSE
+ if(!skip_trails && user.alpha == NINJA_ALPHA_INVISIBILITY)
+ configure_jetpack(skip_trails = TRUE)
+ else if(skip_trails && user.alpha != NINJA_ALPHA_INVISIBILITY)
+ configure_jetpack(skip_trails = FALSE)
+ return ..()
+
+
diff --git a/code/game/objects/items/weapons/tanks/tanks.dm b/code/game/objects/items/weapons/tanks/tanks.dm
index 6595d0b319f..ab723214de6 100644
--- a/code/game/objects/items/weapons/tanks/tanks.dm
+++ b/code/game/objects/items/weapons/tanks/tanks.dm
@@ -18,8 +18,9 @@
var/volume = 70
var/fillable = TRUE
-/obj/item/tank/New()
- ..()
+
+/obj/item/tank/Initialize(mapload)
+ . = ..()
air_contents = new /datum/gas_mixture()
air_contents.volume = volume //liters
@@ -28,7 +29,7 @@
populate_gas()
START_PROCESSING(SSobj, src)
- return
+
/obj/item/tank/Destroy()
QDEL_NULL(air_contents)
diff --git a/code/game/objects/items/weapons/tanks/watertank.dm b/code/game/objects/items/weapons/tanks/watertank.dm
index 0c9c8b8c0fb..db4bc1310bd 100644
--- a/code/game/objects/items/weapons/tanks/watertank.dm
+++ b/code/game/objects/items/weapons/tanks/watertank.dm
@@ -135,7 +135,7 @@
else
return 1
-/obj/item/reagent_containers/spray/mister/Move()
+/obj/item/reagent_containers/spray/mister/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(loc != tank.loc)
loc = tank.loc
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 2b6f3c5f865..ca03d2e4ca6 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -45,7 +45,7 @@
STOP_PROCESSING(SSobj, src)
return ..()
-/obj/structure/Move(atom/newloc, direct = NONE, movetime)
+/obj/structure/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/atom/old = loc
if(!..())
return FALSE
diff --git a/code/game/objects/structures/aliens.dm b/code/game/objects/structures/aliens.dm
index 6ae928ac7c5..5c5afceb0ed 100644
--- a/code/game/objects/structures/aliens.dm
+++ b/code/game/objects/structures/aliens.dm
@@ -181,10 +181,10 @@
/obj/structure/alien/resin/door/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(operating)
- return
+ return .
if(isliving(moving_atom))
var/mob/living/living = moving_atom
diff --git a/code/game/objects/structures/crates_lockers/closets.dm b/code/game/objects/structures/crates_lockers/closets.dm
index b14aa28d2e4..0b5bc9218f6 100644
--- a/code/game/objects/structures/crates_lockers/closets.dm
+++ b/code/game/objects/structures/crates_lockers/closets.dm
@@ -508,15 +508,15 @@ GLOBAL_LIST_EMPTY(closets)
UpdateTransparency(mover, loc)
-/obj/structure/closet/bluespace/Move(NewLoc, direct) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls.
- var/turf/T = get_turf(NewLoc)
+/obj/structure/closet/bluespace/Move(atom/newloc, direct = NONE, glide_size_override = 0) // Allows for "phasing" throug objects but doesn't allow you to stuff your EOC homebois in one of these and push them through walls.
+ var/turf/T = get_turf(newloc)
if(T.density)
return
for(var/atom/A in T.contents)
if(A.density && istype(A, /obj/machinery/door))
return
- UpdateTransparency(src, NewLoc)
- forceMove(NewLoc)
+ UpdateTransparency(src, newloc)
+ forceMove(newloc)
/obj/structure/closet/bluespace/close()
. = ..()
diff --git a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
index 6971fbbe60e..17a4c871e19 100644
--- a/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
+++ b/code/game/objects/structures/crates_lockers/closets/cardboardbox.dm
@@ -41,7 +41,7 @@
. = Move(next_step, direction, delay)
if(!user.client)
return
- user.client.move_delay += ISDIAGONALDIR(direction) ? delay * SQRT_2 : delay
+ user.client.move_delay += ISDIAGONALDIR(direction) ? delay * sqrt(2) : delay
/obj/structure/closet/cardboard/open()
diff --git a/code/game/objects/structures/fence.dm b/code/game/objects/structures/fence.dm
index 473df42debe..0215d755f1a 100644
--- a/code/game/objects/structures/fence.dm
+++ b/code/game/objects/structures/fence.dm
@@ -24,7 +24,7 @@
var/cuttable = TRUE
var/hole_size = NO_HOLE
var/invulnerable = FALSE
- var/shock_cooldown = FALSE
+ COOLDOWN_DECLARE(shock_cooldown)
/obj/structure/fence/Initialize()
. = ..()
@@ -142,19 +142,14 @@
return
. = ..()
-/obj/structure/fence/Bumped(atom/movable/moving_atom)
- ..()
- if(!ismob(moving_atom))
- return
- if(shock_cooldown)
- return
+/obj/structure/fence/Bumped(atom/movable/moving_atom)
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, shock_cooldown) || !ismob(moving_atom))
+ return .
shock(moving_atom, 70)
- shock_cooldown = TRUE // We do not want bump shock spam!
- addtimer(CALLBACK(src, PROC_REF(shock_cooldown)), 1 SECONDS, TIMER_UNIQUE | TIMER_OVERRIDE)
+ COOLDOWN_START(src, shock_cooldown, 1 SECONDS) // We do not want bump shock spam!
-/obj/structure/fence/proc/shock_cooldown()
- shock_cooldown = FALSE
/obj/structure/fence/attack_animal(mob/user)
. = ..()
diff --git a/code/game/objects/structures/grille.dm b/code/game/objects/structures/grille.dm
index 58b04b0c954..83a7fefcc5d 100644
--- a/code/game/objects/structures/grille.dm
+++ b/code/game/objects/structures/grille.dm
@@ -18,8 +18,7 @@
var/rods_broken = 1
var/grille_type
var/broken_type = /obj/structure/grille/broken
- var/shockcooldown = 0
- var/my_shockcooldown = 1 SECONDS
+ COOLDOWN_DECLARE(shock_cooldown)
/obj/structure/grille/fence
var/width = 2
@@ -101,14 +100,14 @@
take_damage(25) //second time turn into broken
. &= ~(FALL_INTERCEPTED | FALL_NO_MESSAGE | FALL_RETAIN_PULL)
+
/obj/structure/grille/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, shock_cooldown) || !ismob(moving_atom))
+ return .
+ shock(moving_atom, 70)
+ COOLDOWN_START(src, shock_cooldown, 1 SECONDS)
- if(ismob(moving_atom))
- if(!(shockcooldown <= world.time))
- return
- shock(moving_atom, 70)
- shockcooldown = world.time + my_shockcooldown
/obj/structure/grille/attack_animal(mob/user)
. = ..()
diff --git a/code/game/objects/structures/holosign.dm b/code/game/objects/structures/holosign.dm
index b1ec7e2f618..2763e9a6f41 100644
--- a/code/game/objects/structures/holosign.dm
+++ b/code/game/objects/structures/holosign.dm
@@ -143,35 +143,25 @@
name = "Charged Energy Field"
desc = "A powerful energy field that blocks movement. Energy arcs off it."
max_integrity = 20
- var/shockcd = 0
+ COOLDOWN_DECLARE(shock_cooldown)
+
/obj/structure/holosign/barrier/cyborg/hacked/bullet_act(obj/item/projectile/P)
take_damage(P.damage, BRUTE, "melee", 1) //Yeah no this doesn't get projectile resistance.
-/obj/structure/holosign/barrier/cyborg/hacked/proc/cooldown()
- shockcd = FALSE
/obj/structure/holosign/barrier/cyborg/hacked/attack_hand(mob/living/user)
. = ..()
- if(.)
+ if(. || !COOLDOWN_FINISHED(src, shock_cooldown) || !isliving(user))
return
- if(!shockcd)
- if(isliving(user))
- var/mob/living/M = user
- M.electrocute_act(15, "Energy Barrier", safety = TRUE)
- shockcd = TRUE
- addtimer(CALLBACK(src, PROC_REF(cooldown)), 5)
-
-/obj/structure/holosign/barrier/cyborg/hacked/Bumped(atom/movable/moving_atom)
- ..()
+ user.electrocute_act(15, "Energy Barrier", safety = TRUE)
+ COOLDOWN_START(src, shock_cooldown, 0.5 SECONDS)
- if(shockcd)
- return
- if(!isliving(moving_atom))
- return
+/obj/structure/holosign/barrier/cyborg/hacked/Bumped(mob/living/moving_living)
+ . = ..()
+ if(!COOLDOWN_FINISHED(src, shock_cooldown) || !isliving(moving_living))
+ return .
+ moving_living.electrocute_act(15, "Energy Barrier", safety = TRUE)
+ COOLDOWN_START(src, shock_cooldown, 0.5 SECONDS)
- var/mob/living/M = moving_atom
- M.electrocute_act(15, "Energy Barrier", safety = TRUE)
- shockcd = TRUE
- addtimer(CALLBACK(src, PROC_REF(cooldown)), 5)
diff --git a/code/game/objects/structures/mineral_doors.dm b/code/game/objects/structures/mineral_doors.dm
index d190746e663..db1767aafed 100644
--- a/code/game/objects/structures/mineral_doors.dm
+++ b/code/game/objects/structures/mineral_doors.dm
@@ -37,7 +37,7 @@
move_update_air(T)
/obj/structure/mineral_door/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(!state)
return TryToSwitchState(moving_atom)
diff --git a/code/game/objects/structures/morgue.dm b/code/game/objects/structures/morgue.dm
index 0eb69e67652..f6d7975eafe 100644
--- a/code/game/objects/structures/morgue.dm
+++ b/code/game/objects/structures/morgue.dm
@@ -727,7 +727,7 @@ GLOBAL_LIST_EMPTY(crematoriums)
return TRUE
-/obj/structure/c_tray/Process_Spacemove(movement_dir = NONE)
+/obj/structure/c_tray/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/game/objects/structures/railings.dm b/code/game/objects/structures/railings.dm
index b2d91eafcea..04f0fd0d22c 100644
--- a/code/game/objects/structures/railings.dm
+++ b/code/game/objects/structures/railings.dm
@@ -143,7 +143,7 @@
..()
handle_layer()
-/obj/structure/railing/Move(newloc, direct, movetime)
+/obj/structure/railing/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
handle_layer()
diff --git a/code/game/objects/structures/stairs.dm b/code/game/objects/structures/stairs.dm
index 6a32ed542ec..1a0b43ff18f 100644
--- a/code/game/objects/structures/stairs.dm
+++ b/code/game/objects/structures/stairs.dm
@@ -52,7 +52,7 @@
if(!isobserver(leaving) && isTerminator() && direction == dir)
leaving.set_currently_z_moving(CURRENTLY_Z_ASCENDING)
INVOKE_ASYNC(src, PROC_REF(stair_ascend), leaving)
- leaving.Bump(src)
+ leaving.Bump(src, custom_bump = TRUE)
return COMPONENT_ATOM_BLOCK_EXIT
/obj/structure/stairs/Cross(atom/movable/AM)
diff --git a/code/game/objects/structures/statues.dm b/code/game/objects/structures/statues.dm
index 7b047fc4128..0ed1d9d22d4 100644
--- a/code/game/objects/structures/statues.dm
+++ b/code/game/objects/structures/statues.dm
@@ -222,7 +222,7 @@
/obj/structure/statue/bananium/Bumped(atom/movable/moving_atom)
honk()
- ..()
+ . = ..()
/obj/structure/statue/bananium/attackby(obj/item/W, mob/user, params)
honk()
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 e900765a983..97653772f2c 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/chairs.dm
@@ -27,7 +27,7 @@
B.setDir(dir)
qdel(src)
-/obj/structure/chair/Move(atom/newloc, direct)
+/obj/structure/chair/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
handle_rotation()
@@ -260,21 +260,20 @@
buildstackamount = 5
pull_push_speed_modifier = 1
-/obj/structure/chair/office/Bump(atom/A)
- ..()
- if(!has_buckled_mobs())
- return
- if(propelled)
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- unbuckle_mob(buckled_mob)
- buckled_mob.throw_at(A, 3, propelled)
- buckled_mob.Weaken(12 SECONDS)
- buckled_mob.Stuttering(12 SECONDS)
- buckled_mob.take_organ_damage(10)
- playsound(loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
- buckled_mob.visible_message(span_danger("[buckled_mob] crashed into [A]!"))
+/obj/structure/chair/office/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.) || !has_buckled_mobs() || !propelled)
+ return .
+ for(var/m in buckled_mobs)
+ var/mob/living/buckled_mob = m
+ unbuckle_mob(buckled_mob)
+ buckled_mob.throw_at(bumped_atom, 3, propelled)
+ buckled_mob.Weaken(12 SECONDS)
+ buckled_mob.Stuttering(12 SECONDS)
+ buckled_mob.take_organ_damage(10)
+ playsound(loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ buckled_mob.visible_message(span_danger("[buckled_mob] crashed into [bumped_atom]!"))
/obj/structure/chair/office/light
icon_state = "officechair_white"
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 f5e95957f8b..9f60fd0d87d 100644
--- a/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
+++ b/code/game/objects/structures/stool_bed_chair_nest/wheelchair.dm
@@ -1,168 +1,129 @@
/obj/structure/chair/wheelchair
name = "wheelchair"
+ desc = "You sit in this. Helps with traumas."
+ base_icon_state = "wheelchair"
icon_state = "wheelchair"
item_chair = null
movable = TRUE
pull_push_speed_modifier = 1
+ /// Overlay used to overlap buckled mob.
var/mutable_appearance/chair_overlay
- var/move_delay = null
+ /// If set we cannot go lower than this delay.
+ var/lowest_move_delay = 0.4 SECONDS
+ /// Currently applied skin, it contains path, not an instance.
+ var/obj/item/fluff/rapid_wheelchair_kit/applied_skin
+ COOLDOWN_DECLARE(wheelchair_move_delay)
/obj/structure/chair/wheelchair/Initialize(mapload)
. = ..()
ADD_TRAIT(src, TRAIT_NO_IMMOBILIZE, INNATE_TRAIT)
+ chair_overlay = mutable_appearance(icon, "wheelchair_overlay", ABOVE_MOB_LAYER)
+ update_icon(UPDATE_OVERLAYS)
-/obj/structure/chair/wheelchair/handle_rotation()
- if(chair_overlay)
- cut_overlay(chair_overlay)
- else
- chair_overlay = mutable_appearance(icon, "[icon_state]_overlay", FLY_LAYER)
- chair_overlay.dir = src.dir
- add_overlay(chair_overlay)
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.setDir(dir)
+/obj/structure/chair/wheelchair/Destroy()
+ chair_overlay = null
+ applied_skin = null
+ return ..()
-/obj/structure/chair/wheelchair/relaymove(mob/user, direction)
- if(propelled)
- return 0
-
- if(!Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc))
- return 0
- if(world.time < move_delay)
+/obj/structure/chair/wheelchair/proc/on_skin_apply(obj/item/fluff/rapid_wheelchair_kit/kit, mob/user)
+ if(applied_skin && applied_skin == kit.type)
+ to_chat(user, span_warning("This [name] is already modified by [kit]!."))
return
- var/calculated_move_delay
- calculated_move_delay += 2 //wheelchairs are not infact sport bikes
-
- if(has_buckled_mobs())
- var/mob/living/buckled_mob = buckled_mobs[1]
- if(buckled_mob.incapacitated())
- return 0
-
- var/mob/living/thedriver = user
- var/mob_delay = thedriver.cached_multiplicative_slowdown
- if(mob_delay > 0)
- calculated_move_delay += mob_delay
-
- if(ishuman(buckled_mob))
- var/mob/living/carbon/human/driver = user
- if(!driver.has_left_hand() && !driver.has_right_hand())
- return 0 // No hands to drive your chair? Tough luck!
-
- for(var/organ_name in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND))
- var/obj/item/organ/external/E = driver.get_organ(organ_name)
- if(!E)
- calculated_move_delay += 4
- else if(E.is_splinted())
- calculated_move_delay += 0.5
- else if(E.has_fracture())
- calculated_move_delay += 1.5
-
- if(calculated_move_delay < 4)
- calculated_move_delay = 4 //no racecarts
- glide_for(calculated_move_delay)
- if(direction & (direction - 1)) //moved diagonally
- calculated_move_delay *= 1.41
-
- move_delay = world.time
- move_delay += calculated_move_delay
-
- 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
- . = 1
-
-/obj/structure/chair/wheelchair/Bump(atom/A)
- ..()
-
- if(!has_buckled_mobs())
- return
- var/mob/living/buckled_mob = buckled_mobs[1]
- if(istype(A, /obj/machinery/door))
- A.Bumped(buckled_mob)
+ to_chat(user, span_notice("You modify the appearance of [src]."))
+ applied_skin = kit.type
+ chair_overlay = mutable_appearance(icon, kit.new_overlay, ABOVE_MOB_LAYER)
+ update_appearance()
+ qdel(kit)
- if(propelled)
- var/mob/living/occupant = buckled_mob
- unbuckle_mob(occupant)
- occupant.throw_at(A, 3, propelled)
+/obj/structure/chair/wheelchair/update_icon_state()
+ icon_state = applied_skin ? initial(applied_skin.new_icon_state) : base_icon_state
- occupant.Weaken(12 SECONDS)
- occupant.Stuttering(12 SECONDS)
- playsound(src.loc, 'sound/weapons/punch1.ogg', 50, 1, -1)
- if(isliving(A))
- var/mob/living/victim = A
- victim.Weaken(12 SECONDS)
- victim.Stuttering(12 SECONDS)
- victim.take_organ_damage(10)
- occupant.visible_message("[occupant] crashed into \the [A]!")
+/obj/structure/chair/wheelchair/update_overlays()
+ . = ..()
+ . += chair_overlay
-/obj/structure/chair/wheelchair/bike
- name = "bicycle"
- desc = "Two wheels of FURY!"
- //placeholder until i get a bike sprite
- icon = 'icons/obj/vehicles/motorcycle.dmi'
- icon_state = "motorcycle_4dir"
-/obj/structure/chair/wheelchair/bike/relaymove(mob/user, direction)
- if(propelled)
- return 0
+/obj/structure/chair/wheelchair/update_name(updates = ALL)
+ . = ..()
+ name = applied_skin ? initial(applied_skin.new_name) : initial(name)
- if(!Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc)) //bikes in space.
- return 0
- if(world.time < move_delay)
- return
+/obj/structure/chair/wheelchair/update_desc(updates = ALL)
+ . = ..()
+ desc = applied_skin ? initial(applied_skin.new_desc) : initial(desc)
+
+
+/obj/structure/chair/wheelchair/handle_layer()
+ return
+
+
+/obj/structure/chair/wheelchair/relaymove(mob/user, direction)
+ if(!COOLDOWN_FINISHED(src, wheelchair_move_delay))
+ return FALSE
+ var/turf/next_step = get_step(src, direction)
+ if(!next_step || propelled || !Process_Spacemove(direction) || !has_gravity(loc) || !isturf(loc) || !has_buckled_mobs() || user != buckled_mobs[1])
+ COOLDOWN_START(src, wheelchair_move_delay, 0.5 SECONDS)
+ return FALSE
+
+ var/calculated_move_delay = user.cached_multiplicative_slowdown
+
+ if(ishuman(user))
+ var/mob/living/carbon/human/driver = user
+ if(!driver.num_hands)
+ COOLDOWN_START(src, wheelchair_move_delay, 0.5 SECONDS)
+ return FALSE // No hands to drive your chair? Tough luck!
+
+ for(var/organ_name in list(BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_PRECISE_L_HAND, BODY_ZONE_PRECISE_R_HAND))
+ var/obj/item/organ/external/bodypart = driver.get_organ(organ_name)
+ if(!bodypart)
+ calculated_move_delay += 4
+ else if(bodypart.is_splinted())
+ calculated_move_delay += 0.5
+ else if(bodypart.has_fracture())
+ calculated_move_delay += 1.5
+
+ if(lowest_move_delay && calculated_move_delay < lowest_move_delay)
+ calculated_move_delay = lowest_move_delay //no racecarts
+
+ . = Move(next_step, direction)
+ if(ISDIAGONALDIR(direction) && loc == next_step)
+ calculated_move_delay *= sqrt(2)
+
+ set_glide_size(DELAY_TO_GLIDE_SIZE(calculated_move_delay))
+ COOLDOWN_START(src, wheelchair_move_delay, calculated_move_delay)
+
+
+/obj/structure/chair/wheelchair/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.) || !has_buckled_mobs())
+ return .
+
+ var/mob/living/buckled_mob = buckled_mobs[1]
+ if(istype(bumped_atom, /obj/machinery/door))
+ bumped_atom.Bumped(buckled_mob)
+
+ if(!propelled)
+ return .
+
+ var/mob/living/occupant = buckled_mob
+ unbuckle_mob(occupant)
+
+ occupant.throw_at(bumped_atom, 3, propelled)
+
+ occupant.Weaken(12 SECONDS)
+ occupant.Stuttering(12 SECONDS)
+ playsound(src.loc, 'sound/weapons/punch1.ogg', 50, TRUE, -1)
+ if(isliving(bumped_atom))
+ var/mob/living/victim = bumped_atom
+ victim.Weaken(12 SECONDS)
+ victim.Stuttering(12 SECONDS)
+ victim.take_organ_damage(10)
+
+ occupant.visible_message(span_danger("[occupant] crashed into [bumped_atom]!"))
- var/calculated_move_delay
- calculated_move_delay = 0 //bikes are infact sport bikes
-
- if(has_buckled_mobs())
- var/mob/living/buckled_mob = buckled_mobs[1]
- if(buckled_mob.incapacitated())
- unbuckle_mob(buckled_mob) //if the rider is incapacitated, unbuckle them (they can't balance so they fall off)
- return 0
-
- var/mob/living/thedriver = user
- var/mob_delay = thedriver.cached_multiplicative_slowdown
- if(mob_delay > 0)
- calculated_move_delay += mob_delay
-
- if(ishuman(buckled_mob))
- var/mob/living/carbon/human/driver = user
- var/obj/item/organ/external/l_hand = driver.get_organ(BODY_ZONE_PRECISE_L_HAND)
- var/obj/item/organ/external/r_hand = driver.get_organ(BODY_ZONE_PRECISE_R_HAND)
- if(!l_hand && !r_hand)
- calculated_move_delay += 0.5 //I can ride my bike with no handlebars... (but it's slower)
-
- for(var/organ_name in list(BODY_ZONE_L_LEG, BODY_ZONE_R_LEG, BODY_ZONE_PRECISE_L_FOOT, BODY_ZONE_PRECISE_R_FOOT))
- var/obj/item/organ/external/E = driver.get_organ(organ_name)
- if(!E)
- return 0 //Bikes need both feet/legs to work. missing even one makes it so you can't ride the bike
- else if(E.is_splinted())
- calculated_move_delay += 0.5
- else if(E.has_fracture())
- calculated_move_delay += 1.5
-
- move_delay = world.time
- move_delay += calculated_move_delay
-
- 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
- . = 1
diff --git a/code/game/objects/structures/tables_racks.dm b/code/game/objects/structures/tables_racks.dm
index 493f6f3b8dd..4c702d123c7 100644
--- a/code/game/objects/structures/tables_racks.dm
+++ b/code/game/objects/structures/tables_racks.dm
@@ -700,7 +700,7 @@
if(!held.anchored && held.move_resist != INFINITY && is_type_in_typecache(held, typecache_can_hold))
held_items += held.UID()
-/obj/structure/table/tray/Move(NewLoc, direct)
+/obj/structure/table/tray/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/atom/OldLoc = loc
. = ..()
@@ -721,7 +721,7 @@
if(OldLoc != held.loc)
held_items -= held_uid
continue
- held.forceMove(NewLoc)
+ held.forceMove(newloc)
/obj/structure/table/tray/can_be_pulled(user, force, show_message)
diff --git a/code/game/objects/structures/transit_tubes/station.dm b/code/game/objects/structures/transit_tubes/station.dm
index dd01702738b..b032b376e84 100644
--- a/code/game/objects/structures/transit_tubes/station.dm
+++ b/code/game/objects/structures/transit_tubes/station.dm
@@ -45,15 +45,19 @@
/obj/structure/transit_tube/station/should_stop_pod(pod, from_dir)
return TRUE
-/obj/structure/transit_tube/station/Bumped(mob/living/L)
- if(!pod_moving && L.dir == boarding_dir && hatch_state == TRANSIT_TUBE_OPEN && isliving(L) && !is_type_in_list(L, disallowed_mobs))
- for(var/obj/structure/transit_tube_pod/pod in loc)
- if(length(pod.contents))
- to_chat(L, "The pod is already occupied.")
- return
- if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions()))))
- pod.move_into(L)
- return
+
+/obj/structure/transit_tube/station/Bumped(mob/living/moving_living, skip_effect = FALSE)
+ . = ..()
+ if(skip_effect || pod_moving || moving_living.dir != boarding_dir || hatch_state == TRANSIT_TUBE_OPEN || !isliving(moving_living) || is_type_in_list(moving_living, disallowed_mobs))
+ return .
+ for(var/obj/structure/transit_tube_pod/pod in loc)
+ if(length(pod.contents))
+ to_chat(moving_living, "The pod is already occupied.")
+ break
+ if(!pod.moving && ((pod.dir in directions()) || (reverse_launch && (turn(pod.dir, 180) in directions()))))
+ pod.move_into(moving_living)
+ break
+
/obj/structure/transit_tube/station/attack_hand(mob/user)
if(pod_moving)
@@ -221,16 +225,18 @@
return TRUE
return FALSE
-/obj/structure/transit_tube/station/dispenser/Bumped(mob/living/L)
- if(!(istype(L) && L.dir == boarding_dir) || L.anchored)
- return
- if(isliving(L) && !is_type_in_list(L, disallowed_mobs))
- var/obj/structure/transit_tube_pod/dispensed/pod = new(loc)
- L.visible_message("[pod] forms around [L].", "[pod] materializes around you.")
- playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE)
- pod.dir = turn(dir, -90)
- pod.move_into(L)
- launch_pod()
+
+/obj/structure/transit_tube/station/dispenser/Bumped(mob/living/moving_living, skip_effect = TRUE)
+ . = ..()
+ if(!isliving(moving_living) || moving_living.dir != boarding_dir || moving_living.anchored || is_type_in_list(moving_living, disallowed_mobs))
+ return .
+ var/obj/structure/transit_tube_pod/dispensed/pod = new(loc)
+ moving_living.visible_message("[pod] forms around [moving_living].", "[pod] materializes around you.")
+ playsound(src, 'sound/weapons/emitter2.ogg', 50, TRUE)
+ pod.dir = turn(dir, -90)
+ pod.move_into(moving_living)
+ launch_pod()
+
/obj/structure/transit_tube/station/dispenser/pod_stopped(obj/structure/transit_tube_pod/pod)
playsound(src, 'sound/machines/ding.ogg', 50, TRUE)
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 bf47793b41f..8060032869a 100644
--- a/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
+++ b/code/game/objects/structures/transit_tubes/transit_tube_pod.dm
@@ -53,7 +53,7 @@
return ..()
-/obj/structure/transit_tube_pod/Process_Spacemove(movement_dir = NONE)
+/obj/structure/transit_tube_pod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
if(moving) //No drifting while moving in the tubes
return TRUE
return ..()
diff --git a/code/game/objects/structures/tribune.dm b/code/game/objects/structures/tribune.dm
index 3e56b27c3b2..a6061b85b57 100644
--- a/code/game/objects/structures/tribune.dm
+++ b/code/game/objects/structures/tribune.dm
@@ -43,7 +43,7 @@
..()
handle_layer()
-/obj/structure/tribune/Move(newloc, direct, movetime)
+/obj/structure/tribune/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
handle_layer()
diff --git a/code/game/shuttle_engines.dm b/code/game/shuttle_engines.dm
index 1a07ffbd300..c6be6bc3833 100644
--- a/code/game/shuttle_engines.dm
+++ b/code/game/shuttle_engines.dm
@@ -54,7 +54,7 @@
desc = "A very large bluespace engine used to propel very large ships."
// bound_width = 64
// bound_height = 64
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
/obj/structure/shuttle/engine/large/Initialize()
..()
@@ -77,7 +77,7 @@
pixel_y = -32
// bound_width = 96
// bound_height = 96
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
/obj/structure/shuttle/engine/huge/Initialize()
..()
diff --git a/code/game/turfs/simulated/minerals.dm b/code/game/turfs/simulated/minerals.dm
index 976d5693bcf..2da5980da68 100644
--- a/code/game/turfs/simulated/minerals.dm
+++ b/code/game/turfs/simulated/minerals.dm
@@ -145,7 +145,7 @@
/turf/simulated/mineral/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(ishuman(moving_atom))
var/mob/living/carbon/human/H = moving_atom
if((istype(H.l_hand,/obj/item/pickaxe)) && (!H.hand))
diff --git a/code/game/turfs/space/space.dm b/code/game/turfs/space/space.dm
index cfa31ca4704..37c95debf00 100644
--- a/code/game/turfs/space/space.dm
+++ b/code/game/turfs/space/space.dm
@@ -159,9 +159,6 @@
var/turf/T = get_step(L.loc,turn(A.dir, 180))
L.pulling.zMove(null, T, ZMOVE_ALLOW_BUCKLED)
- //now we're on the new z_level, proceed the space drifting
- spawn(0)//Let a diagonal move finish, if necessary
- A.newtonian_move(A.inertia_dir)
/turf/space/proc/check_taipan_availability(atom/movable/A as mob|obj, destination_z)
var/mob/living/check_mob = A
@@ -205,7 +202,7 @@
var/list/y_arr
if(src.x <= 1)
- if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust))
+ if(istype(A, /obj/effect/meteor))
qdel(A)
return
@@ -280,7 +277,7 @@
A.loc.Entered(A)
else if(src.y >= world.maxy)
- if(istype(A, /obj/effect/meteor)||istype(A, /obj/effect/space_dust))
+ if(istype(A, /obj/effect/meteor))
qdel(A)
return
var/list/cur_pos = src.get_global_map_pos()
diff --git a/code/game/turfs/turf.dm b/code/game/turfs/turf.dm
index 91dc58f893c..886c9ff37f2 100644
--- a/code/game/turfs/turf.dm
+++ b/code/game/turfs/turf.dm
@@ -191,7 +191,7 @@
if(obstacle == mover || obstacle == oldloc)
continue
if(!obstacle.CanExit(mover, movement_dir))
- mover.Bump(obstacle, TRUE)
+ mover.Bump(obstacle, custom_bump = TRUE)
return FALSE
var/border_dir = get_dir(src, mover)
@@ -203,14 +203,14 @@
continue
if(border_obstacle.flags & ON_BORDER)
if(!border_obstacle.CanPass(mover, border_dir))
- mover.Bump(border_obstacle, TRUE)
+ mover.Bump(border_obstacle, custom_bump = TRUE)
return FALSE
else
large_dense += border_obstacle
//Then, check the turf itself
if(!CanPass(mover, border_dir))
- mover.Bump(src, TRUE)
+ mover.Bump(src, custom_bump = TRUE)
return FALSE
//Finally, check objects/mobs to block entry that are not on the border
@@ -228,7 +228,7 @@
tompost_bump = obstacle
top_layer = current_layer
if(tompost_bump)
- mover.Bump(tompost_bump, TRUE)
+ mover.Bump(tompost_bump, custom_bump = TRUE)
return FALSE
return TRUE //Nothing found to block so return success!
diff --git a/code/modules/antagonists/space_dragon/carp_rift.dm b/code/modules/antagonists/space_dragon/carp_rift.dm
index a833304faa0..cd7f60a5cc1 100644
--- a/code/modules/antagonists/space_dragon/carp_rift.dm
+++ b/code/modules/antagonists/space_dragon/carp_rift.dm
@@ -107,7 +107,7 @@
newcarp.faction = dragon.owner.current.faction.Copy()
if(SPT_PROB(1.5, seconds_per_tick))
var/rand_dir = pick(GLOB.cardinal)
- Move(get_step(src, rand_dir), rand_dir)
+ SSmove_manager.move_to(src, get_step(src, rand_dir), 1)
return
// Increase time trackers and check for any updated states.
diff --git a/code/modules/antagonists/space_dragon/space_dragon.dm b/code/modules/antagonists/space_dragon/space_dragon.dm
index a9a137c8c92..c322de2607f 100644
--- a/code/modules/antagonists/space_dragon/space_dragon.dm
+++ b/code/modules/antagonists/space_dragon/space_dragon.dm
@@ -95,7 +95,7 @@
RegisterSignal(small_sprite, COMSIG_ACTION_TRIGGER, PROC_REF(add_dragon_overlay))
-/mob/living/simple_animal/hostile/space_dragon/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/space_dragon/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/modules/antagonists/space_ninja/suit/suit.dm b/code/modules/antagonists/space_ninja/suit/suit.dm
index 4e26cec2ce7..ee998057124 100644
--- a/code/modules/antagonists/space_ninja/suit/suit.dm
+++ b/code/modules/antagonists/space_ninja/suit/suit.dm
@@ -403,18 +403,18 @@
ninja.adjust_bodytemperature(BODYTEMP_NORMAL - ninja.bodytemperature)
-/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/ninja, action)
+/obj/item/clothing/suit/space/space_ninja/ui_action_click(mob/ninja, datum/action/action)
if(!isninja(ninja) && !anyone)
to_chat(ninja, span_danger("fÄTaL ÈÈRRoR: 382200-*#00CÖDE RED\nUNAUHORIZED USÈ DETÈCeD\nCoMMÈNCING SUB-R0UIN3 13...\nTÈRMInATING U-U-USÈR..."))
ninja.dust()
return FALSE
- if(action == /datum/action/item_action/advanced/ninja/SpiderOS)
+ if(istype(action, /datum/action/item_action/advanced/ninja/SpiderOS))
ui_interact(ninja)
return TRUE
if(!s_initialized)
to_chat(ninja, span_warning("ERROR: suit offline. Please activate suit."))
return FALSE
- switch(action)
+ switch(action.type)
if(/datum/action/item_action/advanced/ninja/ninja_autodust)
ninja_toggle_autodust()
return TRUE
diff --git a/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm b/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm
index 1bfaba66272..035f05bbd17 100644
--- a/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm
+++ b/code/modules/antagonists/traitor/contractor/items/contractor_hardsuit.dm
@@ -34,8 +34,8 @@
. = ..()
QDEL_NULL(scorpion)
-/obj/item/clothing/suit/space/hardsuit/contractor/ui_action_click(user, action)
- switch(action)
+/obj/item/clothing/suit/space/hardsuit/contractor/ui_action_click(user, datum/action/action)
+ switch(action.type)
if(/datum/action/item_action/toggle_helmet)
ToggleHelmet(user)
return TRUE
diff --git a/code/modules/arcade/prize_datums.dm b/code/modules/arcade/prize_datums.dm
index 33dbc0517f9..8341808652d 100644
--- a/code/modules/arcade/prize_datums.dm
+++ b/code/modules/arcade/prize_datums.dm
@@ -344,5 +344,5 @@ GLOBAL_DATUM_INIT(global_prizes, /datum/prizes, new())
/datum/prize_item/bike
name = "Awesome Bike!"
desc = "WOAH."
- typepath = /obj/structure/chair/wheelchair/bike
+ typepath = /obj/vehicle/motorcycle
cost = 10000 //max stack + 1 tickets.
diff --git a/code/modules/assembly/holder.dm b/code/modules/assembly/holder.dm
index c10d5e3d4eb..595037dd328 100644
--- a/code/modules/assembly/holder.dm
+++ b/code/modules/assembly/holder.dm
@@ -146,16 +146,11 @@
process_movement(user)
-/obj/item/assembly_holder/Bump(atom/A)
- ..()
- var/triggered
- if(ismob(A) || isobj(A))
- var/atom/movable/AM = A
- if(AM.throwing?.thrower)
- triggered = AM.throwing.thrower
- else if(ismob(AM))
- triggered = AM
- process_movement(triggered)
+/obj/item/assembly_holder/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.) || !ismob(bumped_atom))
+ return .
+ process_movement(bumped_atom)
/obj/item/assembly_holder/throw_impact(atom/hit_atom, datum/thrownthing/throwingdatum) // called when a throw stops
diff --git a/code/modules/assembly/infrared.dm b/code/modules/assembly/infrared.dm
index aa14fb9171f..2c5c8b18d24 100644
--- a/code/modules/assembly/infrared.dm
+++ b/code/modules/assembly/infrared.dm
@@ -111,7 +111,7 @@
..()
-/obj/item/assembly/infra/Move(atom/newloc, direct = 0, movetime)
+/obj/item/assembly/infra/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/prev_dir = dir
. = ..()
dir = prev_dir
@@ -304,11 +304,14 @@
I.process()
-/obj/effect/beam/i_beam/Bump()
+/obj/effect/beam/i_beam/Bump(atom/bumped_atom, custom_bump)
+ if(!custom_bump)
+ return null
qdel(src)
/obj/effect/beam/i_beam/Bumped(atom/movable/moving_atom)
+ . = ..()
hit(moving_atom)
diff --git a/code/modules/assembly/proximity.dm b/code/modules/assembly/proximity.dm
index 0aa93c57feb..da574e9a8f8 100644
--- a/code/modules/assembly/proximity.dm
+++ b/code/modules/assembly/proximity.dm
@@ -101,7 +101,7 @@
holder?.update_icon()
-/obj/item/assembly/prox_sensor/Move(atom/newloc, direct = NONE, movetime)
+/obj/item/assembly/prox_sensor/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
sense()
diff --git a/code/modules/awaymissions/gateway.dm b/code/modules/awaymissions/gateway.dm
index b0590114653..f258814d2e9 100644
--- a/code/modules/awaymissions/gateway.dm
+++ b/code/modules/awaymissions/gateway.dm
@@ -142,11 +142,8 @@ GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null)
//okay, here's the good teleporting stuff
/obj/machinery/gateway/centerstation/Bumped(atom/movable/moving_atom)
- if(!ready)
- return
- if(!active)
- return
- if(!awaygate)
+ . = ..()
+ if(!ready || !active || !awaygate)
return
if(awaygate.calibrated)
moving_atom.forceMove(get_step(awaygate.loc, SOUTH))
@@ -254,11 +251,8 @@ GLOBAL_DATUM_INIT(the_gateway, /obj/machinery/gateway/centerstation, null)
/obj/machinery/gateway/centeraway/Bumped(atom/movable/moving_atom)
- if(!ready)
- return
- if(!active)
- return
- if(!stationgate || QDELETED(stationgate))
+ . = ..()
+ if(!ready || !active || QDELETED(stationgate))
return
if(isliving(moving_atom))
if(exilecheck(moving_atom))
diff --git a/code/modules/awaymissions/mission_code/wildwest.dm b/code/modules/awaymissions/mission_code/wildwest.dm
index d484054d701..aca62a5ff11 100644
--- a/code/modules/awaymissions/mission_code/wildwest.dm
+++ b/code/modules/awaymissions/mission_code/wildwest.dm
@@ -140,28 +140,23 @@
layer = 3
icon = 'icons/mob/blob.dmi'
icon_state = "blobpod"
- var/triggerproc = "triggerrad1" //name of the proc thats called when the mine is triggered
- var/triggered = 0
+ var/triggered = FALSE
-/obj/effect/meatgrinder/Crossed(AM as mob|obj, oldloc)
+
+/obj/effect/meatgrinder/Crossed(atom/movable/AM, oldloc)
+ . = ..()
Bumped(AM)
-/obj/effect/meatgrinder/Bumped(atom/movable/moving_atom)
- if(triggered)
- return
-
- if(ishuman(moving_atom))
- for(var/mob/O in viewers(world.view, src.loc))
- to_chat(O, "[moving_atom] triggered the [bicon(src)] [src]")
- triggered = 1
- call(src,triggerproc)(moving_atom)
-
-/obj/effect/meatgrinder/proc/triggerrad1(mob)
- for(var/mob/O in viewers(world.view, src.loc))
- do_sparks(3, 1, src)
- explosion(mob, 1, 0, 0, 0)
- qdel(src)
+/obj/effect/meatgrinder/Bumped(atom/movable/moving_atom)
+ . = ..()
+ if(triggered || !ishuman(moving_atom))
+ return .
+ visible_message(span_warning("[moving_atom] triggered the [bicon(src)] [src]!"))
+ triggered = TRUE
+ do_sparks(3, 1, src)
+ explosion(src, 1, 0, 0, 0)
+ qdel(src)
/////For the Wishgranter///////////
diff --git a/code/modules/awaymissions/zvis.dm b/code/modules/awaymissions/zvis.dm
index dc88224a7c0..3cbaec07078 100644
--- a/code/modules/awaymissions/zvis.dm
+++ b/code/modules/awaymissions/zvis.dm
@@ -287,35 +287,38 @@
near_render_block -= T
/obj/effect/view_portal/visual/Bumped(atom/movable/thing)
- if((isobj(thing) || isliving(thing)) && other && teleport)
- if(!near_render_block)
- setup_near()
-
- var/mob/living/M = thing
- // make the person glide onto the dest, giving a smooth transition
- var/ox = thing.x - x
- var/oy = thing.y - y
+ . = ..()
+ if(!ismovable(thing) || !other || !teleport)
+ return .
+
+ if(!near_render_block)
+ setup_near()
+
+ var/mob/living/M = thing
+ // make the person glide onto the dest, giving a smooth transition
+ var/ox = thing.x - x
+ var/oy = thing.y - y
+ if(istype(M) && M.client)
+ ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
+ // cover up client-side map loading
+ M.screen_loc = "CENTER"
+ M.client.screen += M
+ for(var/T in tiles)
+ M.client.screen += tiles[T]
+
+ // wait a tick for the screen to replicate across network
+ // or this whole exercise of covering the transition is pointless
+ spawn(1)
+ thing.forceMove(locate(other.x + ox, other.y + oy, other.z))
+ sleep(1)
if(istype(M) && M.client)
- ADD_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
- // cover up client-side map loading
- M.screen_loc = "CENTER"
- M.client.screen += M
for(var/T in tiles)
- M.client.screen += tiles[T]
-
- // wait a tick for the screen to replicate across network
- // or this whole exercise of covering the transition is pointless
- spawn(1)
- thing.forceMove(locate(other.x + ox, other.y + oy, other.z))
- sleep(1)
- if(istype(M) && M.client)
- for(var/T in tiles)
- M.client.screen -= tiles[T]
- M.client.screen -= M
- M.screen_loc = initial(M.screen_loc)
- thing.forceMove(get_turf(other.loc))
- if(istype(M) && M.client)
- REMOVE_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
+ M.client.screen -= tiles[T]
+ M.client.screen -= M
+ M.screen_loc = initial(M.screen_loc)
+ thing.forceMove(get_turf(other.loc))
+ if(istype(M) && M.client)
+ REMOVE_TRAIT(M, TRAIT_NO_TRANSFORM, UNIQUE_TRAIT_SOURCE(src))
/obj/effect/view_portal/visual/attack_ghost(mob/user)
diff --git a/code/modules/client/client_defines.dm b/code/modules/client/client_defines.dm
index 41990e6dd74..d71fdcc857e 100644
--- a/code/modules/client/client_defines.dm
+++ b/code/modules/client/client_defines.dm
@@ -17,9 +17,12 @@
//OTHER//
/////////
var/datum/preferences/prefs = null
+
///Move delay of controlled mob, any keypresses inside this period will persist until the next proper move
var/move_delay = 0
- var/current_move_delay = 0
+ ///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/area = null
var/time_joined_as_mouse = null //when the client last spawned as a mouse
diff --git a/code/modules/clothing/clothing.dm b/code/modules/clothing/clothing.dm
index f974659b7e6..7174a96958c 100644
--- a/code/modules/clothing/clothing.dm
+++ b/code/modules/clothing/clothing.dm
@@ -806,6 +806,7 @@ BLIND // can't see anything
. = ..()
if(jetpack && ispath(jetpack))
jetpack = new jetpack(src)
+ jetpack.our_suit = src
/obj/item/clothing/suit/space/screwdriver_act(mob/user, obj/item/I)
@@ -821,6 +822,7 @@ BLIND // can't see anything
jetpack.turn_off(user)
jetpack.forceMove(drop_location())
jetpack = null
+ jetpack.our_suit = null
to_chat(user, span_notice("You successfully remove the jetpack from [src]."))
@@ -836,6 +838,7 @@ BLIND // can't see anything
if(jetpack)
for(var/datum/action/action as anything in jetpack.actions)
action.Remove(user)
+ jetpack.turn_off(user)
/obj/item/clothing/suit/space/attackby(obj/item/I, mob/user, params)
@@ -852,6 +855,7 @@ BLIND // can't see anything
if(user.drop_transfer_item_to_loc(I, src))
jetpack = I
+ jetpack.our_suit = src
to_chat(user, span_notice("You successfully install the jetpack into [src]."))
return
return ..()
diff --git a/code/modules/clothing/masks/gasmask.dm b/code/modules/clothing/masks/gasmask.dm
index 822f41f86e6..00b700a1e0d 100644
--- a/code/modules/clothing/masks/gasmask.dm
+++ b/code/modules/clothing/masks/gasmask.dm
@@ -405,12 +405,12 @@
can_toggle = FALSE
actions_types = list(/datum/action/item_action/halt, /datum/action/item_action/selectphrase)
-/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, actiontype)
- if(actiontype == /datum/action/item_action/halt)
+/obj/item/clothing/mask/gas/sechailer/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/halt))
halt()
- else if(actiontype == /datum/action/item_action/adjust)
+ else if(istype(action, /datum/action/item_action/adjust))
adjustmask(user)
- else if(actiontype == /datum/action/item_action/selectphrase)
+ else if(istype(action, /datum/action/item_action/selectphrase))
var/key = phrase_list[phrase]
var/message = phrase_list[key]
diff --git a/code/modules/customitems/item_defines.dm b/code/modules/customitems/item_defines.dm
index 5c39b7d81b5..32b597cde6b 100644
--- a/code/modules/customitems/item_defines.dm
+++ b/code/modules/customitems/item_defines.dm
@@ -182,26 +182,27 @@
desc = "A weathered Vox thermonocle, doesn't seem to work anymore."
icon_state = "thermoncle"
+
/obj/item/fluff/rapid_wheelchair_kit //Rapidvalj: Hakikarahiti
name = "wheelchair conversion kit"
desc = "An assorted set of exchangable parts for a wheelchair."
icon_state = "modkit"
+ var/new_icon_state = "vox_wheelchair"
+ var/new_overlay = "vox_wheelchair_overlay"
+ var/new_name = "vox wheelchair"
+ var/new_desc = "A luxurious Vox Wheelchair, weathered from use."
+
-/obj/item/fluff/rapid_wheelchair_kit/afterattack(atom/target, mob/user, proximity)
+/obj/item/fluff/rapid_wheelchair_kit/afterattack(obj/structure/chair/wheelchair/target, mob/user, proximity)
if(!proximity || !ishuman(user) || user.incapacitated())
return
- if(istype(target, /obj/structure/chair/wheelchair) && !istype(target, /obj/structure/chair/wheelchair/bike))
- to_chat(user, "You modify the appearance of [target].")
- var/obj/structure/chair/wheelchair/chair = target
- chair.icon_state = "vox_wheelchair"
- chair.name = "vox wheelchair"
- chair.desc = "A luxurious Vox Wheelchair, weathered from use."
- chair.handle_rotation()
- qdel(src)
+ if(istype(target))
+ target.on_skin_apply(src, user)
return
- to_chat(user, "You can't modify [target]!")
+ to_chat(user, span_warning("You cannot modify [target]!"))
+
/obj/item/lighter/zippo/fluff/purple // GodOfOreos: Jason Conrad
name = "purple engraved zippo"
diff --git a/code/modules/detective_work/scanner.dm b/code/modules/detective_work/scanner.dm
index 61a7b5ba780..370f0ac7ecf 100644
--- a/code/modules/detective_work/scanner.dm
+++ b/code/modules/detective_work/scanner.dm
@@ -60,8 +60,8 @@
else
to_chat(user, "В записях станции не найдено совпадений.")
-/obj/item/detective_scanner/ui_action_click(mob/user, actiontype)
- if(actiontype == /datum/action/item_action/print_forensic_report)
+/obj/item/detective_scanner/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/print_forensic_report))
print_scanner_report()
else
clear_scanner()
diff --git a/code/modules/events/dust.dm b/code/modules/events/dust.dm
index 10f7ef9cf72..d39e3b4add4 100644
--- a/code/modules/events/dust.dm
+++ b/code/modules/events/dust.dm
@@ -1,102 +1,12 @@
/datum/event/dust
var/qnty = 1
+
/datum/event/dust/setup()
- qnty = rand(1,5)
+ qnty = rand(1, 5)
+
/datum/event/dust/start()
while(qnty-- > 0)
- new /obj/effect/space_dust/weak()
-
-/obj/effect/space_dust
- name = "Space Dust"
- desc = "Dust in space."
- icon = 'icons/obj/meteor.dmi'
- icon_state = "space_dust"
- density = TRUE
- anchored = TRUE
- var/strength = 2 //ex_act severity number
- var/life = 2 //how many things we hit before qdel(src)
- var/atom/goal = null
- var/shake_chance = 50
-
-/obj/effect/space_dust/weak
- strength = 3
- life = 1
-
-/obj/effect/space_dust/strong
- strength = 1
- life = 6
-
-/obj/effect/space_dust/super
- strength = 1
- life = 40
-
-/obj/effect/space_dust/New()
- . = ..()
- var/startx = 0
- var/starty = 0
- var/endy = 0
- var/endx = 0
- var/startside = pick(GLOB.cardinal)
-
- switch(startside)
- if(NORTH)
- starty = world.maxy-(TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
- endy = TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
- if(EAST)
- starty = rand((TRANSITIONEDGE+1),world.maxy-(TRANSITIONEDGE+1))
- startx = world.maxx-(TRANSITIONEDGE+1)
- endy = rand(TRANSITIONEDGE, world.maxy-TRANSITIONEDGE)
- endx = TRANSITIONEDGE
- if(SOUTH)
- starty = (TRANSITIONEDGE+1)
- startx = rand((TRANSITIONEDGE+1), world.maxx-(TRANSITIONEDGE+1))
- endy = world.maxy-TRANSITIONEDGE
- endx = rand(TRANSITIONEDGE, world.maxx-TRANSITIONEDGE)
- if(WEST)
- starty = rand((TRANSITIONEDGE+1), world.maxy-(TRANSITIONEDGE+1))
- startx = (TRANSITIONEDGE+1)
- endy = rand(TRANSITIONEDGE,world.maxy-TRANSITIONEDGE)
- endx = world.maxx-TRANSITIONEDGE
- goal = locate(endx, endy, 1)
- src.x = startx
- src.y = starty
- src.z = pick(levels_by_trait(STATION_LEVEL))
- walk_towards(src, goal, 1)
-
-/obj/effect/space_dust/Bump(atom/A)
- if(QDELETED(src))
- return
- if(prob(shake_chance))
- for(var/mob/M in range(10, src))
- if(!M.stat && !istype(M, /mob/living/silicon/ai))
- shake_camera(M, 3, 1)
- playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1)
-
- INVOKE_ASYNC(src, PROC_REF(impact_meteor), A) // ex_act can have some sleeps in it
-
-/obj/effect/space_dust/proc/impact_meteor(atom/A)
- var/turf/where = get_turf(A)
- if(ismob(A))
- A.ex_act(strength)//This should work for now I guess
- else if(!istype(A, /obj/machinery/power/emitter) && !istype(A, /obj/machinery/field/generator)) //Protect the singularity from getting released every round!
- A.ex_act(strength) //Changing emitter/field gen ex_act would make it immune to bombs and C4
-
- life--
- if(life <= 0)
- walk(src, 0)
- on_shatter(where)
- qdel(src)
-
-/obj/effect/space_dust/proc/on_shatter(turf/where)
- return
-
-/obj/effect/space_dust/Bumped(atom/movable/moving_atom)
- Bump(moving_atom)
- return
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 1, GLOB.meteors_space_dust)
-/obj/effect/space_dust/ex_act(severity)
- qdel(src)
diff --git a/code/modules/events/immovable_rod.dm b/code/modules/events/immovable_rod.dm
index 035b6f5b661..26d50e7223b 100644
--- a/code/modules/events/immovable_rod.dm
+++ b/code/modules/events/immovable_rod.dm
@@ -20,6 +20,7 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
var/turf/endT = spaceDebrisFinishLoc(startside, level)
new /obj/effect/immovablerod/event(startT, endT)
+
/obj/effect/immovablerod
name = "Immovable Rod"
desc = "What the fuck is that?"
@@ -28,24 +29,40 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
throwforce = 100
density = TRUE
anchored = TRUE
- var/z_original = 0
- var/destination
+ movement_type = FLYING
+ move_force = INFINITY
+ move_resist = INFINITY
+ pull_force = INFINITY
+ /// The turf we're looking to coast to
+ var/turf/destination_turf
+ /// Whether we notify ghosts
var/notify = TRUE
+ ///Lower = faster
var/move_delay = 1
-/obj/effect/immovablerod/New(atom/start, atom/end, delay)
+
+/obj/effect/immovablerod/Initialize(mapload, atom/target_atom, move_delay = 1)
. = ..()
- loc = start
- z_original = z
- destination = end
- move_delay = delay
- if(notify)
- notify_ghosts("\A [src] is inbound!",
- enter_link="(Click to follow)",
- source=src, action=NOTIFY_FOLLOW)
+
GLOB.poi_list |= src
- if(end && end.z==z_original)
- walk_towards(src, destination, move_delay)
+
+ src.move_delay = move_delay
+
+ if(notify)
+ notify_ghosts("[src] is inbound!", enter_link="(Click to follow)", source = src, action = NOTIFY_FOLLOW)
+
+ destination_turf = get_turf(target_atom)
+ if(!destination_turf || destination_turf.z != src.z)
+ return
+
+ SSmove_manager.move_towards(src, destination_turf, move_delay)
+
+
+/obj/effect/immovablerod/Destroy()
+ destination_turf = null
+ GLOB.poi_list.Remove(src)
+ return ..()
+
/obj/effect/immovablerod/Topic(href, href_list)
if(href_list["follow"])
@@ -53,46 +70,67 @@ In my current plan for it, 'solid' will be defined as anything with density == 1
if(istype(ghost))
ghost.ManualFollow(src)
-/obj/effect/immovablerod/Destroy()
- GLOB.poi_list.Remove(src)
- return ..()
/obj/effect/immovablerod/ex_act(severity)
- return 0
+ return
+
/obj/effect/immovablerod/singularity_act()
return
+
/obj/effect/immovablerod/singularity_pull()
return
-/obj/effect/immovablerod/Bump(atom/clong)
+
+/obj/effect/immovablerod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ return TRUE
+
+
+/obj/effect/immovablerod/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
+
if(prob(10))
- playsound(src, 'sound/effects/bang.ogg', 50, 1)
+ playsound(src, 'sound/effects/bang.ogg', 50, TRUE)
audible_message("ЛЯЗГ")
- if(clong && prob(25))
- x = clong.x
- y = clong.y
+ var/turf/ref_bumped_turf = get_turf(bumped_atom)
+
+ if(isturf(bumped_atom) || isobj(bumped_atom))
+ if(bumped_atom.density)
+ bumped_atom.ex_act(EXPLODE_HEAVY)
+
+ else if(isliving(bumped_atom))
+ var/mob/living/bumped_living = bumped_atom
+ if(ishuman(bumped_living))
+ var/mob/living/carbon/human/bumped_human = bumped_living
+ bumped_human.visible_message(
+ span_danger("[bumped_human.name] пронизан незыблемым стержнем!"),
+ span_userdanger("Стержень пронзает Вас!"),
+ span_danger("Вы слышите ЛЯЗГ!"),
+ )
+ bumped_human.adjustBruteLoss(160)
+ if(bumped_living.density || prob(10))
+ bumped_living.ex_act(EXPLODE_HEAVY)
+
+ if(ref_bumped_turf != destination_turf && !QDELETED(src))
+ forceMove(ref_bumped_turf)
+ if(prob(25))
+ var/new_dir = turn(dir, pick(5; 90, 5; -90, 45, -45))
+ destination_turf = get_edge_target_turf(src, new_dir)
+ SSmove_manager.move_towards(src, destination_turf, move_delay)
- if(istype(clong, /turf) || isobj(clong))
- if(clong.density)
- clong.ex_act(2)
-
- else if(istype(clong, /mob))
- if(ishuman(clong))
- var/mob/living/carbon/human/H = clong
- H.visible_message("[H.name] пронизан незыблемым стержнем!" , "Стержень пронзает тебя!" , "Вы слышите ЛЯЗГ!")
- H.adjustBruteLoss(160)
- if(clong.density || prob(10))
- clong.ex_act(2)
/obj/effect/immovablerod/event
var/tiles_moved = 0
-/obj/effect/immovablerod/event/Move()
+
+/obj/effect/immovablerod/event/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/atom/oldloc = loc
. = ..()
tiles_moved++
if(get_dist(oldloc, loc) > 2 && tiles_moved > 10) // We went on a journey, commit sudoku
qdel(src)
+
diff --git a/code/modules/events/meaty_gore.dm b/code/modules/events/meaty_gore.dm
index 6672e670057..a1f23466d0d 100644
--- a/code/modules/events/meaty_gore.dm
+++ b/code/modules/events/meaty_gore.dm
@@ -7,7 +7,7 @@
/datum/event/meteor_wave/gore/tick()
if(waves && activeFor >= next_meteor)
- spawn() spawn_meteors(rand(5,8), GLOB.meteors_gore)
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, rand(5, 8), GLOB.meteors_gore)
next_meteor += rand(15, 30)
waves--
endWhen = (waves ? next_meteor + 1 : activeFor + 15)
diff --git a/code/modules/events/meaty_ops.dm b/code/modules/events/meaty_ops.dm
index 7c1ea501cda..1c242162d85 100644
--- a/code/modules/events/meaty_ops.dm
+++ b/code/modules/events/meaty_ops.dm
@@ -8,7 +8,7 @@
/datum/event/meteor_wave/goreop/tick()
if(waves && activeFor >= next_meteor)
- spawn() spawn_meteors(5, GLOB.meteors_ops)
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 5, GLOB.meteors_ops)
next_meteor += rand(15, 30)
waves--
endWhen = (waves ? next_meteor + 1 : activeFor + 15)
diff --git a/code/modules/events/meaty_ores.dm b/code/modules/events/meaty_ores.dm
index d8abcb836d7..aedb5c7dc00 100644
--- a/code/modules/events/meaty_ores.dm
+++ b/code/modules/events/meaty_ores.dm
@@ -1,36 +1,20 @@
+/datum/event/dust/meaty
+
+
+/datum/event/dust/meaty/setup()
+ qnty = rand(45,125)
+
+
/datum/event/dust/meaty/announce()
if(prob(16))
GLOB.event_announcement.Announce("Неизвестные биологические объекты были обнаружены рядом с [station_name()], пожалуйста, будьте наготове.", "ВНИМАНИЕ: НЕОПОЗНАННЫЕ ФОРМЫ ЖИЗНИ.")
else
GLOB.event_announcement.Announce("На пути станции были обнаружены мясориты.", "ВНИМАНИЕ: МЯСОРИТЫ.", new_sound = 'sound/AI/meteors.ogg')
-/datum/event/dust/meaty/setup()
- qnty = rand(45,125)
/datum/event/dust/meaty/start()
while(qnty-- > 0)
- new /obj/effect/space_dust/meaty()
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, 1, GLOB.meteors_pigs)
if(prob(10))
- sleep(rand(10,15))
-
-/obj/effect/space_dust/meaty
- icon = 'icons/mob/animal.dmi'
- icon_state = "pig"
+ sleep(rand(1 SECONDS, 1.5 SECONDS))
- strength = 1
- life = 3
- shake_chance = 20
-
-/obj/effect/space_dust/meaty/impact_meteor(atom/A)
- new /obj/effect/decal/cleanable/blood(get_turf(A))
- ..()
-
-/obj/effect/space_dust/meaty/on_shatter(turf/where)
- if(prob(80))
- gibs(where)
- if(prob(45))
- new /obj/item/reagent_containers/food/snacks/meat(where)
- else if(prob(10))
- explosion(where, 0, pick(0,1), pick(2,3), 0, cause = src)
- else
- new /mob/living/simple_animal/pig(where)
diff --git a/code/modules/events/meteors.dm b/code/modules/events/meteors.dm
index 81efd34fc96..0eed1856f45 100644
--- a/code/modules/events/meteors.dm
+++ b/code/modules/events/meteors.dm
@@ -16,7 +16,7 @@
//meteor showers are lighter and more common,
/datum/event/meteor_wave/tick()
if(waves && activeFor >= next_meteor)
- spawn() spawn_meteors(severity * rand(1,2), get_meteors())
+ INVOKE_ASYNC(GLOBAL_PROC, /proc/spawn_meteors, severity * rand(1, 2), get_meteors())
next_meteor += rand(15, 30) / severity
waves--
endWhen = (waves ? next_meteor + 1 : activeFor + 15)
diff --git a/code/modules/flufftext/Hallucination.dm b/code/modules/flufftext/Hallucination.dm
index 5acf28567c9..fe136e0f6f7 100644
--- a/code/modules/flufftext/Hallucination.dm
+++ b/code/modules/flufftext/Hallucination.dm
@@ -235,8 +235,7 @@ GLOBAL_LIST_INIT(major_hallutinations, list("fake"=20,"death"=10,"xeno"=10,"sing
if(pump)
borer = new(pump.loc,target)
for(var/i in 0 to 10)
- borer.glide_for(3)
- walk_to(borer, get_step(borer, get_cardinal_dir(borer, T)))
+ SSmove_manager.move_to(borer, T, 1, rand(2, 4))
if(borer.Adjacent(T))
to_chat(T, "You feel a creeping, horrible sense of dread come over you, freezing your limbs and setting your heart racing.")
T.Stun(8 SECONDS)
diff --git a/code/modules/instruments/objs/structures/drumkit.dm b/code/modules/instruments/objs/structures/drumkit.dm
index 77bec99bf63..3ff28c62377 100644
--- a/code/modules/instruments/objs/structures/drumkit.dm
+++ b/code/modules/instruments/objs/structures/drumkit.dm
@@ -15,7 +15,7 @@
handle_layer()
handle_offsets()
-/obj/structure/musician/drumkit/Move(NewLoc, Dir = 0, movetime)
+/obj/structure/musician/drumkit/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
handle_layer()
handle_offsets()
diff --git a/code/modules/lighting/lighting_emissive_blocker.dm b/code/modules/lighting/lighting_emissive_blocker.dm
index ba6ea24bc57..a57b2cc66d6 100644
--- a/code/modules/lighting/lighting_emissive_blocker.dm
+++ b/code/modules/lighting/lighting_emissive_blocker.dm
@@ -59,7 +59,7 @@
/atom/movable/emissive_blocker/Uncrossed(atom/movable/AM)
return
-/atom/movable/emissive_blocker/Bump(atom/A, yes)
+/atom/movable/emissive_blocker/Bump(atom/bumped_atom, custom_bump)
return
/atom/movable/emissive_blocker/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable)
diff --git a/code/modules/lighting/lighting_object.dm b/code/modules/lighting/lighting_object.dm
index f3c485349ea..67994513f79 100644
--- a/code/modules/lighting/lighting_object.dm
+++ b/code/modules/lighting/lighting_object.dm
@@ -139,7 +139,7 @@ GLOBAL_LIST_EMPTY(default_lighting_underlays_by_z)
/atom/movable/lighting_object/Uncrossed(atom/movable/AM)
return
-/atom/movable/lighting_object/Bump(atom/A, yes)
+/atom/movable/lighting_object/Bump(atom/bumped_atom, custom_bump)
return
/atom/movable/lighting_object/throw_at(atom/target, range, speed, mob/thrower, spin, diagonals_first, datum/callback/callback, force, dodgeable)
diff --git a/code/modules/mining/equipment/kinetic_crusher.dm b/code/modules/mining/equipment/kinetic_crusher.dm
index 29871c8f56b..640e5e13776 100644
--- a/code/modules/mining/equipment/kinetic_crusher.dm
+++ b/code/modules/mining/equipment/kinetic_crusher.dm
@@ -153,7 +153,7 @@
update_icon()
playsound(src.loc, 'sound/weapons/crusher_reload.ogg', 135)
-/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, actiontype)
+/obj/item/twohanded/kinetic_crusher/ui_action_click(mob/user, action)
set_light_on(!light_on)
playsound(user, 'sound/weapons/empty.ogg', 100, TRUE)
update_icon()
diff --git a/code/modules/mining/equipment/mineral_scanner.dm b/code/modules/mining/equipment/mineral_scanner.dm
index dc846a8a45b..562ca3ba0f0 100644
--- a/code/modules/mining/equipment/mineral_scanner.dm
+++ b/code/modules/mining/equipment/mineral_scanner.dm
@@ -119,7 +119,7 @@
plane = FULLSCREEN_PLANE
layer = FLASH_LAYER
icon = 'icons/effects/ore_visuals.dmi'
- appearance_flags = 0 //to avoid having TILE_BOUND in the flags, so that the 480x480 icon states let you see it no matter where you are
+ appearance_flags = LONG_GLIDE //to avoid having TILE_BOUND in the flags, so that the 480x480 icon states let you see it no matter where you are
duration = 35
pixel_x = -224
pixel_y = -224
diff --git a/code/modules/mining/lavaland/loot/colossus_loot.dm b/code/modules/mining/lavaland/loot/colossus_loot.dm
index da465c1fd0c..b957e2fa8d4 100644
--- a/code/modules/mining/lavaland/loot/colossus_loot.dm
+++ b/code/modules/mining/lavaland/loot/colossus_loot.dm
@@ -71,7 +71,7 @@
return 1
/obj/machinery/anomalous_crystal/Bumped(atom/movable/moving_atom)
- ..()
+ . = ..()
if(ismob(moving_atom))
ActivationReaction(moving_atom,"mob_bump")
diff --git a/code/modules/mining/lavaland/loot/hierophant_loot.dm b/code/modules/mining/lavaland/loot/hierophant_loot.dm
index 650e791d146..5e837c6beb7 100644
--- a/code/modules/mining/lavaland/loot/hierophant_loot.dm
+++ b/code/modules/mining/lavaland/loot/hierophant_loot.dm
@@ -122,8 +122,8 @@
update_icon(UPDATE_ICON_STATE)
-/obj/item/hierophant_club/ui_action_click(mob/user, actiontype)
- if(actiontype == /datum/action/item_action/toggle_unfriendly_fire) //toggle friendly fire...
+/obj/item/hierophant_club/ui_action_click(mob/user, action)
+ if(istype(action, /datum/action/item_action/toggle_unfriendly_fire)) //toggle friendly fire...
friendly_fire_check = !friendly_fire_check
to_chat(user, "You toggle friendly fire [friendly_fire_check ? "off":"on"]!")
return
diff --git a/code/modules/mining/lavaland/loot/tendril_loot.dm b/code/modules/mining/lavaland/loot/tendril_loot.dm
index 80776e17e02..d12b4c6b2b1 100644
--- a/code/modules/mining/lavaland/loot/tendril_loot.dm
+++ b/code/modules/mining/lavaland/loot/tendril_loot.dm
@@ -155,22 +155,26 @@
desc = "A boat used for traversing lava."
icon_state = "goliath_boat"
icon = 'icons/obj/lavaland/dragonboat.dmi'
- held_key_type = /obj/item/oar
+ layer = ABOVE_MOB_LAYER
+ key_type = /obj/item/oar
+ key_in_hands = TRUE
resistance_flags = LAVA_PROOF | FIRE_PROOF
- /// The last time we told the user that they can't drive on land, so we don't spam them
- var/last_message_time = 0
-/obj/vehicle/lavaboat/relaymove(mob/user, direction)
- var/turf/next = get_step(src, direction)
- var/turf/current = get_turf(src)
- if(istype(next, /turf/simulated/floor/plating/lava/smooth) || istype(current, /turf/simulated/floor/plating/lava/smooth)) //We can move from land to lava, or lava to land, but not from land to land
- ..()
- else
- if(last_message_time + 1 SECONDS < world.time)
- to_chat(user, "Boats don't go on land!")
- last_message_time = world.time
+/obj/vehicle/lavaboat/relaymove(mob/user, direction)
+ if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown))
return FALSE
+ //We can move from land to lava, or lava to land, but not from land to land
+ if(!istype(get_step(src, direction), /turf/simulated/floor/plating/lava/smooth) && !istype(get_turf(src), /turf/simulated/floor/plating/lava/smooth))
+ to_chat(user, span_warning("You cannot traverse futher!"))
+ COOLDOWN_START(src, vehicle_move_cooldown, 0.5 SECONDS)
+ return FALSE
+ return ..()
+
+
+/obj/vehicle/lavaboat/handle_vehicle_layer()
+ return
+
/obj/item/oar
name = "oar"
@@ -213,11 +217,12 @@
/obj/vehicle/lavaboat/dragon
name = "mysterious boat"
desc = "This boat moves where you will it, without the need for an oar."
- held_key_type = null
+ key_type = null
+ key_in_hands = FALSE
icon_state = "dragon_boat"
generic_pixel_y = 2
generic_pixel_x = 1
- vehicle_move_delay = 1
+ vehicle_move_delay = 0.25 SECONDS
//Wisp Lantern
/obj/item/wisp_lantern
diff --git a/code/modules/mob/dead/observer/observer.dm b/code/modules/mob/dead/observer/observer.dm
index 9c784170732..0914976b494 100644
--- a/code/modules/mob/dead/observer/observer.dm
+++ b/code/modules/mob/dead/observer/observer.dm
@@ -238,35 +238,36 @@ 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 = NONE)
+/mob/dead/observer/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
-/mob/dead/observer/Move(NewLoc, direct)
- if(world.time < last_movement)
- return
- last_movement = world.time + 0.5 // cap to 20fps
- glide_size = 8
- update_parallax_contents()
+/mob/dead/observer/Move(atom/newloc, direct = NONE, glide_size_override = 8)
setDir(direct)
ghostimage.setDir(dir)
- var/oldloc = loc
+ if(glide_size_override)
+ set_glide_size(glide_size_override)
- if(NewLoc)
- forceMove(NewLoc)
+ if(newloc)
+ abstract_move(newloc)
+ update_parallax_contents()
else
- forceMove(get_turf(src)) //Get out of closets and such as a ghost
+ var/turf/destination = get_turf(src)
+
if((direct & NORTH) && y < world.maxy)
- y++
+ destination = get_step(destination, NORTH)
+
else if((direct & SOUTH) && y > 1)
- y--
+ destination = get_step(destination, SOUTH)
+
if((direct & EAST) && x < world.maxx)
- x++
+ destination = get_step(destination, EAST)
+
else if((direct & WEST) && x > 1)
- x--
+ destination = get_step(destination, WEST)
- Moved(oldloc, direct)
+ abstract_move(destination)//Get out of closets and such as a ghost
/mob/dead/observer/Stat()
diff --git a/code/modules/mob/living/carbon/carbon.dm b/code/modules/mob/living/carbon/carbon.dm
index a6cb8cbc360..d9d7adb5911 100644
--- a/code/modules/mob/living/carbon/carbon.dm
+++ b/code/modules/mob/living/carbon/carbon.dm
@@ -32,7 +32,7 @@
adjustBruteLoss(10)
-/mob/living/carbon/Move(NewLoc, direct)
+/mob/living/carbon/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(.)
if(nutrition && stat != DEAD && !isvampire(src))
diff --git a/code/modules/mob/living/carbon/human/human_defines.dm b/code/modules/mob/living/carbon/human/human_defines.dm
index 1db4d77ac53..9e0287af468 100644
--- a/code/modules/mob/living/carbon/human/human_defines.dm
+++ b/code/modules/mob/living/carbon/human/human_defines.dm
@@ -4,7 +4,7 @@
voice_name = "unknown"
icon = 'icons/mob/human.dmi'
icon_state = "body_m_s"
- appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE
+ appearance_flags = KEEP_TOGETHER|TILE_BOUND|PIXEL_SCALE|LONG_GLIDE
deathgasp_on_death = TRUE
hud_possible = list(HEALTH_HUD,STATUS_HUD,ID_HUD,WANTED_HUD,IMPMINDSHIELD_HUD,IMPCHEM_HUD,IMPTRACK_HUD,SPECIALROLE_HUD,GLAND_HUD,THOUGHT_HUD,DIAG_STAT_HUD,DIAG_HUD)
pressure_resistance = 25
diff --git a/code/modules/mob/living/carbon/human/human_movement.dm b/code/modules/mob/living/carbon/human/human_movement.dm
index ac87fa7c3ca..75269e83267 100644
--- a/code/modules/mob/living/carbon/human/human_movement.dm
+++ b/code/modules/mob/living/carbon/human/human_movement.dm
@@ -16,29 +16,15 @@
return considering
-/mob/living/carbon/human/Process_Spacemove(movement_dir = NONE)
- . = ..()
- if(.)
- return .
-
- var/list/jetpacks = list()
-
- if(istype(back, /obj/item/tank/jetpack))
- jetpacks += back
-
- var/obj/item/clothing/suit/space/space_suit = wear_suit
- if(istype(space_suit) && space_suit.jetpack)
- jetpacks += space_suit.jetpack
-
- for(var/obj/item/tank/jetpack/jetpack as anything in jetpacks)
- if((movement_dir || jetpack.stabilizers) && jetpack.allow_thrust(0.01, src, should_leave_trail = movement_dir))
- return TRUE
-
- if(dna.species.spec_Process_Spacemove(src, movement_dir))
+/mob/living/carbon/human/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ if(movement_type & FLYING)
return TRUE
+ if(dna.species.spec_Process_Spacemove(src, movement_dir, continuous_move = FALSE))
+ return TRUE
+ return ..()
-/mob/living/carbon/human/Move(NewLoc, direct)
+/mob/living/carbon/human/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(.) // did we actually move?
if(body_position != LYING_DOWN && !buckled && !throwing)
@@ -61,7 +47,7 @@
var/obj/item/clothing/shoes/S = shoes
- if(S && body_position != LYING_DOWN && loc == NewLoc)
+ if(S && body_position != LYING_DOWN && loc == newloc)
SEND_SIGNAL(S, COMSIG_SHOES_STEP_ACTION)
//Bloody footprints
diff --git a/code/modules/mob/living/carbon/human/species/_species.dm b/code/modules/mob/living/carbon/human/species/_species.dm
index 2ccc89a755b..b908b1cd005 100644
--- a/code/modules/mob/living/carbon/human/species/_species.dm
+++ b/code/modules/mob/living/carbon/human/species/_species.dm
@@ -1147,7 +1147,7 @@ It'll return null if the organ doesn't correspond, so include null checks when u
return user.get_organ_slot(INTERNAL_ORGAN_EYES)
-/datum/species/proc/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir)
+/datum/species/proc/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir, continuous_move = FALSE)
return FALSE
diff --git a/code/modules/mob/living/carbon/human/species/moth.dm b/code/modules/mob/living/carbon/human/species/moth.dm
index 97b32fd6780..e50bf8a5157 100644
--- a/code/modules/mob/living/carbon/human/species/moth.dm
+++ b/code/modules/mob/living/carbon/human/species/moth.dm
@@ -126,7 +126,7 @@
apply_damage(I.force * FLYSWATTER_DAMAGE_MULTIPLIER, I.damtype, affecting, FALSE, H) //making flyswatters do 10x damage to moff
-/datum/species/moth/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir)
+/datum/species/moth/spec_Process_Spacemove(mob/living/carbon/human/user, movement_dir, continuous_move = FALSE)
. = FALSE
var/turf/user_turf = get_turf(user)
if(!user_turf)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 7b39dd6d0a2..2dc9c1b3ade 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -167,21 +167,23 @@
return .
-//Generic Bump(). Override MobBump() and ObjBump() instead of this.
-/mob/living/Bump(atom/A, yes)
- if(..()) //we are thrown onto something
- return
- if(buckled || !yes || now_pushing)
- return
- if(ismob(A))
- if(MobBump(A))
- return
- if(isobj(A))
- if(ObjBump(A))
- return
- if(istype(A, /atom/movable))
- if(PushAM(A, move_force))
- return
+// Generic Bump(). Override MobBump() and ObjBump() instead of this.
+/mob/living/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.)) // byond bump or we are thrown onto something
+ return .
+ if(buckled || now_pushing)
+ return .
+ if(ismob(bumped_atom))
+ if(MobBump(bumped_atom))
+ return TRUE
+ else if(isobj(bumped_atom))
+ if(ObjBump(bumped_atom))
+ return TRUE
+ else if(ismovable(bumped_atom))
+ if(PushAM(bumped_atom, move_force))
+ return TRUE
+
//Called when we bump into a mob
/mob/living/proc/MobBump(mob/M)
@@ -304,13 +306,13 @@
if(!client && (mob_size < MOB_SIZE_SMALL))
return
now_pushing = TRUE
- var/t = get_dir(src, AM)
+ var/dir_to_target = get_dir(src, AM)
var/push_anchored = FALSE
if((AM.move_resist * MOVE_FORCE_CRUSH_RATIO) <= force)
- if(move_crush(AM, move_force, t))
+ if(move_crush(AM, move_force, dir_to_target))
push_anchored = TRUE
if((AM.move_resist * MOVE_FORCE_FORCEPUSH_RATIO) <= force) //trigger move_crush and/or force_push regardless of if we can push it normally
- if(force_push(AM, move_force, t, push_anchored))
+ if(force_push(AM, move_force, dir_to_target, push_anchored))
push_anchored = TRUE
if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO)))
now_pushing = FALSE
@@ -318,22 +320,21 @@
if(istype(AM, /obj/structure/window))
var/obj/structure/window/W = AM
if(W.fulltile)
- for(var/obj/structure/window/win in get_step(W,t))
+ for(var/obj/structure/window/win in get_step(W, dir_to_target))
now_pushing = FALSE
return
if(pulling == AM)
stop_pulling()
- if(client)
- client.current_move_delay *= AM.get_pull_push_speed_modifier(client.current_move_delay)
- glide_for(client.current_move_delay)
+ //if(client)
+ // client.move_delay += AM.get_pull_push_speed_modifier(client.move_delay)
- AM.glide_size = glide_size
var/current_dir
if(isliving(AM))
current_dir = AM.dir
- if(step(AM, t))
- step(src, t)
+ if(AM.Move(get_step(AM.loc, dir_to_target), dir_to_target, glide_size))
+ AM.add_fingerprint(src)
+ Move(get_step(loc, dir_to_target), dir_to_target)
if(current_dir)
AM.setDir(current_dir)
now_pushing = FALSE
@@ -792,8 +793,7 @@
return
-
-/mob/living/Move(atom/newloc, direct, movetime)
+/mob/living/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(lying_angle != 0)
lying_angle_on_movement(direct)
@@ -815,9 +815,9 @@
. = ..()
if(.)
step_count++
- pull_pulled(old_loc, pulling, movetime)
+ pull_pulled(old_loc, pulling, glide_size_override)
if(!currently_grab_pulled)
- pull_grabbed(old_loc, direct, movetime)
+ pull_grabbed(old_loc, direct, glide_size_override)
if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //seperated from our puller and not in the middle of a diagonal move
pulledby.stop_pulling()
@@ -849,7 +849,7 @@
return
var/pull_dir = get_dir(src, pulling)
- pulling.glide_size = glide_size
+ pulling.set_glide_size(glide_size)
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir))) // puller and pullee more than one tile away or in diagonal position
// This sucks.
// Pulling things up/down & into other z-levels. Conga line lives.
diff --git a/code/modules/mob/living/silicon/pai/pai.dm b/code/modules/mob/living/silicon/pai/pai.dm
index 36f7ca227b3..8d552e99ffa 100644
--- a/code/modules/mob/living/silicon/pai/pai.dm
+++ b/code/modules/mob/living/silicon/pai/pai.dm
@@ -516,12 +516,9 @@
card.forceMove(card.loc)
icon_state = "[chassis]"
-/mob/living/silicon/pai/Bump()
+/mob/living/silicon/pai/Bump(atom/bumped_atom, custom_bump)
return
-/mob/living/silicon/pai/Bumped(atom/movable/moving_atom)
- return ..()
-
/mob/living/silicon/pai/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE)
return FALSE
diff --git a/code/modules/mob/living/silicon/robot/drone/drone.dm b/code/modules/mob/living/silicon/robot/drone/drone.dm
index de4a6901b05..cd32280f724 100644
--- a/code/modules/mob/living/silicon/robot/drone/drone.dm
+++ b/code/modules/mob/living/silicon/robot/drone/drone.dm
@@ -359,13 +359,10 @@
to_chat(src, "Make sure crew members do not notice you..")
-/mob/living/silicon/robot/drone/Bump(atom/movable/AM, yes)
- if(is_type_in_list(AM, allowed_bumpable_objects))
+/mob/living/silicon/robot/drone/Bump(atom/bumped_atom, custom_bump)
+ if(custom_bump && is_type_in_list(bumped_atom, allowed_bumpable_objects))
return ..()
-/mob/living/silicon/robot/drone/Bumped(atom/movable/moving_atom)
- return ..()
-
/mob/living/silicon/robot/drone/start_pulling(atom/movable/AM, force = pull_force, show_message = FALSE)
if(is_type_in_list(AM, pullable_drone_items))
diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm
index 26a30ee5399..a7441600ad1 100644
--- a/code/modules/mob/living/silicon/robot/robot.dm
+++ b/code/modules/mob/living/silicon/robot/robot.dm
@@ -112,8 +112,12 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
hud_possible = list(SPECIALROLE_HUD, DIAG_STAT_HUD, DIAG_HUD, DIAG_BATT_HUD)
var/default_cell_type = /obj/item/stock_parts/cell/high
- var/ionpulse = 0 // Jetpack-like effect.
- var/ionpulse_on = 0 // Jetpack-like effect.
+ ///Jetpack-like effect.
+ var/ionpulse = FALSE
+ ///Jetpack-like effect.
+ var/ionpulse_on = FALSE
+ ///Ionpulse effect.
+ var/datum/effect_system/trail_follow/ion/ion_trail
var/datum/action/innate/research_scanner/scanner = null
var/list/module_actions = list()
@@ -189,16 +193,10 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
diag_hud_set_borgcell()
scanner = new()
scanner.Grant(src)
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail))
if(length(module?.borg_skins) <= 1 && (has_transform_animation || module?.has_transform_animation))
transform_animation(icon_state, TRUE)
-/mob/living/silicon/robot/proc/create_trail(datum/source, atom/oldloc, _dir, forced)
- if(ionpulse_on)
- var/turf/T = get_turf(oldloc)
- if(!T.has_gravity(T))
- new /obj/effect/particle_effect/ion_trails(T, _dir)
/mob/living/silicon/robot/proc/init(alien, connect_to_AI = TRUE, mob/living/silicon/ai/ai_to_sync_to = null)
aiCamera = new/obj/item/camera/siliconcam/robot_camera(src)
@@ -330,6 +328,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
QDEL_NULL(robot_suit)
QDEL_NULL(spark_system)
QDEL_NULL(self_diagnosis)
+ QDEL_NULL(ion_trail)
return ..()
/mob/living/silicon/robot/proc/pick_module(var/forced_module = null)
@@ -658,15 +657,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
alerts.set_content(dat_text)
alerts.open()
+
/mob/living/silicon/robot/proc/ionpulse()
if(!ionpulse_on)
return FALSE
-
- if(!cell || cell.charge <= 50)
+ if(!cell || !cell.use(25)) // 500 steps on a default cell.
toggle_ionpulse(silent = TRUE)
return FALSE
-
- cell.charge -= 25 // 500 steps on a default cell.
return TRUE
@@ -676,12 +673,11 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
to_chat(src, span_notice("No thrusters are installed!"))
return
- ionpulse_on = !ionpulse_on
+ if(!ion_trail)
+ ion_trail = new
+ ion_trail.set_up(src)
- if(ionpulse_on)
- add_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade)
- else
- remove_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade)
+ ionpulse_on = !ionpulse_on
if(!silent)
to_chat(src, span_notice("You [ionpulse_on ? "" : "de"]activate your ion thrusters."))
@@ -689,6 +685,13 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
if(thruster_button)
thruster_button.icon_state = "ionpulse[ionpulse_on]"
+ if(ionpulse_on)
+ ion_trail.start()
+ add_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade)
+ else
+ ion_trail.stop()
+ remove_movespeed_modifier(/datum/movespeed_modifier/robot_jetpack_upgrade)
+
/mob/living/silicon/robot/blob_act(obj/structure/blob/B)
if(stat != DEAD)
@@ -1380,7 +1383,7 @@ GLOBAL_LIST_INIT(robot_verbs_default, list(
drop_hat()
qdel(src)
-/mob/living/silicon/robot/Move(a, b, flag)
+/mob/living/silicon/robot/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/oldLoc = src.loc
. = ..()
if(.)
diff --git a/code/modules/mob/living/silicon/robot/robot_movement.dm b/code/modules/mob/living/silicon/robot/robot_movement.dm
index f091b8b6b94..b406426fa96 100644
--- a/code/modules/mob/living/silicon/robot/robot_movement.dm
+++ b/code/modules/mob/living/silicon/robot/robot_movement.dm
@@ -1,7 +1,10 @@
-/mob/living/silicon/robot/Process_Spacemove(movement_dir = NONE)
+/mob/living/silicon/robot/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ . = ..()
+ if(.)
+ return TRUE
if(ionpulse())
return TRUE
- return ..()
+ return FALSE
/mob/living/silicon/robot/experience_pressure_difference(pressure_difference, direction)
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index d3ec06c561b..110324aedcd 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -629,7 +629,7 @@ Pass the desired type path itself, declaring a temporary var beforehand is not r
if(!length(path))
return FALSE
- glide_for(BOT_STEP_DELAY)
+ set_glide_size(DELAY_TO_GLIDE_SIZE(BOT_STEP_DELAY))
if(!step_towards(src, path[1]))
tries++
return FALSE
diff --git a/code/modules/mob/living/simple_animal/bot/ed209bot.dm b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
index c8e02bf4d16..48a219ba1c5 100644
--- a/code/modules/mob/living/simple_animal/bot/ed209bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/ed209bot.dm
@@ -111,7 +111,7 @@
target = null
oldtarget_name = null
set_anchored(FALSE)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
last_found = world.time
set_weapon()
@@ -279,7 +279,7 @@
switch(mode)
if(BOT_IDLE) // idle
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
if(!lasercolor) //lasertag bots don't want to arrest anyone
look_for_perp() // see if any criminals are in range
@@ -289,7 +289,7 @@
if(BOT_HUNT) // hunting for perp
// if can't reach perp for long enough, go idle
if(frustration >= 8)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
back_to_idle()
@@ -309,8 +309,7 @@
else if(!disabled) // not next to perp
var/turf/olddist = get_dist(src, target)
- glide_for(BOT_STEP_DELAY)
- walk_to(src, target,1,4)
+ SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY)
if((get_dist(src, target)) >= (olddist))
frustration++
else
@@ -420,7 +419,7 @@
/mob/living/simple_animal/bot/ed209/explode()
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
visible_message("[src] blows apart!")
var/turf/Tsec = get_turf(src)
@@ -563,7 +562,7 @@
if(lasertag_check)
icon_state = "[lasercolor]ed2090"
disabled = TRUE
- walk_to(src, 0)
+ SSmove_manager.stop_looping(src)
target = null
addtimer(CALLBACK(src, PROC_REF(unset_disabled)), 10 SECONDS)
return TRUE
diff --git a/code/modules/mob/living/simple_animal/bot/griefsky.dm b/code/modules/mob/living/simple_animal/bot/griefsky.dm
index e556f5b2085..6338f4f1730 100644
--- a/code/modules/mob/living/simple_animal/bot/griefsky.dm
+++ b/code/modules/mob/living/simple_animal/bot/griefsky.dm
@@ -151,7 +151,7 @@
switch(mode)
if(BOT_IDLE) // idle
icon_state = "[base_icon][on]"
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
look_for_perp() // see if any criminals are in range
if(!mode && auto_patrol) // still idle, and set to patrol
@@ -160,7 +160,7 @@
icon_state = spin_icon
playsound(loc,'sound/effects/spinsabre.ogg',50,1,-1)
if(frustration >= frustration_number) // general beepsky doesn't give up so easily, jedi scum
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
back_to_idle()
return
@@ -174,8 +174,7 @@
return
else // not next to perp
var/turf/olddist = get_dist(src, target)
- glide_for(BOT_STEP_DELAY)
- walk_to(src, target,1,3) //he's a fast fucker
+ SSmove_manager.move_to(src, target, 1, 3) //he's a fast fucker
if((get_dist(src, target)) >= (olddist))
frustration++
else
@@ -244,7 +243,7 @@
/mob/living/simple_animal/bot/secbot/griefsky/explode()
- walk_to(src,0)
+ SSmove_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 f260427721f..8157dc5ec6e 100644
--- a/code/modules/mob/living/simple_animal/bot/honkbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/honkbot.dm
@@ -67,7 +67,7 @@
target = null
oldtarget_name = null
set_anchored(FALSE)
- walk_to(src, 0)
+ SSmove_manager.stop_looping(src)
last_found = world.time
spam_flag = FALSE
@@ -214,14 +214,14 @@
switch(mode)
if(BOT_IDLE) // idle
- walk_to(src, 0)
+ SSmove_manager.stop_looping(src)
look_for_perp()
if(!mode && auto_patrol)
mode = BOT_START_PATROL
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)
+ SSmove_manager.stop_looping(src)
playsound(loc, 'sound/misc/sadtrombone.ogg', 25, TRUE, -1)
back_to_idle()
return
@@ -239,8 +239,7 @@
return
else // not next to perp
var/turf/olddist = get_dist(src, target)
- glide_for(BOT_STEP_DELAY)
- walk_to(src, target, 1, 4)
+ SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY)
if((get_dist(src, target)) >= (olddist))
frustration++
else
@@ -304,7 +303,7 @@
/mob/living/simple_animal/bot/honkbot/explode() //doesn't drop cardboard nor its assembly, since its a very frail material.
- walk_to(src, 0)
+ SSmove_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/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index 287368654ec..de27a7d7658 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -692,7 +692,7 @@
bot_reset() // otherwise go idle
-/mob/living/simple_animal/bot/mulebot/Move(turf/simulated/next)
+/mob/living/simple_animal/bot/mulebot/Move(turf/simulated/next, direct = NONE, glide_size_override = 0)
. = ..()
if(. && istype(next))
@@ -720,19 +720,22 @@
/**
* Called when bot bumps into anything.
*/
-/mob/living/simple_animal/bot/mulebot/Bump(atom/obs)
- if(wires.is_cut(WIRE_MOB_AVOIDANCE)) // usually just bumps, but if avoidance disabled knock over mobs
- var/mob/living/L = obs
- if(ismob(L))
- if(isrobot(L))
- visible_message(span_danger("[src] bumps into [L]!"))
- else
- if(!paicard)
- add_attack_logs(src, L, "Knocked down")
- visible_message(span_danger("[src] knocks over [L]!"))
- L.stop_pulling()
- L.Weaken(16 SECONDS)
- return ..()
+/mob/living/simple_animal/bot/mulebot/Bump(mob/living/bumped_living, custom_bump)
+ . = ..()
+ if(isnull(.) || !wires.is_cut(WIRE_MOB_AVOIDANCE) || !isliving(bumped_living))
+ return .
+
+ // usually just bumps, but if avoidance disabled knock over mobs
+ if(isrobot(bumped_living))
+ visible_message(span_danger("[src] bumps into [bumped_living]!"))
+ return .
+
+ if(paicard)
+ return .
+
+ add_attack_logs(src, bumped_living, "Knocked down")
+ visible_message(span_danger("[src] knocks over [bumped_living]!"))
+ bumped_living.Weaken(16 SECONDS)
/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
diff --git a/code/modules/mob/living/simple_animal/bot/secbot.dm b/code/modules/mob/living/simple_animal/bot/secbot.dm
index 226d44a3ba3..560e8fc2331 100644
--- a/code/modules/mob/living/simple_animal/bot/secbot.dm
+++ b/code/modules/mob/living/simple_animal/bot/secbot.dm
@@ -140,7 +140,7 @@
target = null
oldtarget_name = null
set_anchored(FALSE)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
last_found = world.time
@@ -361,7 +361,7 @@
switch(mode)
if(BOT_IDLE) // idle
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
look_for_perp() // see if any criminals are in range
if(!mode && auto_patrol) // still idle, and set to patrol
@@ -370,7 +370,7 @@
if(BOT_HUNT) // hunting for perp
// if can't reach perp for long enough, go idle
if(frustration >= 8)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
back_to_idle()
return
@@ -386,8 +386,7 @@
else // not next to perp
var/turf/olddist = get_dist(src, target)
- glide_for(BOT_STEP_DELAY)
- walk_to(src, target,1,4)
+ SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY)
if((get_dist(src, target)) >= (olddist))
frustration++
else
@@ -493,7 +492,7 @@
/mob/living/simple_animal/bot/secbot/explode()
- walk_to(src,0)
+ SSmove_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.dm b/code/modules/mob/living/simple_animal/bot/syndicate.dm
index 60674386ac4..ee007facf74 100644
--- a/code/modules/mob/living/simple_animal/bot/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/bot/syndicate.dm
@@ -94,14 +94,14 @@
saved_turf = current_turf
switch(mode)
if(BOT_IDLE)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
look_for_perp()
if(!mode && auto_patrol)
mode = BOT_START_PATROL
if(BOT_HUNT)
if(frustration >= 8)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
set_path(null)
back_to_idle()
if(target)
@@ -111,8 +111,7 @@
return
shootAt(target)
var/turf/olddist = get_dist(src, target)
- glide_for(BOT_STEP_DELAY)
- walk_to(src, target,1,4)
+ SSmove_manager.move_to(src, target, 1, BOT_STEP_DELAY)
if((get_dist(src, target)) >= (olddist))
frustration++
else
@@ -183,7 +182,7 @@
if(!QDELETED(src))
if(depotarea)
depotarea.list_remove(src, depotarea.guard_list)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
visible_message(span_userdanger("[src] blows apart!"))
do_sparks(3, 1, src)
new /obj/effect/decal/cleanable/blood/oil(loc)
@@ -220,7 +219,7 @@
return
-/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/bot/ed209/syndicate/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/modules/mob/living/simple_animal/constructs.dm b/code/modules/mob/living/simple_animal/constructs.dm
index 60c4a939792..3d76fa92c66 100644
--- a/code/modules/mob/living/simple_animal/constructs.dm
+++ b/code/modules/mob/living/simple_animal/constructs.dm
@@ -338,7 +338,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 = NONE)
+/mob/living/simple_animal/hostile/construct/harvester/Process_Spacemove(movement_dir = NONE, 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 c6d7cab2340..14e26503863 100644
--- a/code/modules/mob/living/simple_animal/friendly/cat.dm
+++ b/code/modules/mob/living/simple_animal/friendly/cat.dm
@@ -196,7 +196,7 @@
if(!stat && !resting && !buckled)
turns_since_scan++
if(turns_since_scan > 5)
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
turns_since_scan = 0
if((movement_target) && !(isturf(movement_target.loc) || ishuman(movement_target.loc) ))
movement_target = null
@@ -209,9 +209,9 @@
movement_target = snack
break
if(movement_target)
+ StopResting()
stop_automated_movement = 1
- glide_for(3)
- walk_to(src,movement_target,0,3)
+ SSmove_manager.move_to(src, movement_target, 1, 4)
/mob/living/simple_animal/pet/cat/Proc
diff --git a/code/modules/mob/living/simple_animal/friendly/crab.dm b/code/modules/mob/living/simple_animal/friendly/crab.dm
index 5f6020c0dd1..93105a1f791 100644
--- a/code/modules/mob/living/simple_animal/friendly/crab.dm
+++ b/code/modules/mob/living/simple_animal/friendly/crab.dm
@@ -26,15 +26,6 @@
holder_type = /obj/item/holder/crab
mob_size = MOB_SIZE_SMALL
-/mob/living/simple_animal/crab/handle_automated_movement()
- //CRAB movement
- if(!stat)
- if(isturf(src.loc) && !resting && !buckled) //This is so it only moves if it's not inside a closet, gentics machine, etc.
- turns_since_move++
- if(turns_since_move >= turns_per_move)
- var/east_vs_west = pick(4, 8)
- if(Process_Spacemove(east_vs_west))
- Move(get_step(src, east_vs_west), east_vs_west)
/mob/living/simple_animal/crab/royal
name = "королевский краб"
diff --git a/code/modules/mob/living/simple_animal/friendly/dog.dm b/code/modules/mob/living/simple_animal/friendly/dog.dm
index 7a5a90b28a0..2a260ce33d4 100644
--- a/code/modules/mob/living/simple_animal/friendly/dog.dm
+++ b/code/modules/mob/living/simple_animal/friendly/dog.dm
@@ -660,7 +660,7 @@
maxHealth = 60
health = 60
-/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/pet/dog/corgi/puppy/void/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //Void puppies can navigate space.
/mob/living/simple_animal/pet/dog/corgi/puppy/slime
diff --git a/code/modules/mob/living/simple_animal/friendly/snail.dm b/code/modules/mob/living/simple_animal/friendly/snail.dm
index e4f8f6a3136..8db1ada3e0a 100644
--- a/code/modules/mob/living/simple_animal/friendly/snail.dm
+++ b/code/modules/mob/living/simple_animal/friendly/snail.dm
@@ -30,10 +30,10 @@
reagents = new()
holder_type = /obj/item/holder/snail
-/mob/living/simple_animal/snail/Process_Spacemove(movement_dir = NONE)
- return TRUE
+/mob/living/simple_animal/snail/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ return TRUE // why???
-/mob/living/simple_animal/snail/Move(atom/newloc, direct, movetime)
+/mob/living/simple_animal/snail/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/oldLoc = src.loc
. = ..()
if(.)
diff --git a/code/modules/mob/living/simple_animal/hostile/bear.dm b/code/modules/mob/living/simple_animal/hostile/bear.dm
index 1f0023e2fe2..cdf128072bd 100644
--- a/code/modules/mob/living/simple_animal/hostile/bear.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bear.dm
@@ -63,7 +63,7 @@
else
icon_state = "[icon_living]floor"
-/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/bear/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //No drifting in space for space bears!
/mob/living/simple_animal/hostile/bear/brown
diff --git a/code/modules/mob/living/simple_animal/hostile/bees.dm b/code/modules/mob/living/simple_animal/hostile/bees.dm
index f9d5afafcab..cbe696743b8 100644
--- a/code/modules/mob/living/simple_animal/hostile/bees.dm
+++ b/code/modules/mob/living/simple_animal/hostile/bees.dm
@@ -53,7 +53,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 = NONE)
+/mob/living/simple_animal/hostile/poison/bees/Process_Spacemove(movement_dir = NONE, 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 cc59031271d..f533fbf02a5 100644
--- a/code/modules/mob/living/simple_animal/hostile/carp.dm
+++ b/code/modules/mob/living/simple_animal/hostile/carp.dm
@@ -95,7 +95,7 @@
base_dead_overlay.appearance_flags = RESET_COLOR
add_overlay(base_dead_overlay)
-/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/carp/Process_Spacemove(movement_dir = NONE, 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 9d7e146da29..091890e3aa6 100644
--- a/code/modules/mob/living/simple_animal/hostile/deathsquid.dm
+++ b/code/modules/mob/living/simple_animal/hostile/deathsquid.dm
@@ -33,7 +33,7 @@
-/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/deathsquid/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //copypasta from carp code
/mob/living/simple_animal/hostile/deathsquid/ex_act(severity)
diff --git a/code/modules/mob/living/simple_animal/hostile/faithless.dm b/code/modules/mob/living/simple_animal/hostile/faithless.dm
index 002dc17f100..c71a656cdf3 100644
--- a/code/modules/mob/living/simple_animal/hostile/faithless.dm
+++ b/code/modules/mob/living/simple_animal/hostile/faithless.dm
@@ -29,7 +29,7 @@
faction = list("faithless")
gold_core_spawnable = HOSTILE_SPAWN
-/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/faithless/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/hostile/faithless/AttackingTarget()
diff --git a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
index 1a3b4ef1f78..81fd894210e 100644
--- a/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
+++ b/code/modules/mob/living/simple_animal/hostile/floorcluwne.dm
@@ -124,10 +124,9 @@
/mob/living/simple_animal/hostile/floor_cluwne/Goto(target, delay, minimum_distance)
if(!manifested && !is_type_in_typecache(get_area(current_victim.loc), invalid_area_typecache))
- glide_for(delay)
- walk_to(src, target, minimum_distance, delay)
+ SSmove_manager.move_to(src, target, minimum_distance, delay)
else
- walk_to(src,0)
+ SSmove_manager.stop_looping(src)
/mob/living/simple_animal/hostile/floor_cluwne/FindTarget()
diff --git a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
index c5b00f7ce9f..b48be0a138b 100644
--- a/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
+++ b/code/modules/mob/living/simple_animal/hostile/giant_spider.dm
@@ -52,12 +52,12 @@
if(C.can_inject(null, FALSE, inject_target, FALSE))
C.reagents.add_reagent("spidertoxin", venom_per_bite)
-/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup()
+/mob/living/simple_animal/hostile/poison/giant_spider/get_spacemove_backup(moving_direction, continuous_move)
. = ..()
// If we don't find any normal thing to use, attempt to use any nearby spider structure instead.
if(!.)
- for(var/obj/structure/spider/S in range(1, get_turf(src)))
- return S
+ for(var/obj/structure/spider/spider_thing in range(1, get_turf(src)))
+ return spider_thing
//nursemaids - these create webs and eggs
/mob/living/simple_animal/hostile/poison/giant_spider/nurse
@@ -97,7 +97,7 @@
Goto(pick(urange(20, src, 1)), move_to_delay)
spawn(50)
stop_automated_movement = 0
- walk(src,0)
+ SSmove_manager.stop_looping(src)
return 1
/mob/living/simple_animal/hostile/poison/giant_spider/nurse/proc/GiveUp(C)
@@ -202,7 +202,7 @@
busy = SPINNING_COCOON
src.visible_message("\the [src] begins to secrete a sticky substance around \the [cocoon_target].")
stop_automated_movement = 1
- walk(src,0)
+ SSmove_manager.stop_looping(src)
spawn(50)
if(busy == SPINNING_COCOON)
if(cocoon_target && istype(cocoon_target.loc, /turf) && get_dist(src,cocoon_target) <= 1)
diff --git a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm
index a4988d94684..a4c664e40df 100644
--- a/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm
+++ b/code/modules/mob/living/simple_animal/hostile/gorilla/gorilla_actions.dm
@@ -177,7 +177,7 @@
else if(find_phrase(full_message, list("жди", "ожидай", "ждать")))
if(!is_waiting)
is_waiting = TRUE
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
oogaooga(100)
if(is_bipedal)
if(LAZYLEN(crates_in_hand))
@@ -501,7 +501,7 @@
*/
/mob/living/simple_animal/hostile/gorilla/proc/reset_behavior(play_emote = TRUE, end_of_excitement = FALSE)
check_buckled_gorilla()
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
toggle_ai(AI_ON)
if(play_emote)
oogaooga(100, 200)
diff --git a/code/modules/mob/living/simple_animal/hostile/hostile.dm b/code/modules/mob/living/simple_animal/hostile/hostile.dm
index d52fe358076..3f70dd84dda 100644
--- a/code/modules/mob/living/simple_animal/hostile/hostile.dm
+++ b/code/modules/mob/living/simple_animal/hostile/hostile.dm
@@ -8,11 +8,16 @@
var/rapid = 0 //How many shots per volley.
var/rapid_fire_delay = 2 //Time between rapid fire shots
+ ///Are we dodging?
var/dodging = TRUE
- var/approaching_target = FALSE //We should dodge now
- var/in_melee = FALSE //We should sidestep now
+ ///We should dodge now
+ var/approaching_target = FALSE
+ ///We should sidestep now
+ var/in_melee = FALSE
+ ///Probability that we dodge
var/dodge_prob = 30
- var/sidestep_per_cycle = 1 //How many sidesteps per npcpool cycle when in melee
+ ///How many sidesteps per npcpool cycle when in melee
+ var/sidestep_per_cycle = 1
var/projectiletype //set ONLY it and NULLIFY casingtype var, if we have ONLY projectile
var/projectilesound
@@ -56,9 +61,6 @@
var/mob_attack_logs = list() //for hostiles and megafauna
- /// Used to disable gliding if mob is too slow, like goliath
- var/needs_gliding = TRUE
-
tts_seed = "Vort_e2"
dirslash_enabled = TRUE
@@ -87,9 +89,8 @@
/mob/living/simple_animal/hostile/Life(seconds, times_fired)
. = ..()
- if(!.)
- walk(src, 0)
- return FALSE
+ if(!.) // dead
+ SSmove_manager.stop_looping(src)
/mob/living/simple_animal/hostile/handle_automated_action()
if(AIStatus == AI_OFF)
@@ -107,6 +108,7 @@
toggle_ai(AI_IDLE) // otherwise we go idle
return 1
+
/mob/living/simple_animal/hostile/handle_automated_movement()
. = ..()
if(dodging && target && in_melee && isturf(loc) && isturf(target.loc))
@@ -116,7 +118,8 @@
for(var/i in 1 to sidestep_per_cycle)
addtimer(cb, (i - 1) * sidestep_delay)
else //Otherwise randomize it to make the players guessing.
- addtimer(cb,rand(1, SSnpcpool.wait))
+ addtimer(cb, rand(1, SSnpcpool.wait))
+
/mob/living/simple_animal/hostile/proc/sidestep()
if(!target || !isturf(target.loc) || !isturf(loc) || stat == DEAD)
@@ -125,17 +128,17 @@
var/static/list/cardinal_sidestep_directions = list(-90, -45, 0, 45, 90)
var/static/list/diagonal_sidestep_directions = list(-45, 0, 45)
- var/chosen_dir = 0
- if(target_dir & (target_dir - 1))
+ var/chosen_dir = NONE
+ if(ISDIAGONALDIR(target_dir))
chosen_dir = pick(diagonal_sidestep_directions)
else
chosen_dir = pick(cardinal_sidestep_directions)
if(chosen_dir)
- chosen_dir = turn(target_dir, chosen_dir)
- var/step_loc = get_step(src, chosen_dir)
- Move(step_loc, chosen_dir, 3)
+ chosen_dir = turn(target_dir,chosen_dir)
+ Move(get_step(src, chosen_dir))
face_atom(target) //Looks better if they keep looking at you when dodging
+
/mob/living/simple_animal/hostile/attacked_by(obj/item/I, mob/living/user)
if(stat == CONSCIOUS && !target && AIStatus != AI_OFF && !client && user)
FindTarget(list(user), 1)
@@ -348,13 +351,12 @@
if(!target.Adjacent(targets_from) && ranged_cooldown <= world.time) //But make sure they're not in range for a melee attack and our range attack is off cooldown
OpenFire(target)
if(!Process_Spacemove(NONE)) //Drifting
- walk(src,0)
- return 1
- if(retreat_distance != null) //If we have a retreat distance, check if we need to run from our target
+ SSmove_manager.stop_looping(src)
+ return TRUE
+ if(!isnull(retreat_distance)) //If we have a retreat distance, check if we need to run from our target
if(target_distance <= retreat_distance) //If target's closer than our retreat distance, run
- if(needs_gliding)
- glide_for(move_to_delay)
- walk_away(src,target,retreat_distance,move_to_delay)
+ var/glide_flag = move_to_delay > MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE ? MOVEMENT_LOOP_IGNORE_GLIDE : NONE
+ SSmove_manager.move_away(src, target, retreat_distance, move_to_delay, flags = glide_flag)
else
Goto(target,move_to_delay,minimum_distance) //Otherwise, get to our minimum distance so we chase them
else
@@ -382,14 +384,15 @@
LoseTarget()
return 0
+
/mob/living/simple_animal/hostile/proc/Goto(target, delay, minimum_distance)
if(target == src.target)
approaching_target = TRUE
else
approaching_target = FALSE
- if(needs_gliding)
- glide_for(delay)
- walk_to(src, target, minimum_distance, delay)
+ var/glide_flag = move_to_delay > MAX_SIMPLEMOB_MOVEDELAY_TO_GLIDE ? MOVEMENT_LOOP_IGNORE_GLIDE : NONE
+ SSmove_manager.move_to(src, target, minimum_distance, delay, flags = glide_flag)
+
/mob/living/simple_animal/hostile/adjustHealth(damage, updating_health = TRUE)
. = ..()
@@ -428,7 +431,7 @@
target = null
approaching_target = FALSE
in_melee = FALSE
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
LoseAggro()
//////////////END HOSTILE MOB TARGETTING AND AGGRESSION////////////
@@ -504,27 +507,22 @@
return iswallturf(T) || (ismineralturf(T) && !istype(T, /turf/simulated/mineral/ancient/outer))
-/mob/living/simple_animal/hostile/Move(atom/newloc, direct, movetime)
- . = dodge(direct, movetime)
- if(!.)
- . = ..()
-
-
-/mob/living/simple_animal/hostile/proc/dodge(direct, movetime)
- . = FALSE
- if(client)
- return .
- if(!dodging || !approaching_target || moving_diagonally)
- return .
- if(!isturf(loc))
- return .
- if(!prob(dodge_prob))
- return .
- var/turf/dodge_loc = get_step(loc, pick(turn(direct, 45), turn(direct, -45)))
- if(!length(get_path_to(src, dodge_loc, max_distance = 1, simulated_only = FALSE, skip_first = FALSE)))
- return .
+/mob/living/simple_animal/hostile/Move(atom/newloc, direct = NONE, glide_size_override = 0)
+ if(dodging && approaching_target && prob(dodge_prob) && !moving_diagonally && isturf(loc) && isturf(newloc))
+ return dodge(newloc, dir)
+ else
+ return ..()
+
+
+/mob/living/simple_animal/hostile/proc/dodge(moving_to, move_direction)
+ //Assuming we move towards the target we want to swerve toward them to get closer
+ var/cdir = turn(move_direction, 45)
+ var/ccdir = turn(move_direction, -45)
dodging = FALSE
- . = Move(dodge_loc, direct, movetime)
+ . = Move(get_step(loc, pick(cdir, ccdir)))
+ if(!.)//Can't dodge there so we just carry on
+ . = Move(moving_to, move_direction)
+ face_atom(target)
dodging = TRUE
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
index 197f39034ac..9d29c5800d0 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/ancient_robot.dm
@@ -273,14 +273,14 @@ Difficulty: Very Hard
charging = TRUE
revving_charge = TRUE
DestroySurroundings()
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
setDir(dir)
SLEEP_CHECK_DEATH(delay)
revving_charge = FALSE
var/movespeed = 0.8
- walk_towards(src, T, movespeed)
+ SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed)
- walk(src, 0) // cancel the movement
+ SSmove_manager.stop_looping(src)
charging = FALSE
/mob/living/simple_animal/hostile/megafauna/ancient_robot/MeleeAction(patience = TRUE)
@@ -288,22 +288,23 @@ Difficulty: Very Hard
return
return ..()
-/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(atom/A)
- if(charging)
- if(isliving(A))
- var/mob/living/L = A
- if(!istype(A, /mob/living/simple_animal/hostile/ancient_robot_leg))
- L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!")
- forceMove(get_turf(L))
- var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
- L.apply_damage(25, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
- playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE)
- shake_camera(L, 4, 3)
- shake_camera(src, 2, 3)
- if(mode == GRAV || enraged)
- var/atom/throw_target = get_edge_target_turf(L, get_dir(src, get_step_away(L, src)))
- L.throw_at(throw_target, 3, 2)
- ..()
+
+/mob/living/simple_animal/hostile/megafauna/ancient_robot/Bump(mob/living/bumped_living, custom_bump)
+ . = ..()
+ if(isnull(.) || !charging || istype(bumped_living, /mob/living/simple_animal/hostile/ancient_robot_leg) || !isliving(bumped_living))
+ return .
+ var/turf/living_turf = get_turf(bumped_living)
+ bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!")
+ forceMove(living_turf)
+ var/limb_to_hit = bumped_living.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+ bumped_living.apply_damage(25, BRUTE, limb_to_hit, bumped_living.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
+ playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ shake_camera(bumped_living, 4, 3)
+ shake_camera(src, 2, 3)
+ if(mode == GRAV || enraged)
+ var/atom/throw_target = get_edge_target_turf(bumped_living, get_dir(src, get_step_away(bumped_living, src)))
+ bumped_living.throw_at(throw_target, 3, 2)
+
/mob/living/simple_animal/hostile/megafauna/ancient_robot/proc/body_shield()
body_shield_enabled = TRUE
@@ -463,7 +464,7 @@ Difficulty: Very Hard
/mob/living/simple_animal/hostile/megafauna/ancient_robot/proc/self_destruct()
say(pick("OTZKMXOZE LGORAXK, YKRL JKYZXAIZ GIZOBK", "RUYY IKXZGOT, KTMGMKOTM XKIUBKXE JKTOGR", "VUCKX IUXKY 8-12 HXKGINKJ, UBKXRUGJOTM XKSGOTOTM IUXKY", "KXXUX KXXUX KXXUX KXXUX KXX-", "-ROQK ZKGXY OT XGOT- - -ZOSK ZU JOK"))
visible_message("[src] begins to overload it's core. It is going to explode!")
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
playsound(src,'sound/machines/alarm.ogg',100,0,5)
addtimer(CALLBACK(src, PROC_REF(kaboom)), 10 SECONDS)
@@ -692,24 +693,23 @@ Difficulty: Very Hard
forceMove(core.loc)
core.fix_specific_leg(who_am_i)
-/mob/living/simple_animal/hostile/ancient_robot_leg/proc/leg_movement(turf/T, movespeed) //byond doesn't like calling walk_towards on the legs directly
- walk_towards(src, T, movespeed)
- walk_towards(src, T, movespeed)
+/mob/living/simple_animal/hostile/ancient_robot_leg/proc/leg_movement(turf/T, movespeed)
+ SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
+
+
+/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(mob/living/bumped_living, custom_bump)
+ . = ..()
+ if(isnull(.) || !core.charging || istype(bumped_living, /mob/living/simple_animal/hostile/megafauna/ancient_robot) || !isliving(bumped_living))
+ return .
+ var/turf/living_turf = get_turf(bumped_living)
+ bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!")
+ forceMove(living_turf)
+ var/limb_to_hit = bumped_living.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
+ bumped_living.apply_damage(12.5, BRUTE, limb_to_hit, bumped_living.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
+ playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ shake_camera(bumped_living, 4, 3)
+ shake_camera(src, 2, 3)
-/mob/living/simple_animal/hostile/ancient_robot_leg/Bump(atom/A)
- if(!core.charging)
- return
- if(isliving(A))
- if(!istype(A, /mob/living/simple_animal/hostile/megafauna/ancient_robot))
- var/mob/living/L = A
- L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!")
- forceMove(get_turf(L))
- var/limb_to_hit = L.get_organ(pick(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_R_ARM, BODY_ZONE_L_ARM, BODY_ZONE_R_LEG, BODY_ZONE_L_LEG))
- L.apply_damage(12.5, BRUTE, limb_to_hit, L.run_armor_check(limb_to_hit, "melee", null, null, armour_penetration))
- playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE)
- shake_camera(L, 4, 3)
- shake_camera(src, 2, 3)
- ..()
/mob/living/simple_animal/hostile/ancient_robot_leg/ex_act(severity, target)
switch(severity)
@@ -738,6 +738,7 @@ Difficulty: Very Hard
..()
/mob/living/simple_animal/hostile/ancient_robot_leg/Moved(atom/OldLoc, Dir, Forced = FALSE)
+ SHOULD_CALL_PARENT(FALSE) // I'm sorry
playsound(src, 'sound/effects/meteorimpact.ogg', 60, TRUE, 2, TRUE) //turned way down from bubblegum levels due to 4 legs
/mob/living/simple_animal/hostile/ancient_robot_leg/mob_negates_gravity()
@@ -767,11 +768,12 @@ Difficulty: Very Hard
/obj/item/projectile/energy/shock_revolver/ancient
damage = 5
-/obj/item/projectile/energy/shock_revolver/ancient/Bump(atom/A, yes) // Don't want the projectile hitting the legs
- if(!istype(/mob/living/simple_animal/hostile/ancient_robot_leg, A))
- return ..()
- var/turf/target_turf = get_turf(A)
- loc = target_turf
+
+/obj/item/projectile/energy/shock_revolver/ancient/CanAllowThrough(atom/movable/mover, border_dir)
+ . = ..()
+ if(istype(mover, /mob/living/simple_animal/hostile/ancient_robot_leg))
+ return TRUE
+
/obj/effect/temp_visual/dragon_swoop/bubblegum/ancient_robot //this is the worst path I have ever made
icon_state = "target"
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
index 73e9cfafdd1..798a8c6da7a 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner.dm
@@ -226,7 +226,7 @@ Difficulty: Medium
new /obj/effect/temp_visual/dir_setting/miner_death(loc, dir)
return ..()
-/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc)
+/mob/living/simple_animal/hostile/megafauna/blood_drunk_miner/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(dashing || (newloc && newloc.z == z && (islava(newloc) || ischasm(newloc)))) //we're not stupid!
return FALSE
. = ..()
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
index f790be58520..a048509ed99 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/bubblegum.dm
@@ -240,16 +240,16 @@ Difficulty: Hard
charging = TRUE
revving_charge = TRUE
DestroySurroundings()
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
setDir(dir)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
SLEEP_CHECK_DEATH(delay)
revving_charge = FALSE
var/movespeed = 0.7
- walk_towards(src, T, movespeed)
+ SSmove_manager.move_towards_legacy(src, T, movespeed, flags = MOVEMENT_LOOP_START_FAST, priority = MOVEMENT_ABOVE_SPACE_PRIORITY)
SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed)
- walk(src, 0) // cancel the movement
+ SSmove_manager.stop_looping(src) // cancel the movement
try_bloodattack()
charging = FALSE
@@ -543,19 +543,24 @@ Difficulty: Hard
playsound(src, 'sound/effects/meteorimpact.ogg', 200, TRUE, 2, TRUE)
return ..()
-/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/A)
- if(charging)
- if(isturf(A) || isobj(A) && A.density)
- A.ex_act(EXPLODE_HEAVY)
- if(isliving(A))
- var/mob/living/L = A
- L.visible_message("[src] slams into [L]!", "[src] tramples you into the ground!")
- forceMove(get_turf(L))
- L.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE)
- playsound(get_turf(L), 'sound/effects/meteorimpact.ogg', 100, TRUE)
- shake_camera(L, 4, 3)
- shake_camera(src, 2, 3)
- ..()
+
+/mob/living/simple_animal/hostile/megafauna/bubblegum/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.) || !charging)
+ return .
+ if(isturf(bumped_atom) || (isobj(bumped_atom) && bumped_atom.density))
+ bumped_atom.ex_act(EXPLODE_HEAVY)
+ if(!isliving(bumped_atom))
+ return .
+ var/mob/living/bumped_living = bumped_atom
+ var/turf/living_turf = get_turf(bumped_living)
+ bumped_living.visible_message("[src] slams into [bumped_living]!", "[src] tramples you into the ground!")
+ forceMove(living_turf)
+ bumped_living.apply_damage(istype(src, /mob/living/simple_animal/hostile/megafauna/bubblegum/hallucination) ? 15 : 30, BRUTE)
+ playsound(living_turf, 'sound/effects/meteorimpact.ogg', 100, TRUE)
+ shake_camera(bumped_living, 4, 3)
+ shake_camera(src, 2, 3)
+
/obj/effect/temp_visual/dragon_swoop/bubblegum
duration = 15
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 efb7500c2a8..55a0c2f7a4a 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/drake.dm
@@ -429,7 +429,7 @@ Difficulty: Medium
if(!swooping)
..()
-/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/megafauna/dragon/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/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 3d816c05759..7cb3978df36 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/legion.dm
@@ -51,7 +51,7 @@ Difficulty: Medium
enraged_loot = /obj/item/disk/fauna_research/legion
vision_range = 13
elimination = 1
- appearance_flags = PIXEL_SCALE
+ appearance_flags = PIXEL_SCALE|LONG_GLIDE
mouse_opacity = MOUSE_OPACITY_ICON
stat_attack = UNCONSCIOUS // Overriden from /tg/ - otherwise Legion starts chasing its minions
@@ -209,7 +209,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 = NONE)
+/mob/living/simple_animal/hostile/megafauna/legion/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/hostile/megafauna/legion/adjustHealth(amount, updating_health = TRUE)
diff --git a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
index a460b45e7dd..6dae8efdbeb 100644
--- a/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
+++ b/code/modules/mob/living/simple_animal/hostile/megafauna/swarmer.dm
@@ -139,7 +139,7 @@ GLOBAL_LIST_INIT(AISwarmerCapsByType, list(/mob/living/simple_animal/hostile/swa
return
-/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc)
+/mob/living/simple_animal/hostile/swarmer/ai/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(!newloc)
return FALSE
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm
index cf87927bf94..2953654d9b9 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/basilisk.dm
@@ -34,7 +34,6 @@
loot = list(/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER},
/obj/item/stack/ore/diamond{layer = ABOVE_MOB_LAYER})
tts_seed = "Antimage"
- needs_gliding = FALSE
/obj/item/projectile/temp/basilisk
name = "freezing blast"
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm
index bd18612953a..c0b348f084c 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/goliath.dm
@@ -43,7 +43,6 @@
food_type = list(/obj/item/reagent_containers/food/snacks/meat, /obj/item/reagent_containers/food/snacks/grown/ash_flora/cactus_fruit, /obj/item/reagent_containers/food/snacks/grown/ash_flora/mushroom_leaf)
tame_chance = 0
bonus_tame_chance = 10
- needs_gliding = FALSE
/mob/living/simple_animal/hostile/asteroid/goliath/bullet_act(var/obj/item/projectile/P)
@@ -135,26 +134,30 @@
return
charging = TRUE
revving_charge = TRUE
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
setDir(dir)
var/obj/effect/temp_visual/decoy/D = new /obj/effect/temp_visual/decoy(loc,src)
animate(D, alpha = 0, color = "#FF0000", transform = matrix()*2, time = 3)
SLEEP_CHECK_DEATH(delay)
revving_charge = FALSE
var/movespeed = 0.7
- walk_towards(src, T, movespeed)
+ SSmove_manager.move_towards(src, T, movespeed)
SLEEP_CHECK_DEATH(get_dist(src, T) * movespeed)
- walk(src, 0) // cancel the movement
+ SSmove_manager.stop_looping(src) // cancel the movement
charging = FALSE
-/mob/living/simple_animal/hostile/asteroid/goliath/beast/Bump(atom/A)
- if(isturf(A) && charging)
- wall_slam(A)
+
+/mob/living/simple_animal/hostile/asteroid/goliath/beast/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.) || !charging || !isturf(bumped_atom))
+ return .
+ wall_slam(bumped_atom)
+
/mob/living/simple_animal/hostile/asteroid/goliath/beast/proc/wall_slam(atom/A)
charging = FALSE
Stun(100, TRUE, TRUE)
- walk(src, 0) // Cancel the movement
+ SSmove_manager.stop_looping(src) // Cancel the movement
if(ismineralturf(A))
var/turf/simulated/mineral/M = A
if(M.mineralAmt < 7)
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
index d744aef17f2..3a816a5b843 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/hivelord.dm
@@ -32,7 +32,7 @@
pass_flags = PASSTABLE
butcher_results = list(/obj/item/organ/internal/regenerative_core = 1)
var/brood_type = /mob/living/simple_animal/hostile/asteroid/hivelordbrood
- needs_gliding = FALSE
+
/mob/living/simple_animal/hostile/asteroid/hivelord/OpenFire(the_target)
if(world.time >= ranged_cooldown)
@@ -88,7 +88,6 @@
pass_flags = PASSTABLE | PASSMOB
density = FALSE
del_on_death = 1
- needs_gliding = FALSE
var/life_time = 10 SECONDS
diff --git a/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm b/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm
index 6736bc928bc..5842205e900 100644
--- a/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm
+++ b/code/modules/mob/living/simple_animal/hostile/mining/marrow_weaver.dm
@@ -34,7 +34,7 @@
var/melee_damage_upper_angery1 = 20
var/anger_move_to_delay = 8
var/anger_speed = 4
- needs_gliding = FALSE
+
/mob/living/simple_animal/hostile/asteroid/marrowweaver/adjustHealth(amount, updating_health = TRUE)
if(buttmad == 0)
@@ -47,7 +47,6 @@
set_varspeed(anger_speed)
poison_type = "venom"
poison_per_bite = 6
- needs_gliding = TRUE
else if(buttmad == 1)
if(health > maxHealth/2)
buttmad = 0
@@ -57,7 +56,6 @@
poison_type = initial(poison_type)
set_varspeed(initial(speed))
poison_per_bite = initial(poison_per_bite)
- needs_gliding = FALSE
..()
/mob/living/simple_animal/hostile/asteroid/marrowweaver/AttackingTarget()
diff --git a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
index ce1c6025c34..4d6924df255 100644
--- a/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
+++ b/code/modules/mob/living/simple_animal/hostile/retaliate/drone.dm
@@ -30,18 +30,22 @@
deathmessage = "suddenly breaks apart."
del_on_death = 1
var/passive_mode = TRUE // if true, don't target anything.
+ var/datum/effect_system/trail_follow/ion/ion_trail
+
/mob/living/simple_animal/hostile/malf_drone/Initialize(mapload)
. = ..()
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail))
- update_icons()
+ ion_trail = new
+ ion_trail.set_up(src)
+ ion_trail.start()
+
+
+/mob/living/simple_animal/hostile/malf_drone/Destroy()
+ QDEL_NULL(ion_trail)
+ return ..()
-/mob/living/simple_animal/hostile/malf_drone/proc/create_trail(datum/source, atom/oldloc, _dir, forced)
- var/turf/T = get_turf(oldloc)
- if(!T.has_gravity())
- new /obj/effect/particle_effect/ion_trails(T, _dir)
-/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/malf_drone/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/hostile/malf_drone/ListTargets()
diff --git a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm
index e8c2bb0a117..bf6f5132bc3 100644
--- a/code/modules/mob/living/simple_animal/hostile/spaceworms.dm
+++ b/code/modules/mob/living/simple_animal/hostile/spaceworms.dm
@@ -57,7 +57,7 @@
ADD_TRAIT(src, TRAIT_NO_FLOATING_ANIM, INNATE_TRAIT)
-/mob/living/simple_animal/hostile/spaceWorm/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/spaceWorm/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //space worms can flyyyyyy
//Worm Head, Controls the AI for the entire worm "entity"
@@ -130,8 +130,11 @@
attemptToEat(target)
//Attempt to eat things we bump into, Mobs, Walls, Clowns
-/mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/obstacle)
- attemptToEat(obstacle)
+/mob/living/simple_animal/hostile/spaceWorm/wormHead/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(isnull(.))
+ return .
+ attemptToEat(bumped_atom)
//Attempt to eat things, only the head can eat
/mob/living/simple_animal/hostile/spaceWorm/wormHead/proc/attemptToEat(var/atom/noms)
diff --git a/code/modules/mob/living/simple_animal/hostile/statue.dm b/code/modules/mob/living/simple_animal/hostile/statue.dm
index 339f55d0d15..f57731bca53 100644
--- a/code/modules/mob/living/simple_animal/hostile/statue.dm
+++ b/code/modules/mob/living/simple_animal/hostile/statue.dm
@@ -64,8 +64,8 @@
if(creator)
src.creator = creator
-/mob/living/simple_animal/hostile/statue/Move(turf/NewLoc)
- if(can_be_seen(NewLoc))
+/mob/living/simple_animal/hostile/statue/Move(atom/newloc, direct = NONE, glide_size_override = 0)
+ if(can_be_seen(newloc))
if(client)
to_chat(src, "You cannot move, there are eyes on you!")
return 0
diff --git a/code/modules/mob/living/simple_animal/hostile/syndicate.dm b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
index d79affe5d37..efa729cfdcf 100644
--- a/code/modules/mob/living/simple_animal/hostile/syndicate.dm
+++ b/code/modules/mob/living/simple_animal/hostile/syndicate.dm
@@ -300,9 +300,9 @@
wander = 0
alert_on_spacing = FALSE
-/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = NONE)
- return TRUE
+/mob/living/simple_animal/hostile/syndicate/melee/autogib/depot/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ return TRUE
/mob/living/simple_animal/hostile/syndicate/melee/space
@@ -314,7 +314,7 @@
speed = 1
loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/melee/energy/sword/saber/red, /obj/item/shield/energy/syndie)
-/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/syndicate/melee/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/hostile/syndicate/ranged
@@ -337,7 +337,7 @@
speed = 1
loot = list(/obj/effect/mob_spawn/human/corpse/syndicatecommando, /obj/item/gun/projectile/automatic/c20r)
-/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/syndicate/ranged/space/Process_Spacemove(movement_dir = NONE, 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 17a7efd541a..5b84eee0a2f 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
@@ -253,7 +253,7 @@
visible_message("[src] begins to secrete a sticky substance around [cocoon_target].")
playsound(src.loc, 'sound/creatures/terrorspiders/wrap.ogg', 120, 1)
stop_automated_movement = 1
- walk(src,0)
+ SSmove_manager.stop_looping(src)
if(do_after(src, 4 SECONDS, 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/builder.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm
index 1fb92710980..9af20582d0f 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/builder.dm
@@ -56,7 +56,7 @@
visible_message("[src] buries its long fangs deep into the [inject_target] of [target]!")
return TRUE
-/mob/living/simple_animal/hostile/poison/terror_spider/builder/Move(atom/newloc, dir, step_x, step_y) //moves slow while not in web, but fast while in. does not regenerate if not in web
+/mob/living/simple_animal/hostile/poison/terror_spider/builder/Move(atom/newloc, direct = NONE, glide_size_override = 0) //moves slow while not in web, but fast while in. does not regenerate if not in web
. = ..()
var/obj/structure/spider/terrorweb/W = locate() in get_turf(src)
if(W)
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm
index 7bdab378ddf..2065688439f 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/lurker.dm
@@ -31,7 +31,7 @@
tts_seed = "Cassiopeia"
var/prob_ai_massweb = 10
-/mob/living/simple_animal/hostile/poison/terror_spider/lurker/Move(atom/newloc, dir, step_x, step_y)
+/mob/living/simple_animal/hostile/poison/terror_spider/lurker/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(stat == DEAD)
icon_state = icon_dead
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 5f99f1dd409..7047e60aa4d 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
@@ -32,11 +32,6 @@
GLOB.ts_spiderling_list -= src
return ..()
-/obj/structure/spider/spiderling/terror_spiderling/Bump(obj/O)
- if(istype(O, /obj/structure/table))
- forceMove(O.loc)
- . = ..()
-
/obj/structure/spider/spiderling/terror_spiderling/Destroy()
for(var/obj/structure/spider/spiderling/terror_spiderling/S in view(7, src))
@@ -151,8 +146,7 @@
new_area.Entered(src)
else
frustration++
- glide_for(3)
- walk_to(src, entry_vent, 1)
+ SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4))
if(frustration > 2)
entry_vent = null
else if(prob(33))
@@ -167,8 +161,7 @@
for(var/obj/machinery/atmospherics/unary/vent_pump/v in view(7,src))
if(!v.welded)
entry_vent = v
- glide_for(3)
- walk_to(src, entry_vent, 1)
+ SSmove_manager.move_to(src, entry_vent, 1, rand(2, 4))
break
diff --git a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
index 8d2923d77a4..e8bb14778f7 100644
--- a/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
+++ b/code/modules/mob/living/simple_animal/hostile/terror_spiders/terror_spiders.dm
@@ -388,12 +388,13 @@ GLOBAL_LIST_EMPTY(ts_spiderling_list)
return TRUE
-/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup()
+/mob/living/simple_animal/hostile/poison/terror_spider/get_spacemove_backup(moving_direction, continuous_move)
. = ..()
// If we don't find any normal thing to use, attempt to use any nearby spider structure instead.
if(!.)
- for(var/obj/structure/spider/S in range(1, get_turf(src)))
- return S
+ for(var/obj/structure/spider/spider_thing in range(1, get_turf(src)))
+ return spider_thing
+
/mob/living/simple_animal/hostile/poison/terror_spider/Stat()
..()
diff --git a/code/modules/mob/living/simple_animal/hostile/undead.dm b/code/modules/mob/living/simple_animal/hostile/undead.dm
index 977036192d3..757c6afc5d8 100644
--- a/code/modules/mob/living/simple_animal/hostile/undead.dm
+++ b/code/modules/mob/living/simple_animal/hostile/undead.dm
@@ -59,7 +59,7 @@
AddElement(/datum/element/simple_flying)
-/mob/living/simple_animal/hostile/ghost/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/hostile/ghost/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/hostile/ghost/Life(seconds, times_fired)
diff --git a/code/modules/mob/living/simple_animal/parrot.dm b/code/modules/mob/living/simple_animal/parrot.dm
index 624c0d01b49..3ebb7d6bc60 100644
--- a/code/modules/mob/living/simple_animal/parrot.dm
+++ b/code/modules/mob/living/simple_animal/parrot.dm
@@ -136,7 +136,7 @@
if(held_item)
custom_emote(EMOTE_VISIBLE, "lets go of [held_item.name]!")
drop_held_item()
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
return ..()
@@ -409,7 +409,7 @@
//-----WANDERING - This is basically a 'I dont know what to do yet' state
else if(parrot_state == PARROT_WANDER)
//Stop movement, we'll set it later
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
parrot_interest = null
//Wander around aimlessly. This will help keep the loops from searches down
@@ -449,7 +449,7 @@
//-----STEALING
else if(parrot_state == (PARROT_SWOOP|PARROT_STEAL))
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
if(!parrot_interest || held_item || !(parrot_interest in view(src)))
parrot_state = PARROT_SWOOP|PARROT_RETURN
@@ -476,13 +476,12 @@
parrot_state = PARROT_SWOOP|PARROT_RETURN
return
- glide_for(parrot_speed)
- walk_to(src, path_to_take[2], 0, parrot_speed)
+ SSmove_manager.move_to(src, path_to_take[2], 0, parrot_speed)
return
//-----RETURNING TO PERCH
else if(parrot_state == (PARROT_SWOOP|PARROT_RETURN))
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
if(!parrot_perch || !isturf(parrot_perch.loc)) //Make sure the perch exists and somehow isnt inside of something else.
parrot_perch = null
@@ -502,20 +501,19 @@
parrot_state = PARROT_WANDER
return
- glide_for(parrot_speed)
- walk_to(src, path_to_take[2], 0, parrot_speed)
+ SSmove_manager.move_to(src, path_to_take[2], 0, parrot_speed)
return
//-----FLEEING
else if(parrot_state == (PARROT_SWOOP|PARROT_FLEE))
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
if(!parrot_interest || !isliving(parrot_interest) || !Adjacent(parrot_interest)) //Sanity
parrot_state = PARROT_WANDER
parrot_interest = null
return
- walk_away(src, parrot_interest, 0, parrot_speed - parrot_been_shot)
+ SSmove_manager.move_away(src, parrot_interest, 0, parrot_speed - parrot_been_shot)
parrot_been_shot--
return
@@ -561,12 +559,11 @@
//Otherwise, fly towards the mob!
else
// No pathfinding here because the parrot is pissed and isn't thinking rationally.
- glide_for(parrot_speed)
- walk_to(src, parrot_interest, 1, parrot_speed)
+ SSmove_manager.move_to(src, parrot_interest, 1, parrot_speed)
return
//-----STATE MISHAP
else //This should not happen. If it does lets reset everything and try again
- walk(src, 0)
+ SSmove_manager.stop_looping(src)
parrot_interest = null
parrot_perch = null
drop_held_item()
diff --git a/code/modules/mob/living/simple_animal/shade.dm b/code/modules/mob/living/simple_animal/shade.dm
index 6aedb15f9e6..83246dc8561 100644
--- a/code/modules/mob/living/simple_animal/shade.dm
+++ b/code/modules/mob/living/simple_animal/shade.dm
@@ -48,7 +48,7 @@
icon_state = holy ? "shade_angelic" : "shade"
-/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/shade/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
diff --git a/code/modules/mob/living/simple_animal/simple_animal.dm b/code/modules/mob/living/simple_animal/simple_animal.dm
index 3e081860323..f90b391086c 100644
--- a/code/modules/mob/living/simple_animal/simple_animal.dm
+++ b/code/modules/mob/living/simple_animal/simple_animal.dm
@@ -231,22 +231,32 @@
set_stat(CONSCIOUS)
..()
+
/mob/living/simple_animal/proc/handle_automated_action()
set waitfor = FALSE
return
+
/mob/living/simple_animal/proc/handle_automated_movement()
set waitfor = FALSE
- if(!stop_automated_movement && wander)
- if((isturf(loc) || allow_movement_on_non_turfs) && !resting && !buckled && (mobility_flags & MOBILITY_MOVE)) //This is so it only moves if it's not inside a closet, gentics machine, etc.
- turns_since_move++
- if(turns_since_move >= turns_per_move)
- if(!(stop_automated_movement_when_pulled && pulledby)) //Soma animals don't move when pulled
- var/anydir = pick(GLOB.cardinal)
- if(Process_Spacemove(anydir))
- Move(get_step(src,anydir), anydir, cached_multiplicative_slowdown)
- turns_since_move = 0
- return 1
+ if(stop_automated_movement || !wander)
+ return
+ if(!isturf(loc) && !allow_movement_on_non_turfs)
+ return
+ if(!(mobility_flags & MOBILITY_MOVE))
+ return TRUE
+
+ turns_since_move++
+ if(turns_since_move < turns_per_move)
+ return TRUE
+ if(stop_automated_movement_when_pulled && pulledby) //Some animals don't move when pulled
+ return TRUE
+ var/anydir = pick(GLOB.cardinal)
+ if(Process_Spacemove(anydir))
+ Move(get_step(src, anydir), anydir)
+ turns_since_move = 0
+ return TRUE
+
/mob/living/simple_animal/proc/handle_automated_speech(override)
set waitfor = FALSE
@@ -677,7 +687,7 @@
/mob/living/simple_animal/Login()
..()
- walk(src, 0) // if mob is moving under ai control, then stop AI movement
+ SSmove_manager.stop_looping(src) // if mob is moving under ai control, then stop AI movement
/mob/living/simple_animal/say(message, verb = "says", sanitize = TRUE, ignore_speech_problems = FALSE, ignore_atmospherics = FALSE, ignore_languages = FALSE)
@@ -727,3 +737,12 @@
/mob/living/simple_animal/proc/pull_constraint(atom/movable/AM, show_message = FALSE)
return TRUE
+
+
+/mob/living/simple_animal/update_movespeed()
+ . = ..()
+ if(cached_multiplicative_slowdown > END_GLIDE_SPEED)
+ ADD_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT)
+ else
+ REMOVE_TRAIT(src, TRAIT_NO_GLIDE, SPEED_TRAIT)
+
diff --git a/code/modules/mob/living/simple_animal/slime/slime.dm b/code/modules/mob/living/simple_animal/slime/slime.dm
index 96dec5d957f..ec8eb1d508f 100644
--- a/code/modules/mob/living/simple_animal/slime/slime.dm
+++ b/code/modules/mob/living/simple_animal/slime/slime.dm
@@ -238,7 +238,7 @@
Atkcool = TRUE
addtimer(VARSET_CALLBACK(src, Atkcool, FALSE), 4.5 SECONDS)
-/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = NONE)
+/mob/living/simple_animal/slime/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
/mob/living/simple_animal/slime/Stat()
diff --git a/code/modules/mob/mob_defines.dm b/code/modules/mob/mob_defines.dm
index 1e294e97c86..5342e41e0f9 100644
--- a/code/modules/mob/mob_defines.dm
+++ b/code/modules/mob/mob_defines.dm
@@ -1,7 +1,6 @@
/mob
density = TRUE
layer = MOB_LAYER
- glide_size = 1.5
animate_movement = SLIDE_STEPS
pressure_resistance = 8
throwforce = 10
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index ea4f1292ecc..1b0964e6c09 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -1,36 +1,55 @@
-/client/verb/toggle_throw_mode()
- set hidden = 1
- if(iscarbon(mob))
- var/mob/living/carbon/C = mob
- C.toggle_throw_mode()
- else
- to_chat(usr, "Это существо не может бросать предметы.")
-
-/client/proc/Move_object(direct)
- if(mob && mob.control_object)
- if(mob.control_object.density)
- step(mob.control_object, direct)
- if(!mob.control_object)
- return
- mob.control_object.setDir(direct)
- else
- mob.control_object.forceMove(get_step(mob.control_object, direct))
- return
-
-
-/client/Move(n, direct)
- if(world.time < move_delay)
- return
+/**
+ * Move a client in a direction
+ *
+ * Huge proc, has a lot of functionality
+ *
+ * Mostly it will despatch to the mob that you are the owner of to actually move
+ * in the physical realm
+ *
+ * Things that stop you moving as a mob:
+ * * world time being less than your next move_delay
+ * * not being in a mob, or that mob not having a loc
+ * * missing the new_loc and direct parameters
+ * * having TRAIT_NO_TRANSFORM
+ * * being in remote control of an object (calls Move_object instead)
+ * * being dead (it ghosts you instead)
+ *
+ * Things that stop you moving as a mob living (why even have OO if you're just shoving it all
+ * in the parent proc with istype checks right?):
+ * * having incorporeal_move set (calls Process_Incorpmove() instead)
+ * * being in remote control of a movable, (calls remote_control() instead)
+ * * being grabbed
+ * * being buckled (relaymove() is called to the buckled atom instead)
+ * * having your loc be some other mob (relaymove() is called on that mob instead)
+ * * Not having MOBILITY_MOVE
+ * * Failing Process_Spacemove() call
+ *
+ * At this point, if the mob is is confused, then a random direction and target turf will be calculated for you to travel to instead
+ *
+ * Now the parent call is made (to the byond builtin move), which moves you
+ *
+ * Some final move delay calculations (doubling if you moved diagonally successfully)
+ *
+ * if mob throwing is set I believe it's unset at this point via a call to finalize
+ *
+ * Finally if you're pulling an object and it's dense, you are turned 180 after the move
+ * (if you ask me, this should be at the top of the move so you don't dance around) // LATER
+ *
+ */
+/client/Move(new_loc, direct)
+ if(world.time < move_delay) //do not move anything ahead of this check please
+ return FALSE
input_data.desired_move_dir_add = NONE
input_data.desired_move_dir_sub = NONE
var/old_move_delay = move_delay
move_delay = world.time + world.tick_lag //this is here because Move() can now be called multiple times per tick
- if(!mob || !mob.loc)
- return 0
- if(!n || !direct) // why did we never check this before?
+ if(!direct || !new_loc)
+ return FALSE
+
+ if(!mob?.loc)
return FALSE
if(HAS_TRAIT(mob, TRAIT_NO_TRANSFORM))
@@ -40,19 +59,17 @@
return Move_object(direct)
if(!isliving(mob))
- return mob.Move(n, direct)
+ return mob.Move(new_loc, direct)
if(mob.stat == DEAD)
mob.ghostize()
- return 0
+ return FALSE
- if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, n, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE)
+ if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_LIVING_MOVE, new_loc, direct) & COMSIG_MOB_CLIENT_BLOCK_PRE_LIVING_MOVE)
return FALSE
- var/mob/living/L = mob //Already checked for isliving earlier
- if(L.incorporeal_move)//Move though walls
- move_delay = world.time + 0.5 // cap to 20fps
- L.glide_size = 8
+ var/mob/living/living_mob = mob //Already checked for isliving earlier
+ if(living_mob.incorporeal_move)//Move though walls
Process_Incorpmove(direct)
return FALSE
@@ -61,33 +78,31 @@
if(isAI(mob))
if(istype(mob.loc, /obj/item/aicard))
- var/obj/O = mob.loc
- return O.relaymove(mob, direct) // aicards have special relaymove stuff
- return AIMove(n, direct, mob)
+ return mob.loc.relaymove(mob, direct) // aicards have special relaymove stuff
+ return AIMove(new_loc, direct, mob)
if(Process_Grab())
- return
+ return FALSE
if(mob.buckled) //if we're buckled to something, tell it we moved.
return mob.buckled.relaymove(mob, direct)
- if(!(L.mobility_flags & MOBILITY_MOVE))
+ if(!(living_mob.mobility_flags & MOBILITY_MOVE))
return FALSE
if(!mob.lastarea)
mob.lastarea = get_area(mob.loc)
- if(isobj(mob.loc) || ismob(mob.loc)) //Inside an object, tell it we moved
- var/atom/O = mob.loc
- return O.relaymove(mob, direct)
+ if(ismovable(mob.loc)) //Inside an object, tell it we moved
+ return mob.loc.relaymove(mob, direct)
if(!mob.Process_Spacemove(direct))
- return 0
+ return FALSE
if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_MOVE, args) & COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE)
return FALSE
- if(HAS_TRAIT(mob, TRAIT_RESTRAINED) && mob.pulledby) // Why being pulled while cuffed prevents you from moving
+ if(mob.pulledby && HAS_TRAIT(mob, TRAIT_RESTRAINED)) // Why being pulled while cuffed prevents you from moving
var/mob/puller = mob.pulledby
if(!puller.incapacitated() && !HAS_TRAIT(puller, TRAIT_HANDS_BLOCKED) && mob.Adjacent(puller))
to_chat(src, span_warning("Вы скованы и не можете пошевелиться!"))
@@ -96,44 +111,70 @@
puller.stop_pulling()
//We are now going to move
- current_move_delay = mob.cached_multiplicative_slowdown
+ var/add_delay = mob.cached_multiplicative_slowdown
+
+ if(mob.pulling && mob.has_gravity())
+ var/mob/living/silicon/robot/robot = mob
+ if(!(STRONG in living_mob.mutations) && !isconstruct(living_mob) && !istype(living_mob, /mob/living/simple_animal/hostile/clockwork) && !istype(living_mob, /mob/living/simple_animal/hostile/guardian) && !(isrobot(mob) && (/obj/item/borg/upgrade/vtec in robot.upgrades))) //No slowdown for STRONG gene //Blood cult constructs //Clockwork constructs //Borgs with VTEC //Holopigs
+ add_delay *= min(1.4, mob.pulling.get_pull_push_speed_modifier(add_delay))
+
+ if(locate(/obj/item/grab, mob))
+ add_delay += 7
- if(!istype(get_turf(mob), /turf/space) && mob.pulling)
- var/mob/living/M = mob
- var/mob/living/silicon/robot/R = mob
- if(!(STRONG in M.mutations) && !isconstruct(M) && !istype(M, /mob/living/simple_animal/hostile/clockwork) && !istype(M, /mob/living/simple_animal/hostile/guardian) && !(istype(R) && (/obj/item/borg/upgrade/vtec in R.upgrades))) //No slowdown for STRONG gene //Blood cult constructs //Clockwork constructs //Borgs with VTEC //Holopigs
- current_move_delay *= min(1.4, mob.pulling.get_pull_push_speed_modifier(current_move_delay))
+ 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
+ //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
- if(locate(/obj/item/grab, mob))
- current_move_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
+ var/old_dir = mob.dir
+
+ . = ..()
- . = mob.SelfMove(n, direct, current_move_delay)
- mob.setDir(direct)
+ if(mob.loc == new_loc)
+ mob.last_movement = world.time
+ if(ISDIAGONALDIR(direct)) //moved diagonally successfully
+ add_delay *= sqrt(2)
- if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully
- current_move_delay *= 1.41 //Will prevent mob diagonal moves from smoothing accurately, sadly
+ 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 += current_move_delay
+ move_delay += add_delay
- if(mob.pulledby)
- mob.pulledby.stop_pulling()
+ if(.) // If mob is null here, we deserve the runtime
+ mob.throwing?.finalize()
- if(mob && .)
- if(mob.throwing)
- mob.throwing.finalize()
+ // At this point we've moved the client's attached mob. This is one of the only ways to guess that a move was done
+ // as a result of player input and not because they were pulled or any other magic.
+ SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_MOVED, direct, old_dir)
- for(var/obj/O in mob)
- O.on_mob_move(direct, mob)
+ for(var/obj/object in mob.contents)
+ object.on_mob_move(direct, mob)
-/mob/proc/SelfMove(turf/n, direct, movetime)
- return Move(n, direct, movetime)
+/client/proc/Move_object(direct)
+ if(mob.control_object.density)
+ step(mob.control_object, direct)
+ if(!mob.control_object)
+ return
+ mob.control_object.setDir(direct)
+ else
+ mob.control_object.forceMove(get_step(mob.control_object, direct))
+
///Process_Grab()
///Called by client/Move()
@@ -176,9 +217,19 @@
return FALSE
-///Process_Incorpmove
-///Called by client/Move()
-///Allows mobs to run though walls
+/**
+ * Allows mobs to ignore density and phase through objects
+ *
+ * Called by client/Move()
+ *
+ * The behaviour depends on the incorporeal_move value of the mob
+ *
+ * * INCORPOREAL_MOVE_BASIC - forceMoved to the next tile with no stop
+ * * INCORPOREAL_NINJA - the same but leaves a cool effect path
+ * * INCORPOREAL_REVENANT - the same but blocked by holy tiles
+ *
+ * You'll note this is another mob living level proc living at the client level
+ */
/client/proc/Process_Incorpmove(direct)
var/turf/mobloc = get_turf(mob)
if(!isliving(mob))
@@ -186,8 +237,10 @@
var/mob/living/L = mob
switch(L.incorporeal_move)
if(INCORPOREAL_NORMAL)
- L.forceMove(get_step(L, direct))
- L.dir = direct
+ var/T = get_step(L, direct)
+ if(T)
+ L.forceMove(T)
+ L.setDir(direct)
if(INCORPOREAL_NINJA)
if(prob(50))
var/locx
@@ -215,30 +268,31 @@
return
else
return
- L.glide_size = L.glide_size * 2
- L.forceMove(locate(locx,locy,mobloc.z))
- spawn(0)
+ var/target = locate(locx,locy,mobloc.z)
+ if(target)
+ L.forceMove(target)
var/limit = 2//For only two trailing shadows.
- for(var/turf/T as anything in get_line(mobloc, L.loc))
+ for(var/turf/T in get_line(mobloc, L.loc))
new /obj/effect/temp_visual/dir_setting/ninja/shadow(T, L.dir)
limit--
- if(limit<=0)
+ if(limit <= 0)
break
else
new /obj/effect/temp_visual/dir_setting/ninja/shadow(mobloc, L.dir)
- L.forceMove(get_step(L, direct))
- L.dir = direct
+ var/T = get_step(L, direct)
+ if(T)
+ L.forceMove(T)
+ L.setDir(direct)
if(INCORPOREAL_REVENANT) //Incorporeal move, but blocked by holy-watered tiles
var/turf/simulated/floor/stepTurf = get_step(L, direct)
- if(stepTurf.flags & NOJAUNT)
- to_chat(L, span_warning("Святые силы блокируют ваш путь."))
- ADD_TRAIT(L, TRAIT_NO_TRANSFORM, INCORPOREAL_TRAIT)
- spawn(2)
- REMOVE_TRAIT(L, TRAIT_NO_TRANSFORM, INCORPOREAL_TRAIT)
- else
- L.forceMove(get_step(L, direct))
- L.dir = direct
- return 1
+ if(stepTurf)
+ if(stepTurf.flags & NOJAUNT)
+ move_delay += 0.5 SECONDS
+ to_chat(L, span_warning("Святые силы блокируют Ваш путь."))
+ return
+ L.forceMove(stepTurf)
+ L.setDir(direct)
+ return TRUE
/**
@@ -250,7 +304,7 @@
*
* You can move in space if you have a spacewalk ability
*/
-/mob/Process_Spacemove(movement_dir = NONE)
+/mob/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
. = ..()
if(.)
return .
@@ -258,25 +312,25 @@
if(buckled)
return TRUE
- var/atom/movable/backup = get_spacemove_backup(movement_dir)
+ var/atom/movable/backup = get_spacemove_backup(movement_dir, continuous_move)
if(!backup)
return FALSE
- if(!istype(backup) || !movement_dir || backup.anchored)
+ if(continuous_move || !istype(backup) || !movement_dir || backup.anchored)
return TRUE
// last pushoff exists for one reason
// to ensure pushing a mob doesn't just lead to it considering us as backup, and failing
last_pushoff = world.time
- if(backup.newtonian_move(REVERSE_DIR(movement_dir))) //You're pushing off something movable, so it moves
+ if(backup.newtonian_move(REVERSE_DIR(movement_dir), instant = TRUE)) //You're pushing off something movable, so it moves
// We set it down here so future calls to Process_Spacemove by the same pair in the same tick don't lead to fucky
backup.last_pushoff = world.time
- to_chat(src, span_info("Вы отталкиваетесь от [backup] для продолжения движения."))
+ to_chat(src, span_info("Вы отталкиваетесь от [backup.name] для продолжения движения."))
return TRUE
-/mob/get_spacemove_backup(moving_direction)
+/mob/get_spacemove_backup(moving_direction, continuous_move)
for(var/atom/pushover as anything in range(1, get_turf(src)))
if(pushover == src)
continue
@@ -305,7 +359,6 @@
//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 = SSmove_manager.processing_on(rebound, SSspacedrift)
// If you're moving toward it and you're both going the same direction, stop
@@ -314,7 +367,6 @@
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)
@@ -501,6 +553,15 @@
mob.toggle_move_intent()
+/client/verb/toggle_throw_mode()
+ set hidden = 1
+ if(iscarbon(mob))
+ var/mob/living/carbon/C = mob
+ C.toggle_throw_mode()
+ else
+ to_chat(usr, "Это существо не может бросать предметы.")
+
+
/mob/proc/toggle_move_intent()
return
diff --git a/code/modules/movespeed/_movespeed_modifier.dm b/code/modules/movespeed/_movespeed_modifier.dm
index f6df8f3d2b6..b9337be13ae 100644
--- a/code/modules/movespeed/_movespeed_modifier.dm
+++ b/code/modules/movespeed/_movespeed_modifier.dm
@@ -83,7 +83,7 @@ GLOBAL_LIST_EMPTY(movespeed_modification_cache)
return TRUE
remove_movespeed_modifier(existing, update = FALSE)
if(length(movespeed_modification))
- BINARY_INSERT_TG(type_or_datum.id, movespeed_modification, /datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE)
+ BINARY_INSERT(type_or_datum.id, movespeed_modification, /datum/movespeed_modifier, type_or_datum, priority, COMPARE_VALUE)
LAZYSET(movespeed_modification, type_or_datum.id, type_or_datum)
if(update)
update_movespeed()
diff --git a/code/modules/power/singularity/containment_field.dm b/code/modules/power/singularity/containment_field.dm
index 66f0b98548c..2db20952c29 100644
--- a/code/modules/power/singularity/containment_field.dm
+++ b/code/modules/power/singularity/containment_field.dm
@@ -87,14 +87,15 @@
var/hasShocked = 0 //Used to add a delay between shocks. In some cases this used to crash servers by spawning hundreds of sparks every second.
-/obj/machinery/field/Bumped(atom/movable/mover)
+/obj/machinery/field/Bumped(atom/movable/moving_atom)
+ . = ..()
if(hasShocked)
- return
- if(isliving(mover))
- shock_field(mover)
- return
- if(ismachinery(mover) || isstructure(mover) || ismecha(mover))
- bump_field(mover)
+ return .
+ if(isliving(moving_atom))
+ shock_field(moving_atom)
+ return .
+ if(ismachinery(moving_atom) || isstructure(moving_atom) || ismecha(moving_atom))
+ bump_field(moving_atom)
/obj/machinery/field/CanAllowThrough(atom/movable/mover, border_dir)
diff --git a/code/modules/power/singularity/narsie.dm b/code/modules/power/singularity/narsie.dm
index 0cc39a63f1f..9d52cf929b5 100644
--- a/code/modules/power/singularity/narsie.dm
+++ b/code/modules/power/singularity/narsie.dm
@@ -77,13 +77,17 @@
mezzer()
-/obj/singularity/narsie/Bump(atom/A)//you dare stand before a god?!
- godsmack(A)
- return
+/obj/singularity/narsie/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE)//you dare stand before a god?!
+ . = ..()
+ if(. || isnull(.))
+ return .
+ godsmack(bumped_atom)
+
-/obj/singularity/narsie/Bumped(atom/movable/moving_atom)
+/obj/singularity/narsie/Bumped(atom/movable/moving_atom, effect_applied = TRUE)
+ . = ..()
godsmack(moving_atom)
- return
+
/obj/singularity/narsie/proc/godsmack(atom/A)
if(istype(A,/obj/))
diff --git a/code/modules/power/singularity/particle_accelerator/particle.dm b/code/modules/power/singularity/particle_accelerator/particle.dm
index d37a4f8f42d..b6c535e035c 100644
--- a/code/modules/power/singularity/particle_accelerator/particle.dm
+++ b/code/modules/power/singularity/particle_accelerator/particle.dm
@@ -38,10 +38,12 @@
B.take_damage(energy * 0.6)
movement_range = 0
-/obj/effect/accelerated_particle/Bump(obj/singularity/S)
- if(!istype(S))
- return ..()
- S.energy += energy
+
+/obj/effect/accelerated_particle/Bump(obj/singularity/bumped_singulo, custom_bump)
+ . = ..()
+ if(. || isnull(.) || !istype(bumped_singulo))
+ return .
+ bumped_singulo.energy += energy
/obj/effect/accelerated_particle/ex_act(severity)
diff --git a/code/modules/power/singularity/ratvar.dm b/code/modules/power/singularity/ratvar.dm
index 0b4d442ff73..9ddd98f01f8 100644
--- a/code/modules/power/singularity/ratvar.dm
+++ b/code/modules/power/singularity/ratvar.dm
@@ -64,13 +64,17 @@
mezzer()
-/obj/singularity/ratvar/Bump(atom/A)//you dare stand before a god?!
- godsmack(A)
- return
+/obj/singularity/ratvar/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE)//you dare stand before a god?!
+ . = ..()
+ if(. || isnull(.))
+ return .
+ godsmack(bumped_atom)
+
-/obj/singularity/ratvar/Bumped(atom/movable/moving_atom)
+/obj/singularity/ratvar/Bumped(atom/movable/moving_atom, effect_applied = TRUE)
+ . = ..()
godsmack(moving_atom)
- return
+
/obj/singularity/ratvar/proc/godsmack(atom/A)
if(istype(A,/obj/))
diff --git a/code/modules/power/singularity/singularity.dm b/code/modules/power/singularity/singularity.dm
index b657aebee69..c887e05391c 100644
--- a/code/modules/power/singularity/singularity.dm
+++ b/code/modules/power/singularity/singularity.dm
@@ -7,7 +7,7 @@
density = TRUE
layer = MASSIVE_OBJ_LAYER
light_range = 6
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
var/current_size = 1
var/allowed_size = 1
var/contained = 1 //Are we going to move around?
@@ -53,7 +53,7 @@
target = null
return ..()
-/obj/singularity/Move(atom/newloc, direct)
+/obj/singularity/Move(atom/newloc, direct = NONE, glide_size_override = 0)
if(current_size >= STAGE_FIVE || check_turfs_in(direct))
last_failed_movement = 0//Reset this because we moved
return ..()
@@ -76,7 +76,7 @@
consume(user)
return 1
-/obj/singularity/Process_Spacemove(movement_dir = NONE) //The singularity stops drifting for no man!
+/obj/singularity/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE) //The singularity stops drifting for no man!
return FALSE
/obj/singularity/blob_act(obj/structure/blob/B)
@@ -103,14 +103,17 @@
return 0 //Will there be an impact? Who knows. Will we see it? No.
-/obj/singularity/Bump(atom/A)
- consume(A)
- return
+/obj/singularity/Bump(atom/bumped_atom, custom_bump, effect_applied = FALSE)
+ . = ..()
+ if(. || isnull(.) || effect_applied)
+ return .
+ consume(bumped_atom)
-/obj/singularity/Bumped(atom/movable/moving_atom)
- consume(moving_atom)
- return
+/obj/singularity/Bumped(atom/movable/moving_atom, effect_applied = FALSE)
+ . = ..()
+ if(!effect_applied)
+ consume(moving_atom)
/obj/singularity/process()
diff --git a/code/modules/power/supermatter/supermatter.dm b/code/modules/power/supermatter/supermatter.dm
index 9f9672ab983..2f8070fc660 100644
--- a/code/modules/power/supermatter/supermatter.dm
+++ b/code/modules/power/supermatter/supermatter.dm
@@ -451,9 +451,10 @@
user.apply_effect(150, IRRADIATE)
/obj/machinery/power/supermatter_shard/Bumped(atom/movable/moving_atom)
+ . = ..()
if(isnucleation(moving_atom))
nuclear_touch(moving_atom)
- return
+ return .
if(isliving(moving_atom))
moving_atom.visible_message("\The [moving_atom] slams into \the [src] inducing a resonance... [moving_atom.p_their(TRUE)] body starts to glow and catch flame before flashing into ash.",\
"You slam into \the [src] as your ears are filled with unearthly ringing. Your last thought is \"Oh, fuck.\"",\
diff --git a/code/modules/power/tesla/energy_ball.dm b/code/modules/power/tesla/energy_ball.dm
index 3654c1c5b0d..ca61c3a1472 100644
--- a/code/modules/power/tesla/energy_ball.dm
+++ b/code/modules/power/tesla/energy_ball.dm
@@ -111,12 +111,18 @@
EB.orbit(src, orbitsize, pick(FALSE, TRUE), rand(10, 25), pick(3, 4, 5, 6, 36))
-/obj/singularity/energy_ball/Bump(atom/A)
- dust_mobs(A)
+/obj/singularity/energy_ball/Bump(atom/bumped_atom, custom_bump, effect_applied = TRUE)
+ . = ..()
+ if(. || isnull(.))
+ return .
+ dust_mobs(bumped_atom)
+
-/obj/singularity/energy_ball/Bumped(atom/movable/moving_atom)
+/obj/singularity/energy_ball/Bumped(atom/movable/moving_atom, effect_applied = TRUE)
+ . = ..()
dust_mobs(moving_atom)
+
/obj/singularity/energy_ball/attack_tk(mob/user)
if(iscarbon(user))
var/mob/living/carbon/C = user
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 0abbddb05cd..afb77771223 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -4,7 +4,7 @@
icon = 'icons/obj/weapons/projectile.dmi'
icon_state = "detective"
item_state = "gun"
- appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER
+ appearance_flags = TILE_BOUND|PIXEL_SCALE|KEEP_TOGETHER|LONG_GLIDE
flags = CONDUCT
slot_flags = ITEM_SLOT_BELT
materials = list(MAT_METAL=2000)
diff --git a/code/modules/projectiles/guns/projectile/automatic.dm b/code/modules/projectiles/guns/projectile/automatic.dm
index 18ea045c42d..cd0c7327be2 100644
--- a/code/modules/projectiles/guns/projectile/automatic.dm
+++ b/code/modules/projectiles/guns/projectile/automatic.dm
@@ -53,8 +53,8 @@
update_icon()
return 1
-/obj/item/gun/projectile/automatic/ui_action_click(var/owner, var/action_type)
- if (ispath(action_type, /datum/action/item_action/toggle_firemode))
+/obj/item/gun/projectile/automatic/ui_action_click(mob/user, action, leftclick)
+ if(istype(action, /datum/action/item_action/toggle_firemode))
burst_select()
return TRUE
@@ -149,10 +149,10 @@
icon_state = "wt550[magazine ? "-[CEILING(get_ammo(FALSE)/4, 1)*4]" : ""]"
-/obj/item/gun/projectile/automatic/wt550/ui_action_click(owner, action_type)
+/obj/item/gun/projectile/automatic/wt550/ui_action_click(mob/user, action, leftclick)
if(..())
return TRUE
- if(action_type == /datum/action/item_action/toggle_gunlight)
+ if(istype(action, /datum/action/item_action/toggle_gunlight))
toggle_gunlight()
return TRUE
@@ -179,10 +179,10 @@
item_state = "SP-91-RC[magazine ? "-[get_ammo(FALSE) ? "20" : "0"]" : ""]"
-/obj/item/gun/projectile/automatic/sp91rc/ui_action_click(owner, action_type)
+/obj/item/gun/projectile/automatic/sp91rc/ui_action_click(mob/user, action, leftclick)
if(..())
return TRUE
- if(action_type == /datum/action/item_action/toggle_gunlight)
+ if(istype(action, /datum/action/item_action/toggle_gunlight))
toggle_gunlight()
return TRUE
@@ -481,10 +481,10 @@
icon_state = "[initial(icon_state)][magazine ? "" : "-e"][suppressed ? "-suppressed" : ""]"
-/obj/item/gun/projectile/automatic/sfg/ui_action_click(owner, action_type)
+/obj/item/gun/projectile/automatic/sfg/ui_action_click(mob/user, action, leftclick)
if(..())
return TRUE
- if(action_type == /datum/action/item_action/toggle_gunlight)
+ if(istype(action, /datum/action/item_action/toggle_gunlight))
toggle_gunlight()
return TRUE
diff --git a/code/modules/projectiles/projectile.dm b/code/modules/projectiles/projectile.dm
index 449fbfdd0d2..5105ba581f5 100644
--- a/code/modules/projectiles/projectile.dm
+++ b/code/modules/projectiles/projectile.dm
@@ -240,55 +240,56 @@
return 50 //if the projectile doesn't do damage, play its hitsound at 50% volume
-/obj/item/projectile/Bump(atom/A, yes)
- if(!yes) //prevents double bumps.
- return FALSE
+/obj/item/projectile/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.))
+ return .
- if(check_ricochet(A) && check_ricochet_flag(A) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL))
+ if(check_ricochet(bumped_atom) && check_ricochet_flag(bumped_atom) && ricochets < ricochets_max && is_reflectable(REFLECTABILITY_PHYSICAL))
ricochets++
- if(A.handle_ricochet(src))
- on_ricochet(A)
+ if(bumped_atom.handle_ricochet(src))
+ on_ricochet(bumped_atom)
ignore_source_check = TRUE
range = initial(range)
return TRUE
if(firer && !ignore_source_check)
- if(A == firer || (A == firer.loc && ismecha(A))) //cannot shoot yourself or your mech
- loc = A.loc
+ if(bumped_atom == firer || (bumped_atom == firer.loc && ismecha(bumped_atom))) //cannot shoot yourself or your mech
+ loc = bumped_atom.loc
return FALSE
- var/distance = get_dist(get_turf(A), starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
+ var/turf/bumped_turf = get_turf(bumped_atom)
+ var/distance = get_dist(bumped_turf, starting) // Get the distance between the turf shot from and the mob we hit and use that for the calculations.
if(!forced_accuracy)
- if(get_dist(A, original) <= 1)
+ if(get_dist(bumped_atom, original) <= 1)
def_zone = ran_zone(def_zone, max(100 - (7 * distance), 5)) //Lower accurancy/longer range tradeoff. 7 is a balanced number to use.
else
def_zone = pick(list(BODY_ZONE_HEAD, BODY_ZONE_CHEST, BODY_ZONE_L_ARM, BODY_ZONE_R_ARM, BODY_ZONE_L_LEG, BODY_ZONE_R_LEG)) // If we were aiming at one target but another one got hit, no accuracy is applied
- if(isturf(A) && hitsound_wall)
+ if(isturf(bumped_atom) && hitsound_wall)
var/volume = clamp(vol_by_damage() + 20, 0, 100)
if(suppressed)
volume = 5
playsound(loc, hitsound_wall, volume, 1, -1)
- else if(ishuman(A))
- var/mob/living/carbon/human/H = A
- var/obj/item/organ/external/organ = H.get_organ(check_zone(def_zone))
+ else if(ishuman(bumped_atom))
+ var/mob/living/carbon/human/bumped_human = bumped_atom
+ var/obj/item/organ/external/organ = bumped_human.get_organ(check_zone(def_zone))
if(isnull(organ))
return FALSE
- var/turf/target_turf = get_turf(A)
- prehit(A)
- var/permutation = A.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null
+ prehit(bumped_atom)
+ var/permutation = bumped_atom.bullet_act(src, def_zone) // searches for return value, could be deleted after run so check A isn't null
if(permutation == -1 || forcedodge)// the bullet passes through a dense object!
if(forcedodge > 0)
forcedodge -= 1
- loc = target_turf
- if(A)
- LAZYADD(permutated, A)
+ loc = bumped_turf
+ if(bumped_atom)
+ LAZYADD(permutated, bumped_atom)
return FALSE
else
- if(A && A.density && !ismob(A) && !(A.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile.
+ if(bumped_atom && bumped_atom.density && !ismob(bumped_atom) && !(bumped_atom.flags & ON_BORDER)) //if we hit a dense non-border obj or dense turf then we also hit one of the mobs on that tile.
var/list/mobs_list = list()
- for(var/mob/living/L in target_turf)
- mobs_list += L
+ for(var/mob/living/mob in bumped_turf)
+ mobs_list += mob
if(mobs_list.len)
var/mob/living/picked_mob = pick(mobs_list)
prehit(picked_mob)
@@ -296,7 +297,7 @@
qdel(src)
-/obj/item/projectile/Process_Spacemove(movement_dir = NONE)
+/obj/item/projectile/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE //Bullets don't drift in space
@@ -348,7 +349,7 @@
step_towards(src, T)
if(original && (original.layer >= PROJECTILE_HIT_THRESHHOLD_LAYER || ismob(original)))
if(loc == get_turf(original) && !(original in permutated))
- Bump(original, TRUE)
+ Bump(original, custom_bump = TRUE)
if(QDELETED(src)) //deleted on last move
return
if(!forcemoved)
@@ -417,7 +418,7 @@
/obj/item/projectile/Crossed(atom/movable/AM, oldloc) //A mob moving on a tile with a projectile is hit by it.
..()
if(isliving(AM) && AM.density && !(pass_flags & PASSMOB))
- Bump(AM, TRUE)
+ Bump(AM, custom_bump = TRUE)
/obj/item/projectile/Destroy()
diff --git a/code/modules/projectiles/projectile/magic.dm b/code/modules/projectiles/projectile/magic.dm
index b307a6525c0..2296ef3cb3f 100644
--- a/code/modules/projectiles/projectile/magic.dm
+++ b/code/modules/projectiles/projectile/magic.dm
@@ -43,19 +43,19 @@
var/turf/T3 = get_step(src,dir)
var/mob/living/L = locate(/mob/living) in T1 //if there's a mob alive in our front right diagonal, we hit it.
if(L && L.stat != DEAD)
- Bump(L) //Magic Bullet #teachthecontroversy
+ Bump(L, custom_bump = TRUE) //Magic Bullet #teachthecontroversy
return
L = locate(/mob/living) in T2
if(L && L.stat != DEAD)
- Bump(L)
+ Bump(L, custom_bump = TRUE)
return
L = locate(/mob/living) in T3
if(L && L.stat != DEAD)
- Bump(L)
+ Bump(L, custom_bump = TRUE)
return
..()
-/obj/item/projectile/magic/fireball/on_hit(var/target)
+/obj/item/projectile/magic/fireball/on_hit(atom/target, blocked = 0, hit_zone)
. = ..()
var/turf/T = get_turf(target)
explosion(T, exp_devastate, exp_heavy, exp_light, exp_flash, 0, flame_range = exp_fire, cause = src)
diff --git a/code/modules/projectiles/projectile/special.dm b/code/modules/projectiles/projectile/special.dm
index 9454d62cc90..f9e0b32af8b 100644
--- a/code/modules/projectiles/projectile/special.dm
+++ b/code/modules/projectiles/projectile/special.dm
@@ -133,18 +133,17 @@
damage_type = BRUTE
nodamage = TRUE
flag = "bullet"
+ hitsound = 'sound/effects/meteorimpact.ogg'
+
+
+/obj/item/projectile/meteor/on_hit(atom/target, blocked, hit_zone)
+ . = ..()
+ if(blocked >= 100)
+ return FALSE
+ for(var/mob/mob in urange(10, src))
+ if(!mob.stat)
+ shake_camera(mob, 3, 1)
-/obj/item/projectile/meteor/Bump(atom/A, yes)
- if(yes)
- return
- if(A == firer)
- loc = A.loc
- return
- playsound(loc, 'sound/effects/meteorimpact.ogg', 40, 1)
- for(var/mob/M in urange(10, src))
- if(!M.stat)
- shake_camera(M, 3, 1)
- qdel(src)
/obj/item/projectile/energy/floramut
name = "alpha somatoray"
@@ -216,13 +215,16 @@
name = "snap-pop"
icon = 'icons/obj/toy.dmi'
icon_state = "snappop"
+ nodamage = TRUE
+ damage = 0
+
-/obj/item/projectile/clown/Bump(atom/A as mob|obj|turf|area)
+/obj/item/projectile/clown/bullet_act(obj/item/projectile/projectile, def_zone)
do_sparks(3, 1, src)
new /obj/effect/decal/cleanable/ash(loc)
visible_message("The [name] explodes!","You hear a snap!")
- playsound(src, 'sound/effects/snap.ogg', 50, 1)
- qdel(src)
+ playsound(src, 'sound/effects/snap.ogg', 50, TRUE)
+ . = ..()
/obj/item/projectile/beam/wormhole
name = "bluespace beam"
diff --git a/code/modules/reagents/reagent_dispenser.dm b/code/modules/reagents/reagent_dispenser.dm
index 785d64d8a40..4eed19fdcfe 100644
--- a/code/modules/reagents/reagent_dispenser.dm
+++ b/code/modules/reagents/reagent_dispenser.dm
@@ -195,10 +195,12 @@
if(rig)
rig.hear_message(M, msg)
-/obj/structure/reagent_dispensers/fueltank/Bump()
- ..()
- if(rig)
- rig.process_movement()
+
+/obj/structure/reagent_dispensers/fueltank/Bump(atom/bumped_atom, custom_bump)
+ . = ..()
+ if(. || isnull(.) || !rig)
+ return .
+ rig.process_movement()
/obj/structure/reagent_dispensers/peppertank
diff --git a/code/modules/recycling/disposal.dm b/code/modules/recycling/disposal.dm
index 5bc413e0e32..8b953983937 100644
--- a/code/modules/recycling/disposal.dm
+++ b/code/modules/recycling/disposal.dm
@@ -623,24 +623,26 @@
return
/obj/machinery/disposal/deliveryChute/Bumped(atom/movable/moving_atom) //Go straight into the chute
- ..()
- if(ismecha(moving_atom) || isspacepod(moving_atom)) return
-
- if(isprojectile(moving_atom) || iseffect(moving_atom))
- return
+ . = ..()
+ if(ismecha(moving_atom) || isspacepod(moving_atom) || isprojectile(moving_atom) || iseffect(moving_atom))
+ return .
switch(dir)
if(NORTH)
- if(moving_atom.loc.y != src.loc.y+1) return
+ if(moving_atom.loc.y != src.loc.y+1)
+ return
if(EAST)
- if(moving_atom.loc.x != src.loc.x+1) return
+ if(moving_atom.loc.x != src.loc.x+1)
+ return
if(SOUTH)
- if(moving_atom.loc.y != src.loc.y-1) return
+ if(moving_atom.loc.y != src.loc.y-1)
+ return
if(WEST)
- if(moving_atom.loc.x != src.loc.x-1) return
+ if(moving_atom.loc.x != src.loc.x-1)
+ return
if(isobj(moving_atom) || isliving(moving_atom))
- moving_atom.loc = src
+ moving_atom.forceMove(src)
if(mode != OFF)
flush()
diff --git a/code/modules/ruins/lavalandruin_code/puzzle.dm b/code/modules/ruins/lavalandruin_code/puzzle.dm
index 8af8b9e17c8..98fb35b98cc 100644
--- a/code/modules/ruins/lavalandruin_code/puzzle.dm
+++ b/code/modules/ruins/lavalandruin_code/puzzle.dm
@@ -203,8 +203,8 @@
var/obj/effect/sliding_puzzle/source
var/icon/puzzle_icon
-/obj/structure/puzzle_element/Move(nloc, dir)
- if(!isturf(nloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1)
+/obj/structure/puzzle_element/Move(atom/newloc, direct = NONE, glide_size_override = 0)
+ if(!isturf(newloc) || moving_diagonally || get_dist(get_step(src,dir),get_turf(source)) > 1)
return 0
. = ..()
diff --git a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
index 57ada89aa7d..b387b68d088 100644
--- a/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
+++ b/code/modules/ruins/objects_and_mobs/necropolis_gate.dm
@@ -5,7 +5,7 @@
icon = 'icons/effects/96x96.dmi'
icon_state = "gate_full"
flags = ON_BORDER
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
layer = TABLE_LAYER
anchored = TRUE
density = TRUE
@@ -207,7 +207,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
/obj/effect/temp_visual/necropolis
icon = 'icons/effects/96x96.dmi'
icon_state = "door_closing"
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
duration = 6
layer = EDGED_TURF_LAYER
pixel_x = -32
@@ -222,7 +222,7 @@ GLOBAL_DATUM(necropolis_gate, /obj/structure/necropolis_gate/legion_gate)
desc = "A massive arch over the necropolis gate, set into a massive tower of stone."
icon = 'icons/effects/160x160.dmi'
icon_state = "arch_full"
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
layer = TABLE_LAYER
anchored = TRUE
pixel_x = -64
diff --git a/code/modules/spacepods/spacepod.dm b/code/modules/spacepods/spacepod.dm
index ec7c6128e2d..ebcbaf782ed 100644
--- a/code/modules/spacepods/spacepod.dm
+++ b/code/modules/spacepods/spacepod.dm
@@ -1,11 +1,12 @@
-#define DAMAGE 1
-#define FIRE_OLAY 2
-#define POD_LIGHT 1
-#define WINDOW 2
-#define RIM 3
-#define PAINT 4
-#define NO_GRAVITY_SPEED 1.5
-#define GRAVITY_SPEED 4
+#define DAMAGE 1
+#define FIRE_OLAY 2
+#define POD_LIGHT 1
+#define WINDOW 2
+#define RIM 3
+#define PAINT 4
+
+#define NO_GRAVITY_SPEED (0.15 SECONDS)
+#define GRAVITY_SPEED (0.4 SECONDS)
/obj/item/pod_paint_bucket
name = "space pod paintkit"
@@ -51,10 +52,11 @@
var/list/pod_paint_effect
var/list/colors = new/list(4)
var/health = 250
- var/empcounter = 0 //Used for disabling movement when hit by an EMP
var/lights = 0
var/lights_power = 6
+ var/can_paint = TRUE
+
var/list/icon_light_color = list("pod_civ" = LIGHT_COLOR_WHITE, \
"pod_mil" = "#BBF093", \
"pod_synd" = LIGHT_COLOR_RED, \
@@ -63,10 +65,11 @@
"pod_industrial" = "#CCCC00")
var/unlocked = TRUE
-
var/move_delay = NO_GRAVITY_SPEED
- var/next_move = 0
- var/can_paint = TRUE
+ COOLDOWN_DECLARE(spacepod_move_cooldown)
+ COOLDOWN_DECLARE(cooldown_emp) //Used for disabling movement when hit by an EMP
+ var/datum/effect_system/trail_follow/spacepod/ion_trail
+
/obj/spacepod/proc/apply_paint(mob/user as mob)
var/part_type
@@ -94,7 +97,7 @@
/obj/spacepod/get_cell()
return battery
-/obj/spacepod/New()
+/obj/spacepod/Initialize(mapload)
. = ..()
if(!pod_overlays)
pod_overlays = new/list(2)
@@ -108,7 +111,6 @@
pod_paint_effect[PAINT] = image(icon,icon_state = "PAINT")
bound_width = 64
bound_height = 64
- dir = EAST
battery = new battery_type(src)
add_cabin()
add_airtank()
@@ -122,29 +124,10 @@
cargo_hold.max_w_class = 5 //fit almost anything
cargo_hold.max_combined_w_class = 0 //you can optimize your stash with larger items
START_PROCESSING(SSobj, src)
- RegisterSignal(src, COMSIG_MOVABLE_MOVED, PROC_REF(create_trail))
+ ion_trail = new
+ ion_trail.set_up(src)
+ ion_trail.start()
-/obj/spacepod/proc/create_trail()
- var/turf/T = get_turf(src)
- var/atom/oldposition
- var/atom/oldloc
- switch(dir)
- if(NORTH)
- oldposition = get_step(T, SOUTH)
- oldloc = get_step(oldposition, EAST)
- if(SOUTH) // More difficult, offset to the north!
- oldposition = get_step(get_step(src, NORTH), NORTH)
- oldloc = get_step(oldposition, EAST)
- if(EAST) // Just one to the north should suffice
- oldposition = get_step(T, WEST)
- oldloc = get_step(oldposition, NORTH)
- if(WEST) // One to the east and north from there
- oldposition = get_step(get_step(src, EAST), EAST)
- oldloc = get_step(oldposition, NORTH)
-
- if(!T.has_gravity(T))
- new /obj/effect/particle_effect/ion_trails(oldposition, dir)
- new /obj/effect/particle_effect/ion_trails(oldloc, dir)
/obj/spacepod/Destroy()
if(equipment_system.cargo_system)
@@ -154,6 +137,7 @@
QDEL_NULL(battery)
QDEL_NULL(cabin_air)
QDEL_NULL(internal_tank)
+ QDEL_NULL(ion_trail)
occupant_sanity_check()
if(pilot)
eject_pilot()
@@ -164,11 +148,11 @@
STOP_PROCESSING(SSobj, src)
return ..()
+
/obj/spacepod/process()
give_air()
regulate_temp()
- if(src.empcounter > 0)
- src.empcounter--
+
/obj/spacepod/proc/update_icons()
if(!pod_overlays)
@@ -321,8 +305,8 @@
if(battery && battery.charge > 0)
battery.use((battery.charge/3)/(severity*2))
deal_damage(80 / severity)
- if(empcounter < (40 / severity))
- empcounter = 40 / severity
+ if(COOLDOWN_TIMELEFT(src, cooldown_emp) < (80 SECONDS / severity))
+ COOLDOWN_START(src, cooldown_emp, 80 SECONDS / severity)
switch(severity)
if(1)
@@ -619,8 +603,10 @@
/obj/spacepod/syndi/unlocked
unlocked = TRUE
-/obj/spacepod/sec/New()
- ..()
+
+/obj/spacepod/sec/Initialize(mapload)
+ . = ..()
+
var/obj/item/spacepod_equipment/weaponry/burst_taser/T = new /obj/item/spacepod_equipment/weaponry/taser
T.loc = equipment_system
equipment_system.weapon_system = T
@@ -644,8 +630,8 @@
equipment_system.lock_system.id = 100000
equipment_system.installed_modules += K
-/obj/spacepod/random/New()
- ..()
+/obj/spacepod/random/Initialize(mapload)
+ . = ..()
icon_state = pick("pod_civ", "pod_black", "pod_mil", "pod_synd", "pod_gold", "pod_industrial")
switch(icon_state)
if("pod_civ")
@@ -1050,69 +1036,63 @@
else //just delete the cabin gas, we're in space or some shit
qdel(removed)
+
+// it looks really good with default Process_Spacemove and newtonian movement actually, should make a button to turn it on/off
+/obj/spacepod/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
+ return TRUE // obviously
+
+
/obj/spacepod/relaymove(mob/user, direction)
- if(user != src.pilot)
- return
- handlerelaymove(user, direction)
-
-/obj/spacepod/proc/handlerelaymove(mob/user, direction)
- if(world.time < next_move)
- return 0
- var/moveship = 1
- if(has_gravity(loc))
- move_delay = GRAVITY_SPEED
- else
- move_delay = NO_GRAVITY_SPEED
- if(battery && battery.charge >= 1 && health && empcounter == 0)
- if(!(direction & (UP|DOWN)))
- src.dir = direction
- switch(direction)
- if(NORTH)
- if(inertia_dir == SOUTH)
- inertia_dir = NONE
- moveship = 0
- if(SOUTH)
- if(inertia_dir == NORTH)
- inertia_dir = NONE
- moveship = 0
- if(EAST)
- if(inertia_dir == WEST)
- inertia_dir = NONE
- moveship = 0
- if(WEST)
- if(inertia_dir == EAST)
- inertia_dir = NONE
- moveship = 0
- if(moveship)
- if(direction & (UP|DOWN))
- var/turf/above = GET_TURF_ABOVE(loc)
- if((direction & UP) && can_z_move(DOWN, above, z_move_flags = ZMOVE_FALL_FLAGS)) // going up and can fall down is bad.
- return
- if(!zMove(direction))
- return
- pilot.update_z(z) // after we moved
- else
- Move(get_step(src, direction), direction)
- if(equipment_system.cargo_system)
- for(var/turf/T in locs)
- for(var/obj/item/I in T.contents)
- equipment_system.cargo_system.passover(I)
+ if(!COOLDOWN_FINISHED(src, spacepod_move_cooldown))
+ return FALSE
- else
- if(!battery)
- to_chat(user, "No energy cell detected.")
- else if(battery.charge < 1)
- to_chat(user, "Not enough charge left.")
- else if(!health)
- to_chat(user, "She's dead, Jim")
- else if(empcounter != 0)
- to_chat(user, "The pod control interface isn't responding. The console indicates [empcounter] seconds before reboot.")
- else
- to_chat(user, "Unknown error has occurred, yell at the coders.")
- next_move = world.time + move_delay * 10 // Don't make it spam
+ if(!pilot || user != pilot || !direction)
+ COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS) // Don't make it spam
return FALSE
- battery.charge = max(0, battery.charge - 1)
- next_move = world.time + move_delay
+
+ . = TRUE
+
+ if(!battery)
+ to_chat(user, span_warning("No energy cell detected."))
+ . = FALSE
+ else if(!battery.use(1))
+ to_chat(user, span_warning("Not enough charge left."))
+ . = FALSE
+ else if(health <= 0)
+ to_chat(user, span_warning("She's dead, Jim."))
+ . = FALSE
+ else if(!COOLDOWN_FINISHED(src, cooldown_emp))
+ to_chat(user, span_warning("The pod control interface isn't responding. The console indicates [COOLDOWN_TIMELEFT(src, cooldown_emp)] seconds before reboot."))
+ . = FALSE
+ if(!.)
+ COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS)
+ return .
+
+ if(direction & (UP|DOWN))
+ COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS)
+ var/turf/above = GET_TURF_ABOVE(loc)
+ if((direction & UP) && can_z_move(DOWN, above, z_move_flags = ZMOVE_FALL_FLAGS)) // going up and can fall down is bad.
+ return FALSE
+ . = zMove(direction)
+ if(.)
+ pilot.update_z(z) // after we moved
+ else
+ var/turf/next_step = get_step(src, direction)
+ if(!next_step)
+ COOLDOWN_START(src, spacepod_move_cooldown, 0.5 SECONDS)
+ return FALSE
+ var/calculated_move_delay = has_gravity(loc) ? GRAVITY_SPEED : NO_GRAVITY_SPEED
+ . = Move(next_step, direction)
+ if(ISDIAGONALDIR(direction) && loc == next_step)
+ calculated_move_delay *= sqrt(2)
+ set_glide_size(DELAY_TO_GLIDE_SIZE(calculated_move_delay))
+ COOLDOWN_START(src, spacepod_move_cooldown, calculated_move_delay)
+
+ if(. && equipment_system.cargo_system)
+ for(var/atom/pod_loc as anything in locs)
+ for(var/obj/item/item in pod_loc.contents)
+ equipment_system.cargo_system.passover(item)
+
//// Damaged spacepod
/obj/spacepod/civilian/damaged
diff --git a/code/modules/station_goals/bfl.dm b/code/modules/station_goals/bfl.dm
index 4ecc626f533..cc5e310a8f6 100644
--- a/code/modules/station_goals/bfl.dm
+++ b/code/modules/station_goals/bfl.dm
@@ -495,7 +495,7 @@
return ..()
-/obj/machinery/bfl_lens/Move(atom/newloc, direction, movetime)
+/obj/machinery/bfl_lens/Move(atom/newloc, direct = NONE, glide_size_override = 0)
. = ..()
if(!.)
return
diff --git a/code/modules/station_goals/brs/brs_rift_effect.dm b/code/modules/station_goals/brs/brs_rift_effect.dm
index 3f1964f4159..784df836635 100644
--- a/code/modules/station_goals/brs/brs_rift_effect.dm
+++ b/code/modules/station_goals/brs/brs_rift_effect.dm
@@ -3,7 +3,7 @@
desc = "Аномальное образование с неизвестными свойствами."
icon = 'icons/obj/engines_and_power/singularity.dmi'
icon_state = "singularity_fog"
- appearance_flags = 0
+ appearance_flags = LONG_GLIDE
layer = MASSIVE_OBJ_LAYER
invisibility = INVISIBILITY_ANOMALY
level = 1 // t-ray scaners show only things with level = 1
diff --git a/code/modules/station_goals/shield.dm b/code/modules/station_goals/shield.dm
index 6ac84b979f7..2aaab022b5f 100644
--- a/code/modules/station_goals/shield.dm
+++ b/code/modules/station_goals/shield.dm
@@ -204,9 +204,10 @@ GLOBAL_LIST_INIT(meteor_shields, list())
continue
if(!emagged && space_los(meteor_to_destroy))
Beam(get_turf(meteor_to_destroy), icon_state = "sat_beam", time = 5, maxdistance = kill_range)
- qdel(meteor_to_destroy)
+ if(meteor_to_destroy.shield_defense(src))
+ qdel(meteor_to_destroy)
-/obj/machinery/satellite/meteor_shield/Process_Spacemove(movement_dir = NONE)
+/obj/machinery/satellite/meteor_shield/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return active
/obj/machinery/satellite/meteor_shield/toggle(user)
diff --git a/code/modules/surgery/organs/augments_tail.dm b/code/modules/surgery/organs/augments_tail.dm
index 4e8f7febb74..4777d000d31 100644
--- a/code/modules/surgery/organs/augments_tail.dm
+++ b/code/modules/surgery/organs/augments_tail.dm
@@ -95,7 +95,7 @@
implant_ability.Remove(owner)
. = ..()
-/obj/item/organ/internal/cyberimp/tail/blade/ui_action_click(mob/user, actiontype, leftclick)
+/obj/item/organ/internal/cyberimp/tail/blade/ui_action_click(mob/user, action, leftclick)
if(implant_emp_downtime) // 100 sec cooldown after EMP
to_chat(owner, span_warning("Ваш имплант всё ещё перегружен после ЭМИ!"))
diff --git a/code/modules/surgery/organs/organ_internal.dm b/code/modules/surgery/organs/organ_internal.dm
index 10a75cf573d..0a23337a1b9 100644
--- a/code/modules/surgery/organs/organ_internal.dm
+++ b/code/modules/surgery/organs/organ_internal.dm
@@ -316,10 +316,10 @@
if(isobj(H.shoes))
var/thingy = H.shoes
if(H.drop_item_ground(H.shoes))
- walk_away(thingy,H,15,2)
+ SSmove_manager.move_away(thingy, H, 15, 2)
spawn(20)
if(thingy)
- walk(thingy,0)
+ SSmove_manager.stop_looping(thingy)
/obj/item/organ/internal/honktumor/cursed
diff --git a/code/modules/vehicle/ambulance.dm b/code/modules/vehicle/ambulance.dm
index 76400a9d3b0..3bc53c8657d 100644
--- a/code/modules/vehicle/ambulance.dm
+++ b/code/modules/vehicle/ambulance.dm
@@ -2,7 +2,7 @@
name = "ambulance"
desc = "This is what the paramedic uses to run over people they need to take to medbay."
icon_state = "docwagon2"
- vehicle_move_delay = 1.5
+ vehicle_move_delay = 0.3 SECONDS
key_type = /obj/item/key/ambulance
var/obj/structure/bed/amb_trolley/bed = null
var/datum/action/ambulance_alarm/AA
@@ -13,11 +13,20 @@
light_power = 3
light_color = "#F70027"
+
/obj/vehicle/ambulance/Initialize(mapload)
. = ..()
AA = new(src)
soundloop = new(list(src), FALSE)
+
+/obj/vehicle/ambulance/Destroy()
+ QDEL_NULL(soundloop)
+ QDEL_NULL(AA)
+ bed = null
+ return ..()
+
+
/datum/action/ambulance_alarm
name = "Toggle Sirens"
icon_icon = 'icons/obj/vehicles/vehicles.dmi'
@@ -73,37 +82,38 @@
/obj/vehicle/ambulance/handle_vehicle_offsets()
- ..()
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- switch(buckled_mob.dir)
- if(SOUTH)
- buckled_mob.pixel_x = 0
- buckled_mob.pixel_y = 7
- if(WEST)
- buckled_mob.pixel_x = 13
- buckled_mob.pixel_y = 7
- if(NORTH)
- buckled_mob.pixel_x = 0
- buckled_mob.pixel_y = 4
- if(EAST)
- buckled_mob.pixel_x = -13
- buckled_mob.pixel_y = 7
-
-/obj/vehicle/ambulance/Move(newloc, Dir, movetime)
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.setDir(dir)
+ switch(dir)
+ if(SOUTH)
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 7
+ if(WEST)
+ buckled_mob.pixel_x = 13
+ buckled_mob.pixel_y = 7
+ if(NORTH)
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 4
+ if(EAST)
+ buckled_mob.pixel_x = -13
+ buckled_mob.pixel_y = 7
+
+
+/obj/vehicle/ambulance/Move(atom/newloc, direct = NONE, glide_size_override = 0)
var/oldloc = loc
if(bed && !Adjacent(bed))
bed = null
. = ..()
- if(bed && get_dist(oldloc, loc) <= 2)
- bed.glide_size = glide_size
+ if(. && bed && get_dist(oldloc, loc) <= 2)
bed.Move(oldloc, get_dir(bed, oldloc))
- bed.dir = Dir
+ bed.set_glide_size(glide_size)
+ bed.setDir(direct)
if(bed.has_buckled_mobs())
- for(var/m in bed.buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.setDir(Dir)
+ for(var/mob/living/buckled_mob as anything in bed.buckled_mobs)
+ buckled_mob.setDir(direct)
+
/obj/structure/bed/amb_trolley
name = "ambulance train trolley"
diff --git a/code/modules/vehicle/atv.dm b/code/modules/vehicle/atv.dm
index dde08aff979..06f06cb4e40 100644
--- a/code/modules/vehicle/atv.dm
+++ b/code/modules/vehicle/atv.dm
@@ -9,72 +9,128 @@
integrity_failure = 70
generic_pixel_x = 0
generic_pixel_y = 4
- vehicle_move_delay = 1
+ vehicle_move_delay = 0.25 SECONDS
pull_push_speed_modifier = 1
- var/static/mutable_appearance/atvcover
+ var/mutable_appearance/atvcover
+
/obj/vehicle/atv/Initialize(mapload)
. = ..()
- atvcover = mutable_appearance(icon, atvcover, ABOVE_MOB_LAYER)
+ atvcover = mutable_appearance(icon, "atvcover", ABOVE_MOB_LAYER + 0.1)
-/obj/vehicle/atv/post_buckle_mob(mob/living/target)
- . = ..()
- add_overlay(atvcover)
+/obj/vehicle/atv/Destroy()
+ atvcover = null
+ return ..()
-/obj/vehicle/atv/post_unbuckle_mob(mob/living/target)
+/obj/vehicle/atv/update_overlays()
. = ..()
- cut_overlay(atvcover)
+ if(!has_buckled_mobs())
+ return .
+ . += atvcover
+
+
+/obj/vehicle/atv/handle_vehicle_icons()
+ update_icon(UPDATE_OVERLAYS)
/obj/vehicle/atv/handle_vehicle_layer()
- if(dir == SOUTH)
- layer = ABOVE_MOB_LAYER
- else
- layer = OBJ_LAYER
+ return
+
//TURRETS!
/obj/vehicle/atv/turret
- var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = null
+ var/obj/machinery/porta_turret/syndicate/vehicle_turret/turret = /obj/machinery/porta_turret/syndicate/vehicle_turret
-/obj/machinery/porta_turret/syndicate/vehicle_turret
- name = "mounted turret"
- scan_range = 7
- emp_vulnerable = 1
- density = FALSE
/obj/vehicle/atv/turret/Initialize(mapload)
. = ..()
- turret = new(loc)
- //turret.base = src
+ turret = new turret(loc)
+ handle_vehicle_offsets()
+ handle_vehicle_icons()
+ RegisterSignal(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE, PROC_REF(on_glide_size_update))
+ RegisterSignal(turret, COMSIG_PARENT_QDELETING, PROC_REF(on_turret_deleting))
+
+
+/obj/vehicle/atv/turret/Destroy()
+ QDEL_NULL(turret)
+ return ..()
+
+
+/obj/vehicle/atv/turret/proc/on_glide_size_update(datum/source, new_glide_size)
+ SIGNAL_HANDLER
+ turret?.set_glide_size(new_glide_size)
+
+
+/obj/vehicle/atv/turret/proc/on_turret_deleting(datum/source)
+ SIGNAL_HANDLER
+ UnregisterSignal(src, COMSIG_MOVABLE_UPDATE_GLIDE_SIZE)
+ turret = null
+
+
+/obj/vehicle/atv/turret/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
+ . = ..()
+ if(. && turret)
+ turret.forceMove(loc)
+
/obj/vehicle/atv/turret/handle_vehicle_layer()
+ if(!turret)
+ return
+ if(!has_buckled_mobs())
+ turret.layer = OBJ_LAYER + 0.01
+ return
if(dir == SOUTH)
- layer = ABOVE_MOB_LAYER
+ turret.layer = OBJ_LAYER + 0.01
else
- layer = OBJ_LAYER
+ turret.layer = ABOVE_MOB_LAYER + 0.01
+
+
+/obj/vehicle/atv/turret/update_overlays()
+ . = list(atvcover)
- if(turret)
- if(dir == NORTH)
- turret.layer = ABOVE_MOB_LAYER
- else
- turret.layer = OBJ_LAYER
/obj/vehicle/atv/turret/handle_vehicle_offsets()
- ..()
- if(turret)
- turret.loc = loc
- switch(dir)
- if(NORTH)
- turret.pixel_x = 0
- turret.pixel_y = 4
- if(EAST)
- turret.pixel_x = -12
- turret.pixel_y = 4
- if(SOUTH)
- turret.pixel_x = 0
- turret.pixel_y = 4
- if(WEST)
- turret.pixel_x = 12
- turret.pixel_y = 4
+ . = ..()
+ if(!turret)
+ return
+
+ switch(dir)
+ if(NORTH)
+ turret.pixel_x = 0
+ turret.pixel_y = 4
+ if(EAST)
+ turret.pixel_x = -12
+ turret.pixel_y = 4
+ if(SOUTH)
+ turret.pixel_x = 0
+ turret.pixel_y = 4
+ if(WEST)
+ turret.pixel_x = 12
+ turret.pixel_y = 4
+
+
+/obj/vehicle/atv/turret/fast
+ turret = /obj/machinery/porta_turret/syndicate/vehicle_turret/fast
+
+
+/obj/machinery/porta_turret/syndicate/vehicle_turret
+ name = "mounted turret"
+ animate_movement = SLIDE_STEPS
+ scan_range = 7
+ emp_vulnerable = TRUE
+ density = FALSE
+ layer = OBJ_LAYER + 0.01
+
+
+/obj/machinery/porta_turret/syndicate/vehicle_turret/fast
+ projectile = /obj/item/projectile/bullet/weakbullet4/c9mmte
+ eprojectile = /obj/item/projectile/bullet/weakbullet4/c9mmte
+ shot_delay = 0.2 SECONDS
+
+
+/obj/machinery/porta_turret/syndicate/vehicle_turret/fast/Initialize(mapload)
+ . = ..()
+ makeSpeedProcess()
+
diff --git a/code/modules/vehicle/janicart.dm b/code/modules/vehicle/janicart.dm
index 3e61fc16851..8a1137565b7 100644
--- a/code/modules/vehicle/janicart.dm
+++ b/code/modules/vehicle/janicart.dm
@@ -11,34 +11,35 @@
QDEL_NULL(trash_bag)
return ..()
+
/obj/vehicle/janicart/handle_vehicle_offsets()
- ..()
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- switch(buckled_mob.dir)
- if(NORTH)
- buckled_mob.pixel_x = 0
- buckled_mob.pixel_y = 4
- if(EAST)
- buckled_mob.pixel_x = -12
- buckled_mob.pixel_y = 7
- if(SOUTH)
- buckled_mob.pixel_x = 0
- buckled_mob.pixel_y = 7
- if(WEST)
- buckled_mob.pixel_x = 12
- buckled_mob.pixel_y = 7
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.setDir(dir)
+ switch(dir)
+ if(NORTH)
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 4
+ if(EAST)
+ buckled_mob.pixel_x = -12
+ buckled_mob.pixel_y = 7
+ if(SOUTH)
+ buckled_mob.pixel_x = 0
+ buckled_mob.pixel_y = 7
+ if(WEST)
+ buckled_mob.pixel_x = 12
+ buckled_mob.pixel_y = 7
+
-/obj/vehicle/janicart/Move(atom/OldLoc, Dir)
+/obj/vehicle/janicart/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
. = ..()
- if(floorbuffer)
- var/turf/tile = loc
- if(isturf(tile))
- tile.clean_blood()
- for(var/obj/effect/check in tile)
- if(check.is_cleanable())
- qdel(check)
+ if(. && floorbuffer && isturf(loc))
+ loc.clean_blood()
+ for(var/obj/effect/check in loc)
+ if(check.is_cleanable())
+ qdel(check)
+
/obj/vehicle/janicart/examine(mob/user)
. = ..()
@@ -64,7 +65,7 @@
qdel(I)
to_chat(user,"You upgrade [src] with [I].")
update_icon(UPDATE_OVERLAYS)
- else if(trash_bag && (!is_key(I) || is_key(inserted_key))) // don't put a key in the trash when we need it
+ else if(trash_bag && (initial(key_type.type) != I.type)) // don't put a key in the trash when we need it
trash_bag.attackby(I, user)
else
return ..()
diff --git a/code/modules/vehicle/motorcycle.dm b/code/modules/vehicle/motorcycle.dm
index f5fe4a986f3..072907a4529 100644
--- a/code/modules/vehicle/motorcycle.dm
+++ b/code/modules/vehicle/motorcycle.dm
@@ -5,27 +5,32 @@
icon_state = "motorcycle_4dir"
generic_pixel_x = 0
generic_pixel_y = 4
- vehicle_move_delay = 1
+ vehicle_move_delay = 0.25 SECONDS
pull_push_speed_modifier = 1
var/mutable_appearance/bikecover
+
/obj/vehicle/motorcycle/Initialize(mapload)
. = ..()
- bikecover = mutable_appearance(icon, "motorcycle_overlay_4d", ABOVE_MOB_LAYER)
+ bikecover = mutable_appearance(icon, "motorcycle_4dir_overlay", ABOVE_MOB_LAYER)
-/obj/vehicle/motorcycle/post_buckle_mob(mob/living/target)
- . = ..()
- add_overlay(bikecover)
+/obj/vehicle/motorcycle/Destroy()
+ bikecover = null
+ return ..()
-/obj/vehicle/motorcycle/post_unbuckle_mob(mob/living/target)
+/obj/vehicle/motorcycle/update_overlays()
. = ..()
- cut_overlay(bikecover)
+ if(!has_buckled_mobs())
+ return .
+ . += bikecover
+
+
+/obj/vehicle/motorcycle/handle_vehicle_icons()
+ update_icon(UPDATE_OVERLAYS)
/obj/vehicle/motorcycle/handle_vehicle_layer()
- if(dir == SOUTH)
- layer = ABOVE_MOB_LAYER
- else
- layer = OBJ_LAYER
+ return
+
diff --git a/code/modules/vehicle/secway.dm b/code/modules/vehicle/secway.dm
index d449bab788d..8d9d95a1f17 100644
--- a/code/modules/vehicle/secway.dm
+++ b/code/modules/vehicle/secway.dm
@@ -8,7 +8,7 @@
integrity_failure = 50
generic_pixel_x = 0
generic_pixel_y = 4
- vehicle_move_delay = 1
+ vehicle_move_delay = 0.25 SECONDS
pull_push_speed_modifier = 1
diff --git a/code/modules/vehicle/snowmobile.dm b/code/modules/vehicle/snowmobile.dm
index 5eecaae6e2b..830e9b29783 100644
--- a/code/modules/vehicle/snowmobile.dm
+++ b/code/modules/vehicle/snowmobile.dm
@@ -14,11 +14,11 @@
/obj/vehicle/snowmobile/key/Initialize(mapload)
. = ..()
- inserted_key = new /obj/item/key/snowmobile(null)
+ inserted_key = new /obj/item/key/snowmobile(src)
/obj/vehicle/snowmobile/blue/key/Initialize(mapload)
. = ..()
- inserted_key = new /obj/item/key/snowmobile(null)
+ inserted_key = new /obj/item/key/snowmobile(src)
/obj/item/key/snowmobile
name = "snowmobile key"
diff --git a/code/modules/vehicle/speedbike.dm b/code/modules/vehicle/speedbike.dm
index 7b4ba3f45e1..057bd6c0e0e 100644
--- a/code/modules/vehicle/speedbike.dm
+++ b/code/modules/vehicle/speedbike.dm
@@ -2,49 +2,69 @@
name = "Speedbike"
icon = 'icons/obj/vehicles/bike.dmi'
icon_state = "speedbike_blue"
- layer = MOB_LAYER - 0.1
- vehicle_move_delay = 0
+ vehicle_move_delay = 0.15 SECONDS
pull_push_speed_modifier = 1
var/overlay_state = "cover_blue"
- var/mutable_appearance/overlay
+ var/mutable_appearance/cover_overlay
+
/obj/vehicle/space/speedbike/Initialize(mapload)
. = ..()
- overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER)
- add_overlay(overlay)
+ cover_overlay = mutable_appearance(icon, overlay_state, ABOVE_MOB_LAYER)
-/obj/vehicle/space/speedbike/Move(newloc,move_dir)
- if(has_buckled_mobs())
- new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc)
+
+/obj/vehicle/space/speedbike/Destroy()
+ cover_overlay = null
+ return ..()
+
+
+/obj/vehicle/space/speedbike/update_overlays()
. = ..()
+ if(!has_buckled_mobs())
+ return .
+ . += cover_overlay
+
+
+/obj/vehicle/space/speedbike/handle_vehicle_icons()
+ update_icon(UPDATE_OVERLAYS)
+
+
+/obj/vehicle/space/speedbike/Move(atom/newloc, direct = NONE, glide_size_override = 0)
+ if(has_buckled_mobs())
+ new /obj/effect/temp_visual/dir_setting/speedbike_trail(loc, direct)
+ return ..()
+
/obj/vehicle/space/speedbike/handle_vehicle_layer()
+ return
+
+
+/obj/vehicle/space/speedbike/handle_vehicle_offsets()
switch(dir)
- if(NORTH,SOUTH)
+ if(NORTH, SOUTH)
pixel_x = -16
pixel_y = -16
- if(EAST,WEST)
+ if(EAST, WEST)
pixel_x = -18
pixel_y = 0
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ 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
-/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
/obj/vehicle/space/speedbike/red
icon_state = "speedbike_red"
diff --git a/code/modules/vehicle/sportscar.dm b/code/modules/vehicle/sportscar.dm
index 7e6305918ee..db063b31d6b 100644
--- a/code/modules/vehicle/sportscar.dm
+++ b/code/modules/vehicle/sportscar.dm
@@ -3,49 +3,66 @@
desc = "A very luxurious vehicle."
icon = 'icons/obj/vehicles/sportscar.dmi'
icon_state = "sportscar"
- generic_pixel_x = 0
- generic_pixel_y = 4
- vehicle_move_delay = 1
+ vehicle_move_delay = 0.25 SECONDS
pull_push_speed_modifier = 1
- var/mutable_appearance/carcover
-/obj/vehicle/car/Initialize(mapload)
- . = ..()
- carcover = mutable_appearance(icon, "sportscar_cover", ABOVE_MOB_LAYER)
+#define CAR_COVER_NORTH 1
+#define CAR_COVER_SOUTH 2
+#define CAR_COVER_EAST 3
+#define CAR_COVER_WEST 4
-/obj/vehicle/car/post_buckle_mob(mob/living/target)
+/obj/vehicle/car/update_overlays()
. = ..()
- add_overlay(carcover)
+ if(!has_buckled_mobs())
+ return .
+ var/static/list/car_covers_cache[4]
+ car_covers_cache[CAR_COVER_NORTH] = mutable_appearance(icon, "sportscar_north", ABOVE_MOB_LAYER)
+ car_covers_cache[CAR_COVER_SOUTH] = mutable_appearance(icon, "sportscar_south", ABOVE_MOB_LAYER)
+ car_covers_cache[CAR_COVER_EAST] = mutable_appearance(icon, "sportscar_east", ABOVE_MOB_LAYER)
+ car_covers_cache[CAR_COVER_WEST] = mutable_appearance(icon, "sportscar_west", ABOVE_MOB_LAYER)
-/obj/vehicle/car/post_unbuckle_mob(mob/living/target)
- . = ..()
- cut_overlay(carcover)
+ switch(dir)
+ if(NORTH)
+ . += car_covers_cache[CAR_COVER_NORTH]
+ if(SOUTH)
+ . += car_covers_cache[CAR_COVER_SOUTH]
+ if(EAST)
+ . += car_covers_cache[CAR_COVER_EAST]
+ if(WEST)
+ . += car_covers_cache[CAR_COVER_WEST]
+
+#undef CAR_COVER_NORTH
+#undef CAR_COVER_SOUTH
+#undef CAR_COVER_EAST
+#undef CAR_COVER_WEST
+
+
+/obj/vehicle/car/handle_vehicle_icons()
+ update_icon(UPDATE_OVERLAYS)
/obj/vehicle/car/handle_vehicle_offsets()
- ..()
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- switch(buckled_mob.dir)
- if(NORTH)
- buckled_mob.pixel_x = 2
- buckled_mob.pixel_y = 20
- if(EAST)
- buckled_mob.pixel_x = 20
- buckled_mob.pixel_y = 23
- if(SOUTH)
- buckled_mob.pixel_x = 20
- buckled_mob.pixel_y = 27
- if(WEST)
- buckled_mob.pixel_x = 34
- buckled_mob.pixel_y = 10
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.setDir(dir)
+ switch(dir)
+ if(NORTH)
+ buckled_mob.pixel_x = 2
+ buckled_mob.pixel_y = 20
+ if(EAST)
+ buckled_mob.pixel_x = 20
+ buckled_mob.pixel_y = 23
+ if(SOUTH)
+ buckled_mob.pixel_x = 20
+ buckled_mob.pixel_y = 27
+ if(WEST)
+ buckled_mob.pixel_x = 34
+ buckled_mob.pixel_y = 10
/obj/vehicle/car/handle_vehicle_layer()
- if(dir == SOUTH)
- layer = ABOVE_MOB_LAYER
- else
- layer = OBJ_LAYER
+ return // we got custom layers, dont worry
+
diff --git a/code/modules/vehicle/vehicle.dm b/code/modules/vehicle/vehicle.dm
index eb459a39d97..f9c4366feeb 100644
--- a/code/modules/vehicle/vehicle.dm
+++ b/code/modules/vehicle/vehicle.dm
@@ -11,23 +11,28 @@
buckle_lying = 0
max_integrity = 300
armor = list("melee" = 30, "bullet" = 30, "laser" = 30, "energy" = 0, "bomb" = 30, "bio" = 0, "rad" = 0, "fire" = 60, "acid" = 60)
- var/key_type
- var/held_key_type //Similar to above, but the vehicle needs the key in hands as opposed to inserted into the ignition
+ /// Item required for the vehicle to be inserted into the ignition.
+ var/obj/item/key_type
+ /// Whehter our key should be in mob hands, rather than in the vehicle ignintion.
+ var/key_in_hands = FALSE
+ /// Currently inerted key.
var/obj/item/key/inserted_key
- var/key_type_exact = TRUE //can subtypes work
- var/last_vehicle_move = 0 //used for move delays
- var/vehicle_move_delay = 2 //tick delay between movements, lower = faster, higher = slower
- var/auto_door_open = TRUE
- var/needs_gravity = 0 //To allow non-space vehicles to move in no gravity or not, mostly for adminbus
- //Pixels
- var/generic_pixel_x = 0 //All dirs show this pixel_x for the driver
- var/generic_pixel_y = 0 //All dirs shwo this pixel_y for the driver
- var/spaceworthy = FALSE
+ /// To allow non-space vehicles to move in no gravity or not, mostly for adminbus.
+ var/needs_gravity = FALSE
+ /// All dirs apply this pixel_x for the driver.
+ var/generic_pixel_x = 0
+ /// All dirs apply this pixel_y for the driver.
+ var/generic_pixel_y = 0
+ /// Delay between movements in deciseconds, lower = faster, higher = slower
+ var/vehicle_move_delay = 0.35 SECONDS
+ COOLDOWN_DECLARE(vehicle_move_cooldown)
/obj/vehicle/Initialize(mapload)
. = ..()
handle_vehicle_layer()
+ handle_vehicle_icons()
+
/obj/vehicle/Destroy()
QDEL_NULL(inserted_key)
@@ -37,77 +42,101 @@
/obj/vehicle/examine(mob/user)
. = ..()
if(key_type)
- if(!inserted_key)
- . += "Put a key inside it by clicking it with the key."
+ if(key_in_hands)
+ . += span_info("[src] requires the [initial(key_type.name)] to be held in hands to start driving.")
else
- . += "Alt-click [src] to remove the key."
+ if(inserted_key)
+ . += span_info("Alt-click [src] to remove the key.")
+ else
+ . += span_info("[src] requires the [initial(key_type.name)] to be inserted into ignintion to start driving.")
+
if(resistance_flags & ON_FIRE)
- . += "It's on fire!"
+ . += span_warning("It's on fire!")
var/healthpercent = obj_integrity/max_integrity * 100
switch(healthpercent)
if(50 to 99)
- . += "It looks slightly damaged."
+ . += span_notice("It looks slightly damaged.")
if(25 to 50)
- . += "It appears heavily damaged."
+ . += span_notice("It appears heavily damaged.")
if(0 to 25)
- . += "It's falling apart!"
+ . += span_warning("It's falling apart!")
+
/obj/vehicle/attackby(obj/item/I, mob/user, params)
- if(key_type && !is_key(inserted_key) && is_key(I))
- if(user.drop_transfer_item_to_loc(I, src))
- to_chat(user, "You insert [I] into [src].")
- if(inserted_key) //just in case there's an invalid key
- inserted_key.forceMove(drop_location())
- inserted_key = I
- else
- to_chat(user, "[I] seems to be stuck to your hand!")
+ if(!key_type || I.type != key_type)
+ return ..()
+
+ if(inserted_key)
+ to_chat(user, span_warning("[src] already has [inserted_key.name] in the ignition!"))
return
- return ..()
+
+ if(!user.drop_transfer_item_to_loc(I, src))
+ return
+
+ to_chat(user, span_notice("You insert [I] into [src]'s ignintion."))
+ inserted_key = I
+
/obj/vehicle/AltClick(mob/living/user)
- if(!istype(user))
+ if(!istype(user) || !Adjacent(user))
return
if(user.incapacitated() || HAS_TRAIT(user, TRAIT_HANDS_BLOCKED))
- to_chat(user, "You can't do that right now!")
+ to_chat(user, span_warning("You can't do that right now!"))
return
- if(inserted_key && user.Adjacent(user))
- if(!(user in buckled_mobs))
- to_chat(user, "You must be riding [src] to remove [src]'s key!")
- return
- to_chat(user, "You remove [inserted_key] from [src].")
- inserted_key.forceMove_turf()
- user.put_in_hands(inserted_key, ignore_anim = FALSE)
- inserted_key = null
-
-/obj/vehicle/proc/is_key(obj/item/I)
- return I ? (key_type_exact ? (I.type == key_type) : istype(I, key_type)) : FALSE
-
-/obj/vehicle/proc/held_keycheck(mob/user)
- if(held_key_type)
- if(istype(user.l_hand, held_key_type) || istype(user.r_hand, held_key_type))
- return TRUE
- else
+ if(!inserted_key)
+ to_chat(user, span_warning("[src] has no inserted keys!"))
+ return
+ if(!(user in buckled_mobs))
+ to_chat(user, span_warning("You must be riding [src] to remove [src]'s key!"))
+ return
+ to_chat(user, span_notice("You remove [inserted_key] from [src]."))
+ inserted_key.forceMove_turf()
+ user.put_in_hands(inserted_key, ignore_anim = FALSE)
+ inserted_key = null
+
+
+/obj/vehicle/proc/keycheck(mob/user, provide_feedback = TRUE)
+ if(!key_type)
return TRUE
- return FALSE
+ if(key_in_hands)
+ if(!(user.l_hand && user.l_hand.type == key_type) && !(user.r_hand && user.r_hand.type == key_type))
+ if(provide_feedback)
+ to_chat(user, span_warning("You'll need the [initial(key_type.name)] in one of your hands to drive [src]!"))
+ return FALSE
+ return TRUE
+ if(!(inserted_key && inserted_key.type == key_type))
+ if(provide_feedback)
+ to_chat(user, span_warning("[src] has no inserted keys!"))
+ return FALSE
+ return TRUE
+
//APPEARANCE
/obj/vehicle/proc/handle_vehicle_layer()
- if(dir != NORTH)
+ if(!has_buckled_mobs())
+ layer = OBJ_LAYER
+ return
+ if(dir == SOUTH)
layer = ABOVE_MOB_LAYER
else
layer = OBJ_LAYER
+/// Used to update vehicle icons if needed
+/obj/vehicle/proc/handle_vehicle_icons()
+ return
+
+
//Override this to set your vehicle's various pixel offsets
//if they differ between directions, otherwise use the
//generic variables
/obj/vehicle/proc/handle_vehicle_offsets()
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.setDir(dir)
- buckled_mob.pixel_x = generic_pixel_x
- buckled_mob.pixel_y = generic_pixel_y
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.setDir(dir)
+ buckled_mob.pixel_x = generic_pixel_x
+ buckled_mob.pixel_y = generic_pixel_y
/obj/item/key
@@ -120,82 +149,76 @@
//BUCKLE HOOKS
/obj/vehicle/post_buckle_mob(mob/living/target)
+ handle_vehicle_layer()
handle_vehicle_offsets()
+ handle_vehicle_icons()
/obj/vehicle/post_unbuckle_mob(mob/living/target)
- target.pixel_x = 0
- target.pixel_y = 0
+ target.pixel_x = target.base_pixel_x //+ target.body_position_pixel_x_offset
+ target.pixel_y = target.base_pixel_y //+ target.body_position_pixel_y_offset
+ handle_vehicle_offsets()
+ handle_vehicle_layer()
+ handle_vehicle_icons()
/obj/vehicle/bullet_act(obj/item/projectile/Proj)
- if(has_buckled_mobs())
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.bullet_act(Proj)
+ if(!has_buckled_mobs())
+ return
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.bullet_act(Proj)
+
//MOVEMENT
+
/obj/vehicle/relaymove(mob/user, direction)
- if(world.time < last_vehicle_move)
- return
+ if(!COOLDOWN_FINISHED(src, vehicle_move_cooldown) || !has_buckled_mobs() || user != buckled_mobs[1])
+ return FALSE
- if(key_type && !is_key(inserted_key))
- last_vehicle_move = world.time + 5
- to_chat(user, "[src] has no key inserted!")
- return
+ var/turf/next_step = get_step(src, direction)
+ if(!next_step || !isturf(loc) || !Process_Spacemove(direction) || !keycheck(user))
+ COOLDOWN_START(src, vehicle_move_cooldown, 0.5 SECONDS)
+ return FALSE
if(user.incapacitated())
unbuckle_mob(user)
- return
+ return FALSE
- if(held_keycheck(user))
- if(!Process_Spacemove(direction) || !isturf(loc))
- return
-
- last_vehicle_move = get_config_multiplicative_speed_by_path(/mob/living/carbon/human) + vehicle_move_delay
- Move(get_step(src, direction), direction, last_vehicle_move)
-
- if(direction & (direction - 1)) //moved diagonally
- last_vehicle_move *= 1.41
- last_vehicle_move += world.time
-
- if(has_buckled_mobs())
- if(issimulatedturf(loc))
- var/turf/simulated/T = loc
- if(T.wet == TURF_WET_LUBE) //Lube! Fall off!
- playsound(src, 'sound/misc/slip.ogg', 50, 1, -3)
- for(var/m in buckled_mobs)
- var/mob/living/buckled_mob = m
- buckled_mob.Weaken(10 SECONDS)
- unbuckle_all_mobs()
- step(src, dir)
-
- handle_vehicle_layer()
- handle_vehicle_offsets()
- else
- to_chat(user, "You'll need the keys in one of your hands to drive [src].")
+ var/add_delay = vehicle_move_delay
+
+ . = Move(next_step, direction)
+ if(ISDIAGONALDIR(direction) && loc == next_step)
+ add_delay *= sqrt(2)
+
+ set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay))
+ COOLDOWN_START(src, vehicle_move_cooldown, add_delay)
-/obj/vehicle/Move(NewLoc, Dir = 0, movetime)
+/obj/vehicle/Moved(atom/OldLoc, Dir, Forced = FALSE, momentum_change = TRUE)
. = ..()
+ if(!.)
+ return .
handle_vehicle_layer()
handle_vehicle_offsets()
+ handle_vehicle_icons()
+ if(has_buckled_mobs() && issimulatedturf(loc))
+ var/turf/simulated/check_turf = loc
+ if(check_turf.wet == TURF_WET_LUBE)
+ playsound(src, 'sound/misc/slip.ogg', 50, TRUE, -3)
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ buckled_mob.Weaken(10 SECONDS)
+ unbuckle_all_mobs()
-/obj/vehicle/Bump(atom/movable/M)
- if(!spaceworthy && isspaceturf(get_turf(src)))
- return FALSE
+/obj/vehicle/Bump(atom/bumped_atom, custom_bump)
. = ..()
- if(auto_door_open)
- if(istype(M, /obj/machinery/door) && has_buckled_mobs())
- for(var/m in buckled_mobs)
- M.Bumped(m)
-
-/obj/vehicle/proc/RunOver(var/mob/living/carbon/human/H)
- return //write specifics for different vehicles
+ if(. || isnull(.) || !has_buckled_mobs() || !istype(bumped_atom, /obj/machinery/door))
+ return .
+ for(var/mob/living/buckled_mob as anything in buckled_mobs)
+ bumped_atom.Bumped(buckled_mob)
-/obj/vehicle/Process_Spacemove(movement_dir = NONE)
+/obj/vehicle/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
if(has_gravity())
return TRUE
@@ -207,9 +230,11 @@
return FALSE
+
/obj/vehicle/space
pressure_resistance = INFINITY
- spaceworthy = TRUE
-/obj/vehicle/space/Process_Spacemove(movement_dir = NONE)
+
+/obj/vehicle/space/Process_Spacemove(movement_dir = NONE, continuous_move = FALSE)
return TRUE
+
diff --git a/icons/effects/effects.dmi b/icons/effects/effects.dmi
index 37295319bb3..4cf287bfe54 100644
Binary files a/icons/effects/effects.dmi and b/icons/effects/effects.dmi differ
diff --git a/icons/obj/vehicles/4wheeler.dmi b/icons/obj/vehicles/4wheeler.dmi
index e1bfd5e18fd..eaa38aefb13 100644
Binary files a/icons/obj/vehicles/4wheeler.dmi and b/icons/obj/vehicles/4wheeler.dmi differ
diff --git a/paradise.dme b/paradise.dme
index 2370ba33490..ab0530eb6fd 100644
--- a/paradise.dme
+++ b/paradise.dme
@@ -132,6 +132,7 @@
#include "code\__DEFINES\dcs\helpers.dm"
#include "code\__DEFINES\dcs\mapping.dm"
#include "code\__DEFINES\dcs\signals.dm"
+#include "code\__DEFINES\dcs\signals_object.dm"
#include "code\__DEFINES\traits\_traits.dm"
#include "code\__DEFINES\traits\declarations.dm"
#include "code\__DEFINES\traits\sources.dm"
@@ -283,7 +284,6 @@
#include "code\controllers\subsystem\parallax.dm"
#include "code\controllers\subsystem\runechat.dm"
#include "code\controllers\subsystem\shuttles.dm"
-#include "code\controllers\subsystem\spacedrift.dm"
#include "code\controllers\subsystem\speech_controller.dm"
#include "code\controllers\subsystem\statistics.dm"
#include "code\controllers\subsystem\sun.dm"
@@ -296,6 +296,10 @@
#include "code\controllers\subsystem\verb_manager.dm"
#include "code\controllers\subsystem\vote.dm"
#include "code\controllers\subsystem\weather.dm"
+#include "code\controllers\subsystem\movement\move_handler.dm"
+#include "code\controllers\subsystem\movement\movement.dm"
+#include "code\controllers\subsystem\movement\movement_types.dm"
+#include "code\controllers\subsystem\movement\spacedrift.dm"
#include "code\controllers\subsystem\non-firing\alarm.dm"
#include "code\controllers\subsystem\non-firing\assets.dm"
#include "code\controllers\subsystem\non-firing\atoms.dm"
@@ -394,6 +398,7 @@
#include "code\datums\components\cross_shock.dm"
#include "code\datums\components\decal.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\examine_override.dm"
@@ -401,6 +406,7 @@
#include "code\datums\components\fullauto.dm"
#include "code\datums\components\hide_highest_offset.dm"
#include "code\datums\components\jackboots.dm"
+#include "code\datums\components\jetpack.dm"
#include "code\datums\components\label.dm"
#include "code\datums\components\material_container.dm"
#include "code\datums\components\overlay_lighting.dm"