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
🆑
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!
/🆑
This commit is contained in:
SmArtKar
2025-04-15 20:21:10 +12:00
committed by GitHub
parent f6cf7a03c5
commit b49553bdf4
19 changed files with 234 additions and 147 deletions
+10 -27
View File
@@ -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