diff --git a/code/game/objects/items/weapons/storage/internal.dm b/code/game/objects/items/weapons/storage/internal.dm
index 698bf98f632..6a1f25f08dc 100644
--- a/code/game/objects/items/weapons/storage/internal.dm
+++ b/code/game/objects/items/weapons/storage/internal.dm
@@ -21,15 +21,20 @@
/obj/item/storage/internal/mob_can_equip(M, slot, disable_warning = FALSE, bypass_blocked_check = FALSE, is_overlay_check = FALSE)
return 0 //make sure this is never picked up
-//Helper procs to cleanly implement internal storages - storage items that provide inventory slots for other items.
-//These procs are completely optional, it is up to the master item to decide when it's storage get's opened by calling open()
-//However they are helpful for allowing the master item to pretend it is a storage item itself.
-//If you are using these you will probably want to override attackby() as well.
-//See /obj/item/clothing/suit/storage for an example.
-
-//items that use internal storage have the option of calling this to emulate default storage MouseDrop behaviour.
-//returns 1 if the master item's parent's MouseDrop() should be called, 0 otherwise. It's strange, but no other way of
-//doing it without the ability to call another proc's parent, really.
+/**
+ * Helper procs to cleanly implement internal storages - storage items that provide inventory slots for other items.
+ * These procs are completely optional, it is up to the master item to decide when it's storage get's opened by calling open()
+ *
+ * However they are helpful for allowing the master item to pretend it is a storage item itself.
+ *
+ * If you are using these you will probably want to override attackby() as well.
+ * See /obj/item/clothing/suit/storage for an example.
+ *
+ * items that use internal storage have the option of calling this to emulate default storage MouseDrop behaviour.
+ *
+ * Returns 1 if the master item's parent's MouseDrop() should be called, 0 otherwise.
+ * It's strange, but no other way of doing it without the ability to call another proc's parent, really.
+ */
/obj/item/storage/internal/proc/handle_mousedrop(mob/user as mob, obj/over_object as obj)
if (ishuman(user) || issmall(user)) //so monkeys can take off their backpacks -- Urist
@@ -48,20 +53,22 @@
return 0
if (!( user.restrained() ) && !( user.stat ))
+ if(!user.prepare_for_slotmove(real_master_item)) //Prevents removing hardsuits when they have storage modules in them.
+ return 0
switch(over_object.name)
if("right hand")
- user.u_equip(real_master_item)
user.equip_to_slot_if_possible(real_master_item, slot_r_hand)
if("left hand")
- user.u_equip(real_master_item)
user.equip_to_slot_if_possible(real_master_item, slot_l_hand)
real_master_item.add_fingerprint(user)
return 0
return 0
-//items that use internal storage have the option of calling this to emulate default storage attack_hand behaviour.
-//returns 1 if the master item's parent's attack_hand() should be called, 0 otherwise.
-//It's strange, but no other way of doing it without the ability to call another proc's parent, really.
+/**
+ * items that use internal storage have the option of calling this to emulate default storage attack_hand behaviour.
+ * returns 1 if the master item's parent's attack_hand() should be called, 0 otherwise.
+ * It's strange, but no other way of doing it without the ability to call another proc's parent, really.
+ */
/obj/item/storage/internal/proc/handle_attack_hand(mob/user as mob)
var/obj/item/real_master_item = special_master_item_handling ? get_master_item() : master_item
diff --git a/code/game/objects/structures/machinery/rechargestation.dm b/code/game/objects/structures/machinery/rechargestation.dm
index ad254b64536..c7f0f64314a 100644
--- a/code/game/objects/structures/machinery/rechargestation.dm
+++ b/code/game/objects/structures/machinery/rechargestation.dm
@@ -135,26 +135,26 @@
if(ishuman(occupant))
var/mob/living/carbon/human/H = occupant
- var/obj/item/organ/internal/machine/power_core/IC = H.internal_organs_by_name[BP_CELL]
- if(istype(IC))
- target = IC.cell
-
- // Different reactor types have different external recharge speeds.
if(isipc(H))
reactor = H.internal_organs_by_name[BP_REACTOR]
if(!istype(reactor))
return
- if((!target || target.percent() > 98) && istype(H.back, /obj/item/rig))
+ if((!target || target.percent() > 99) && istype(H.back, /obj/item/rig))
var/obj/item/rig/R = H.back
if(R.cell && !R.cell.fully_charged())
target = R.cell
+ if(!target)
+ var/obj/item/organ/internal/machine/power_core/IC = H.internal_organs_by_name[BP_CELL]
+ if(istype(IC))
+ target = IC.cell
+
if(target && !target.fully_charged())
var/diff = min(target.maxcharge - target.charge, charging_power * CELLRATE * seconds_per_tick) // Capped by charging_power / tick
var/charge_used = cell.use(diff)
- if(!reactor) // not an IPC
+ if(!reactor || target.percent() < 99) // not an IPC
target.give(charge_used * charging_efficiency)
else
reactor.generate_power(charge_used * charging_efficiency * reactor.external_charge_multiplier)
diff --git a/code/modules/clothing/spacesuits/rig/modules/combat.dm b/code/modules/clothing/spacesuits/rig/modules/combat.dm
index efb4ac97f1c..71ea072c234 100644
--- a/code/modules/clothing/spacesuits/rig/modules/combat.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/combat.dm
@@ -23,6 +23,7 @@
module_type = MODULETYPE_USABLE_ACTIVE
icon_state = "grenade"
+ suit_overlay_active = "mounted-gun"
interface_name = "integrated grenade launcher"
interface_desc = "Discharges loaded grenades against the wearer's location."
@@ -117,6 +118,8 @@
module_cooldown = 0
icon_state = "lcannon"
+ suit_overlay_active = "mounted-lascannon"
+
engage_string = "Configure"
category = MODULE_HEAVY_COMBAT
@@ -137,6 +140,24 @@
QDEL_NULL(gun)
. = ..()
+/obj/item/rig_module/mounted/proc/get_firemode_name()
+ if(!gun || !length(gun.firemodes))
+ return "No firemodes"
+
+ var/datum/firemode/current_mode = gun.firemodes[gun.sel_mode]
+ return current_mode ? current_mode.name : "Unknown"
+
+/obj/item/rig_module/mounted/get_configuration()
+ . = ..()
+ if(gun && length(gun.firemodes) > 1)
+ .["firemode"] = add_ui_configuration("Fire mode", "button", get_firemode_name())
+
+/obj/item/rig_module/mounted/configure_edit(key, value, mob/user)
+ switch(key)
+ if("firemode")
+ if(gun)
+ gun.toggle_firing_mode(user)
+
/obj/item/rig_module/mounted/engage(atom/target, mob/user)
if(!..())
return FALSE
@@ -158,7 +179,7 @@
construction_cost= list(MATERIAL_STEEL = 7000, MATERIAL_GLASS = 2250, MATERIAL_URANIUM = 3250, MATERIAL_GOLD = 2500)
construction_time = 300
- suit_overlay_active = "mounted-lascannon"
+ suit_overlay_active = "mounted-wrist"
interface_name = "mounted energy gun"
interface_desc = "A forearm-mounted suit-powered energy gun."
@@ -211,7 +232,7 @@
desc = "A forearm-mounted suit-powered ballistic submachine gun."
icon_state = "smg"
- suit_overlay_active = "mounted-ion"
+ suit_overlay_active = "mounted-wrist"
interface_name = "mounted submachine gun"
interface_desc = "A forearm-mounted suit-powered ballistic submachine gun."
@@ -223,7 +244,7 @@
desc = "A forearm-mounted suit-powered xray laser gun."
icon_state = "xray"
- suit_overlay_active = "mounted-ion"
+ suit_overlay_active = "mounted-wrist"
interface_name = "mounted xray laser gun"
interface_desc = "A forearm-mounted suit-powered xray laser gun."
@@ -293,6 +314,7 @@
icon_state = "thermaldrill"
interface_name = "thermal drill"
interface_desc = "A potent drill that can pierce rock walls over long distances."
+ suit_overlay_active = "mounted-wrist"
use_power_cost = 20
gun_type = /obj/item/gun/energy/vaurca/thermaldrill/mounted
@@ -448,6 +470,7 @@
name = "mounted tqi-qop carbine"
desc = "A shoulder-mounted cell-powered tqi-qop carbine."
icon_state = "pulse"
+ suit_overlay_active = "mounted-gun"
interface_name = "mounted tqi-qop carbine"
interface_desc = "A shoulder-mounted cell-powered tqi-qop carbine."
gun_type = /obj/item/gun/energy/gun/qukala/mounted
diff --git a/code/modules/clothing/spacesuits/rig/modules/modules.dm b/code/modules/clothing/spacesuits/rig/modules/modules.dm
index 489a42f9c5a..3251c73b7a9 100644
--- a/code/modules/clothing/spacesuits/rig/modules/modules.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/modules.dm
@@ -429,19 +429,7 @@
return TRUE
return FALSE
-/mob/living/carbon/human/ClickOn(atom/A, params)
- . = ..()
- if (ismob(A) && istype(back, /obj/item/rig))
- var/obj/item/rig/R = back
- R.attack_disrupt_check(src)
-
-/mob/living/carbon/human/throw_item(atom/target)
- . = ..()
- if (ismob(src) && istype(back, /obj/item/rig))
- var/obj/item/rig/R = back
- R.attack_disrupt_check(src)
-
/obj/item/rig/proc/attack_disrupt_check()
for (var/obj/item/rig_module/module in installed_modules)
if (module.active && module.attackdisrupts)
- module.deactivate()
+ module.deactivate(src.wearer)
diff --git a/code/modules/clothing/spacesuits/rig/modules/ninja.dm b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
index 3853ec56494..7628e857da4 100644
--- a/code/modules/clothing/spacesuits/rig/modules/ninja.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/ninja.dm
@@ -341,10 +341,11 @@
category = MODULE_SPECIAL
/obj/item/rig_module/device/door_hack/process()
- if(holder && holder.wearer)
- if(!(locate(/obj/item/multitool/hacktool/rig) in holder.wearer))
- deactivate()
- return FALSE
+ if(active) //Don't need to run locate every tick if the suit isn't in use.
+ if(holder && holder.wearer)
+ if(!(locate(/obj/item/multitool/hacktool/rig) in holder.wearer))
+ deactivate()
+ return FALSE
return ..()
diff --git a/code/modules/clothing/spacesuits/rig/modules/utility.dm b/code/modules/clothing/spacesuits/rig/modules/utility.dm
index b472928ddf7..c32639f0abd 100644
--- a/code/modules/clothing/spacesuits/rig/modules/utility.dm
+++ b/code/modules/clothing/spacesuits/rig/modules/utility.dm
@@ -499,6 +499,7 @@
if("stabilizers")
if(engage(src, user))
jets.toggle_rockets_stabilization(user)
+ stabilize = jets.stabilization_on
/obj/item/rig_module/maneuvering_jets/activate(mob/user)
if(!..())
diff --git a/code/modules/clothing/spacesuits/rig/rig.dm b/code/modules/clothing/spacesuits/rig/rig.dm
index bbff4693b8e..c0e2f4792e8 100644
--- a/code/modules/clothing/spacesuits/rig/rig.dm
+++ b/code/modules/clothing/spacesuits/rig/rig.dm
@@ -244,6 +244,26 @@
if(helmet)
helmet.tint = (active? vision_restriction : offline_vision_restriction)
+/**
+ * Updates the hardsuit's slowdown if it changes.
+ * This gives a heavy slowdown penalty if the suit is deployed but the boots are not.
+ * Otherwise, it is an easy exploit to make any hardsuit stealthy by wearing everything EXCEPT the boots.
+ * Now you can creep in your hardsuit, but you have to go slowly to do it.
+ */
+/obj/item/rig/proc/update_slowdown()
+ var/new_slowdown = initial(slowdown)
+
+ if(offline)
+ new_slowdown = offline_slowdown
+ else if(chest && wearer?.wear_suit == chest && (!boots || wearer.shoes != boots)) //If the chest is deployed, but the boots are not.
+ new_slowdown = offline_slowdown + 2 //The boots aren't taking the weight of the suit, so the wearer isn't benefiting from the hardsuit's powered servos.
+
+ if(slowdown != new_slowdown)
+ slowdown = new_slowdown
+ wearer?.update_equipment_speed_mods() //This should only proc if worn, but just in case we check.
+ if(slowdown == offline_slowdown + 2)
+ to_chat(wearer, SPAN_WARNING("Without the boots deployed, the hardsuit's servos aren't supporting the weight of the suit."))
+
/obj/item/rig/proc/suit_is_deployed()
if(!istype(wearer) || src.loc != wearer || wearer.back != src)
return 0
@@ -475,9 +495,8 @@
offline = 0
if(istype(wearer) && !wearer.wearing_rig)
wearer.wearing_rig = src
- if(slowdown != initial(slowdown))
- slowdown = initial(slowdown)
- wearer?.update_equipment_speed_mods()
+
+ update_slowdown()
if(offline != previous_offline_status)
update_icon(TRUE)
@@ -499,9 +518,7 @@
for(var/obj/item/rig_module/module in installed_modules)
module.deactivate()
offline = 2
- if(slowdown != offline_slowdown)
- slowdown = offline_slowdown
- wearer?.update_equipment_speed_mods()
+ update_slowdown()
return
if(malfunction_delay > 0)
@@ -516,6 +533,9 @@
if(power_usage_this_tick)
cell.use(power_usage_this_tick)
+ if(!malfunctioning && !electrified && !open && offline && !wearer) //If nothing is wrong with it and it is not in use, it should stop processing.
+ return PROCESS_KILL
+
/obj/item/rig/proc/check_power_cost(var/mob/living/user, var/cost, var/use_unconcious, var/obj/item/rig_module/mod, var/user_is_ai)
if(!istype(user))
@@ -784,10 +804,12 @@
if(selected_module.suit_overlay_active)
selected_module.suit_overlay = selected_module.suit_overlay_active
sound_to(usr, module?.sound_activate)
+ balloon_alert_to_viewers("deploys \the [module.interface_name]", "deploys \the [module.interface_name]")
else
sound_to(usr, selected_module?.sound_deactivate)
selected_module.suit_overlay = null
selected_module = null
+ balloon_alert_to_viewers("retracts \the [module.interface_name]", "retracts \the [module.interface_name]")
update_icon(TRUE)
playsound(src.loc, 'sound/items/rfd_dispense.ogg', 25, FALSE)
if("select_charge_type")
@@ -843,9 +865,13 @@
if(!do_after(M, seal_delay))
return FALSE
+ if(wearer && wearer != M)
+ unregister_wearer_signals(wearer)
+
M.visible_message(SPAN_NOTICE("[M] struggles into \the [src]."), SPAN_NOTICE("You struggle into \the [src]."))
wearer = M
wearer.wearing_rig = src
+ register_wearer_signals(wearer)
update_icon(TRUE)
return TRUE
return TRUE
@@ -895,16 +921,6 @@
var/mob/living/carbon/human/holder
holder = use_obj.loc
if(istype(holder))
- // Special code for boots. This is to prevent boots from being retracted while the chest piece is deployed.
- // Otherwise, it is an easy exploit to make any hardsuit stealthy by wearing everything EXCEPT the boots.
- var/obj/item/chest_slot = holder.wear_suit
- var/obj/item/chest_obj = chest
- 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)
to_chat(wearer, "Your [use_obj.name] [use_obj.gender == PLURAL ? "retract" : "retracts"] swiftly.")
playsound(src, 'sound/machines/rig/rig_retract.ogg', 30, FALSE)
@@ -924,6 +940,9 @@
to_chat(wearer, SPAN_WARNING("You must remain still while the suit deploys its parts."))
piece_being_deployed = FALSE
return FALSE
+ if(!wearer || wearer.back != src) ///Prevents an edge case where a suit with a storage module can be removed while deploying, causing a runtime.
+ 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)
var/obj/item/boots_slot = wearer.shoes
@@ -951,6 +970,7 @@
helmet.update_light(wearer)
update_icon(TRUE)
+ update_slowdown()
piece_being_deployed = FALSE
return TRUE
@@ -1020,12 +1040,43 @@
toggle_piece(piece, H, ONLY_RETRACT)
/obj/item/rig/proc/null_wearer(var/mob/user)
+ unregister_wearer_signals(wearer)
for(var/piece in list("helmet","gauntlets",BP_CHEST,"boots"))
toggle_piece(piece, user, ONLY_RETRACT)
if(wearer)
wearer.wearing_rig = null
wearer = null
+/obj/item/rig/proc/register_wearer_signals(mob/living/carbon/human/new_wearer)
+ if(!new_wearer)
+ return
+
+ RegisterSignal(new_wearer, COMSIG_MOB_CLICKON, PROC_REF(handle_wearer_click))
+
+/obj/item/rig/proc/unregister_wearer_signals(mob/living/carbon/human/old_wearer)
+ if(!old_wearer)
+ return
+
+ UnregisterSignal(old_wearer, COMSIG_MOB_CLICKON)
+
+/obj/item/rig/proc/handle_wearer_click(mob/user, atom/target, modifiers)
+ SIGNAL_HANDLER
+ if(offline == 2)
+ return
+ if(LAZYACCESS(modifiers, SHIFT_CLICK))
+ return
+ if(LAZYACCESS(modifiers, ALT_CLICK))
+ return
+ if(LAZYACCESS(modifiers, RIGHT_CLICK))
+ return
+
+ if(user.in_throw_mode && (isturf(target) || isturf(target.loc)) && user.throw_item(target)) //Prevents throwing items while remaining cloaked.
+ attack_disrupt_check()
+
+ if(ismob(target)) //This doesn't prevent guns firing at turfs, that is handled in /obj/item/gun/proc/handle_post_fire
+ if(target != user)
+ attack_disrupt_check()
+
/obj/item/rig/on_slotmove(var/mob/user)
..()
null_wearer(user)
diff --git a/code/modules/clothing/spacesuits/rig/rig_verbs.dm b/code/modules/clothing/spacesuits/rig/rig_verbs.dm
index b5ed0d6e5ef..077a7a08726 100644
--- a/code/modules/clothing/spacesuits/rig/rig_verbs.dm
+++ b/code/modules/clothing/spacesuits/rig/rig_verbs.dm
@@ -149,7 +149,7 @@
if(!check_power_cost(usr))
return
- deploy(wearer)
+ retract(wearer)
/obj/item/rig/verb/toggle_seals_verb()
set name = "Engage/Disengage Hardsuit"
diff --git a/code/modules/projectiles/gun.dm b/code/modules/projectiles/gun.dm
index 27e7f0df9b8..55f06406841 100644
--- a/code/modules/projectiles/gun.dm
+++ b/code/modules/projectiles/gun.dm
@@ -597,12 +597,11 @@ ABSTRACT_TYPE(/obj/item/gun)
if(recoil)
shake_camera(user, recoil + 1, recoil)
- if(ishuman(user) && user.invisibility == INVISIBILITY_LEVEL_TWO) //shooting will disable a rig cloaking device
+ if(ishuman(user))
var/mob/living/carbon/human/H = user
if(istype(H.back, /obj/item/rig))
var/obj/item/rig/R = H.back
- for(var/obj/item/rig_module/stealth_field/S in R.installed_modules)
- S.deactivate()
+ R.attack_disrupt_check() //This currently handles decloaking ninjas who shoot guns. Other modules could use attack_disrupt_check() in future.
update_icon()
/obj/item/gun/proc/play_fire_sound()
diff --git a/code/modules/reagents/reagent_containers/spray.dm b/code/modules/reagents/reagent_containers/spray.dm
index 842c322d15d..29e258ddab5 100644
--- a/code/modules/reagents/reagent_containers/spray.dm
+++ b/code/modules/reagents/reagent_containers/spray.dm
@@ -104,12 +104,11 @@
D.set_color()
D.set_up(my_target, spray_size, 10)
- if(ishuman(user) && user.invisibility == INVISIBILITY_LEVEL_TWO) //shooting will disable a rig cloaking device
+ if(ishuman(user))
var/mob/living/carbon/human/H = user
if(istype(H.back,/obj/item/rig))
var/obj/item/rig/R = H.back
- for(var/obj/item/rig_module/stealth_field/S in R.installed_modules)
- S.deactivate()
+ R.attack_disrupt_check() //This currently handles decloaking ninjas who spray acid or lube. Other modules could use attack_disrupt_check() in future.
/obj/item/reagent_containers/spray/attack_self(var/mob/user)
if(!possible_transfer_amounts)
diff --git a/html/changelogs/Fenodyree-HardsuitBugfixes.yml b/html/changelogs/Fenodyree-HardsuitBugfixes.yml
new file mode 100644
index 00000000000..ab97af516ed
--- /dev/null
+++ b/html/changelogs/Fenodyree-HardsuitBugfixes.yml
@@ -0,0 +1,11 @@
+author: Fenodyree
+delete-after: True
+changes:
+ - bugfix: "Fixed the Retract All Hardsuit Parts verb, by making hardsuit boots retractable with the chestpiece deployed, at a heavy slowdown"
+ - bugfix: "Fixed the stealth system module not decloaking when the user fires a gun or hits a mob."
+ - bugfix: "Fixed being able to instantly remove your hardsuit if it had a storage module in it."
+ - bugfix: "Fixed mounted energy guns being stuck in taser mode. Also extends support to other guns with multiple firemods."
+ - bugfix: "Fixed most hardsuit modules not having an on-mob sprite."
+ - bugfix: "Fixed synthetic chargers not charging the hardsuit, if the wearer was an IPC."
+ - bugfix: "Fixed the hardsuit UI not updating when toggling jetpack stabalizers."
+ - bugfix: "Fixed the advanced hacking tool searching the wearer's entire contents to see if it had been dropped every tick."
diff --git a/icons/mob/rig_modules.dmi b/icons/mob/rig_modules.dmi
index a184129e692..f18fb52f28c 100644
Binary files a/icons/mob/rig_modules.dmi and b/icons/mob/rig_modules.dmi differ