Files
Citadel-Station-13-RP/code/game/objects/item-inventory-rendering.dm
b4a124e557 [IDB IGNORE] cyborg refactor: icons, chassis, modules base (#6929)
- separates the composition of cyborgs into modules, iconsets, chassis,
upgrades
- modules / chassis atm can provide items, so can upgrades; in the
future we will want to probably generalize the item API a bit so
components can work
- chassis now can (unimplemented) easily be made to give different stats
- reorganizes all the cyborg sprites
- adds generalized item mounts with redirection support and tears out
legacy cyborg items
- entirely rebalances modules & writes some new lawsets in for future
use

---------

Co-authored-by: silicons <silicons@silicons.dev>
2026-01-13 18:39:38 +00:00

503 lines
22 KiB
Plaintext

//* This file is explicitly licensed under the MIT license. *//
//* Copyright (c) 2024 Citadel Station Developers *//
/**
* Item rendering system
* Procs in here can be called and overridden as needed, but you should know what you're doing
* if you choose to do so!
*
* * IF YOU ONLY CARE ABOUT MAKING A NEW ITEM OR ARE CONVERTING AN ITEM, JUST READ THIS!!
*
* 0. Do you want the on-mob icons to be *entirely* shared/defaulted? If so, why are you here?
* Use the old system, by putting your states into the default icons as defined in
* [code/modules/mob/inventory/slot_meta.dm]
* and setting inhand_state and worn_state so they're found.
* If you want to share *some* icons, read State Generation and
* use worn_icon, and inhand_icon to do what you need to do.
*
* !! If your sprite already exists in default icons, it will override what !!
* !! we do here. This is an unfortunate tradeoff of speed we have to make. !!
* !! Make sure to set worn_species_default to FALSE to stop this. !!
* ?? THIS IS SOMETIMES A GOOD THING. ??
* ?? There's common states available for inhands and some worn stuff. ??
* ?? You just need to set inhand_state and worn_state to use them! ??
*
* Otherwise, continue on with the rest of the steps, setting
* worn_render_flags = NONE
* to instruct the system to ignore default icons entirely.
*
* ?? A note on bitfields: worn_render_flags ??
* ?? Without getting into the nitty gritty, worn_render_flags control all ??
* ?? behavior and logic. When the guide below says "add" a flag of a certain ??
* ?? name to it, it means boolean OR it. ??
* ?? For noncoders, basically: to combine, say, WORN_RENDER_SLOT_ONE_FOR_ALL ??
* ?? and WORN_RENDER_INHAND_ONE_FOR_ALL, you just put a | between them, like so: ??
* ?? worn_render_flags = WORN_RENDER_SLOT_ONE_FOR_ALL | WORN_RENDER_INHAND_ONE_FOR_ALL ??
* ?? Congratulations, you used a bitfield. ??
*
* 1. Have a sensical place for your item's .dmi.
* e.g. /obj/item/pickaxe goes in icons/modules/mining/tools/pickaxe.dmi
* 2. Put what the item looks like in the world, and in your inventory, as the base state.
* e.g. a sprite of a pickaxe as state `pickaxe`. Set your item's icon_state to this,
* and its icon to the file you made.
* 3. Add inhands if needed with state_left and state_right.
* In this case, it'd be
* pickaxe_left
* pickaxe_right
* If you for some reason want it to use a singular state only when inhand,
* set worn_inhand_ignored to TRUE, and it'll render as pickaxe_all instead.
* 4. Does the item go in multiple slots and require multiple sprites?
* If not, add WORN_RENDER_SLOT_ONE_TO_ALL to worn_render_flags,
* and it'll render as pickaxe_all always.
* pickaxe_all
* If so, set states for each slot id, e.g. in this case,
* pickaxe_back
* pickaxe_belt
* A full list of slot ids are in [code/modules/mob/inventory/slot_meta.dm].
* 5. Do you care about bodytypes? If so, set worn_bodytypes to the bodytypes you want
* to implement with bitfield notation, aka
* worn_bodytypes = BODYTYPE_TESHARI | BODYTYPE_UNATHI
* Then, put in states for the bodytypes in question like so:
* pickaxe_back_teshari
* pickaxe_back_unathi
* A full list of available bodytypes and what the string keys for them are
* in [code/__DEFINES/inventory/bodytypes.dm].
* The default bodytypes, as well as any bodytypes that aren't explicitly set as implemented,
* do not get appended (e.g. if a human or vox puts it on it's still pickaxe_back)
* 6. Do you want the base state, in this case 'pickaxe', for on-mob generation
* to be different from icon_state? This is useful if you want to switch
* your item's icon state or worn state without impacting the others.
* If so, set overrides for worn (held in inventory slots) and inhand (held in hands)
* worn_state = "pickaxe_onbody"
* inhand_state = "pickaxe_onhand"
* like so. Generation for the previous parts will become
* pickaxe_onbody_back
* pickaxe_onbody_belt
* or in the example of "ignore slot"
* pickaxe_onbody_all
* and for inhands
* pickaxe_onhand_left
* pickaxe_onhand_right
* 7. Defaulting: In the case you want to default slot/inhands (usually one but not the other),
* simply add to worn_render_flags, depending on which:
* WORN_RENDER_SLOT_ALLOW_DEFAULT
* WORN_RENDER_INHAND_ALLOW_DEFAULt
* 8. Congratulations, you're done!
*
* * Overall Goal
* - Being able to stuff an entire item's assets in one icon
* - Otherwise being able to split an entire item's assets into modular folders
* - Generic inhands and slot icons go in general onmob item folders
* - Not forcing usage of something as asinine as suit.dmi
*
* * Limitations
* - inhand state only supports left and right; no weird index-based for now. this can easily change.
*
* * State Generation
* This is only pertinent to single-icon rendering, which the system falls back to
* if it can't find anything else. This is what people should be using for non-reused
* item sprites.
*
* This system also supports worn_icon and inhand_icon if you really want to split it up.
*
* Pattern:
*
* state_slot_bodytype, where
* state: (inhand_state if inhand, worn_state if not) || icon_state
* slot: the inventory slot's `render_key`, or null if worn slot is ignored
* if it's a hand, it'll be _left or _right if it's not ignored.
* bodytype: if not trampled, or ignored, the bodytype as string (see DEFINES)
* if converted to default, or it is default, or is ignored, this is null
*
* Obviously the _ part of the state is left out if the part doesn't exist
*
* Examples:
* in world/on map/in inventory:
* pickaxe
*
* worn:
* pickaxe_belt (implicit default bodytype)
* pickaxe_belt_teshari
* pickaxe_left
* pickaxe_right
* pickaxe_right_teshari (overrides on teshari)
*
* * Coloration
*
* ! Coloration WIP. For now, color var only.
* ! TODO: GAGS, polychromatic overlays with _1, _2, _3, _..., and RED/BLUE, RED/GREEN, GREEN/BLUE matrices.
* ! TODO: For most of these, it will require mutating the icon states used.
*
* * Alignment
*
* Mobs are usually able to be shifted left/right, but are always aligned so that their bottom pixels
* are on the first pixel of their 'real' tile.
*
* With that in mind, we allow using the x_mob_y_align variable to shift sprites up, to avoid
* needing entirely centered sprites.
*
* That said, x alignment shifting will never happen due to limitations, so x still has to use centering.
*
* * Why mutable appearances?
* Rendering of equipment changes regularly. They're quite literally built to be changed.
* Since items always have the same direction as wearer, this means we don't have to use images.
*
* * Centering
* All sprites are centered on the mob regardless of dimensions.
* The species/mob in question can then pixel shift the resulting object as fit.
*
* ? Everything else: read the procs.
*/
/obj/item
//! LEGACY
//** These specify item/icon overrides for _slots_
/// Overrides the default item_state for particular slots.
var/list/item_state_slots = list()
/// Used to specify the icon file to be used when the item is worn. If not set the default icon for that slot will be used.
/// If icon_override or sprite_sheets are set they will take precendence over this, assuming they apply to the slot in question.
/// Only slot_l_hand/slot_r_hand are implemented at the moment. Others to be implemented as needed.
var/list/item_icons
/// Used to override hardcoded clothing dmis in human clothing proc. //TODO: Get rid of this crap -Zandario
var/icon_override = null
//** These specify item/icon overrides for _species_
//TODO Refactor this from the ground up. Too many overrides. -Zandario
//! THIS IS NOW BANNED. DO NOT USE THIS OR I WILL PUT A LEMON ON YOUR EYES. ~silicons
//! Use the new bodytype system, PLEASE.
//! Accessories are currently exempt from the ban, but also accessories need refactored
//! God this is fucking asinine
/* Species-specific sprites, concept stolen from Paradise//vg/.
* ex:
* sprite_sheets = list(
* BODYTYPE_STRING_TAJARAN = 'icons/cat/are/bad'
* )
* If index term exists and icon_override is not set, this sprite sheet will be used.
*/
var/list/sprite_sheets
/// Species-specific sprite sheets for inventory sprites
/// Works similarly to worn sprite_sheets, except the alternate sprites are used when the clothing/refit_for_species() proc is called.
var/list/sprite_sheets_obj
// todo: remove
/// worn icon file
var/icon/default_worn_icon
//! NEW RENDERING SYSTEM (to be used by all new content tm); read comment section at top
//? for when base icon is used for render
/// icon alignment y shift
var/icon_mob_y_align = 0
//? for equipment slots: prioritized over icon, icon_state, icon dimensions
/// state to use; icon_state is used if this isn't set
var/worn_state
/// worn icon used instead of base icon
var/icon/worn_icon
/// dimensions of our worn icon file if different from icon
var/worn_x_dimension = 32
/// dimensions of our worn icon file if different from icon
var/worn_y_dimension = 32
/// worn icon alignment y shift
var/worn_mob_y_align = 0
//? for hands: prioritized over icon, icon_state, icon dimensions
/// state to use; worn_state, then icon_state is used if this isn't set
var/inhand_state
/// inhand icon used instead of base icon
var/icon/inhand_icon
/// dimensions of inhand sprites if different from icon
var/inhand_x_dimension = 32
/// dimensions of inhand sprites if different from icon
var/inhand_y_dimension = 32
/// inhnad icon alignment y shift
var/inhand_mob_y_align = 0
/// inhand default domain aka which icon we grab to check for state
var/inhand_default_type = INHAND_DEFAULT_ICON_GENERAL
//? for belts
/// state to use in [icons/mob/clothing/belt.dmi] for belt overlay
var/belt_state
//? general handling directives
/**
* bodytypes that *can* get trampled to default if the default icon is not found on species
*
* for slot defaults, this means using the default icon state.
* for new rendering, this means using the fallback state on the slot for a bodytype.
*/
var/datum/bodytypes/worn_bodytypes_fallback = BODYTYPES_ALL
/// bodytypes that are implemented. Anything not in here is converted to default, if slot fallback state is unavailable.
var/datum/bodytypes/worn_bodytypes = BODYTYPES(BODYTYPE_DEFAULT)
/// bodytypes that just skip rendering (i hate teshari)
var/datum/bodytypes/worn_bodytypes_invisible = BODYTYPES_NONE
/// worn rendering flags
var/worn_render_flags = WORN_RENDER_INHAND_ALLOW_DEFAULT | WORN_RENDER_SLOT_ALLOW_DEFAULT
//? support for adminbus
/// vv only; slot id to icon; worn_x_dimension and worn_y_dimension will be used in this case.
VAR_PRIVATE/list/worn_icon_override
/// vv only; slot id to state
VAR_PRIVATE/list/worn_state_override
/// vv only; set to override layer
VAR_PRIVATE/worn_layer_override
/obj/item/Initialize(mapload)
. = ..()
CONSTRUCT_BODYTYPES(worn_bodytypes)
CONSTRUCT_BODYTYPES(worn_bodytypes_invisible)
CONSTRUCT_BODYTYPES(worn_bodytypes_fallback)
/**
* update our worn icon if we can
*/
/obj/item/proc/update_worn_icon()
if(!worn_slot)
return // acceptable
var/mob/M = get_worn_mob()
ASSERT(M) // not acceptable
if(held_index)
M.update_inv_hand(held_index)
return
switch(worn_slot)
if(SLOT_ID_BACK)
M.update_inv_back()
if(SLOT_ID_BELT)
M.update_inv_belt()
if(SLOT_ID_GLASSES)
M.update_inv_glasses()
if(SLOT_ID_GLOVES)
M.update_inv_gloves()
if(SLOT_ID_HANDCUFFED)
M.update_inv_handcuffed()
if(SLOT_ID_HANDS)
CRASH("why did we go here when we should have short-circuited at the held_index check?")
if(SLOT_ID_HEAD)
M.update_inv_head()
if(SLOT_ID_LEFT_EAR, SLOT_ID_RIGHT_EAR)
M.update_inv_ears()
if(SLOT_ID_MASK)
M.update_inv_wear_mask()
if(SLOT_ID_SHOES)
M.update_inv_shoes()
if(SLOT_ID_SUIT)
M.update_inv_wear_suit()
if(SLOT_ID_SUIT_STORAGE)
M.update_inv_s_store()
if(SLOT_ID_UNIFORM)
M.update_inv_w_uniform()
if(SLOT_ID_WORN_ID)
M.update_inv_wear_id()
/**
* Renders either a list, or a single image or mutable appearance of what we should be applied to a mob with.
*
* @params
* * M - the mob we're rendering
* * slot_id_or_hand_index - the slot ID or numerical held index we're in
* * bodytype - the effective bodytype
*/
/obj/item/proc/render_mob_appearance(mob/M, slot_id_or_hand_index, bodytype = BODYTYPE_DEFAULT)
// SHOULD_NOT_OVERRIDE(TRUE) // if you think you need to, rethink.
// todo: eh reevaluate later
// determine if in hands
var/inhands = isnum(slot_id_or_hand_index)? slot_id_or_hand_index : null
var/datum/inventory_slot/slot_meta
// resolve slot
if(inhands)
slot_meta = resolve_inventory_slot((slot_id_or_hand_index % 2)? /datum/inventory_slot/abstract/hand/left : /datum/inventory_slot/abstract/hand/right)
else
slot_meta = resolve_inventory_slot(slot_id_or_hand_index)
var/list/resolved = resolve_worn_assets(M, slot_meta, inhands, bodytype)
var/rendered = list(_render_mob_appearance(M, slot_meta, inhands, bodytype, resolved[WORN_DATA_ICON], resolved[WORN_DATA_STATE], resolved[WORN_DATA_LAYER], resolved [WORN_DATA_SIZE_X], resolved[WORN_DATA_SIZE_Y], resolved[WORN_DATA_ALIGN_Y]))
SEND_SIGNAL(M, COMSIG_CARBON_UPDATING_OVERLAY, rendered, CARBON_APPEARANCE_UPDATE_CLOTHING)
return rendered[1]
/obj/item/proc/_render_mob_appearance(mob/M, datum/inventory_slot/slot_meta, inhands, bodytype, icon_used, state_used, layer_used, dim_x, dim_y, align_y)
SHOULD_NOT_OVERRIDE(TRUE) // if you think you need to, rethink.
PRIVATE_PROC(TRUE) // if you think you need to call this, rethink.
var/list/additional = render_additional(M, icon_used, state_used, layer_used, dim_x, dim_y, align_y, bodytype, inhands, slot_meta)
// todo: signal with (args, add)
// todo: args' indices should be defines
var/no_render = inhands? (worn_render_flags & WORN_RENDER_INHAND_NO_RENDER) : ((worn_render_flags & WORN_RENDER_SLOT_NO_RENDER) || worn_bodytypes_invisible?.contains(bodytype))
var/mutable_appearance/MA
// worn_state_guard makes us not render if we'd render the same as in-inventory icon.
if(no_render) // don't bother
return additional
MA = mutable_appearance(icon_used, state_used, layer_used, FLOAT_PLANE)
// temporary - until coloration
MA.color = color
MA = center_appearance(MA, dim_x, dim_y)
MA.pixel_y += align_y
MA = render_apply_overlays(MA, bodytype, inhands, slot_meta, icon_used)
MA = render_apply_blood(MA, bodytype, inhands, slot_meta, icon_used)
MA = render_apply_custom(M, MA, bodytype, inhands, slot_meta, icon_used, align_y)
return length(additional)? (additional + MA) : MA
/**
* override to apply custom stuff to rendering; called last
*
* icon/icon state/layer information is included in the mutable appearance
*/
/obj/item/proc/render_apply_custom(mob/M, mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used, align_y)
return MA
/**
* override to determine how we apply blood overlays to rendering
*
* icon/icon state/layer information is included in the mutable appearance
*/
/obj/item/proc/render_apply_blood(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used)
return MA
/**
* override to apply overlays to our current mutable appearance; called first
*/
/obj/item/proc/render_apply_overlays(mutable_appearance/MA, bodytype, inhands, datum/inventory_slot/slot_meta, icon_used)
if(addblends)
var/mutable_appearance/adding = mutable_appearance(icon = MA.icon, icon_state = addblends)
adding.blend_mode = BLEND_ADD
MA.add_overlay(adding)
return MA
/**
* override to include additional appearances while rendering
*/
/obj/item/proc/render_additional(mob/M, icon/icon_used, state_used, layer_used, dim_x, dim_y, align_y, bodytype, inhands, datum/inventory_slot/slot_meta)
RETURN_TYPE(/list)
return list()
/**
* returns a tuple of (icon, state, layer, size_x, size_y)
*
* @params
* - M - mob putting us on; optional
* - slot_meta - inventory slot datum
* - inhands - if we're going to inhands
* - bodytype - bodytype in question
*/
/obj/item/proc/resolve_worn_assets(mob/M, datum/inventory_slot/slot_meta, inhands, bodytype)
if(istext(slot_meta))
slot_meta = resolve_inventory_slot(slot_meta)
var/list/data = new /list(WORN_DATA_LIST_SIZE)
//? state ; item_state_slots --> (worn_state | inhand_state) --> item_state --> icon_state
data[WORN_DATA_STATE] = resolve_legacy_state(M, slot_meta, inhands, bodytype)
//? icon, size
//* icon_override
if(icon_override)
data[WORN_DATA_ICON] = icon_override
switch(slot_meta.id)
if(SLOT_ID_LEFT_HAND)
data[WORN_DATA_STATE] += "_l"
if(SLOT_ID_RIGHT_HAND)
data[WORN_DATA_STATE] += "_r"
if(SLOT_ID_LEFT_EAR)
data[WORN_DATA_STATE] += "_l"
if(SLOT_ID_RIGHT_EAR)
data[WORN_DATA_STATE] += "_l"
data[WORN_DATA_SIZE_X] = worn_x_dimension
data[WORN_DATA_SIZE_Y] = worn_y_dimension
//* species-specific sprite sheets
else if(length(sprite_sheets) && !inhands && (sprite_sheets[bodytype_to_string(bodytype)]))
data[WORN_DATA_ICON] = sprite_sheets[bodytype_to_string(bodytype)]
data[WORN_DATA_SIZE_X] = WORLD_ICON_SIZE
data[WORN_DATA_SIZE_Y] = WORLD_ICON_SIZE
//* slot-specific sprite sheets
else if(item_icons?[slot_meta.id])
data[WORN_DATA_ICON] = item_icons[slot_meta.id]
data[WORN_DATA_SIZE_X] = worn_x_dimension
data[WORN_DATA_SIZE_Y] = worn_y_dimension
//* item default_worn_icon override
else if(default_worn_icon && !inhands)
// todo: rework
data[WORN_DATA_ICON] = default_worn_icon
data[WORN_DATA_SIZE_X] = worn_x_dimension
data[WORN_DATA_SIZE_Y] = worn_y_dimension
//* inventory slot defaults
else if(inhands? (worn_render_flags & WORN_RENDER_INHAND_ALLOW_DEFAULT) : (worn_render_flags & WORN_RENDER_SLOT_ALLOW_DEFAULT))
var/list/resolved = slot_meta.resolve_default_assets(bodytype, data[WORN_DATA_STATE], M, src, inhand_default_type)
if(!resolved && (bodytype != BODYTYPE_DEFAULT) && worn_bodytypes_fallback?.contains(bodytype))
// attempt 2 - use fallback if available
if(!(slot_meta.handle_worn_fallback(bodytype, data)))
// attempt 3 - convert to default if specified to convert
resolved = slot_meta.resolve_default_assets(BODYTYPE_DEFAULT, data[WORN_DATA_STATE], M, src, inhand_default_type)
if(resolved)
data[WORN_DATA_ICON] = resolved[1]
data[WORN_DATA_SIZE_X] = resolved[2]
data[WORN_DATA_SIZE_Y] = resolved[3]
//* Now, the actual intended render system.
if(!data[WORN_DATA_ICON])
// grab icon based on priority
if(!inhands && !worn_bodytypes?.contains(bodytype) && worn_bodytypes_fallback?.contains(bodytype) && slot_meta.handle_worn_fallback(bodytype, data))
// special: if bodytypes isn't in, and species has fallback
// .. well don't do anything as handle_sprite_fallback will write to the data list.
else if(inhands && inhand_icon)
data[WORN_DATA_ICON] = inhand_icon
data[WORN_DATA_SIZE_X] = inhand_x_dimension
data[WORN_DATA_SIZE_Y] = inhand_y_dimension
data[WORN_DATA_ALIGN_Y] = inhand_mob_y_align
data[WORN_DATA_STATE] = resolve_worn_state(inhands, (worn_render_flags & WORN_RENDER_SLOT_USE_PLURAL) ?(slot_meta.render_key_plural || slot_meta.render_key) : slot_meta.render_key, bodytype)
else if(!inhands && worn_icon)
data[WORN_DATA_ICON] = worn_icon
data[WORN_DATA_SIZE_X] = worn_x_dimension
data[WORN_DATA_SIZE_Y] = worn_y_dimension
data[WORN_DATA_ALIGN_Y] = worn_mob_y_align
data[WORN_DATA_STATE] = resolve_worn_state(inhands, (worn_render_flags & WORN_RENDER_SLOT_USE_PLURAL)? (slot_meta.render_key_plural || slot_meta.render_key) : slot_meta.render_key, bodytype)
else
data[WORN_DATA_ICON] = icon
data[WORN_DATA_SIZE_X] = icon_x_dimension
data[WORN_DATA_SIZE_Y] = icon_y_dimension
data[WORN_DATA_ALIGN_Y] = icon_mob_y_align
data[WORN_DATA_STATE] = resolve_worn_state(inhands, (worn_render_flags & WORN_RENDER_SLOT_USE_PLURAL)? (slot_meta.render_key_plural || slot_meta.render_key) : slot_meta.render_key, bodytype)
//? layer ; worn_layer --> slot defaults for the item in question
data[WORN_DATA_LAYER] = worn_layer_override || slot_meta.resolve_default_layer(bodytype, M, src)
//* Handle overrides
if(LAZYACCESS(worn_icon_override, slot_meta.id))
data[WORN_DATA_ICON] = worn_icon_override[slot_meta.id]
// if you fuck this up, Skill Issue. We have to align somehow.
data[WORN_DATA_SIZE_X] = icon_x_dimension
data[WORN_DATA_SIZE_Y] = icon_y_dimension
if(LAZYACCESS(worn_state_override, slot_meta.id))
data[WORN_DATA_STATE] = worn_state_override[slot_meta.id]
return data
/obj/item/proc/debug_worn_assets(slot_or_id, mob/M = get_worn_mob(), bodytype)
var/mob/living/carbon/human/H = ishuman(M)? M : null
var/datum/inventory_slot/slot_meta
if(isnull(slot_or_id))
slot_or_id = inv_slot_or_index
if(isnum(slot_or_id))
slot_meta = resolve_inventory_slot((slot_or_id % 2)? /datum/inventory_slot/abstract/hand/left : /datum/inventory_slot/abstract/hand/right)
else
slot_meta = resolve_inventory_slot(slot_or_id)
if(isnull(bodytype) && H)
bodytype = H.species.get_effective_bodytype(H, src, slot_meta)
. = resolve_worn_assets(M, slot_meta, isnum(slot_or_id), bodytype)
.[WORN_DATA_ICON] = "[.[WORN_DATA_ICON]]"
// todo: remove, aka get rid of fucking uniform _s state
/obj/item/proc/resolve_legacy_state(mob/M, datum/inventory_slot/slot_meta, inhands, bodytype)
return (item_state_slots?[slot_meta.id]) || (inhands? inhand_state : worn_state) || item_state || icon_state
/obj/item/proc/resolve_worn_state(inhands, slot_key, bodytype)
// PRIVATE_PROC(TRUE)
if(inhands)
return "[base_worn_state(inhands, slot_key, bodytype)][(worn_render_flags & WORN_RENDER_INHAND_ONE_FOR_ALL)? "_all" : "_[slot_key]"]"
return "[base_worn_state(inhands, slot_key, bodytype)][(worn_render_flags & WORN_RENDER_SLOT_ONE_FOR_ALL)? "_all" : "_[slot_key]"][((bodytype != BODYTYPE_DEFAULT) && worn_bodytypes?.contains(bodytype))? "_[bodytype_to_string(bodytype)]" : ""]"
/obj/item/proc/base_worn_state(inhands, slot_key, bodytype)
if(inhands)
return inhand_state || icon_state
return worn_state || icon_state