diff --git a/code/__DEFINES/atmospherics.dm b/code/__DEFINES/atmospherics.dm
index e728d49956..88c1b763a5 100644
--- a/code/__DEFINES/atmospherics.dm
+++ b/code/__DEFINES/atmospherics.dm
@@ -255,10 +255,10 @@
//HELPERS
#define PIPING_LAYER_SHIFT(T, PipingLayer) \
- if(T.dir & NORTH || T.dir & SOUTH) { \
+ if(T.dir & (NORTH|SOUTH)) { \
T.pixel_x = (PipingLayer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_X;\
} \
- if(T.dir & WEST || T.dir & EAST) { \
+ if(T.dir & (WEST|EAST)) { \
T.pixel_y = (PipingLayer - PIPING_LAYER_DEFAULT) * PIPING_LAYER_P_Y;\
}
diff --git a/code/__DEFINES/mobs.dm b/code/__DEFINES/mobs.dm
index c89bd4843e..61850ed2fd 100644
--- a/code/__DEFINES/mobs.dm
+++ b/code/__DEFINES/mobs.dm
@@ -288,6 +288,10 @@
#define HUMAN_FIRE_STACK_ICON_NUM 3
+#define GRAB_PIXEL_SHIFT_PASSIVE 6
+#define GRAB_PIXEL_SHIFT_AGGRESSIVE 12
+#define GRAB_PIXEL_SHIFT_NECK 16
+
#define PULL_PRONE_SLOWDOWN 0.6
#define FIREMAN_CARRY_SLOWDOWN 0
#define PIGGYBACK_CARRY_SLOWDOWN 1
diff --git a/code/datums/components/riding.dm b/code/datums/components/riding.dm
index 00bb392787..651acda6e1 100644
--- a/code/datums/components/riding.dm
+++ b/code/datums/components/riding.dm
@@ -20,6 +20,8 @@
var/ride_check_ridden_incapacitated = FALSE
var/list/offhands = list() // keyed list containing all the current riding offsets associated by mob
+ var/del_on_unbuckle_all = FALSE
+
/datum/component/riding/Initialize()
if(!ismovable(parent))
return COMPONENT_INCOMPATIBLE
@@ -28,8 +30,11 @@
RegisterSignal(parent, COMSIG_MOVABLE_MOVED, .proc/vehicle_moved)
/datum/component/riding/proc/vehicle_mob_unbuckle(datum/source, mob/living/M, force = FALSE)
+ var/atom/movable/AM = parent
restore_position(M)
unequip_buckle_inhands(M)
+ if(del_on_unbuckle_all && !AM.has_buckled_mobs())
+ qdel(src)
/datum/component/riding/proc/vehicle_mob_buckle(datum/source, mob/living/M, force = FALSE)
handle_vehicle_offsets()
@@ -194,6 +199,7 @@
///////Yes, I said humans. No, this won't end well...//////////
/datum/component/riding/human
+ del_on_unbuckle_all = TRUE
var/fireman_carrying = FALSE
/datum/component/riding/human/Initialize()
@@ -261,6 +267,7 @@
user.visible_message("[AM] pushes [user] off of [AM.p_them()]!")
/datum/component/riding/cyborg
+ del_on_unbuckle_all = TRUE
/datum/component/riding/cyborg/Initialize()
. = ..()
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index b86c7e9729..32dde45195 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -25,6 +25,7 @@
var/inertia_move_delay = 5
var/pass_flags = 0
var/moving_diagonally = 0 //0: not doing a diagonal move. 1 and 2: doing the first/second step of the diagonal move
+ var/atom/movable/moving_from_pull //attempt to resume grab after moving instead of before.
var/list/client_mobs_in_contents // This contains all the client mobs within this container
var/list/acted_explosions //for explosion dodging
glide_size = 8
@@ -207,6 +208,7 @@
if(!Process_Spacemove(get_dir(pulling.loc, A)))
return
step(pulling, get_dir(pulling.loc, A))
+ return TRUE
/atom/movable/proc/check_pulling()
if(pulling)
@@ -224,6 +226,8 @@
if(pulling.anchored || pulling.move_resist > move_force)
stop_pulling()
return
+ if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1) //separated from our puller and not in the middle of a diagonal move.
+ pulledby.stop_pulling()
/atom/movable/Destroy(force)
QDEL_NULL(proximity_monitor)
diff --git a/code/game/atoms_movement.dm b/code/game/atoms_movement.dm
index d418652cd8..3d2b6d9e91 100644
--- a/code/game/atoms_movement.dm
+++ b/code/game/atoms_movement.dm
@@ -51,13 +51,8 @@
/atom/movable/Move(atom/newloc, direct)
var/atom/movable/pullee = pulling
var/turf/T = loc
- if(pulling)
- if(pullee && get_dist(src, pullee) > 1)
- stop_pulling()
-
- if(pullee && pullee.loc != loc && !isturf(pullee.loc) ) //to be removed once all code that changes an object's loc uses forceMove().
- log_game("DEBUG:[src]'s pull on [pullee] wasn't broken despite [pullee] being in [pullee.loc]. Pull stopped manually.")
- stop_pulling()
+ if(!moving_from_pull)
+ check_pulling()
if(!loc || !newloc)
return FALSE
var/atom/oldloc = loc
@@ -130,19 +125,16 @@
if(has_buckled_mobs() && !handle_buckled_mob_movement(loc,direct)) //movement failed due to buckled mob(s)
return FALSE
- if(pulling && pulling == pullee) //we were pulling a thing and didn't lose it during our move.
+ if(pulling && pulling == pullee && pulling != moving_from_pull) //we were pulling a thing and didn't lose it during our move.
if(pulling.anchored)
stop_pulling()
else
var/pull_dir = get_dir(src, pulling)
//puller and pullee more than one tile away or in diagonal position
if(get_dist(src, pulling) > 1 || (moving_diagonally != SECOND_DIAG_STEP && ((pull_dir - 1) & pull_dir)))
+ pulling.moving_from_pull = src
pulling.Move(T, get_dir(pulling, T)) //the pullee tries to reach our previous position
- if(pulling && get_dist(src, pulling) > 1) //the pullee couldn't keep up
- stop_pulling()
- if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move.
- pulledby.stop_pulling()
-
+ pulling.moving_from_pull = null
Moved(oldloc, direct)
/atom/movable/proc/handle_buckled_mob_movement(newloc,direct)
diff --git a/code/game/objects/buckling.dm b/code/game/objects/buckling.dm
index 0e14af75a9..bacb8c6669 100644
--- a/code/game/objects/buckling.dm
+++ b/code/game/objects/buckling.dm
@@ -55,8 +55,12 @@
M.buckling = null
return FALSE
- if(M.pulledby && buckle_prevents_pull)
- M.pulledby.stop_pulling()
+ if(M.pulledby)
+ if(buckle_prevents_pull)
+ M.pulledby.stop_pulling()
+ else if(isliving(M.pulledby))
+ var/mob/living/L = M.pulledby
+ L.reset_pull_offsets(M, TRUE)
if(!check_loc && M.loc != loc)
M.forceMove(loc)
@@ -137,4 +141,7 @@
"You unbuckle yourself from [src].",\
"You hear metal clanking.")
add_fingerprint(user)
+ if(isliving(M.pulledby))
+ var/mob/living/L = M.pulledby
+ L.set_pull_offsets(M, L.grab_state)
return M
diff --git a/code/modules/atmospherics/machinery/pipes/manifold.dm b/code/modules/atmospherics/machinery/pipes/manifold.dm
index 77452fd6fd..aa8ee65bd8 100644
--- a/code/modules/atmospherics/machinery/pipes/manifold.dm
+++ b/code/modules/atmospherics/machinery/pipes/manifold.dm
@@ -28,6 +28,8 @@
/obj/machinery/atmospherics/pipe/manifold/update_icon()
cut_overlays()
+ if(!center)
+ center = mutable_appearance(icon, "manifold_center")
PIPING_LAYER_DOUBLE_SHIFT(center, piping_layer)
add_overlay(center)
diff --git a/code/modules/atmospherics/machinery/pipes/manifold4w.dm b/code/modules/atmospherics/machinery/pipes/manifold4w.dm
index 1bcca8d5ae..56c0408d18 100644
--- a/code/modules/atmospherics/machinery/pipes/manifold4w.dm
+++ b/code/modules/atmospherics/machinery/pipes/manifold4w.dm
@@ -26,6 +26,8 @@
/obj/machinery/atmospherics/pipe/manifold4w/update_icon()
cut_overlays()
+ if(!center)
+ center = mutable_appearance(icon, "manifold_center")
PIPING_LAYER_DOUBLE_SHIFT(center, piping_layer)
add_overlay(center)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index 89f8381087..5189f47fbc 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -316,6 +316,45 @@
update_pull_movespeed()
+ set_pull_offsets(M, state)
+
+/mob/living/proc/set_pull_offsets(mob/living/M, grab_state = GRAB_PASSIVE)
+ if(M.buckled)
+ return //don't make them change direction or offset them if they're buckled into something.
+ var/offset = 0
+ switch(grab_state)
+ if(GRAB_PASSIVE)
+ offset = GRAB_PIXEL_SHIFT_PASSIVE
+ if(GRAB_AGGRESSIVE)
+ offset = GRAB_PIXEL_SHIFT_AGGRESSIVE
+ if(GRAB_NECK)
+ offset = GRAB_PIXEL_SHIFT_NECK
+ if(GRAB_KILL)
+ offset = GRAB_PIXEL_SHIFT_NECK
+ M.setDir(get_dir(M, src))
+ switch(M.dir)
+ if(NORTH)
+ animate(M, pixel_x = 0, pixel_y = offset, 3)
+ if(SOUTH)
+ animate(M, pixel_x = 0, pixel_y = -offset, 3)
+ if(EAST)
+ if(M.lying == 270) //update the dragged dude's direction if we've turned
+ M.lying = 90
+ M.update_transform() //force a transformation update, otherwise it'll take a few ticks for update_mobility() to do so
+ M.lying_prev = M.lying
+ animate(M, pixel_x = offset, pixel_y = 0, 3)
+ if(WEST)
+ if(M.lying == 90)
+ M.lying = 270
+ M.update_transform()
+ M.lying_prev = M.lying
+ animate(M, pixel_x = -offset, pixel_y = 0, 3)
+
+/mob/living/proc/reset_pull_offsets(mob/living/M, override)
+ if(!override && M.buckled)
+ return
+ animate(M, pixel_x = 0, pixel_y = 0, 1)
+
//mob verbs are a lot faster than object verbs
//for more info on why this is not atom/pull, see examinate() in mob.dm
/mob/living/verb/pulled(atom/movable/AM as mob|obj in oview(1))
@@ -328,6 +367,8 @@
stop_pulling()
/mob/living/stop_pulling()
+ if(ismob(pulling))
+ reset_pull_offsets(pulling)
..()
update_pull_movespeed()
update_pull_hud_icon()
diff --git a/code/modules/mob/living/living_defense.dm b/code/modules/mob/living/living_defense.dm
index 39a94232a3..3e4fbd9625 100644
--- a/code/modules/mob/living/living_defense.dm
+++ b/code/modules/mob/living/living_defense.dm
@@ -264,6 +264,7 @@
update_mobility() //we fall down
if(!buckled && !density)
Move(user.loc)
+ user.set_pull_offsets(src, grab_state)
return 1
/mob/living/attack_hand(mob/user)
diff --git a/code/modules/mob/living/living_movement.dm b/code/modules/mob/living/living_movement.dm
index ddcf3e6292..6a84162ea8 100644
--- a/code/modules/mob/living/living_movement.dm
+++ b/code/modules/mob/living/living_movement.dm
@@ -75,8 +75,12 @@
. = ..()
- if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1)//separated from our puller and not in the middle of a diagonal move.
+ if(pulledby && moving_diagonally != FIRST_DIAG_STEP && get_dist(src, pulledby) > 1 && (pulledby != moving_from_pull))//separated from our puller and not in the middle of a diagonal move.
pulledby.stop_pulling()
+ else
+ if(isliving(pulledby))
+ var/mob/living/L = pulledby
+ L.set_pull_offsets(src, pulledby.grab_state)
if(active_storage && !(CanReach(active_storage.parent,view_only = TRUE)))
active_storage.close(src)
@@ -84,6 +88,14 @@
if(lying && !buckled && prob(getBruteLoss()*200/maxHealth))
makeTrail(newloc, T, old_direction)
+
+/mob/living/Move_Pulled(atom/A)
+ . = ..()
+ if(!. || !isliving(A))
+ return
+ var/mob/living/L = A
+ set_pull_offsets(L, grab_state)
+
/mob/living/forceMove(atom/destination)
stop_pulling()
if(buckled)
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index fdc60b30cb..adf3e349b7 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -109,6 +109,8 @@
/// Process_Grab(): checks for grab, attempts to break if so. Return TRUE to prevent movement.
/client/proc/Process_Grab()
if(mob.pulledby)
+ if((mob.pulledby == mob.pulling) && (mob.pulledby.grab_state == GRAB_PASSIVE)) //Don't autoresist passive grabs if we're grabbing them too.
+ return
if(mob.incapacitated(ignore_restraints = 1))
move_delay = world.time + 10
return TRUE