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