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:
@@ -1,11 +1,13 @@
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Shared.CCVar;
|
||||
using Content.Shared.Decals;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Humanoid.Markings;
|
||||
using Content.Shared.Humanoid.Prototypes;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Inventory;
|
||||
using Content.Shared.Preferences;
|
||||
using Robust.Shared;
|
||||
using Robust.Shared.Configuration;
|
||||
@@ -114,22 +116,22 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Toggles a humanoid's sprite layer visibility.
|
||||
/// </summary>
|
||||
/// <param name="uid">Humanoid mob's UID</param>
|
||||
/// <param name="ent">Humanoid entity</param>
|
||||
/// <param name="layer">Layer to toggle visibility for</param>
|
||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||
public void SetLayerVisibility(EntityUid uid,
|
||||
/// <param name="visible">Whether to hide or show the layer. If more than once piece of clothing is hiding the layer, it may remain hidden.</param>
|
||||
/// <param name="source">Equipment slot that has the clothing that is (or was) hiding the layer. If not specified, the change is "permanent" (i.e., see <see cref="HumanoidAppearanceComponent.PermanentlyHidden"/>)</param>
|
||||
public void SetLayerVisibility(Entity<HumanoidAppearanceComponent?> ent,
|
||||
HumanoidVisualLayers layer,
|
||||
bool visible,
|
||||
bool permanent = false,
|
||||
HumanoidAppearanceComponent? humanoid = null)
|
||||
SlotFlags? source = null)
|
||||
{
|
||||
if (!Resolve(uid, ref humanoid, false))
|
||||
if (!Resolve(ent.Owner, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
var dirty = false;
|
||||
SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
|
||||
SetLayerVisibility(ent!, layer, visible, source, ref dirty);
|
||||
if (dirty)
|
||||
Dirty(uid, humanoid);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -163,49 +165,75 @@ public abstract class SharedHumanoidAppearanceSystem : EntitySystem
|
||||
/// <summary>
|
||||
/// Sets the visibility for multiple layers at once on a humanoid's sprite.
|
||||
/// </summary>
|
||||
/// <param name="uid">Humanoid mob's UID</param>
|
||||
/// <param name="ent">Humanoid entity</param>
|
||||
/// <param name="layers">An enumerable of all sprite layers that are going to have their visibility set</param>
|
||||
/// <param name="visible">The visibility state of the layers given</param>
|
||||
/// <param name="permanent">If this is a permanent change, or temporary. Permanent layers are stored in their own hash set.</param>
|
||||
/// <param name="humanoid">Humanoid component of the entity</param>
|
||||
public void SetLayersVisibility(EntityUid uid, IEnumerable<HumanoidVisualLayers> layers, bool visible, bool permanent = false,
|
||||
HumanoidAppearanceComponent? humanoid = null)
|
||||
public void SetLayersVisibility(Entity<HumanoidAppearanceComponent?> ent,
|
||||
IEnumerable<HumanoidVisualLayers> layers,
|
||||
bool visible)
|
||||
{
|
||||
if (!Resolve(uid, ref humanoid))
|
||||
if (!Resolve(ent.Owner, ref ent.Comp, false))
|
||||
return;
|
||||
|
||||
var dirty = false;
|
||||
|
||||
foreach (var layer in layers)
|
||||
{
|
||||
SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
|
||||
SetLayerVisibility(ent!, layer, visible, null, ref dirty);
|
||||
}
|
||||
|
||||
if (dirty)
|
||||
Dirty(uid, humanoid);
|
||||
Dirty(ent);
|
||||
}
|
||||
|
||||
protected virtual void SetLayerVisibility(
|
||||
EntityUid uid,
|
||||
HumanoidAppearanceComponent humanoid,
|
||||
/// <inheritdoc cref="SetLayerVisibility(Entity{HumanoidAppearanceComponent?},HumanoidVisualLayers,bool,Nullable{SlotFlags})"/>
|
||||
public virtual void SetLayerVisibility(
|
||||
Entity<HumanoidAppearanceComponent> ent,
|
||||
HumanoidVisualLayers layer,
|
||||
bool visible,
|
||||
bool permanent,
|
||||
SlotFlags? source,
|
||||
ref bool dirty)
|
||||
{
|
||||
#if DEBUG
|
||||
if (source is {} s)
|
||||
{
|
||||
DebugTools.AssertNotEqual(s, SlotFlags.NONE);
|
||||
// Check that only a single bit in the bitflag is set
|
||||
var powerOfTwo = BitOperations.RoundUpToPowerOf2((uint)s);
|
||||
DebugTools.AssertEqual((uint)s, powerOfTwo);
|
||||
}
|
||||
#endif
|
||||
|
||||
if (visible)
|
||||
{
|
||||
if (permanent)
|
||||
dirty |= humanoid.PermanentlyHidden.Remove(layer);
|
||||
if (source is not {} slot)
|
||||
{
|
||||
dirty |= ent.Comp.PermanentlyHidden.Remove(layer);
|
||||
}
|
||||
else if (ent.Comp.HiddenLayers.TryGetValue(layer, out var oldSlots))
|
||||
{
|
||||
// This layer might be getting hidden by more than one piece of equipped clothing.
|
||||
// remove slot flag from the set of slots hiding this layer, then check if there are any left.
|
||||
ent.Comp.HiddenLayers[layer] = ~slot & oldSlots;
|
||||
if (ent.Comp.HiddenLayers[layer] == SlotFlags.NONE)
|
||||
ent.Comp.HiddenLayers.Remove(layer);
|
||||
|
||||
dirty |= humanoid.HiddenLayers.Remove(layer);
|
||||
dirty |= (oldSlots & slot) != 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (permanent)
|
||||
dirty |= humanoid.PermanentlyHidden.Add(layer);
|
||||
if (source is not { } slot)
|
||||
{
|
||||
dirty |= ent.Comp.PermanentlyHidden.Add(layer);
|
||||
}
|
||||
else
|
||||
{
|
||||
var oldSlots = ent.Comp.HiddenLayers.GetValueOrDefault(layer);
|
||||
ent.Comp.HiddenLayers[layer] = slot | oldSlots;
|
||||
dirty |= (oldSlots & slot) != slot;
|
||||
}
|
||||
|
||||
dirty |= humanoid.HiddenLayers.Add(layer);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user