From 1b54762d3650df0c4d558d71452d29deb4f2b26b Mon Sep 17 00:00:00 2001 From: Batrachophreno Date: Mon, 1 Jun 2026 06:46:56 -0400 Subject: [PATCH] Hardsuit fixes (#22519) Various fixes and cleanups for hardsuits following the rework. changes: - bugfix: "Adds additional safety checks to hardsuit deployment logic to better handle boot checks." - bugfix: "Hardsuit Health Analyzer module works again." - rscadd: "Adds 'Retract All Parts' verb for hardsuits." - rscdel: "Removes Vitals Scanner module; hardsuits which had this module by default have had it replaced with the Health Analzyer module." - rscdel: "Removes hardsuit Maneuvering Jets onmob sprites." --- .../components/medical/medicalAnalyzer.dm | 9 ++++ code/datums/uplink/hardsuits_and_modules.dm | 7 --- .../spacesuits/rig/modules/utility.dm | 47 ++++++++--------- code/modules/clothing/spacesuits/rig/rig.dm | 48 +++++++++++++++++- .../clothing/spacesuits/rig/rig_verbs.dm | 21 ++++++++ .../clothing/spacesuits/rig/suits/light.dm | 8 +-- html/changelogs/Bat-HardsuitFixes.yml | 8 +++ icons/mob/rig_modules.dmi | Bin 5740 -> 4399 bytes 8 files changed, 110 insertions(+), 38 deletions(-) create mode 100644 html/changelogs/Bat-HardsuitFixes.yml diff --git a/code/datums/components/medical/medicalAnalyzer.dm b/code/datums/components/medical/medicalAnalyzer.dm index 6fdb128836c..0a970b2e58f 100644 --- a/code/datums/components/medical/medicalAnalyzer.dm +++ b/code/datums/components/medical/medicalAnalyzer.dm @@ -84,6 +84,15 @@ reagent_results = list() return TRUE +/datum/component/health_analyzer/ui_status(mob/user, datum/ui_state/state) + var/obj/item/rig_module/containing_rig_module = owner.loc + if(!containing_rig_module) + return ..() + if(containing_rig_module?.holder?.wearer == user) + return UI_INTERACTIVE + + return UI_CLOSE + /datum/component/health_analyzer/proc/attack(mob/living/target_mob, mob/living/user, target_zone) sound_scan = TRUE diff --git a/code/datums/uplink/hardsuits_and_modules.dm b/code/datums/uplink/hardsuits_and_modules.dm index b2458049dd3..faf5c820eaa 100644 --- a/code/datums/uplink/hardsuits_and_modules.dm +++ b/code/datums/uplink/hardsuits_and_modules.dm @@ -104,13 +104,6 @@ path = /obj/item/rig_module/storage desc = "A small hardsuit mounted storage unit, capable of holding a few items." -/datum/uplink_item/item/hardsuit_modules/vitalscanner - name = "Vitals Tracker Module" - telecrystal_cost = 1 - bluecrystal_cost = 1 - path = /obj/item/rig_module/device/healthscanner/vitalscanner - desc = "A module that allows a hardsuit to scan the users vitals, and give readouts of them." - /datum/uplink_item/item/hardsuit_modules/basicinjector name = "Emergency Chemical Injector" telecrystal_cost = 2 diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm index f81510d7289..6d76f923b3d 100644 --- a/code/modules/clothing/spacesuits/rig/modules/utility.dm +++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm @@ -42,6 +42,15 @@ . = ..() +/obj/item/rig_module/device/proc/handle_device_engage(atom/target, mob/user) + var/resolved = target.attackby(device, user) + if(!resolved && device && target) + if(device.afterattack(target, user, TRUE)) + return TRUE + else + return FALSE + return TRUE + /obj/item/rig_module/device/healthscanner name = "health scanner module" desc = "A hardsuit-mounted health scanner." @@ -51,19 +60,25 @@ construction_cost = list("$glass" = 5250, DEFAULT_WALL_MATERIAL = 2500) construction_time = 300 - engage_string = "Run Self-Diagnostic" + engage_string = "Run Diagnostic" device_type = /obj/item/healthanalyzer category = MODULE_MEDICAL -/obj/item/rig_module/device/healthscanner/vitalscanner - name = "integrated vitals tracker" - desc = "A hardsuit-mounted vitals tracker." - interface_name = "vitals tracker" - interface_desc = "Shows an informative health readout of the user." +/obj/item/rig_module/device/healthscanner/engage(atom/target, mob/user) + if(!..() || !device) + return FALSE - category = MODULE_GENERAL + if(!target.Adjacent(user)) + return FALSE + + var/obj/item/healthanalyzer/our_device = device + if(!target || user == target) + our_device.attack_self(user) + return TRUE + + return handle_device_engage(target, user) /obj/item/rig_module/device/drill name = "hardsuit diamond drill mount" @@ -158,15 +173,6 @@ if("rfd_settings") our_device.attack_self(user) -/obj/item/rig_module/device/rfd_c/handle_device_engage(atom/target, mob/user) - var/resolved = target.attackby(device, user) - if(!resolved && device && target) - if(device.afterattack(target, user, TRUE)) - return TRUE - else - return FALSE - return TRUE - /obj/item/rig_module/device/rfd_c/engage(atom/target, mob/user) if(!..() || !device) return FALSE @@ -186,12 +192,6 @@ return handle_device_engage(target, user) -/obj/item/rig_module/device/proc/handle_device_engage(atom/target, mob/user) - var/resolved = target.attackby(device, user) - if(!resolved && device && target) - device.afterattack(target, user, TRUE) - return TRUE - /obj/item/rig_module/chem_dispenser name = "mounted chemical dispenser" desc = "A complex web of tubing and needles suitable for hardsuit use." @@ -477,9 +477,6 @@ construction_cost = list(DEFAULT_WALL_MATERIAL = 15000, MATERIAL_GLASS = 4250, MATERIAL_SILVER = 4250, MATERIAL_URANIUM = 5250) construction_time = 300 - suit_overlay_active = "maneuvering_active" - suit_overlay_inactive = "maneuvering_inactive" - interface_name = "maneuvering jets" interface_desc = "An inbuilt EVA maneuvering system that runs off the hardsuit air supply." diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm index c2787e31c82..4a70736f1ab 100644 --- a/code/modules/clothing/spacesuits/rig/rig.dm +++ b/code/modules/clothing/spacesuits/rig/rig.dm @@ -100,6 +100,8 @@ var/malfunctioning = 0 var/malfunction_delay = 0 var/electrified = 0 + /// Ensures that you can't queue up multiple piece toggle commands at once and bug anything out. + var/piece_being_deployed = FALSE /// Rate at which the suit cell will passively use power when online. var/cell_draw_rate = CHARGE_DRAIN_DEFAULT @@ -842,6 +844,8 @@ return TRUE /obj/item/rig/proc/toggle_piece(var/piece, var/mob/initiator, var/deploy_mode) + if(piece_being_deployed) + return if(!usr || sealing || !cell || !cell.charge) return @@ -877,6 +881,7 @@ use_obj = chest check_slot = wearer.wear_suit + piece_being_deployed = TRUE if(use_obj) // Handle retracting the piece, if possible. if(check_slot == use_obj && deploy_mode != ONLY_DEPLOY) @@ -890,6 +895,7 @@ if(use_obj == boots && chest_slot == chest_obj) to_chat(wearer, SPAN_WARNING("The chest piece of the hardsuit must be retracted before you can retract your boots!")) sound_to(wearer, 'sound/machines/terminal/terminal_error.ogg') + piece_being_deployed = FALSE return if(check_slot == use_obj) @@ -904,16 +910,21 @@ // Handle deploying the piece, if possible. else if (deploy_mode != ONLY_RETRACT) if(check_slot && check_slot == use_obj) + piece_being_deployed = FALSE return TRUE if(!do_after(wearer, 8)) if(wearer) to_chat(wearer, SPAN_WARNING("You must remain still while the suit deploys its parts.")) + piece_being_deployed = FALSE return FALSE // If we're deploying the chest, we also try to deploy boots. If we can't also deploy boots, the entire thing fails. if(use_obj == chest) - if(!toggle_piece("boots", initiator, ONLY_DEPLOY)) + var/obj/item/boots_slot = wearer.shoes + var/obj/item/boots_obj = boots + if(boots_slot != boots_obj) to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as the boots were unable to also deploy!")) playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE) + piece_being_deployed = FALSE return FALSE use_obj.forceMove(wearer) if(src.color) @@ -923,6 +934,7 @@ if(check_slot) to_chat(initiator, SPAN_DANGER("You are unable to deploy \the [piece] as \the [check_slot] [check_slot.gender == PLURAL ? "are" : "is"] in the way.")) playsound(src, 'sound/items/rfd_empty.ogg', 20, FALSE) + piece_being_deployed = FALSE return FALSE else to_chat(wearer, SPAN_NOTICE("Your [use_obj.name] [use_obj.gender == PLURAL ? "deploy" : "deploys"] swiftly.")) @@ -933,10 +945,42 @@ update_icon(TRUE) + piece_being_deployed = FALSE return TRUE /obj/item/rig/proc/deploy(mob/M,var/sealed) + var/mob/living/carbon/human/H = M + if(!H || !istype(H)) return + + if(H.back != src) + return + + if(sealed) + if(H.head) + var/obj/item/garbage = H.head + H.head = null + qdel(garbage) + + if(H.gloves) + var/obj/item/garbage = H.gloves + H.gloves = null + qdel(garbage) + + if(H.shoes) + var/obj/item/garbage = H.shoes + H.shoes = null + qdel(garbage) + + if(H.wear_suit) + var/obj/item/garbage = H.wear_suit + H.wear_suit = null + qdel(garbage) + + for(var/piece in list("helmet","gauntlets","boots",BP_CHEST)) + toggle_piece(piece, H, ONLY_DEPLOY) + +/obj/item/rig/proc/retract(mob/M,var/sealed) var/mob/living/carbon/human/H = M if(!H || !istype(H)) return @@ -966,7 +1010,7 @@ qdel(garbage) for(var/piece in list("helmet","gauntlets",BP_CHEST,"boots")) - toggle_piece(piece, H, ONLY_DEPLOY) + toggle_piece(piece, H, ONLY_RETRACT) /obj/item/rig/proc/null_wearer(var/mob/user) for(var/piece in list("helmet","gauntlets",BP_CHEST,"boots")) diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm index 42e6d4303e0..b5ed0d6e5ef 100644 --- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm +++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm @@ -130,6 +130,27 @@ deploy(wearer) +/obj/item/rig/verb/retract_suit() + set name = "Retract All Hardsuit Parts" + set desc = "Retracts helmet, gloves and boots." + set category = "Hardsuit.Core Functions" + set src = usr.contents + + if(!istype(wearer) || !wearer.back == src) + to_chat(usr, SPAN_WARNING("The hardsuit is not being worn.")) + return + + if(use_check_and_message(usr)) + return + + if(!check_suit_access(usr)) + return + + if(!check_power_cost(usr)) + return + + deploy(wearer) + /obj/item/rig/verb/toggle_seals_verb() set name = "Engage/Disengage Hardsuit" set desc = "Activates or deactivates your hardsuit." diff --git a/code/modules/clothing/spacesuits/rig/suits/light.dm b/code/modules/clothing/spacesuits/rig/suits/light.dm index c0e55fe7fc3..208a5e9c053 100644 --- a/code/modules/clothing/spacesuits/rig/suits/light.dm +++ b/code/modules/clothing/spacesuits/rig/suits/light.dm @@ -223,7 +223,7 @@ boot_type = null initial_modules = list( - /obj/item/rig_module/device/healthscanner/vitalscanner, + /obj/item/rig_module/device/healthscanner, /obj/item/rig_module/chem_dispenser/offworlder, /obj/item/rig_module/storage ) @@ -278,7 +278,7 @@ ) initial_modules = list( - /obj/item/rig_module/device/healthscanner/vitalscanner, + /obj/item/rig_module/device/healthscanner, /obj/item/rig_module/chem_dispenser/offworlder, /obj/item/rig_module/actuators/combat, /obj/item/rig_module/chem_dispenser/combat @@ -293,7 +293,7 @@ /obj/item/rig/light/offworlder/frontier/equipped initial_modules = list( - /obj/item/rig_module/device/healthscanner/vitalscanner, + /obj/item/rig_module/device/healthscanner, /obj/item/rig_module/chem_dispenser/offworlder, /obj/item/rig_module/actuators/combat, /obj/item/rig_module/fabricator/energy_net, @@ -302,7 +302,7 @@ ) /obj/item/rig/light/offworlder/frontier/ninja initial_modules = list( - /obj/item/rig_module/device/healthscanner/vitalscanner, + /obj/item/rig_module/device/healthscanner, /obj/item/rig_module/chem_dispenser/offworlder, /obj/item/rig_module/actuators/combat, /obj/item/rig_module/fabricator/energy_net, diff --git a/html/changelogs/Bat-HardsuitFixes.yml b/html/changelogs/Bat-HardsuitFixes.yml new file mode 100644 index 00000000000..e00295b975b --- /dev/null +++ b/html/changelogs/Bat-HardsuitFixes.yml @@ -0,0 +1,8 @@ +author: Batrachophrenoboocosmomachia +delete-after: True +changes: + - bugfix: "Adds additional safety checks to hardsuit deployment logic to better handle boot checks." + - bugfix: "Hardsuit Health Analyzer module works again." + - rscadd: "Adds 'Retract All Parts' verb for hardsuits." + - rscdel: "Removes Vitals Scanner module; hardsuits which had this module by default have had it replaced with the Health Analzyer module." + - rscdel: "Removes hardsuit Maneuvering Jets onmob sprites." diff --git a/icons/mob/rig_modules.dmi b/icons/mob/rig_modules.dmi index f921263c7548734f37a188244eb494e8b9846691..a184129e69208ed8153558ace0eb977102b1a50b 100644 GIT binary patch literal 4399 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 U^g-v;;r|e$8!%|eb>~O_1$cKmP5=M^ literal 5740 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