diff --git a/code/__DEFINES/dcs/signals.dm b/code/__DEFINES/dcs/signals.dm
index a8d9c860354..2456ce0ddd1 100644
--- a/code/__DEFINES/dcs/signals.dm
+++ b/code/__DEFINES/dcs/signals.dm
@@ -356,6 +356,15 @@
#define COMPONENT_MOVABLE_BLOCK_UNCROSS (1<<0)
///from base of atom/movable/Uncrossed(): (/atom/movable)
#define COMSIG_MOVABLE_UNCROSSED "movable_uncrossed"
+///from base of atom/movable/Cross(): (/atom/movable)
+#define COMSIG_MOVABLE_CROSS_OVER "movable_cross_am"
+///from base of atom/movable/Crossed(): (/atom/movable)
+#define COMSIG_MOVABLE_CROSSED_OVER "movable_crossed_am"
+///from base of atom/movable/Uncross(): (/atom/movable)
+#define COMSIG_MOVABLE_UNCROSS_OVER "movable_uncross_am"
+ #define COMPONENT_MOVABLE_BLOCK_UNCROSS_OVER (1<<0)
+///from base of atom/movable/Uncrossed(): (/atom/movable)
+#define COMSIG_MOVABLE_UNCROSSED_OVER "movable_uncross_am"
///from base of atom/movable/Bump(): (/atom)
#define COMSIG_MOVABLE_BUMP "movable_bump"
///from base of atom/movable/throw_impact(): (/atom/hit_atom, /datum/thrownthing/throwingdatum)
@@ -432,6 +441,20 @@
#define COMSIG_MOB_ALTCLICKON "mob_altclickon"
#define COMSIG_MOB_CANCEL_CLICKON (1<<0)
+/// From base of /mob/living/simple_animal/bot/proc/bot_step()
+#define COMSIG_MOB_BOT_PRE_STEP "mob_bot_pre_step"
+ /// Should always match COMPONENT_MOVABLE_BLOCK_PRE_MOVE as these are interchangeable and used to block movement.
+ #define COMPONENT_MOB_BOT_BLOCK_PRE_STEP COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+/// From base of /mob/living/simple_animal/bot/proc/bot_step()
+#define COMSIG_MOB_BOT_STEP "mob_bot_step"
+
+/// From base of /client/Move()
+#define COMSIG_MOB_CLIENT_PRE_MOVE "mob_client_pre_move"
+ /// Should always match COMPONENT_MOVABLE_BLOCK_PRE_MOVE as these are interchangeable and used to block movement.
+ #define COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE COMPONENT_MOVABLE_BLOCK_PRE_MOVE
+/// From base of /client/Move()
+#define COMSIG_MOB_CLIENT_MOVED "mob_client_moved"
+
///from base of obj/allowed(mob/M): (/obj) returns bool, if TRUE the mob has id access to the obj
#define COMSIG_MOB_ALLOWED "mob_allowed"
///from base of mob/anti_magic_check(): (mob/user, magic, holy, tinfoil, chargecost, self, protection_sources)
diff --git a/code/game/atoms_movable.dm b/code/game/atoms_movable.dm
index 474a04b743d..6633dfeedd8 100644
--- a/code/game/atoms_movable.dm
+++ b/code/game/atoms_movable.dm
@@ -511,6 +511,7 @@
/atom/movable/Cross(atom/movable/AM)
. = TRUE
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSS, AM)
+ SEND_SIGNAL(AM, COMSIG_MOVABLE_CROSS_OVER, src)
return CanPass(AM, AM.loc, TRUE)
//oldloc = old location on atom, inserted when forceMove is called and ONLY when forceMove is called!
@@ -518,16 +519,20 @@
SHOULD_CALL_PARENT(TRUE)
. = ..()
SEND_SIGNAL(src, COMSIG_MOVABLE_CROSSED, AM)
+ SEND_SIGNAL(AM, COMSIG_MOVABLE_CROSSED_OVER, src)
/atom/movable/Uncross(atom/movable/AM, atom/newloc)
. = ..()
if(SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSS, AM) & COMPONENT_MOVABLE_BLOCK_UNCROSS)
return FALSE
+ if(SEND_SIGNAL(AM, COMSIG_MOVABLE_UNCROSS_OVER, src) & COMPONENT_MOVABLE_BLOCK_UNCROSS_OVER)
+ return FALSE
if(isturf(newloc) && !CheckExit(AM, newloc))
return FALSE
/atom/movable/Uncrossed(atom/movable/AM)
SEND_SIGNAL(src, COMSIG_MOVABLE_UNCROSSED, AM)
+ SEND_SIGNAL(AM, COMSIG_MOVABLE_UNCROSSED_OVER, src)
/atom/movable/Bump(atom/A)
if(!A)
diff --git a/code/modules/mob/living/carbon/human/human.dm b/code/modules/mob/living/carbon/human/human.dm
index 4624aa98441..686194c3d0b 100644
--- a/code/modules/mob/living/carbon/human/human.dm
+++ b/code/modules/mob/living/carbon/human/human.dm
@@ -188,7 +188,6 @@
popup.open()
// called when something steps onto a human
-// this could be made more general, but for now just handle mulebot
/mob/living/carbon/human/Crossed(atom/movable/AM)
. = ..()
spreadFire(AM)
diff --git a/code/modules/mob/living/living.dm b/code/modules/mob/living/living.dm
index cc70a3b97bd..11919c51e8b 100644
--- a/code/modules/mob/living/living.dm
+++ b/code/modules/mob/living/living.dm
@@ -228,10 +228,18 @@
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, dir_to_target, push_anchored))
push_anchored = TRUE
+ if(ismob(AM))
+ var/mob/mob_to_push = AM
+ var/atom/movable/mob_buckle = mob_to_push.buckled
+ // If we can't pull them because of what they're buckled to, make sure we can push the thing they're buckled to instead.
+ // If neither are true, we're not pushing anymore.
+ if(mob_buckle && (mob_buckle.buckle_prevents_pull || (force < (mob_buckle.move_resist * MOVE_FORCE_PUSH_RATIO))))
+ now_pushing = FALSE
+ return
if((AM.anchored && !push_anchored) || (force < (AM.move_resist * MOVE_FORCE_PUSH_RATIO)))
now_pushing = FALSE
return
- if (istype(AM, /obj/structure/window))
+ 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, dir_to_target))
diff --git a/code/modules/mob/living/simple_animal/bot/bot.dm b/code/modules/mob/living/simple_animal/bot/bot.dm
index c1c6cead2c3..e61485b7ad8 100644
--- a/code/modules/mob/living/simple_animal/bot/bot.dm
+++ b/code/modules/mob/living/simple_animal/bot/bot.dm
@@ -528,26 +528,26 @@ Pass a positive integer as an argument to override a bot's default speed.
if(step_count >= 1 && tries < BOT_STEP_MAX_RETRIES)
for(var/step_number = 0, step_number < step_count,step_number++)
- addtimer(CALLBACK(src, .proc/bot_step, dest), BOT_STEP_DELAY*step_number)
+ addtimer(CALLBACK(src, .proc/bot_step), BOT_STEP_DELAY*step_number)
else
return FALSE
return TRUE
-
-/mob/living/simple_animal/bot/proc/bot_step(dest) //Step,increase tries if failed
- if(!path)
+/// Performs a step_towards and increments the path if successful. Returns TRUE if the bot moved and FALSE otherwise.
+/mob/living/simple_animal/bot/proc/bot_step()
+ if(!length(path))
return FALSE
- if(path.len > 1)
- step_towards(src, path[1])
- if(get_turf(src) == path[1]) //Successful move
- increment_path()
- tries = 0
- else
- tries++
- return FALSE
- else if(path.len == 1)
- step_to(src, dest)
- set_path(null)
+
+ if(SEND_SIGNAL(src, COMSIG_MOB_BOT_PRE_STEP) & COMPONENT_MOB_BOT_BLOCK_PRE_STEP)
+ return FALSE
+
+ if(!step_towards(src, path[1]))
+ tries++
+ return FALSE
+
+ increment_path()
+ tries = 0
+ SEND_SIGNAL(src, COMSIG_MOB_BOT_STEP)
return TRUE
@@ -1075,12 +1075,15 @@ Pass a positive integer as an argument to override a bot's default speed.
/mob/living/simple_animal/bot/proc/increment_path()
- if(!path || !path.len)
+ if(!length(path))
return
var/image/I = path[path[1]]
if(I)
I.icon_state = null
path.Cut(1, 2)
+ if(!length(path))
+ set_path(null)
+
/mob/living/simple_animal/bot/rust_heretic_act()
adjustBruteLoss(400)
diff --git a/code/modules/mob/living/simple_animal/bot/mulebot.dm b/code/modules/mob/living/simple_animal/bot/mulebot.dm
index a79ea75efe8..6fe9c0a987c 100644
--- a/code/modules/mob/living/simple_animal/bot/mulebot.dm
+++ b/code/modules/mob/living/simple_animal/bot/mulebot.dm
@@ -60,6 +60,15 @@
/mob/living/simple_animal/bot/mulebot/Initialize(mapload)
. = ..()
+
+ RegisterSignal(src, COMSIG_MOB_BOT_PRE_STEP, .proc/check_pre_step)
+ RegisterSignal(src, COMSIG_MOB_CLIENT_PRE_MOVE, .proc/check_pre_step)
+ RegisterSignal(src, COMSIG_MOB_BOT_STEP, .proc/on_bot_step)
+ RegisterSignal(src, COMSIG_MOB_CLIENT_MOVED, .proc/on_bot_step)
+ RegisterSignal(src, COMSIG_MOVABLE_CROSSED_OVER, .proc/on_crossed_over)
+
+ ADD_TRAIT(src, TRAIT_NOMOBSWAP, INNATE_TRAIT)
+
if(prob(0.666) && mapload)
new /mob/living/simple_animal/bot/mulebot/paranormal(loc)
return INITIALIZE_HINT_QDEL
@@ -105,6 +114,7 @@
/mob/living/simple_animal/bot/mulebot/Destroy()
+ UnregisterSignal(src, COMSIG_MOB_BOT_PRE_STEP, COMSIG_MOB_CLIENT_PRE_MOVE, COMSIG_MOB_BOT_STEP, COMSIG_MOB_CLIENT_MOVED, COMSIG_MOVABLE_CROSSED_OVER)
unload(0)
QDEL_NULL(wires)
QDEL_NULL(cell)
@@ -118,13 +128,6 @@
return
return ..()
-/mob/living/simple_animal/bot/mulebot/Cross(atom/movable/AM)
- . = ..()
- if(ishuman(AM))
- RunOver(AM)
-
-
-
/// returns true if the bot is fully powered.
/mob/living/simple_animal/bot/mulebot/proc/has_power(bypass_open_check)
return (!open || bypass_open_check) && cell && cell.charge > 0 && (!wires.is_cut(WIRE_POWER1) && !wires.is_cut(WIRE_POWER2))
@@ -529,9 +532,6 @@
start()
/mob/living/simple_animal/bot/mulebot/Move(atom/newloc, direct) //handle leaving bloody tracks. can't be done via Moved() since that can end up putting the tracks somewhere BEFORE we get bloody.
- if(!has_power((client || paicard))) //turn off if we ran out of power.
- turn_off()
- return FALSE
if(!bloodiness) //important to check this first since Bump() is called in the Move() -> Entered() chain
return ..()
var/atom/oldLoc = loc
@@ -543,13 +543,20 @@
B.setDir(direct)
bloodiness--
-/mob/living/simple_animal/bot/mulebot/Moved() //make sure we always use power after moving.
+/**
+ * Signal handler for COMSIG_MOVABLE_CROSSED_OVER signals sent by this mulebot.
+ *
+ * Intended to be used to crush various things.
+ */
+/mob/living/simple_animal/bot/mulebot/proc/on_crossed_over(atom/movable/source, atom/movable/crossed_atom)
+ SIGNAL_HANDLER
+
+ if(ishuman(crossed_atom))
+ run_over(crossed_atom)
+
+/mob/living/simple_animal/bot/mulebot/Moved()
. = ..()
- if(!cell)
- return
- cell.use(cell_move_power_usage)
- if(cell.charge < cell_move_power_usage) //make sure we have enough power to move again, otherwise turn off.
- turn_off()
+
diag_hud_set_mulebotcell()
/mob/living/simple_animal/bot/mulebot/handle_automated_action()
@@ -588,12 +595,14 @@
path -= next
return
if(isturf(next))
+ if(SEND_SIGNAL(src, COMSIG_MOB_BOT_PRE_STEP) & COMPONENT_MOB_BOT_BLOCK_PRE_STEP)
+ return
var/oldloc = loc
var/moved = step_towards(src, next) // attempt to move
if(moved && oldloc!=loc) // successful move
+ SEND_SIGNAL(src, COMSIG_MOB_BOT_STEP)
blockcount = 0
path -= loc
-
if(destination == home_destination)
mode = BOT_GO_HOME
else
@@ -735,7 +744,7 @@
return ..()
// when mulebot is in the same loc
-/mob/living/simple_animal/bot/mulebot/proc/RunOver(mob/living/carbon/human/H)
+/mob/living/simple_animal/bot/mulebot/proc/run_over(mob/living/carbon/human/H)
log_combat(src, H, "run over", null, "(DAMTYPE: [uppertext(BRUTE)])")
H.visible_message("[src] drives over [H]!", \
"[src] drives over you!")
@@ -829,6 +838,23 @@
if(.)
visible_message("[src]'s safeties are locked on.")
+/// Checks whether the bot can complete a step_towards, checking whether the bot is on and has the charge to do the move. Returns COMPONENT_MOB_BOT_CANCELSTEP if the bot should not step.
+/mob/living/simple_animal/bot/mulebot/proc/check_pre_step(datum/source)
+ SIGNAL_HANDLER
+
+ if(!on)
+ return COMPONENT_MOB_BOT_BLOCK_PRE_STEP
+
+ if((cell && (cell.charge < cell_move_power_usage)) || !has_power((client || paicard)))
+ turn_off()
+ return COMPONENT_MOB_BOT_BLOCK_PRE_STEP
+
+/// Uses power from the cell when the bot steps.
+/mob/living/simple_animal/bot/mulebot/proc/on_bot_step(datum/source)
+ SIGNAL_HANDLER
+
+ cell?.use(cell_move_power_usage)
+
/mob/living/simple_animal/bot/mulebot/paranormal//allows ghosts only unless hacked to actually be useful
name = "\improper GHOULbot"
desc = "A rather ghastly looking... Multiple Utility Load Effector bot? It only seems to accept paranormal forces, and for this reason is fucking useless."
@@ -883,7 +909,6 @@
mode = BOT_IDLE
update_appearance()
-
/mob/living/simple_animal/bot/mulebot/paranormal/update_overlays()
. = ..()
if(!isobserver(load))
@@ -909,3 +934,4 @@
/obj/machinery/bot_core/mulebot
req_access = list(ACCESS_CARGO)
+
diff --git a/code/modules/mob/mob_movement.dm b/code/modules/mob/mob_movement.dm
index 7aecab4da50..7df1a38839f 100644
--- a/code/modules/mob/mob_movement.dm
+++ b/code/modules/mob/mob_movement.dm
@@ -66,7 +66,7 @@
* (if you ask me, this should be at the top of the move so you don't dance around)
*
*/
-/client/Move(n, direct)
+/client/Move(new_loc, direct)
if(world.time < move_delay) //do not move anything ahead of this check please
return FALSE
else
@@ -76,14 +76,14 @@
move_delay = world.time + world.tick_lag //this is here because Move() can now be called mutiple times per tick
if(!mob || !mob.loc)
return FALSE
- if(!n || !direct)
+ if(!new_loc || !direct)
return FALSE
if(mob.notransform)
return FALSE //This is sota the goto stop mobs from moving var
if(mob.control_object)
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 FALSE
@@ -99,7 +99,7 @@
return mob.remote_control.relaymove(mob, direct)
if(isAI(mob))
- return AIMove(n,direct,mob)
+ return AIMove(new_loc,direct,mob)
if(Process_Grab()) //are we restrained by someone's grip?
return
@@ -116,6 +116,10 @@
if(!mob.Process_Spacemove(direct))
return FALSE
+
+ if(SEND_SIGNAL(mob, COMSIG_MOB_CLIENT_PRE_MOVE, new_loc) & COMSIG_MOB_CLIENT_BLOCK_PRE_MOVE)
+ return FALSE
+
//We are now going to move
var/add_delay = mob.cached_multiplicative_slowdown
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay * ( (NSCOMPONENT(direct) && EWCOMPONENT(direct)) ? 2 : 1 ) )) // set it now in case of pulled objects
@@ -135,11 +139,11 @@
newdir = angle2dir(dir2angle(direct) + pick(45, -45))
if(newdir)
direct = newdir
- n = get_step(L, direct)
+ new_loc = get_step(L, direct)
. = ..()
- if((direct & (direct - 1)) && mob.loc == n) //moved diagonally successfully
+ if((direct & (direct - 1)) && mob.loc == new_loc) //moved diagonally successfully
add_delay *= 2
mob.set_glide_size(DELAY_TO_GLIDE_SIZE(add_delay))
move_delay += add_delay
@@ -147,6 +151,10 @@
if(mob.throwing)
mob.throwing.finalize(FALSE)
+ // 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)
+
var/atom/movable/P = mob.pulling
if(P && !ismob(P) && P.density)
mob.setDir(turn(mob.dir, 180))