From 285d4c25c21c0137a671e9fa70d5ccc66cfe8a7f Mon Sep 17 00:00:00 2001 From: MrMelbert <51863163+MrMelbert@users.noreply.github.com> Date: Thu, 6 Jul 2023 22:50:02 -0500 Subject: [PATCH] Robot suit hard-del / exited cleanup (#76583) ## About The Pull Request Constructed cyborgs seemed to hard delete pretty consistently due to their robot suit having poor reference handing Goes through and better implements exited for cyborgs and robot suits Also a hard delete with forced AIs that may have resulted in some metagaming Also fixes 1 nodrop check for hats ## Why It's Good For The Game Hard deletes are bad ## Changelog :cl: Melbert fix: Fixed some hard deletes involving constructed cyborgs fix: Cyborgs with nodrop hats no longer lose them on tip /:cl: --------- Co-authored-by: san7890 --- code/game/objects/items/robot/robot_parts.dm | 125 +++++++++++++----- .../modules/mob/living/silicon/robot/robot.dm | 42 ++---- .../mob/living/silicon/robot/robot_defense.dm | 1 - .../mob/living/silicon/robot/robot_model.dm | 2 +- .../surgery/bodyparts/robot_bodyparts.dm | 44 ++---- 5 files changed, 117 insertions(+), 97 deletions(-) diff --git a/code/game/objects/items/robot/robot_parts.dm b/code/game/objects/items/robot/robot_parts.dm index 66bffa7a0e2..f10af1c628a 100644 --- a/code/game/objects/items/robot/robot_parts.dm +++ b/code/game/objects/items/robot/robot_parts.dm @@ -21,8 +21,13 @@ var/obj/item/bodypart/head/robot/head = null /// Forced name of the cyborg var/created_name = "" + /// Forced master AI of the cyborg var/mob/living/silicon/ai/forced_ai + /// The name of the AI being forced, tracked separately to above + /// so we can reference handle without worrying about making "AI got gibbed" detectors + var/forced_ai_name + /// If the cyborg starts movement free and not under lockdown var/locomotion = TRUE /// If the cyborg synchronizes it's laws with it's master AI @@ -36,6 +41,30 @@ . = ..() update_appearance() +/obj/item/robot_suit/Destroy() + QDEL_NULL(l_arm) + QDEL_NULL(r_arm) + QDEL_NULL(l_leg) + QDEL_NULL(r_leg) + QDEL_NULL(chest) + QDEL_NULL(head) + return ..() + +/obj/item/robot_suit/Exited(atom/movable/gone, direction) + . = ..() + if(gone == l_arm) + l_arm = null + if(gone == r_arm) + r_arm = null + if(gone == l_leg) + l_leg = null + if(gone == r_leg) + r_leg = null + if(gone == chest) + chest = null + if(gone == head) + head = null + /obj/item/robot_suit/prebuilt/Initialize(mapload) . = ..() l_arm = new(src) @@ -76,38 +105,31 @@ var/turf/T = get_turf(src) if(l_leg || r_leg || chest || l_arm || r_arm || head) if(I.use_tool(src, user, 5, volume=50)) - if(l_leg) - l_leg.forceMove(T) - l_leg = null - if(r_leg) - r_leg.forceMove(T) - r_leg = null - if(chest) - if (chest.cell) //Sanity check. - chest.cell.forceMove(T) - chest.cell = null - chest.forceMove(T) - new /obj/item/stack/cable_coil(T, 1) - chest.wired = FALSE - chest = null - if(l_arm) - l_arm.forceMove(T) - l_arm = null - if(r_arm) - r_arm.forceMove(T) - r_arm = null - if(head) - head.forceMove(T) - head.flash1.forceMove(T) - head.flash1 = null - head.flash2.forceMove(T) - head.flash2 = null - head = null + drop_all_parts(T) to_chat(user, span_notice("You disassemble the cyborg shell.")) else to_chat(user, span_warning("There is nothing to remove from the endoskeleton!")) update_appearance() +/// Drops all included parts to the passed location +/// This will also dissassemble the parts being dropped into components as well +/obj/item/robot_suit/proc/drop_all_parts(atom/drop_to = drop_location()) + l_leg?.forceMove(drop_to) + r_leg?.forceMove(drop_to) + l_arm?.forceMove(drop_to) + r_arm?.forceMove(drop_to) + + if(chest) + chest.forceMove(drop_to) + new /obj/item/stack/cable_coil(drop_to, 1) + chest.wired = FALSE + chest.cell?.forceMove(drop_to) + + if(head) + head.flash1?.forceMove(drop_to) + head.flash2?.forceMove(drop_to) + head.forceMove(drop_to) + /obj/item/robot_suit/proc/put_in_hand_or_drop(mob/living/user, obj/item/I) //normal put_in_hands() drops the item ontop of the player, this drops it at the suit's loc if(!user.put_in_hands(I)) I.forceMove(drop_location()) @@ -124,7 +146,7 @@ return var/obj/item/stock_parts/cell/temp_cell = user.is_holding_item_of_type(/obj/item/stock_parts/cell) - var/swap_failed + var/swap_failed = FALSE if(!temp_cell) //if we're not holding a cell swap_failed = TRUE else if(!user.transferItemToLoc(temp_cell, chest)) @@ -292,7 +314,7 @@ O.cell = chest.cell chest.cell.forceMove(O) - chest.cell = null + W.forceMove(O)//Should fix cybros run time erroring when blown up. It got deleted before, along with the frame. if(O.mmi) //we delete the mmi created by robot/New() qdel(O.mmi) @@ -347,7 +369,7 @@ O.cell = chest.cell chest.cell.forceMove(O) - chest.cell = null + O.locked = panel_locked O.job = JOB_CYBORG forceMove(O) @@ -384,7 +406,7 @@ data["locomotion"] = locomotion data["panel"] = panel_locked data["aisync"] = aisync - data["master"] = forced_ai ? forced_ai.name : null + data["master"] = forced_ai_name data["lawsync"] = lawsync return data @@ -417,16 +439,47 @@ log_silicon("[key_name(user)] has [aisync ? "enabled" : "disabled"] the AI sync for a cyborg shell at [loc_name(user)]") return TRUE if("set_ai") - var/selected_ai = select_active_ai(user, z) - if(!in_range(src, user) && loc != user) - return - if(!selected_ai) + if(length(active_ais(check_mind = FALSE, z = z)) <= 0) to_chat(user, span_alert("No active AIs detected.")) return - forced_ai = selected_ai + + var/selected_ai = select_active_ai(user, z) // this one runs input() + if(!in_range(src, user) && loc != user) + return + if(!selected_ai) // null = clear + clear_forced_ai() + return TRUE + if(forced_ai == selected_ai) // same AI = clear + clear_forced_ai() + to_chat(user, span_notice("You reset [src]'s AI setting.")) + return TRUE + + set_forced_ai(selected_ai, user) + to_chat(user, span_notice("You set [src]'s AI setting to [forced_ai_name].")) log_silicon("[key_name(user)] set the default AI for a cyborg shell to [key_name(selected_ai)] at [loc_name(user)]") return TRUE + if("lawsync") lawsync = !lawsync log_silicon("[key_name(user)] has [lawsync ? "enabled" : "disabled"] the law sync for a cyborg shell at [loc_name(user)]") return TRUE + +/// Sets [forced_ai] and [forced_ai_name] to the passed AI +/obj/item/robot_suit/proc/set_forced_ai(mob/living/silicon/ai/ai) + forced_ai = ai + forced_ai_name = ai.name + RegisterSignal(ai, COMSIG_QDELETING, PROC_REF(ai_die)) + +/// Clears [forced_ai] and [forced_ai_name] +/obj/item/robot_suit/proc/clear_forced_ai() + if(forced_ai) + UnregisterSignal(forced_ai, COMSIG_QDELETING) + forced_ai = null + forced_ai_name = null + +/// Clears the forced_ai ref +/obj/item/robot_suit/proc/ai_die(datum/source) + SIGNAL_HANDLER + // Does not use [proc/clear_forced_ai] because we'd like to keep the AI name tracked for metagaming purposes + UnregisterSignal(forced_ai, COMSIG_QDELETING) + forced_ai = null diff --git a/code/modules/mob/living/silicon/robot/robot.dm b/code/modules/mob/living/silicon/robot/robot.dm index 8399a85e1c6..4935c7aea38 100644 --- a/code/modules/mob/living/silicon/robot/robot.dm +++ b/code/modules/mob/living/silicon/robot/robot.dm @@ -162,7 +162,8 @@ QDEL_NULL(spark_system) QDEL_NULL(alert_control) QDEL_LIST(upgrades) - cell = null + QDEL_NULL(cell) + QDEL_NULL(robot_suit) return ..() /mob/living/silicon/robot/Topic(href, href_list) @@ -302,8 +303,9 @@ /mob/living/silicon/robot/proc/after_tip_over(mob/user) - if(hat) + if(hat && !HAS_TRAIT(hat, TRAIT_NODROP)) hat.forceMove(drop_location()) + unbuckle_all_mobs() ///For any special cases for robots after being righted. @@ -529,28 +531,8 @@ undeploy() var/turf/T = get_turf(src) if (robot_suit) - robot_suit.forceMove(T) - robot_suit.l_leg.forceMove(T) - robot_suit.l_leg = null - robot_suit.r_leg.forceMove(T) - robot_suit.r_leg = null - new /obj/item/stack/cable_coil(T, robot_suit.chest.wired) - robot_suit.chest.forceMove(T) - robot_suit.chest.wired = FALSE - robot_suit.chest = null - robot_suit.l_arm.forceMove(T) - robot_suit.l_arm = null - robot_suit.r_arm.forceMove(T) - robot_suit.r_arm = null - robot_suit.head.forceMove(T) - robot_suit.head.flash1.forceMove(T) - robot_suit.head.flash1.burn_out() - robot_suit.head.flash1 = null - robot_suit.head.flash2.forceMove(T) - robot_suit.head.flash2.burn_out() - robot_suit.head.flash2 = null - robot_suit.head = null - robot_suit.update_appearance() + robot_suit.drop_all_parts(T) + else new /obj/item/robot_suit(T) new /obj/item/bodypart/leg/left/robot(T) @@ -564,9 +546,8 @@ for(b=0, b != 2, b++) var/obj/item/assembly/flash/handheld/F = new /obj/item/assembly/flash/handheld(T) F.burn_out() - if (cell) //Sanity check. - cell.forceMove(T) - cell = null + + cell?.forceMove(T) // Cell can be null, if removed beforehand qdel(src) /mob/living/silicon/robot/proc/notify_ai(notifytype, oldname, newname) @@ -773,11 +754,14 @@ *Drones and pAIs might do this, after all. */ /mob/living/silicon/robot/Exited(atom/movable/gone, direction) - if(hat && hat == gone) + . = ..() + if(hat == gone) hat = null if(!QDELETED(src)) //Don't update icons if we are deleted. update_icons() - return ..() + + if(gone == cell) + cell = null ///Use this to add upgrades to robots. It'll register signals for when the upgrade is moved or deleted, if not single use. /mob/living/silicon/robot/proc/add_to_upgrades(obj/item/borg/upgrade/new_upgrade, mob/user) diff --git a/code/modules/mob/living/silicon/robot/robot_defense.dm b/code/modules/mob/living/silicon/robot/robot_defense.dm index 499ad7530e8..f8f4c0bdad6 100644 --- a/code/modules/mob/living/silicon/robot/robot_defense.dm +++ b/code/modules/mob/living/silicon/robot/robot_defense.dm @@ -234,7 +234,6 @@ GLOBAL_LIST_INIT(blacklisted_borg_hats, typecacheof(list( //Hats that don't real cell.add_fingerprint(user) user.put_in_active_hand(cell) to_chat(user, span_notice("You remove \the [cell].")) - cell = null update_icons() diag_hud_set_borgcell() diff --git a/code/modules/mob/living/silicon/robot/robot_model.dm b/code/modules/mob/living/silicon/robot/robot_model.dm index deab04e0d8c..41800f96ed5 100644 --- a/code/modules/mob/living/silicon/robot/robot_model.dm +++ b/code/modules/mob/living/silicon/robot/robot_model.dm @@ -265,7 +265,7 @@ var/mob/living/silicon/robot/cyborg = loc if(cyborg.hat) cyborg.hat.forceMove(drop_location()) - cyborg.hat = null + cyborg.cut_overlays() cyborg.setDir(SOUTH) do_transform_delay() diff --git a/code/modules/surgery/bodyparts/robot_bodyparts.dm b/code/modules/surgery/bodyparts/robot_bodyparts.dm index 7328b3b85b8..03f99912347 100644 --- a/code/modules/surgery/bodyparts/robot_bodyparts.dm +++ b/code/modules/surgery/bodyparts/robot_bodyparts.dm @@ -195,10 +195,10 @@ /obj/item/bodypart/chest/robot/get_cell() return cell -/obj/item/bodypart/chest/robot/handle_atom_del(atom/chest_atom) - if(chest_atom == cell) +/obj/item/bodypart/chest/robot/Exited(atom/movable/gone, direction) + . = ..() + if(gone == cell) cell = null - return ..() /obj/item/bodypart/chest/robot/Destroy() QDEL_NULL(cell) @@ -246,8 +246,6 @@ screwtool.play_tool_sound(src) to_chat(user, span_notice("Remove [cell] from [src].")) cell.forceMove(drop_location()) - cell = null - /obj/item/bodypart/chest/robot/examine(mob/user) . = ..() @@ -267,11 +265,8 @@ if(wired) new /obj/item/stack/cable_coil(drop_loc, 1) wired = FALSE - if(cell) - cell.forceMove(drop_loc) - cell = null - ..() - + cell?.forceMove(drop_loc) + return ..() /obj/item/bodypart/head/robot name = "cyborg head" @@ -322,12 +317,12 @@ #undef EMP_GLITCH -/obj/item/bodypart/head/robot/handle_atom_del(atom/head_atom) - if(head_atom == flash1) +/obj/item/bodypart/head/robot/Exited(atom/movable/gone, direction) + . = ..() + if(gone == flash1) flash1 = null - if(head_atom == flash2) + if(gone == flash2) flash2 = null - return ..() /obj/item/bodypart/head/robot/Destroy() QDEL_NULL(flash1) @@ -373,28 +368,17 @@ if(flash1 || flash2) prytool.play_tool_sound(src) to_chat(user, span_notice("You remove the flash from [src].")) - if(flash1) - flash1.forceMove(drop_location()) - flash1 = null - if(flash2) - flash2.forceMove(drop_location()) - flash2 = null + flash1?.forceMove(drop_location()) + flash2?.forceMove(drop_location()) else to_chat(user, span_warning("There is no flash to remove from [src].")) return TRUE - /obj/item/bodypart/head/robot/drop_organs(mob/user, violent_removal) var/atom/drop_loc = drop_location() - if(flash1) - flash1.forceMove(drop_loc) - flash1 = null - if(flash2) - flash2.forceMove(drop_loc) - flash2 = null - ..() - - + flash1?.forceMove(drop_loc) + flash2?.forceMove(drop_loc) + return ..() /obj/item/bodypart/arm/left/robot/surplus