From f12cc4ff0acb3f105e91033a72227011cd92ce78 Mon Sep 17 00:00:00 2001 From: Timberpoes Date: Fri, 19 Mar 2021 22:50:34 +0000 Subject: [PATCH] Mulebots are no longer bugged into horrific, merciless machines of gore and destruction. (#57378) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. /🆑 --- code/__DEFINES/dcs/signals.dm | 23 +++++++ code/game/atoms_movable.dm | 5 ++ code/modules/mob/living/carbon/human/human.dm | 1 - code/modules/mob/living/living.dm | 10 ++- .../mob/living/simple_animal/bot/bot.dm | 35 +++++----- .../mob/living/simple_animal/bot/mulebot.dm | 64 +++++++++++++------ code/modules/mob/mob_movement.dm | 20 ++++-- 7 files changed, 115 insertions(+), 43 deletions(-) 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))