diff --git a/code/datums/forced_movement.dm b/code/datums/forced_movement.dm
new file mode 100644
index 00000000000..2f9867bafc3
--- /dev/null
+++ b/code/datums/forced_movement.dm
@@ -0,0 +1,90 @@
+//Just new and forget
+/datum/forced_movement
+ var/atom/movable/victim
+ var/atom/target
+ var/last_processed
+ var/steps_per_tick
+ var/allow_climbing
+ var/datum/callback/on_step
+ var/moved_at_all = FALSE
+ //as fast as ssfastprocess
+/datum/forced_movement/New(atom/movable/_victim, atom/_target, _steps_per_tick = 0.5, _allow_climbing = FALSE, datum/callback/_on_step = null)
+ victim = _victim
+ target = _target
+ steps_per_tick = _steps_per_tick
+ allow_climbing = _allow_climbing
+ on_step = _on_step
+
+ . = ..()
+
+ if(_victim && _target && _steps_per_tick && !_victim.force_moving)
+ last_processed = world.time
+ _victim.force_moving = src
+ START_PROCESSING(SSfastprocess, src)
+ else
+ qdel(src) //if you want to overwrite the current forced movement, call qdel(victim.force_moving) before creating this
+
+/datum/forced_movement/Destroy()
+ if(victim.force_moving == src)
+ victim.force_moving = null
+ if(moved_at_all)
+ victim.forceMove(victim.loc) //get the side effects of moving here that require us to currently not be force_moving aka reslipping on ice
+ STOP_PROCESSING(SSfastprocess, src)
+ victim = null
+ target = null
+ return ..()
+
+/datum/forced_movement/process()
+ if(qdeleted(victim) || !victim.loc || qdeleted(target) || !target.loc)
+ qdel(src)
+ return
+ var/steps_to_take = round(steps_per_tick * (world.time - last_processed))
+ if(steps_to_take)
+ for(var/i in 1 to steps_to_take)
+ if(TryMove())
+ moved_at_all = TRUE
+ if(on_step)
+ on_step.InvokeAsync()
+ else
+ qdel(src)
+ return
+ last_processed = world.time
+
+/datum/forced_movement/proc/TryMove(recursive = FALSE)
+ var/atom/movable/vic = victim //sanic
+ var/atom/tar = target
+
+ if(!recursive)
+ . = step_towards(vic, tar)
+
+ //shit way for getting around corners
+ if(!.)
+ if(tar.x > vic.x)
+ if(step(vic, EAST))
+ . = TRUE
+ else if(tar.x < vic.x)
+ if(step(vic, WEST))
+ . = TRUE
+
+ if(!.)
+ if(tar.y > vic.y)
+ if(step(vic, NORTH))
+ . = TRUE
+ else if(tar.y < vic.y)
+ if(step(vic, SOUTH))
+ . = TRUE
+
+ if(!.)
+ if(recursive)
+ return FALSE
+ else
+ . = TryMove(TRUE)
+
+ . = . && (vic.loc != tar.loc)
+
+/mob/Bump(atom/A)
+ . = ..()
+ if(force_moving && force_moving.allow_climbing && istype(A,/obj/structure))
+ var/obj/structure/S = A
+ if(S.climbable)
+ S.do_climb(src)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 876a4fa5ff2..204daf2536b 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -23,6 +23,7 @@
var/list/acted_explosions //for explosion dodging
glide_size = 8
appearance_flags = TILE_BOUND
+ var/datum/forced_movement/force_moving = null //handled soley by forced_movement.dm
@@ -139,10 +140,12 @@
if(pulledby)
pulledby.stop_pulling()
var/atom/oldloc = loc
- if(oldloc)
+ var/same_loc = oldloc == destination.loc
+ if(oldloc && !same_loc)
oldloc.Exited(src, destination)
loc = destination
- destination.Entered(src, oldloc)
+ if(oldloc && !same_loc)
+ destination.Entered(src, oldloc)
var/area/old_area = get_area(oldloc)
var/area/destarea = get_area(destination)
if(old_area != destarea)
diff --git a/code/game/objects/structures.dm b/code/game/objects/structures.dm
index 34683c45b7a..69abe624adb 100644
--- a/code/game/objects/structures.dm
+++ b/code/game/objects/structures.dm
@@ -64,6 +64,12 @@
step(O, get_dir(O, src))
return
+/obj/structure/proc/do_climb(atom/movable/A)
+ if(climbable)
+ density = 0
+ . = step(A,get_dir(A,src.loc))
+ density = 1
+
/obj/structure/proc/climb_structure(mob/user)
src.add_fingerprint(user)
user.visible_message("[user] starts climbing onto [src].", \
@@ -76,8 +82,7 @@
structureclimber = user
if(do_mob(user, user, adjusted_climb_time))
if(src.loc) //Checking if structure has been destroyed
- density = 0
- if(step(user,get_dir(user,src.loc)))
+ if(do_climb(user))
user.visible_message("[user] climbs onto [src].", \
"You climb onto [src].")
add_logs(user, src, "climbed onto")
@@ -85,7 +90,6 @@
. = 1
else
user << "You fail to climb onto [src]."
- density = 1
structureclimber = null
/obj/structure/examine(mob/user)
diff --git a/code/game/turfs/open.dm b/code/game/turfs/open.dm
index 484cfdee7ac..68186429059 100644
--- a/code/game/turfs/open.dm
+++ b/code/game/turfs/open.dm
@@ -141,7 +141,7 @@
return 1
/turf/open/handle_slip(mob/living/carbon/C, s_amount, w_amount, obj/O, lube)
- if((C.movement_type & FLYING) || C.slipping)
+ if(C.movement_type & FLYING)
return 0
if(has_gravity(src))
var/obj/buckled_obj
@@ -173,20 +173,12 @@
if(buckled_obj)
buckled_obj.unbuckle_mob(C)
- step(buckled_obj, olddir)
- else if(lube&SLIDE)
- C.slipping = TRUE
- for(var/i=1, i<5, i++)
- spawn (i)
- if(i == 4)
- C.slipping = FALSE
- step(C, olddir)
- C.spin(1,1)
+ lube |= SLIDE_ICE
+
+ if(lube&SLIDE)
+ new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 4), 1, FALSE, CALLBACK(C, /mob/living/carbon/.proc/spin, 1, 1))
else if(lube&SLIDE_ICE)
- C.slipping = TRUE
- spawn(1)
- C.slipping = FALSE
- step(C, olddir)
+ new /datum/forced_movement(C, get_ranged_target_turf(C, olddir, 1), 1, FALSE) //spinning would be bad for ice, fucks up the next dir
return 1
/turf/open/proc/MakeSlippery(wet_setting = TURF_WET_WATER, min_wet_time = 0, wet_time_to_add = 0) // 1 = Water, 2 = Lube, 3 = Ice, 4 = Permafrost, 5 = Slide
diff --git a/code/modules/mining/lavaland/necropolis_chests.dm b/code/modules/mining/lavaland/necropolis_chests.dm
index 9a2d9acd6de..3361c29d954 100644
--- a/code/modules/mining/lavaland/necropolis_chests.dm
+++ b/code/modules/mining/lavaland/necropolis_chests.dm
@@ -193,14 +193,18 @@
if(firer)
chain = firer.Beam(src, icon_state = "chain", time = INFINITY, maxdistance = INFINITY)
..()
+ //TODO: root the firer until the chain returns
/obj/item/projectile/hook/on_hit(atom/target)
. = ..()
- if(isliving(target))
- var/mob/living/L = target
- if(!L.anchored)
- L.visible_message("[L] is snagged by [firer]'s hook!")
- L.forceMove(get_turf(firer))
+ if(istype(target, /atom/movable))
+ var/atom/movable/A = target
+ if(A.anchored)
+ return
+ A.visible_message("[A] is snagged by [firer]'s hook!")
+ new /datum/forced_movement(A, get_turf(firer), 5, TRUE)
+ //TODO: keep the chain beamed to A
+ //TODO: needs a callback to delete the chain
/obj/item/projectile/hook/Destroy()
qdel(chain)
diff --git a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
index e976375b6ab..81266f9d974 100644
--- a/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
+++ b/code/modules/mob/living/carbon/alien/larva/larva_defense.dm
@@ -22,10 +22,7 @@
if(user.a_intent == INTENT_HARM)
..(user, 1)
adjustBruteLoss(5 + rand(1,9))
- spawn()
- step_away(src,user,15)
- sleep(1)
- step_away(src,user,15)
+ new /datum/forced_movement(src, get_step_away(user,src, 30), 1)
return 1
/mob/living/carbon/alien/larva/do_attack_animation(atom/A, visual_effect_icon, obj/item/used_item, no_effect, end_pixel_y)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 809303da446..29965c802de 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -519,7 +519,7 @@
if (AM.density && AM.anchored)
pressure_resistance_prob_delta -= 20
break
- if(!slipping)
+ if(!force_moving)
..(pressure_difference, direction, pressure_resistance_prob_delta)
/mob/living/verb/resist()
diff --git a/code/modules/mob/living/living_defines.dm b/code/modules/mob/living/living_defines.dm
index 58625ace4a9..0b2fb83bc9f 100644
--- a/code/modules/mob/living/living_defines.dm
+++ b/code/modules/mob/living/living_defines.dm
@@ -74,6 +74,4 @@
var/list/status_effects //a list of all status effects the mob has
- var/slipping = FALSE
-
var/list/implants = null
\ No newline at end of file
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 76f768494d2..d029187d6bd 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -127,10 +127,10 @@
return 0
if(moving)
return 0
+ if(mob.force_moving)
+ return 0
if(isliving(mob))
var/mob/living/L = mob
- if(L.slipping)
- return 0
if(L.incorporeal_move) //Move though walls
Process_Incorpmove(direct)
return 0
diff --git a/tgstation.dme b/tgstation.dme
index 223c360e542..fc8418fe000 100644
--- a/tgstation.dme
+++ b/tgstation.dme
@@ -196,6 +196,7 @@
#include "code\datums\dna.dm"
#include "code\datums\dog_fashion.dm"
#include "code\datums\emotes.dm"
+#include "code\datums\forced_movement.dm"
#include "code\datums\gas_overrides.dm"
#include "code\datums\hud.dm"
#include "code\datums\martial.dm"