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