Fix broken layer hiding on clothes with multiple equipment slots (#34080)

* Fix broken layer hiding on clothes with multiple equipment slots

* Refactor ToggleVisualLayers, HideLayerClothingComponent, and ClothingComponent to allow more
precise layer hide behavior and more CPU efficient layer toggling.

* Adjust HumanoidAppearaceSystem to track which slots are hiding a given layer (e.g. gas mask and welding mask)
Add documentation
Change gas masks to use the new HideLayerClothingComponent structure as an example of its usage

* Fix the delayed snout bug

* Misc cleanup

* Make `bool permanent` implicit from SlotFlags

any non-permanent visibility toggle with `SlotFlags.None` isn't supported with how its set up. And similarly, the slot flags argument does nothing if permanent = true. So IMO it makes more sense to infer it from a nullable arg.

* Split into separate system

Too much pasta

* Remove (hopefully unnecessary) refresh

* Fisk mask networking

AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

* Keep old behaviour, use clearer names?

I'm just guessing at what this was meant to do

* english

* Separate slot name & flag

* dirty = true

* fix comment

* Improved SetLayerVisibility with dirtying logic suggested by @ElectroJr

* Only set mask toggled if DisableOnFold is true

* FoldableClothingSystem fixes

* fix bandana state

* Better comment

---------

Co-authored-by: ElectroJr <leonsfriedrich@gmail.com>
This commit is contained in:
paige404
2025-03-20 09:30:47 -04:00
committed by GitHub
parent 65f393bb14
commit 2e7f01b99e
20 changed files with 370 additions and 184 deletions

View File

@@ -16,9 +16,9 @@ public abstract class ClothingSystem : EntitySystem
{
[Dependency] private readonly SharedItemSystem _itemSys = default!;
[Dependency] private readonly SharedContainerSystem _containerSys = default!;
[Dependency] private readonly SharedHumanoidAppearanceSystem _humanoidSystem = default!;
[Dependency] private readonly InventorySystem _invSystem = default!;
[Dependency] private readonly SharedHandsSystem _handsSystem = default!;
[Dependency] private readonly HideLayerClothingSystem _hideLayer = default!;
public override void Initialize()
{
@@ -29,7 +29,6 @@ public abstract class ClothingSystem : EntitySystem
SubscribeLocalEvent<ClothingComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<ClothingComponent, GotEquippedEvent>(OnGotEquipped);
SubscribeLocalEvent<ClothingComponent, GotUnequippedEvent>(OnGotUnequipped);
SubscribeLocalEvent<ClothingComponent, ItemMaskToggledEvent>(OnMaskToggled);
SubscribeLocalEvent<ClothingComponent, ClothingEquipDoAfterEvent>(OnEquipDoAfter);
SubscribeLocalEvent<ClothingComponent, ClothingUnequipDoAfterEvent>(OnUnequipDoAfter);
@@ -85,59 +84,19 @@ public abstract class ClothingSystem : EntitySystem
}
}
private void ToggleVisualLayers(EntityUid equipee, HashSet<HumanoidVisualLayers> layers, HashSet<HumanoidVisualLayers> appearanceLayers)
{
foreach (HumanoidVisualLayers layer in layers)
{
if (!appearanceLayers.Contains(layer))
continue;
InventorySystem.InventorySlotEnumerator enumerator = _invSystem.GetSlotEnumerator(equipee);
bool shouldLayerShow = true;
while (enumerator.NextItem(out EntityUid item, out SlotDefinition? slot))
{
if (TryComp(item, out HideLayerClothingComponent? comp))
{
if (comp.Slots.Contains(layer))
{
if (TryComp(item, out ClothingComponent? clothing) && clothing.Slots == slot.SlotFlags)
{
//Checks for mask toggling. TODO: Make a generic system for this
if (comp.HideOnToggle && TryComp(item, out MaskComponent? mask))
{
if (clothing.EquippedPrefix != mask.EquippedPrefix)
{
shouldLayerShow = false;
break;
}
}
else
{
shouldLayerShow = false;
break;
}
}
}
}
}
_humanoidSystem.SetLayerVisibility(equipee, layer, shouldLayerShow);
}
}
protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
{
component.InSlot = args.Slot;
CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
component.InSlotFlag = args.SlotFlags;
if ((component.Slots & args.SlotFlags) != SlotFlags.NONE)
{
var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component);
RaiseLocalEvent(uid, ref gotEquippedEvent);
if ((component.Slots & args.SlotFlags) == SlotFlags.NONE)
return;
var didEquippedEvent = new ClothingDidEquippedEvent((uid, component));
RaiseLocalEvent(args.Equipee, ref didEquippedEvent);
}
var gotEquippedEvent = new ClothingGotEquippedEvent(args.Equipee, component);
RaiseLocalEvent(uid, ref gotEquippedEvent);
var didEquippedEvent = new ClothingDidEquippedEvent((uid, component));
RaiseLocalEvent(args.Equipee, ref didEquippedEvent);
}
protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
@@ -152,7 +111,7 @@ public abstract class ClothingSystem : EntitySystem
}
component.InSlot = null;
CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
component.InSlotFlag = null;
}
private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
@@ -162,21 +121,10 @@ public abstract class ClothingSystem : EntitySystem
private void OnHandleState(EntityUid uid, ClothingComponent component, ref ComponentHandleState args)
{
if (args.Current is ClothingComponentState state)
{
SetEquippedPrefix(uid, state.EquippedPrefix, component);
if (component.InSlot != null && _containerSys.TryGetContainingContainer((uid, null, null), out var container))
{
CheckEquipmentForLayerHide(uid, container.Owner);
}
}
}
if (args.Current is not ClothingComponentState state)
return;
private void OnMaskToggled(Entity<ClothingComponent> ent, ref ItemMaskToggledEvent args)
{
//TODO: sprites for 'pulled down' state. defaults to invisible due to no sprite with this prefix
SetEquippedPrefix(ent, args.IsToggled ? args.equippedPrefix : null, ent);
CheckEquipmentForLayerHide(ent.Owner, args.Wearer);
SetEquippedPrefix(uid, state.EquippedPrefix, component);
}
private void OnEquipDoAfter(Entity<ClothingComponent> ent, ref ClothingEquipDoAfterEvent args)
@@ -200,12 +148,6 @@ public abstract class ClothingSystem : EntitySystem
args.Additive += ent.Comp.StripDelay;
}
private void CheckEquipmentForLayerHide(EntityUid equipment, EntityUid equipee)
{
if (TryComp(equipment, out HideLayerClothingComponent? clothesComp) && TryComp(equipee, out HumanoidAppearanceComponent? appearanceComp))
ToggleVisualLayers(equipee, clothesComp.Slots, appearanceComp.HideLayersOnEquip);
}
#region Public API
public void SetEquippedPrefix(EntityUid uid, string? prefix, ClothingComponent? clothing = null)