Fixes transparent_protection being used incorrectly almost everywhere (#90894)

## About The Pull Request

``transparent_protection`` is a set of cover flags which are supposed to
be used for things that can be seen, but cannot be accessed. Replaced
the argument in ``check_obscured_slots`` with a separate
``check_covered_slots`` proc for clarity.

Closes #90827

Also went a bit down a rabbit hole and fixed some UI issues with the
strip menu, as I found out that covered slots also counted as obscured.
Covered, but not obscured slots are now semi-transparent, images are
pixel-perfect, and action buttons no longer are offset. Also slots with
items in them don't have the background, akin to how inventory slots
function in-game.
This commit is contained in:
SmArtKar
2025-05-09 04:52:31 +02:00
committed by Roxy
parent ebf4a2a5ab
commit 1b0c9581f9
14 changed files with 107 additions and 108 deletions

View File

@@ -29,3 +29,6 @@
/// This slot can't be seen, but can be accessed.
#define STRIPPABLE_OBSCURING_HIDDEN 2
/// This slot can be seen, but cannot be accessed
#define STRIPPABLE_OBSCURING_INACCESSIBLE 3

View File

@@ -48,7 +48,7 @@
* Returns true if the parent item is obscured by something else that the wielder is wearing
*/
/datum/component/bloodysoles/proc/is_obscured()
return wielder.check_obscured_slots(TRUE) & equipped_slot || is_under_feet_covered()
return wielder.check_covered_slots() & equipped_slot || is_under_feet_covered()
/**
* Returns true if the parent item is worn in the ITEM_SLOT_ICLOTHING slot and the
@@ -318,7 +318,7 @@
/datum/component/bloodysoles/feet/is_obscured()
if(wielder.shoes)
return TRUE
return wielder.check_obscured_slots(TRUE) & ITEM_SLOT_FEET
return wielder.check_covered_slots() & ITEM_SLOT_FEET
/datum/component/bloodysoles/feet/on_moved(datum/source, OldLoc, Dir, Forced)
if(wielder.num_legs < 2)

View File

@@ -265,13 +265,17 @@
return finish_equip_mob(equipping, source, user)
/datum/strippable_item/mob_item_slot/get_obscuring(atom/source)
if (iscarbon(source))
var/mob/living/carbon/carbon_source = source
return (carbon_source.check_obscured_slots() & item_slot) \
? STRIPPABLE_OBSCURING_COMPLETELY \
: STRIPPABLE_OBSCURING_NONE
if (!iscarbon(source))
return STRIPPABLE_OBSCURING_NONE
return FALSE
var/mob/living/carbon/carbon_source = source
if (carbon_source.check_obscured_slots() & item_slot)
return STRIPPABLE_OBSCURING_COMPLETELY
if (carbon_source.check_covered_slots() & item_slot)
return STRIPPABLE_OBSCURING_INACCESSIBLE
return STRIPPABLE_OBSCURING_NONE
/datum/strippable_item/mob_item_slot/start_unequip(atom/source, mob/user)
. = ..()
@@ -367,8 +371,8 @@
LAZYSET(result, "interacting", TRUE)
var/obscuring = item_data.get_obscuring(owner)
if (obscuring != STRIPPABLE_OBSCURING_NONE)
LAZYSET(result, "obscured", obscuring)
LAZYSET(result, "obscured", obscuring)
if (obscuring != STRIPPABLE_OBSCURING_NONE && obscuring != STRIPPABLE_OBSCURING_INACCESSIBLE)
items[strippable_key] = result
continue
@@ -420,7 +424,8 @@
if (!strippable_item.should_show(owner, user))
return
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
var/obscured = strippable_item.get_obscuring(owner)
if (obscured == STRIPPABLE_OBSCURING_COMPLETELY || obscured == STRIPPABLE_OBSCURING_INACCESSIBLE)
return
var/item = strippable_item.get_item(owner)
@@ -486,7 +491,8 @@
if (!strippable_item.should_show(owner, user))
return
if (strippable_item.get_obscuring(owner) == STRIPPABLE_OBSCURING_COMPLETELY)
var/obscured = strippable_item.get_obscuring(owner)
if (obscured == STRIPPABLE_OBSCURING_COMPLETELY || obscured == STRIPPABLE_OBSCURING_INACCESSIBLE)
return
var/item = strippable_item.get_item(owner)

View File

@@ -137,7 +137,7 @@
var/dirty_hands = !!(target_flags & (ITEM_SLOT_GLOVES|ITEM_SLOT_HANDS))
var/dirty_feet = !!(target_flags & ITEM_SLOT_FEET)
var/slots_to_bloody = target_flags & ~check_obscured_slots()
var/slots_to_bloody = target_flags & ~check_covered_slots()
var/list/all_worn = get_equipped_items()
for(var/obj/item/thing as anything in all_worn)
if(thing.slot_flags & slots_to_bloody)

View File

@@ -1259,47 +1259,21 @@
/mob/living/carbon/wash(clean_types)
. = ..()
// Wash equipped stuff that cannot be covered
for(var/obj/item/held_thing in held_items)
if(held_thing.wash(clean_types))
. = TRUE
if(back?.wash(clean_types))
update_worn_back(0)
. = TRUE
// Check and wash stuff that can be covered
var/obscured = check_obscured_slots()
if(!(obscured & ITEM_SLOT_HEAD) && head?.wash(clean_types))
update_worn_head()
. = TRUE
// If the eyes are covered by anything but glasses, that thing will be covering any potential glasses as well.
if(is_eyes_covered(ITEM_SLOT_MASK|ITEM_SLOT_HEAD) && glasses?.wash(clean_types))
update_worn_glasses()
. = TRUE
if(!(obscured & ITEM_SLOT_MASK) && wear_mask?.wash(clean_types))
update_worn_mask()
. = TRUE
if(!(obscured & ITEM_SLOT_EARS) && ears?.wash(clean_types))
update_worn_ears()
. = TRUE
if(!(obscured & ITEM_SLOT_NECK) && wear_neck?.wash(clean_types))
update_worn_neck()
. = TRUE
if(!(obscured & ITEM_SLOT_FEET) && shoes?.wash(clean_types))
update_worn_shoes()
. = TRUE
if(!(obscured & ITEM_SLOT_GLOVES) && gloves?.wash(clean_types))
update_worn_gloves()
. = TRUE
// Check and wash stuff that isn't covered
var/covered = check_covered_slots()
for(var/obj/item/worn as anything in get_equipped_items())
var/slot = get_slot_by_item(worn)
// Don't wash glasses if something other than them is covering our eyes
if(slot == ITEM_SLOT_EYES && is_eyes_covered(ITEM_SLOT_MASK|ITEM_SLOT_HEAD))
continue
if(!(covered & slot))
// /obj/item/wash() already updates our clothing slot
. ||= worn.wash(clean_types)
/// if any of our bodyparts are bleeding
/mob/living/carbon/proc/is_bleeding()

View File

@@ -638,8 +638,7 @@
* Returns false if we couldn't wash our hands due to them being obscured, otherwise true
*/
/mob/living/carbon/human/proc/wash_hands(clean_types)
var/obscured = check_obscured_slots()
if(obscured & ITEM_SLOT_GLOVES)
if(check_covered_slots() & ITEM_SLOT_GLOVES)
return FALSE
if(gloves)
@@ -660,13 +659,10 @@
if(!is_mouth_covered() && clean_lips())
. = TRUE
if(glasses && is_eyes_covered(ITEM_SLOT_MASK|ITEM_SLOT_HEAD) && glasses.wash(clean_types))
update_worn_glasses()
if(glasses && !is_eyes_covered(ITEM_SLOT_MASK|ITEM_SLOT_HEAD) && glasses.wash(clean_types))
. = TRUE
var/obscured = check_obscured_slots()
if(wear_mask && !(obscured & ITEM_SLOT_MASK) && wear_mask.wash(clean_types))
update_worn_mask()
if(wear_mask && !(check_covered_slots() & ITEM_SLOT_MASK) && wear_mask.wash(clean_types))
. = TRUE
/**
@@ -674,28 +670,11 @@
*/
/mob/living/carbon/human/wash(clean_types)
. = ..()
// Wash equipped stuff that cannot be covered
if(wear_suit?.wash(clean_types))
update_worn_oversuit()
. = TRUE
if(belt?.wash(clean_types))
update_worn_belt()
. = TRUE
// Check and wash stuff that can be covered
var/obscured = check_obscured_slots()
if(!(obscured & ITEM_SLOT_ICLOTHING) && w_uniform?.wash(clean_types))
update_worn_undersuit()
. = TRUE
if(!is_mouth_covered() && clean_lips())
. = TRUE
// Wash hands if exposed
if(!gloves && (clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0 && !(obscured & ITEM_SLOT_GLOVES))
if(!gloves && (clean_types & CLEAN_TYPE_BLOOD) && blood_in_hands > 0 && !(check_covered_slots() & ITEM_SLOT_GLOVES))
blood_in_hands = 0
update_worn_gloves()
. = TRUE

View File

@@ -682,29 +682,29 @@
/mob/living/carbon/human/proc/burn_clothing(seconds_per_tick, stacks)
var/list/burning_items = list()
var/obscured = check_obscured_slots(TRUE)
var/covered = check_covered_slots()
//HEAD//
if(glasses && !(obscured & ITEM_SLOT_EYES))
if(glasses && !(covered & ITEM_SLOT_EYES))
burning_items += glasses
if(wear_mask && !(obscured & ITEM_SLOT_MASK))
if(wear_mask && !(covered & ITEM_SLOT_MASK))
burning_items += wear_mask
if(wear_neck && !(obscured & ITEM_SLOT_NECK))
if(wear_neck && !(covered & ITEM_SLOT_NECK))
burning_items += wear_neck
if(ears && !(obscured & ITEM_SLOT_EARS))
if(ears && !(covered & ITEM_SLOT_EARS))
burning_items += ears
if(head)
burning_items += head
//CHEST//
if(w_uniform && !(obscured & ITEM_SLOT_ICLOTHING))
if(w_uniform && !(covered & ITEM_SLOT_ICLOTHING))
burning_items += w_uniform
if(wear_suit)
burning_items += wear_suit
//ARMS & HANDS//
var/obj/item/clothing/arm_clothes = null
if(gloves && !(obscured & ITEM_SLOT_GLOVES))
if(gloves && !(covered & ITEM_SLOT_GLOVES))
arm_clothes = gloves
else if(wear_suit && ((wear_suit.body_parts_covered & HANDS) || (wear_suit.body_parts_covered & ARMS)))
arm_clothes = wear_suit
@@ -715,7 +715,7 @@
//LEGS & FEET//
var/obj/item/clothing/leg_clothes = null
if(shoes && !(obscured & ITEM_SLOT_FEET))
if(shoes && !(covered & ITEM_SLOT_FEET))
leg_clothes = shoes
else if(wear_suit && ((wear_suit.body_parts_covered & FEET) || (wear_suit.body_parts_covered & LEGS)))
leg_clothes = wear_suit

View File

@@ -95,7 +95,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(uniform.flags_inv)
if(HAS_TRAIT(uniform, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_ICLOTHING))
if(HAS_TRAIT(uniform, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_ICLOTHING))
return
var/target_overlay = uniform.icon_state
@@ -227,7 +227,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_GLOVES))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_GLOVES))
return
var/icon_file = 'icons/mob/clothing/hands.dmi'
@@ -287,7 +287,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_EYES))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_EYES))
return
var/icon_file = 'icons/mob/clothing/eyes.dmi'
@@ -329,7 +329,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_EARS))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_EARS))
return
var/icon_file = 'icons/mob/clothing/ears.dmi'
@@ -366,7 +366,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_NECK))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_NECK))
return
var/icon_file = 'icons/mob/clothing/neck.dmi'
@@ -414,7 +414,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_FEET))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_FEET))
return
var/icon_file = DEFAULT_SHOES_FILE
@@ -470,7 +470,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_SUITSTORE))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_SUITSTORE))
return
var/mutable_appearance/s_store_overlay = worn_item.build_worn_icon(default_layer = SUIT_STORE_LAYER, default_icon_file = 'icons/mob/clothing/belt_mirror.dmi')
@@ -492,7 +492,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_HEAD))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_HEAD))
return
var/icon_file = 'icons/mob/clothing/head/default.dmi'
@@ -537,7 +537,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_BELT))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_BELT))
return
var/icon_file = 'icons/mob/clothing/belt.dmi'
@@ -654,7 +654,7 @@ There are several things that need to be remembered:
if(update_obscured)
update_obscured_slots(worn_item.flags_inv)
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots(transparent_protection = TRUE) & ITEM_SLOT_MASK))
if(HAS_TRAIT(worn_item, TRAIT_NO_WORN_ICON) || (check_obscured_slots() & ITEM_SLOT_MASK))
return
var/icon_file = 'icons/mob/clothing/mask.dmi'

View File

@@ -1,12 +1,24 @@
/mob/living/carbon/proc/check_obscured_slots(transparent_protection)
var/obscured = NONE
/// Returns a list of slots that are *visibly* covered by clothing and thus cannot be seen by others
/mob/living/carbon/proc/check_obscured_slots()
var/hidden_slots = NONE
for(var/obj/item/equipped_item in get_equipped_items())
hidden_slots |= equipped_item.flags_inv
if(transparent_protection)
hidden_slots |= equipped_item.transparent_protection
return hidden_slots_to_inventory_slots(hidden_slots)
/// Returns a list of slots that are protected by other clothing, but could possibly be seen by others, via transparent visors and similar stuff
/mob/living/carbon/proc/check_covered_slots()
var/hidden_slots = NONE
for(var/obj/item/equipped_item in get_equipped_items())
hidden_slots |= equipped_item.flags_inv | equipped_item.transparent_protection
return hidden_slots_to_inventory_slots(hidden_slots)
/// Convers HIDEX to ITEM_SLOT_X, should be phased out in favor of using latter everywhere later
/proc/hidden_slots_to_inventory_slots(hidden_slots)
var/obscured = NONE
if(hidden_slots & HIDENECK)
obscured |= ITEM_SLOT_NECK
if(hidden_slots & HIDEMASK)
@@ -27,7 +39,6 @@
obscured |= ITEM_SLOT_SUITSTORE
if(hidden_slots & HIDEHEADGEAR)
obscured |= ITEM_SLOT_HEAD
return obscured
/mob/living/carbon/get_item_by_slot(slot_id)

View File

@@ -265,7 +265,7 @@
var/mutable_appearance/eye_right = mutable_appearance(eye_icon, "[eye_icon_state]_r", -eyes_layer, parent) // SKYRAT EDIT CHANGE - Customization - ORIGINAL: var/mutable_appearance/eye_right = mutable_appearance('icons/mob/human/human_face.dmi', "[eye_icon_state]_r", -BODY_LAYER, parent)
var/list/overlays = list(eye_left, eye_right)
var/obscured = parent.check_obscured_slots(TRUE)
var/obscured = parent.check_obscured_slots()
if(overlay_ignore_lighting && !(obscured & ITEM_SLOT_EYES))
overlays += emissive_appearance(eye_left.icon, eye_left.icon_state, parent, -BODY_LAYER, alpha = eye_left.alpha)
overlays += emissive_appearance(eye_right.icon, eye_right.icon_state, parent, -BODY_LAYER, alpha = eye_right.alpha)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 306 B

After

Width:  |  Height:  |  Size: 353 B

View File

@@ -200,6 +200,10 @@ function ChoicedSelection(props: ChoicedSelectionProps) {
image,
'centered-image',
])}
style={{
transform:
'translateX(-50%) translateY(-50%) scale(0.8)',
}}
/>
</Button>
);

View File

@@ -10,7 +10,7 @@ import { Window } from '../layouts';
const ROWS = 6; // SKYRAT EDIT CHANGE
const COLUMNS = 6;
const BUTTON_DIMENSIONS = '50px';
const BUTTON_DIMENSIONS = '64px';
type GridSpotKey = string;
@@ -250,6 +250,7 @@ const SLOTS: Record<
enum ObscuringLevel {
Completely = 1,
Hidden = 2,
Inaccessible = 3,
}
type Interactable = {
@@ -274,6 +275,7 @@ type StripMenuItem =
icon: string;
name: string;
alternate?: string[];
obscured: ObscuringLevel;
}
| {
obscured: ObscuringLevel;
@@ -295,7 +297,9 @@ export const StripMenu = (props) => {
}
return (
<Window title={`Stripping ${data.name}`} width={400} height={400}>
// (64 + 6) * 6 + 6 = 426
// (64 + 6) * 5 + 6 + 31 (from title) =
<Window title={`Stripping ${data.name}`} width={426} height={387}>
<Window.Content>
<Stack fill vertical>
{range(0, ROWS).map((row) => (
@@ -330,8 +334,8 @@ export const StripMenu = (props) => {
content = (
<Image
src={`data:image/jpeg;base64,${item.icon}`}
height="100%"
width="100%"
width="64px"
height="64px"
style={{
verticalAlign: 'middle',
}}
@@ -349,12 +353,15 @@ export const StripMenu = (props) => {
background: 'rgba(0, 0, 0, 0.6)',
position: 'absolute',
overflow: 'hidden',
margin: '0px',
maxWidth: '22px', // yes I know its not 20 or 25; they look bad. 22px is perfect
margin: '0',
width: '20px',
height: '20px',
zIndex: '2',
left: `${idx === 0 ? '0' : undefined}`,
right: `${idx === 1 ? '0' : undefined}`,
bottom: '0',
padding: '0',
textAlign: 'center',
};
return (
<Button
@@ -367,6 +374,14 @@ export const StripMenu = (props) => {
}}
tooltip={alternateAction.text}
style={alternateActionStyle}
disabled={
item.obscured === ObscuringLevel.Inaccessible
}
opacity={
item.obscured === ObscuringLevel.Inaccessible
? 0.7
: 1
}
>
<Icon name={alternateAction.icon} />
</Button>
@@ -374,7 +389,11 @@ export const StripMenu = (props) => {
},
);
}
} else if ('obscured' in item) {
} else if (
'obscured' in item &&
(item.obscured === ObscuringLevel.Hidden ||
item.obscured === ObscuringLevel.Completely)
) {
content = (
<Icon
name={
@@ -384,9 +403,10 @@ export const StripMenu = (props) => {
}
size={3}
ml={0}
mt={1.3}
mt={2.5}
style={{
textAlign: 'center',
verticalAlign: 'middle',
height: '100%',
width: '100%',
}}
@@ -429,11 +449,13 @@ export const StripMenu = (props) => {
padding: '0',
}}
>
{slot.image && (
{slot.image && !(item && 'name' in item) && (
<Image
className="centered-image"
src={resolveAsset(slot.image)}
opacity={0.7}
width="64px"
height="64px"
/>
)}

View File

@@ -3,5 +3,5 @@
height: 100%;
left: 50%;
top: 50%;
transform: translateX(-50%) translateY(-50%) scale(0.8);
transform: translateX(-50%) translateY(-50%);
}