Mulebots are no longer bugged into horrific, merciless machines of gore and destruction. (#57378)

About The Pull Request

In https://github.com/tgstation/tgstation/pull/57010/files#diff-08a9c12575e75bb692c000da3f7eea30c0340da26171c5c788fd2b17afef92b5 Floyd did an undocumented change to Mulebot behaviour.

He moved crushing from the /human/Crossed level to mulebot/Cross.

The problem is:
image

Cross (and Crossed) are called when some other object Crosses us, not when we Cross some other object.

This meant that... Well... Walking into a mulebot was deadly. Buckling to a mulebot was deadly.
lGrnThJEWG

And being buckled to a mulebot that ended up moving was deadly multiplied by the number of times the mulebot moved.
QJNXqBHZ8m

Also why can you push mulebots when things are buckled to it? That seems like an oversight.

I added new signals to the atom/movable/...Cross... series of procs that are sent from the thing crossing our AM. The thing crossing our AM can then listen for that signal on itself, and it now knows when it's crossing something! BOOM! CRUSH TIME!

I've also added a check to /mob/living/proc/PushAM to prevent pushing an AM buckled to a thing when you wouldn't be able to pull them. This check also takes into account if you'd just have the raw movement force to push the thing they're buckled to as well. This should put a stop to being able to push mulebots around when they have mobs buckled to them.

Also, you can swap places with active Mulebots. This is probably not intended. I've added TRAIT_NOMOBSWAP to them.

The end result? Mulebots that just work.


And don't worry, you can still crawl under them.

Why It's Good For The Game

Feex.
Changelog

🆑
fix: Mulebots no longer murder people just because that person dared to bump into them.
/🆑
This commit is contained in:
Timberpoes
2021-03-20 11:50:34 +13:00
committed by GitHub
parent 1ce004b70c
commit f12cc4ff0a
7 changed files with 115 additions and 43 deletions
+23
View File
@@ -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)
+5
View File
@@ -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)
@@ -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)
+9 -1
View File
@@ -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))
@@ -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)
@@ -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("<span class='danger'>[src] drives over [H]!</span>", \
"<span class='userdanger'>[src] drives over you!</span>")
@@ -829,6 +838,23 @@
if(.)
visible_message("<span class='notice'>[src]'s safeties are locked on.</span>")
/// 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)
+14 -6
View File
@@ -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))