From b49553bdf4abe783096d9d897d98ed689bbc77c8 Mon Sep 17 00:00:00 2001 From: SmArtKar <44720187+SmArtKar@users.noreply.github.com> Date: Tue, 15 Apr 2025 10:21:10 +0200 Subject: [PATCH] Refactors MODsuit module rendering and allows overslotted parts to show items they overslot when unsealed (#90414) ## About The Pull Request MODsuit modules now render on the part they're attached to, that being first part if required_slots is set, otherwise defaulting to the control module. Instead of using icon ops and a cache, module masking (used by armor boosters and insignias) will instead render the module on all parts, each overlay alpha filtered using the worn piece as the mask. To do this we also migrate modules to separate_worn_overlays, which fixes the issue where they'd always get painted the same color as the back piece, ignoring use_mod_colors's value (which is FALSE by default). So now modules that inherit MOD's color like armor booster will be painted accordingly to their piece. This also means that modules actually layer properly, and don't go ontop of items that they should be under. Additionally, whenever gloves or boots overslot an item, the overslotted item will still render underneath them if they're unsealed. Because it looks weird when your gloves disappear when you extend your MODsuit ones. ![dreamseeker_BaWjJBcMVO](https://github.com/user-attachments/assets/2b374913-7761-4b54-9bbd-cbd57d343fd6) Look at that hip look, she'd have bare hands and ankles without this PR. Closes #90370 ## Why It's Good For The Game Fixes a bunch of visual jank that looks weird, and overslotting displaying overslotted item is just behavior you'd expect normally. ## Changelog :cl: add: When a MODsuit piece overslots an item, it will now render beneath that piece as long as its unsealed. refactor: Refactored how MODsuit modules are rendered, report any bugs on GitHub! /:cl: --- code/__DEFINES/mod.dm | 4 - code/game/objects/structures/mannequin.dm | 47 +------- code/modules/antagonists/clown_ops/clownop.dm | 6 +- code/modules/antagonists/nukeop/outfits.dm | 6 +- .../mob/living/carbon/carbon_update_icons.dm | 54 +++++++++ code/modules/mod/mod_activation.dm | 25 ++--- code/modules/mod/mod_control.dm | 37 ++---- code/modules/mod/mod_part.dm | 24 ++++ code/modules/mod/mod_theme.dm | 4 +- code/modules/mod/modules/_module.dm | 106 ++++++++++++------ code/modules/mod/modules/modules_antag.dm | 18 ++- .../mod/modules/modules_engineering.dm | 4 +- code/modules/mod/modules/modules_general.dm | 20 +++- code/modules/mod/modules/modules_maint.dm | 12 +- code/modules/mod/modules/modules_ninja.dm | 4 +- code/modules/mod/modules/modules_security.dm | 3 + code/modules/mod/modules/modules_supply.dm | 2 +- code/modules/mod/modules/modules_timeline.dm | 3 + code/modules/wiremod/shell/module.dm | 2 + 19 files changed, 234 insertions(+), 147 deletions(-) diff --git a/code/__DEFINES/mod.dm b/code/__DEFINES/mod.dm index dcfcf028d08..1a4bed1ca99 100644 --- a/code/__DEFINES/mod.dm +++ b/code/__DEFINES/mod.dm @@ -57,9 +57,5 @@ /// Global list of all /datum/mod_theme GLOBAL_LIST_INIT(mod_themes, setup_mod_themes()) -/// Global cache of mod skins to masks per different configuration of pulled out parts. -GLOBAL_LIST_EMPTY(mod_masks) -/// Global cache of mod skins to deployed parts to module icon states -GLOBAL_LIST_EMPTY(mod_module_overlays) /// Global list of all ids associated to a /datum/mod_link instance GLOBAL_LIST_EMPTY(mod_link_ids) diff --git a/code/game/objects/structures/mannequin.dm b/code/game/objects/structures/mannequin.dm index e4cbfc7d629..2260751bbf7 100644 --- a/code/game/objects/structures/mannequin.dm +++ b/code/game/objects/structures/mannequin.dm @@ -114,49 +114,12 @@ var/obj/item/worn_item = worn_items[slot_flag] if(!worn_item) continue - var/default_layer = 0 - var/default_icon = null + var/default_icon = get_default_icon_by_slot(text2num(slot_flag)) + var/default_layer = get_default_layer_by_slot(text2num(slot_flag)) var/female_icon = NO_FEMALE_UNIFORM - switch(text2num(slot_flag)) //this kinda sucks because build worn icon kinda sucks - if(ITEM_SLOT_HEAD) - default_layer = HEAD_LAYER - default_icon = 'icons/mob/clothing/head/default.dmi' - if(ITEM_SLOT_EYES) - default_layer = GLASSES_LAYER - default_icon = 'icons/mob/clothing/eyes.dmi' - if(ITEM_SLOT_EARS) - default_layer = EARS_LAYER - default_icon = 'icons/mob/clothing/ears.dmi' - if(ITEM_SLOT_MASK) - default_layer = FACEMASK_LAYER - default_icon = 'icons/mob/clothing/mask.dmi' - if(ITEM_SLOT_NECK) - default_layer = NECK_LAYER - default_icon = 'icons/mob/clothing/neck.dmi' - if(ITEM_SLOT_BACK) - default_layer = BACK_LAYER - default_icon = 'icons/mob/clothing/back.dmi' - if(ITEM_SLOT_BELT) - default_layer = BELT_LAYER - default_icon = 'icons/mob/clothing/belt.dmi' - if(ITEM_SLOT_ID) - default_layer = ID_LAYER - default_icon = 'icons/mob/clothing/id.dmi' - if(ITEM_SLOT_ICLOTHING) - default_layer = UNIFORM_LAYER - default_icon = DEFAULT_UNIFORM_FILE - if(body_type == FEMALE && istype(worn_item, /obj/item/clothing/under)) - var/obj/item/clothing/under/worn_jumpsuit = worn_item - female_icon = worn_jumpsuit.female_sprite_flags - if(ITEM_SLOT_OCLOTHING) - default_layer = SUIT_LAYER - default_icon = DEFAULT_SUIT_FILE - if(ITEM_SLOT_GLOVES) - default_layer = GLOVES_LAYER - default_icon = 'icons/mob/clothing/hands.dmi' - if(ITEM_SLOT_FEET) - default_layer = SHOES_LAYER - default_icon = DEFAULT_SHOES_FILE + if(body_type == FEMALE && istype(worn_item, /obj/item/clothing/under)) + var/obj/item/clothing/under/worn_jumpsuit = worn_item + female_icon = worn_jumpsuit.female_sprite_flags . += worn_item.build_worn_icon(default_layer, default_icon, female_uniform = female_icon) /obj/structure/mannequin/attack_hand_secondary(mob/user, list/modifiers) diff --git a/code/modules/antagonists/clown_ops/clownop.dm b/code/modules/antagonists/clown_ops/clownop.dm index 2fd2daa7489..9e2b79102b1 100644 --- a/code/modules/antagonists/clown_ops/clownop.dm +++ b/code/modules/antagonists/clown_ops/clownop.dm @@ -78,8 +78,7 @@ /datum/outfit/clown_operative/post_equip(mob/living/carbon/human/H, visuals_only) var/obj/item/mod/module/armor_booster/booster = locate() in H.back - booster.active = TRUE - H.update_worn_back() + booster.activate() /datum/outfit/clown_operative_elite name = "Clown Operative (Elite, Preview only)" @@ -89,5 +88,4 @@ /datum/outfit/clown_operative_elite/post_equip(mob/living/carbon/human/H, visuals_only) var/obj/item/mod/module/armor_booster/booster = locate() in H.back - booster.active = TRUE - H.update_worn_back() + booster.activate() diff --git a/code/modules/antagonists/nukeop/outfits.dm b/code/modules/antagonists/nukeop/outfits.dm index 32fe7dfd5c0..9ecfbb9f991 100644 --- a/code/modules/antagonists/nukeop/outfits.dm +++ b/code/modules/antagonists/nukeop/outfits.dm @@ -188,8 +188,7 @@ /datum/outfit/nuclear_operative/post_equip(mob/living/carbon/human/H, visuals_only) var/obj/item/mod/module/armor_booster/booster = locate() in H.back - booster.active = TRUE - H.update_worn_back() + booster.activate() /datum/outfit/nuclear_operative_elite name = "Nuclear Operative (Elite, Preview only)" @@ -201,8 +200,7 @@ /datum/outfit/nuclear_operative_elite/post_equip(mob/living/carbon/human/H, visuals_only) var/obj/item/mod/module/armor_booster/booster = locate() in H.back - booster.active = TRUE - H.update_worn_back() + booster.activate() var/obj/item/shield/energy/shield = locate() in H.held_items shield.icon_state = "[shield.base_icon_state]1" H.update_held_items() diff --git a/code/modules/mob/living/carbon/carbon_update_icons.dm b/code/modules/mob/living/carbon/carbon_update_icons.dm index 6a216fda617..abc96cb33d3 100644 --- a/code/modules/mob/living/carbon/carbon_update_icons.dm +++ b/code/modules/mob/living/carbon/carbon_update_icons.dm @@ -638,3 +638,57 @@ GLOBAL_LIST_EMPTY(masked_leg_icons_cache) new_leg_appearance_lower.layer = -BODYPARTS_LOW_LAYER . += new_leg_appearance_lower return . + +/proc/get_default_icon_by_slot(slot_flag) + switch(slot_flag) + if(ITEM_SLOT_HEAD) + return 'icons/mob/clothing/head/default.dmi' + if(ITEM_SLOT_EYES) + return 'icons/mob/clothing/eyes.dmi' + if(ITEM_SLOT_EARS) + return 'icons/mob/clothing/ears.dmi' + if(ITEM_SLOT_MASK) + return 'icons/mob/clothing/mask.dmi' + if(ITEM_SLOT_NECK) + return 'icons/mob/clothing/neck.dmi' + if(ITEM_SLOT_BACK) + return 'icons/mob/clothing/back.dmi' + if(ITEM_SLOT_BELT) + return 'icons/mob/clothing/belt.dmi' + if(ITEM_SLOT_ID) + return 'icons/mob/clothing/id.dmi' + if(ITEM_SLOT_ICLOTHING) + return DEFAULT_UNIFORM_FILE + if(ITEM_SLOT_OCLOTHING) + return DEFAULT_SUIT_FILE + if(ITEM_SLOT_GLOVES) + return 'icons/mob/clothing/hands.dmi' + if(ITEM_SLOT_FEET) + return DEFAULT_SHOES_FILE + +/proc/get_default_layer_by_slot(slot_flag) + switch(text2num(slot_flag)) + if(ITEM_SLOT_HEAD) + return HEAD_LAYER + if(ITEM_SLOT_EYES) + return GLASSES_LAYER + if(ITEM_SLOT_EARS) + return EARS_LAYER + if(ITEM_SLOT_MASK) + return FACEMASK_LAYER + if(ITEM_SLOT_NECK) + return NECK_LAYER + if(ITEM_SLOT_BACK) + return BACK_LAYER + if(ITEM_SLOT_BELT) + return BELT_LAYER + if(ITEM_SLOT_ID) + return ID_LAYER + if(ITEM_SLOT_ICLOTHING) + return UNIFORM_LAYER + if(ITEM_SLOT_OCLOTHING) + return SUIT_LAYER + if(ITEM_SLOT_GLOVES) + return GLOVES_LAYER + if(ITEM_SLOT_FEET) + return SHOES_LAYER diff --git a/code/modules/mod/mod_activation.dm b/code/modules/mod/mod_activation.dm index a31d7b43742..12de6ac26d7 100644 --- a/code/modules/mod/mod_activation.dm +++ b/code/modules/mod/mod_activation.dm @@ -93,7 +93,7 @@ RegisterSignal(part, COMSIG_ATOM_EXITED, PROC_REF(on_overslot_exit)) if(wearer.equip_to_slot_if_possible(part, part.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) ADD_TRAIT(part, TRAIT_NODROP, MOD_TRAIT) - wearer.update_clothing(slot_flags) + wearer.update_clothing(slot_flags|part.slot_flags) SEND_SIGNAL(src, COMSIG_MOD_PART_DEPLOYED, user, part_datum) if(user) wearer.visible_message(span_notice("[wearer]'s [part.name] deploy[part.p_s()] with a mechanical hiss."), @@ -131,7 +131,9 @@ return FALSE if(SEND_SIGNAL(src, COMSIG_MOD_PART_RETRACTING, user, part_datum) & MOD_CANCEL_RETRACTION) return FALSE + var/unsealing = FALSE if(active && part_datum.sealed) + unsealing = TRUE if(instant) seal_part(part, is_sealed = FALSE) else if(!delayed_seal_part(part)) @@ -144,13 +146,14 @@ var/obj/item/overslot = part_datum.overslotting if(!QDELING(wearer) && !wearer.equip_to_slot_if_possible(overslot, overslot.slot_flags, qdel_on_fail = FALSE, disable_warning = TRUE)) wearer.dropItemToGround(overslot, force = TRUE, silent = TRUE) - wearer.update_clothing(slot_flags) + wearer.update_clothing(slot_flags|part.slot_flags) if(!user) return TRUE wearer.visible_message(span_notice("[wearer]'s [part.name] retract[part.p_s()] back into [src] with a mechanical hiss."), span_notice("[part] retract[part.p_s()] back into [src] with a mechanical hiss."), span_hear("You hear a mechanical hiss.")) - playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) + if (!unsealing) + playsound(src, 'sound/vehicles/mecha/mechmove03.ogg', 25, TRUE, SHORT_RANGE_SOUND_EXTRARANGE) return TRUE /// Starts the activation sequence, where parts of the suit activate one by one until the whole suit is on. @@ -268,7 +271,6 @@ part.heat_protection = NONE part.cold_protection = NONE part.alternate_worn_layer = part_datum.unsealed_layer - generate_suit_mask() update_speed() wearer.update_clothing(part.slot_flags | slot_flags) wearer.update_obscured_slots(part.visor_flags_inv) @@ -281,7 +283,7 @@ for(var/obj/item/mod/module/module as anything in modules) if(module.part_activated || !module.has_required_parts(mod_parts, need_active = TRUE)) continue - module.on_part_activation() + module.on_part_activation() module.part_activated = TRUE else for(var/obj/item/mod/module/module as anything in modules) @@ -300,23 +302,16 @@ active = is_on if(active) for(var/obj/item/mod/module/module as anything in modules) - if(module.part_activated || !module.has_required_parts(mod_parts, need_active = TRUE)) - continue - module.on_part_activation() - module.part_activated = TRUE + if(!module.part_activated && module.has_required_parts(mod_parts, need_active = TRUE)) + module.on_part_activation() else for(var/obj/item/mod/module/module as anything in modules) if(!module.part_activated) continue module.on_part_deactivation() - module.part_activated = FALSE - if(!module.active || (module.allow_flags & MODULE_ALLOW_INACTIVE)) - continue - module.deactivate(display_message = FALSE) update_charge_alert() update_appearance(UPDATE_ICON_STATE) - generate_suit_mask() - wearer.update_clothing(slot_flags) + wearer.update_clothing() /// Quickly deploys all the suit parts and if successful, seals them and turns on the suit. Intended mostly for outfits. /obj/item/mod/control/proc/quick_activation() diff --git a/code/modules/mod/mod_control.dm b/code/modules/mod/mod_control.dm index f64539bbbd5..bf6966499fa 100644 --- a/code/modules/mod/mod_control.dm +++ b/code/modules/mod/mod_control.dm @@ -114,13 +114,10 @@ if(core) QDEL_NULL(core) QDEL_NULL(mod_link) - for(var/datum/mod_part/part_datum as anything in get_part_datums(all = TRUE)) - var/obj/item/part_item = part_datum.part_item - part_datum.part_item = null - part_datum.overslotting = null - mod_parts -= part_datum - if(!QDELING(part_item)) - qdel(part_item) + for(var/part_key in mod_parts) + var/datum/mod_part/part_datum = mod_parts[part_key] + mod_parts -= part_key + qdel(part_datum) return ..() /obj/item/mod/control/atom_destruction(damage_flag) @@ -443,11 +440,14 @@ CRASH("get_part_datum called with incorrect item [part] passed.") /obj/item/mod/control/proc/get_part_from_slot(slot) - var/datum/mod_part/part = mod_parts["[slot]"] - return part?.part_item + RETURN_TYPE(/obj/item) + return get_part_datum_from_slot(slot)?.part_item /obj/item/mod/control/proc/get_part_datum_from_slot(slot) - return mod_parts["[slot]"] + RETURN_TYPE(/datum/mod_part) + for (var/part_key in mod_parts) + if (text2num(part_key) & slot) + return mod_parts[part_key] /obj/item/mod/control/proc/set_wearer(mob/living/carbon/human/user) if(wearer == user) @@ -481,21 +481,6 @@ covered_slots |= part.slot_flags return covered_slots -/obj/item/mod/control/proc/generate_suit_mask() - var/list/parts = get_parts(all = TRUE) - var/covered_slots = get_sealed_slots(parts) - if(GLOB.mod_masks[skin]) - if(GLOB.mod_masks[skin]["[covered_slots]"]) - return GLOB.mod_masks[skin]["[covered_slots]"] - else - GLOB.mod_masks[skin] = list() - var/icon/slot_mask = icon('icons/blanks/32x32.dmi', "nothing") - for(var/obj/item/part as anything in parts) - slot_mask.Blend(icon(part.worn_icon, part.icon_state), ICON_OVERLAY) - slot_mask.Blend("#fff", ICON_ADD) - GLOB.mod_masks[skin]["[covered_slots]"] = slot_mask - return GLOB.mod_masks[skin]["[covered_slots]"] - /obj/item/mod/control/proc/clean_up() if(QDELING(src)) unset_wearer() @@ -602,7 +587,6 @@ modules += new_module complexity += new_module.complexity new_module.mod = src - new_module.RegisterSignal(src, COMSIG_ITEM_GET_WORN_OVERLAYS, TYPE_PROC_REF(/obj/item/mod/module, add_module_overlay)) new_module.on_install() if(wearer) new_module.on_equip() @@ -622,7 +606,6 @@ old_module.on_part_deactivation(deleting = deleting) if(old_module.active) old_module.deactivate(display_message = !deleting, deleting = deleting) - old_module.UnregisterSignal(src, COMSIG_ITEM_GET_WORN_OVERLAYS) old_module.on_uninstall(deleting = deleting) QDEL_LIST_ASSOC_VAL(old_module.pinned_to) old_module.mod = null diff --git a/code/modules/mod/mod_part.dm b/code/modules/mod/mod_part.dm index 88f8024628d..a184df7f7a0 100644 --- a/code/modules/mod/mod_part.dm +++ b/code/modules/mod/mod_part.dm @@ -18,5 +18,29 @@ var/obj/item/overslotting = null /datum/mod_part/Destroy() + // To avoid qdel loops in MOD control units, since they're also a part + if (!QDELING(part_item)) + qdel(part_item) part_item = null + overslotting = null return ..() + +/datum/mod_part/proc/set_item(obj/item/new_part) + part_item = new_part + RegisterSignal(part_item, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(get_separate_worn_overlays)) + +// If we're overslotting an item, add its visual as an underlay +/datum/mod_part/proc/get_separate_worn_overlays(obj/item/source, list/overlays, mutable_appearance/standing, mutable_appearance/draw_target, isinhands, icon_file) + SIGNAL_HANDLER + + if (!overslotting || sealed) + return + + var/checked_slot = source.slot_flags + if (ismob(source.loc)) + var/mob/as_mob = source.loc + checked_slot = as_mob.get_slot_by_item(source) + var/mutable_appearance/worn_overlay = overslotting.build_worn_icon(default_layer = -draw_target.layer + 0.1, default_icon_file = get_default_icon_by_slot(checked_slot)) + for (var/mutable_appearance/overlay in worn_overlay.overlays) + overlay.layer = draw_target.layer + 0.1 + overlays += worn_overlay diff --git a/code/modules/mod/mod_theme.dm b/code/modules/mod/mod_theme.dm index 7ac3aeda0cc..1f9de066352 100644 --- a/code/modules/mod/mod_theme.dm +++ b/code/modules/mod/mod_theme.dm @@ -106,7 +106,7 @@ mod.ui_theme = ui_theme mod.charge_drain = charge_drain var/datum/mod_part/control_part_datum = new() - control_part_datum.part_item = mod + control_part_datum.set_item(mod) mod.mod_parts["[mod.slot_flags]"] = control_part_datum for(var/path in variants[default_skin]) if(!ispath(path)) @@ -116,7 +116,7 @@ var/obj/item/clothing/chestplate = mod_part chestplate.allowed |= allowed_suit_storage var/datum/mod_part/part_datum = new() - part_datum.part_item = mod_part + part_datum.set_item(mod_part) mod.mod_parts["[mod_part.slot_flags]"] = part_datum parts += mod_part diff --git a/code/modules/mod/modules/_module.dm b/code/modules/mod/modules/_module.dm index 2fec22edbc6..fe3f02fc99b 100644 --- a/code/modules/mod/modules/_module.dm +++ b/code/modules/mod/modules/_module.dm @@ -172,9 +172,9 @@ update_signal(used_button) balloon_alert(mod.wearer, "[src] activated, [used_button]-click to use") active = TRUE - mod.wearer.update_clothing(mod.slot_flags) SEND_SIGNAL(src, COMSIG_MODULE_ACTIVATED) on_activation() + update_clothing_slots() return TRUE /// Called when the module is deactivated @@ -191,11 +191,22 @@ else UnregisterSignal(mod.wearer, used_signal) used_signal = null - mod.wearer.update_clothing(mod.slot_flags) SEND_SIGNAL(src, COMSIG_MODULE_DEACTIVATED, mod.wearer) on_deactivation(display_message = TRUE, deleting = FALSE) + update_clothing_slots() return TRUE +/// Call to update all slots visually affected by this module +/obj/item/mod/module/proc/update_clothing_slots() + var/updated_slots = mod.slot_flags + if (mask_worn_overlay) + for (var/obj/item/part as anything in mod.get_parts()) + updated_slots |= part.slot_flags + else if (length(required_slots)) + for (var/slot in required_slots) + updated_slots |= slot + mod.wearer.update_clothing(updated_slots) + /// Called when the module is used /obj/item/mod/module/proc/used() if(!COOLDOWN_FINISHED(src, cooldown_timer)) @@ -212,7 +223,7 @@ return FALSE start_cooldown() addtimer(CALLBACK(mod.wearer, TYPE_PROC_REF(/mob, update_clothing), mod.slot_flags), cooldown_time+1) //need to run it a bit after the cooldown starts to avoid conflicts - mod.wearer.update_clothing(mod.slot_flags) + update_clothing_slots() SEND_SIGNAL(src, COMSIG_MODULE_USED) on_use() return TRUE @@ -263,11 +274,38 @@ /// Called from MODsuit's install() proc, so when the module is installed /obj/item/mod/module/proc/on_install() - return + SHOULD_CALL_PARENT(TRUE) + + if (mask_worn_overlay) + for (var/obj/item/part as anything in mod.get_parts(all = TRUE)) + RegisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay)) + return + + if (!length(required_slots)) + RegisterSignal(mod, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay)) + return + + var/obj/item/part = mod.get_part_from_slot(required_slots[1]) + RegisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS, PROC_REF(add_module_overlay)) /// Called from MODsuit's uninstall() proc, so when the module is uninstalled /obj/item/mod/module/proc/on_uninstall(deleting = FALSE) - return + SHOULD_CALL_PARENT(TRUE) + + if (deleting) + return + + if (mask_worn_overlay) + for (var/obj/item/part as anything in mod.get_parts(all = TRUE)) + UnregisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS) + return + + if (!length(required_slots)) + UnregisterSignal(mod, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS) + return + + var/obj/item/part = mod.get_part_from_slot(required_slots[1]) + UnregisterSignal(part, COMSIG_ITEM_GET_SEPARATE_WORN_OVERLAYS) /// Called when the MODsuit is activated /obj/item/mod/module/proc/on_part_activation() @@ -335,26 +373,44 @@ qdel(src) /// Adds the worn overlays to the suit. -/obj/item/mod/module/proc/add_module_overlay(obj/item/source, list/overlays, mutable_appearance/standing, isinhands, icon_file) +/obj/item/mod/module/proc/add_module_overlay(obj/item/source, list/overlays, mutable_appearance/standing, mutable_appearance/draw_target, isinhands, icon_file) SIGNAL_HANDLER - overlays += generate_worn_overlay(standing) + if (isinhands) + return + + var/list/added_overlays = generate_worn_overlay(source, standing) + if (!added_overlays) + return + + if (!mask_worn_overlay) + overlays += added_overlays + return + + for (var/mutable_appearance/overlay as anything in added_overlays) + overlay.add_filter("mod_mask_overlay", 1, alpha_mask_filter(icon = icon(draw_target.icon, draw_target.icon_state))) + overlays += overlay /// Generates an icon to be used for the suit's worn overlays -/obj/item/mod/module/proc/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/proc/generate_worn_overlay(obj/item/source, mutable_appearance/standing) + if(!mask_worn_overlay) + if(!has_required_parts(mod.mod_parts, need_active = TRUE)) + return + else + var/datum/mod_part/part_datum = mod.get_part_datum(source) + if (!part_datum?.sealed) + return + . = list() - if(!mod.active || !has_required_parts(mod.mod_parts, need_active = TRUE)) - return var/used_overlay = get_current_overlay_state() if (!used_overlay) return - var/mutable_appearance/module_icon - if(mask_worn_overlay) - module_icon = mutable_appearance(get_module_icon_cache(used_overlay), layer = standing.layer + 0.1) - else - module_icon = mutable_appearance(overlay_icon_file, used_overlay, layer = standing.layer + 0.1) - if(!use_mod_colors) - module_icon.appearance_flags |= RESET_COLOR + + var/mutable_appearance/module_icon = mutable_appearance(overlay_icon_file, used_overlay, layer = standing.layer + 0.1) + if(use_mod_colors) + module_icon.color = mod.color + if (mod.cached_color_filter) + module_icon = filter_appearance_recursive(module_icon, mod.cached_color_filter) . += module_icon SEND_SIGNAL(src, COMSIG_MODULE_GENERATE_WORN_OVERLAY, ., standing) @@ -368,22 +424,6 @@ return overlay_state_inactive return null -/obj/item/mod/module/proc/get_module_icon_cache(used_overlay) - var/covered_slots = mod.get_sealed_slots(mod.get_parts(all = TRUE)) - if (GLOB.mod_module_overlays[mod.skin]) - if (GLOB.mod_module_overlays[mod.skin]["[covered_slots]"]) - if (GLOB.mod_module_overlays[mod.skin]["[covered_slots]"][used_overlay]) - return GLOB.mod_module_overlays[mod.skin]["[covered_slots]"][used_overlay] - else - GLOB.mod_module_overlays[mod.skin]["[covered_slots]"] = list() - else - GLOB.mod_module_overlays[mod.skin] = list() - GLOB.mod_module_overlays[mod.skin]["[covered_slots]"] = list() - var/icon/mod_mask = icon(mod.generate_suit_mask()) - mod_mask.Blend(icon(overlay_icon_file, used_overlay), ICON_MULTIPLY) - GLOB.mod_module_overlays[mod.skin]["[covered_slots]"][used_overlay] = mod_mask - return GLOB.mod_module_overlays[mod.skin]["[covered_slots]"][used_overlay] - /// Updates the signal used by active modules to be activated /obj/item/mod/module/proc/update_signal(value) switch(value) diff --git a/code/modules/mod/modules/modules_antag.dm b/code/modules/mod/modules/modules_antag.dm index 9dce0940b3f..7f9d113b124 100644 --- a/code/modules/mod/modules/modules_antag.dm +++ b/code/modules/mod/modules/modules_antag.dm @@ -90,7 +90,7 @@ if (!active) module_slowdowns += space_slowdown -/obj/item/mod/module/armor_booster/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/armor_booster/generate_worn_overlay(obj/item/source, mutable_appearance/standing) overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" overlay_state_active = "[initial(overlay_state_active)]-[mod.skin]" return ..() @@ -105,9 +105,11 @@ REMOVE_TRAIT(mod.wearer, TRAIT_HEAD_INJURY_BLOCKED, REF(src)) /obj/item/mod/module/armor_booster/on_install() + . = ..() RegisterSignal(mod, COMSIG_MOD_GET_VISOR_OVERLAY, PROC_REF(on_visor_overlay)) -/obj/item/mod/module/armor_booster/on_uninstall(deleting) +/obj/item/mod/module/armor_booster/on_uninstall(deleting = FALSE) + . = ..() UnregisterSignal(mod, COMSIG_MOD_GET_VISOR_OVERLAY) /obj/item/mod/module/armor_booster/proc/on_visor_overlay(datum/source, mutable_appearance/standing, list/overrides) @@ -241,7 +243,7 @@ overlay_state_inactive = "module_insignia" mask_worn_overlay = TRUE -/obj/item/mod/module/insignia/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/insignia/generate_worn_overlay(obj/item/source, mutable_appearance/standing) overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" . = ..() for(var/mutable_appearance/appearance as anything in .) @@ -409,6 +411,7 @@ var/obj/item/current_disguise /obj/item/mod/module/chameleon/on_install() + . = ..() var/list/all_disguises = sort_list(subtypesof(get_path_by_slot(mod.slot_flags)), GLOBAL_PROC_REF(cmp_typepaths_asc)) for(var/clothing_path in all_disguises) var/obj/item/clothing = clothing_path @@ -418,6 +421,7 @@ possible_disguises[chameleon_item_name] = clothing_path /obj/item/mod/module/chameleon/on_uninstall(deleting = FALSE) + . = ..() if(current_disguise) return_look() possible_disguises = null @@ -449,7 +453,7 @@ mod.righthand_file = initial(current_disguise.righthand_file) mod.worn_icon_state = initial(current_disguise.worn_icon_state) mod.inhand_icon_state = initial(current_disguise.inhand_icon_state) - mod.wearer.update_clothing(mod.slot_flags) + update_clothing_slots() RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(return_look)) /obj/item/mod/module/chameleon/proc/return_look() @@ -463,7 +467,7 @@ mod.righthand_file = initial(mod.righthand_file) mod.worn_icon_state = null mod.inhand_icon_state = null - mod.wearer.update_clothing(mod.slot_flags) + update_clothing_slots() current_disguise = null UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) @@ -481,10 +485,12 @@ var/old_size /obj/item/mod/module/plate_compression/on_install() + . = ..() old_size = mod.w_class mod.update_weight_class(new_size) /obj/item/mod/module/plate_compression/on_uninstall(deleting = FALSE) + . = ..() mod.update_weight_class(old_size) old_size = null if(!mod.loc) @@ -528,9 +534,11 @@ var/list/traits_to_add = list(TRAIT_SILENT_FOOTSTEPS, TRAIT_UNKNOWN, TRAIT_HEAD_INJURY_BLOCKED) /obj/item/mod/module/infiltrator/on_install() + . = ..() ADD_TRAIT(mod, TRAIT_EXAMINE_SKIP, REF(src)) /obj/item/mod/module/infiltrator/on_uninstall(deleting = FALSE) + . = ..() REMOVE_TRAIT(mod, TRAIT_EXAMINE_SKIP, REF(src)) /obj/item/mod/module/infiltrator/on_part_activation() diff --git a/code/modules/mod/modules/modules_engineering.dm b/code/modules/mod/modules/modules_engineering.dm index 9af2ea0bc5a..018d6d35275 100644 --- a/code/modules/mod/modules/modules_engineering.dm +++ b/code/modules/mod/modules/modules_engineering.dm @@ -62,9 +62,11 @@ var/list/active_traits = list(TRAIT_NO_SLIP_WATER, TRAIT_NO_SLIP_ICE, TRAIT_NO_SLIP_SLIDE, TRAIT_NEGATES_GRAVITY) /obj/item/mod/module/magboot/on_install() + . = ..() RegisterSignal(mod, COMSIG_MOD_UPDATE_SPEED, PROC_REF(on_update_speed)) -/obj/item/mod/module/magboot/on_uninstall(deleting) +/obj/item/mod/module/magboot/on_uninstall(deleting = FALSE) + . = ..() UnregisterSignal(mod, COMSIG_MOD_UPDATE_SPEED) /obj/item/mod/module/magboot/on_activation() diff --git a/code/modules/mod/modules/modules_general.dm b/code/modules/mod/modules/modules_general.dm index 2ddb7ccdc66..3a7c0792b31 100644 --- a/code/modules/mod/modules/modules_general.dm +++ b/code/modules/mod/modules/modules_general.dm @@ -25,6 +25,7 @@ atom_storage.set_locked(STORAGE_FULLY_LOCKED) /obj/item/mod/module/storage/on_install() + . = ..() var/datum/storage/modstorage = mod.create_storage(max_specific_storage = max_w_class, max_total_storage = max_combined_w_class, max_slots = max_items) modstorage.set_real_location(src) modstorage.allow_big_nesting = big_nesting @@ -34,6 +35,7 @@ RegisterSignal(suit, COMSIG_ITEM_PRE_UNEQUIP, PROC_REF(on_suit_unequip)) /obj/item/mod/module/storage/on_uninstall(deleting = FALSE) + . = ..() atom_storage.set_locked(STORAGE_FULLY_LOCKED) QDEL_NULL(mod.atom_storage) if(!deleting) @@ -328,6 +330,7 @@ var/former_visor_mask_flags = NONE /obj/item/mod/module/mouthhole/on_install() + . = ..() var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) if(istype(helmet)) former_helmet_flags = helmet.flags_cover @@ -351,6 +354,7 @@ return FALSE /obj/item/mod/module/mouthhole/on_uninstall(deleting = FALSE) + . = ..() if(deleting) return var/obj/item/clothing/helmet = mod.get_part_from_slot(ITEM_SLOT_HEAD) @@ -375,9 +379,11 @@ required_slots = list(ITEM_SLOT_BACK|ITEM_SLOT_BELT) /obj/item/mod/module/emp_shield/on_install() + . = ..() mod.AddElement(/datum/element/empprotection, EMP_PROTECT_ALL) /obj/item/mod/module/emp_shield/on_uninstall(deleting = FALSE) + . = ..() mod.RemoveElement(/datum/element/empprotection, EMP_PROTECT_ALL) /obj/item/mod/module/emp_shield/advanced @@ -437,7 +443,7 @@ active_power_cost = base_power * light_range return ..() -/obj/item/mod/module/flashlight/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/flashlight/generate_worn_overlay(obj/item/source, mutable_appearance/standing) . = ..() if(!active) return @@ -460,7 +466,7 @@ balloon_alert(mod.wearer, "too dark!") return set_light_color(value) - mod.wearer.update_clothing(mod.slot_flags) + update_clothing_slots() if("light_range") set_light_range(clamp(value, min_range, max_range)) @@ -594,12 +600,14 @@ var/dna = null /obj/item/mod/module/dna_lock/on_install() + . = ..() RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation)) RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal)) RegisterSignal(mod, COMSIG_ATOM_EMP_ACT, PROC_REF(on_emp)) RegisterSignal(mod, COMSIG_ATOM_EMAG_ACT, PROC_REF(on_emag)) /obj/item/mod/module/dna_lock/on_uninstall(deleting = FALSE) + . = ..() UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) UnregisterSignal(mod, COMSIG_ATOM_EMP_ACT) @@ -665,11 +673,15 @@ incompatible_modules = list(/obj/item/mod/module/plasma_stabilizer) required_slots = list(ITEM_SLOT_HEAD) -/obj/item/mod/module/plasma_stabilizer/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/plasma_stabilizer/generate_worn_overlay(obj/item/source, mutable_appearance/standing) + . = ..() + if (!.) + return + var/mutable_appearance/visor_overlay = mod.get_visor_overlay(standing) visor_overlay.appearance_flags |= RESET_COLOR visor_overlay.color = COLOR_VIOLET - return list(visor_overlay) + . += visor_overlay /obj/item/mod/module/plasma_stabilizer/on_equip() ADD_TRAIT(mod.wearer, TRAIT_HEAD_ATMOS_SEALED, REF(src)) diff --git a/code/modules/mod/modules/modules_maint.dm b/code/modules/mod/modules/modules_maint.dm index 989481af76c..3d4f17b7f7a 100644 --- a/code/modules/mod/modules/modules_maint.dm +++ b/code/modules/mod/modules/modules_maint.dm @@ -17,9 +17,11 @@ var/step_change = 0.5 /obj/item/mod/module/springlock/on_install() + . = ..() mod.activation_step_time *= step_change /obj/item/mod/module/springlock/on_uninstall(deleting = FALSE) + . = ..() mod.activation_step_time /= step_change /obj/item/mod/module/springlock/on_part_activation() @@ -149,18 +151,22 @@ return SEND_SOUND(mod.wearer, sound('sound/machines/terminal/terminal_off.ogg', volume = 50, channel = CHANNEL_JUKEBOX)) -/obj/item/mod/module/visor/rave/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/visor/rave/generate_worn_overlay(obj/item/source, mutable_appearance/standing) + . = ..() + if (!.) + return + var/mutable_appearance/visor_overlay = mod.get_visor_overlay(standing) visor_overlay.appearance_flags |= RESET_COLOR if (!isnull(music_player.active_song_sound)) visor_overlay.color = rainbow_order[rave_number] - return list(visor_overlay) + . += visor_overlay /obj/item/mod/module/visor/rave/on_active_process(seconds_per_tick) rave_number++ if(rave_number > length(rainbow_order)) rave_number = 1 - mod.wearer.update_clothing(mod.slot_flags) + update_clothing_slots() rave_screen.update_color(rainbow_order[rave_number]) /obj/item/mod/module/visor/rave/get_configuration() diff --git a/code/modules/mod/modules/modules_ninja.dm b/code/modules/mod/modules/modules_ninja.dm index 5f7abdd712b..e75141cf3d5 100644 --- a/code/modules/mod/modules/modules_ninja.dm +++ b/code/modules/mod/modules/modules_ninja.dm @@ -93,11 +93,9 @@ required_slots = list(ITEM_SLOT_HEAD|ITEM_SLOT_EYES|ITEM_SLOT_MASK) /obj/item/mod/module/welding/camera_vision/on_part_activation() - . = ..() RegisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK, PROC_REF(can_track)) /obj/item/mod/module/welding/camera_vision/on_part_deactivation(deleting = FALSE) - . = ..() UnregisterSignal(mod.wearer, COMSIG_LIVING_CAN_TRACK) /obj/item/mod/module/welding/camera_vision/proc/can_track(datum/source, mob/user) @@ -422,9 +420,11 @@ addtimer(CALLBACK(src, PROC_REF(boost_aftereffects), mod.wearer), 7 SECONDS) /obj/item/mod/module/adrenaline_boost/on_install() + . = ..() RegisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION, PROC_REF(try_boost)) /obj/item/mod/module/adrenaline_boost/on_uninstall(deleting = FALSE) + . = ..() UnregisterSignal(mod, COMSIG_ATOM_ITEM_INTERACTION) /obj/item/mod/module/adrenaline_boost/proc/try_boost(source, mob/user, obj/item/attacking_item) diff --git a/code/modules/mod/modules/modules_security.dm b/code/modules/mod/modules/modules_security.dm index 06bd9352a9c..a19cd09db2b 100644 --- a/code/modules/mod/modules/modules_security.dm +++ b/code/modules/mod/modules/modules_security.dm @@ -22,6 +22,7 @@ guns_typecache = typecacheof(list(/obj/item/gun/ballistic, /obj/item/gun/energy, /obj/item/gun/grenadelauncher, /obj/item/gun/chem, /obj/item/gun/syringe)) /obj/item/mod/module/magnetic_harness/on_install() + . = ..() var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) if(!istype(suit)) return @@ -29,6 +30,7 @@ suit.allowed |= guns_typecache /obj/item/mod/module/magnetic_harness/on_uninstall(deleting = FALSE) + . = ..() if(deleting) return var/obj/item/clothing/suit = mod.get_part_from_slot(ITEM_SLOT_OCLOTHING) @@ -135,6 +137,7 @@ balloon_alert(mod.wearer, "holster full!") /obj/item/mod/module/holster/on_uninstall(deleting = FALSE) + . = ..() if(holstered) holstered.forceMove(drop_location()) diff --git a/code/modules/mod/modules/modules_supply.dm b/code/modules/mod/modules/modules_supply.dm index 3e9d47ebfb0..3eee7969e9c 100644 --- a/code/modules/mod/modules/modules_supply.dm +++ b/code/modules/mod/modules/modules_supply.dm @@ -422,7 +422,7 @@ mod.update_speed() traveled_tiles = 0 -/obj/item/mod/module/ash_accretion/generate_worn_overlay(mutable_appearance/standing) +/obj/item/mod/module/ash_accretion/generate_worn_overlay(obj/item/source, mutable_appearance/standing) overlay_state_inactive = "[initial(overlay_state_inactive)]-[mod.skin]" return ..() diff --git a/code/modules/mod/modules/modules_timeline.dm b/code/modules/mod/modules/modules_timeline.dm index 654aaa0e8ee..701cbc30421 100644 --- a/code/modules/mod/modules/modules_timeline.dm +++ b/code/modules/mod/modules/modules_timeline.dm @@ -20,10 +20,12 @@ var/true_owner_ckey /obj/item/mod/module/eradication_lock/on_install() + . = ..() RegisterSignal(mod, COMSIG_MOD_ACTIVATE, PROC_REF(on_mod_activation)) RegisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL, PROC_REF(on_mod_removal)) /obj/item/mod/module/eradication_lock/on_uninstall(deleting = FALSE) + . = ..() UnregisterSignal(mod, COMSIG_MOD_ACTIVATE) UnregisterSignal(mod, COMSIG_MOD_MODULE_REMOVAL) @@ -228,6 +230,7 @@ INVOKE_ASYNC(chrono_beam, TYPE_PROC_REF(/obj/projectile, fire)) /obj/item/mod/module/tem/on_uninstall(deleting = FALSE) + . = ..() if(!field) return field_disconnect(field) diff --git a/code/modules/wiremod/shell/module.dm b/code/modules/wiremod/shell/module.dm index bec6ce8e5c7..a3c1f8572ea 100644 --- a/code/modules/wiremod/shell/module.dm +++ b/code/modules/wiremod/shell/module.dm @@ -41,11 +41,13 @@ QDEL_LIST_ASSOC_VAL(action_comp.granted_to) /obj/item/mod/module/circuit/on_install() + . = ..() if(!shell?.attached_circuit) return RegisterSignal(shell?.attached_circuit, COMSIG_CIRCUIT_PRE_POWER_USAGE, PROC_REF(override_power_usage)) /obj/item/mod/module/circuit/on_uninstall(deleting = FALSE) + . = ..() if(!shell?.attached_circuit) return for(var/obj/item/circuit_component/equipment_action/action_comp in action_comps)