]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Fix lizard snouts disappearing when wearing masks (#25716)
authorSlamBamActionman <83650252+SlamBamActionman@users.noreply.github.com>
Fri, 19 Apr 2024 05:39:47 +0000 (07:39 +0200)
committerGitHub <noreply@github.com>
Fri, 19 Apr 2024 05:39:47 +0000 (15:39 +1000)
* fixes it

* Snout fix

* Partway commit

* Partway commit

* Update masks and helmets to use the new system

14 files changed:
Content.Shared/Clothing/Components/HideLayerClothingComponent.cs [new file with mode: 0644]
Content.Shared/Clothing/EntitySystems/ClothingSystem.cs
Content.Shared/Humanoid/HumanoidAppearanceComponent.cs
Resources/Prototypes/Entities/Clothing/Head/base_clothinghead.yml
Resources/Prototypes/Entities/Clothing/Head/hardsuit-helmets.yml
Resources/Prototypes/Entities/Clothing/Head/hats.yml
Resources/Prototypes/Entities/Clothing/Head/helmets.yml
Resources/Prototypes/Entities/Clothing/Head/hoods.yml
Resources/Prototypes/Entities/Clothing/Head/welding.yml
Resources/Prototypes/Entities/Clothing/Masks/bandanas.yml
Resources/Prototypes/Entities/Clothing/Masks/masks.yml
Resources/Prototypes/Entities/Clothing/Masks/specific.yml
Resources/Prototypes/Entities/Mobs/Species/dwarf.yml
Resources/Prototypes/Entities/Mobs/Species/human.yml

diff --git a/Content.Shared/Clothing/Components/HideLayerClothingComponent.cs b/Content.Shared/Clothing/Components/HideLayerClothingComponent.cs
new file mode 100644 (file)
index 0000000..ac3d9b9
--- /dev/null
@@ -0,0 +1,24 @@
+using Content.Shared.Humanoid;
+using Robust.Shared.GameStates;
+
+namespace Content.Shared.Clothing.Components;
+
+/// <summary>
+/// This is used for a clothing item that hides an appearance layer.
+/// The entity's HumanoidAppearance component must have the corresponding hideLayerOnEquip value.
+/// </summary>
+[RegisterComponent, NetworkedComponent]
+public sealed partial class HideLayerClothingComponent : Component
+{
+    /// <summary>
+    /// The appearance layer to hide.
+    /// </summary>
+    [DataField]
+    public HashSet<HumanoidVisualLayers> Slots = new();
+
+    /// <summary>
+    /// If true, the layer will only hide when the item is in a toggled state (e.g. masks)
+    /// </summary>
+    [DataField]
+    public bool HideOnToggle = false;
+}
index 85df04d20a172b2badb75ac9fa0ec0e3b93c408a..427d83cc3cee6244baa02ca0ad05c1e1addffe93 100644 (file)
@@ -6,26 +6,19 @@ using Content.Shared.Interaction.Events;
 using Content.Shared.Inventory;
 using Content.Shared.Inventory.Events;
 using Content.Shared.Item;
-using Content.Shared.Tag;
+using Robust.Shared.Containers;
 using Robust.Shared.GameStates;
-using System.Linq;
 
 namespace Content.Shared.Clothing.EntitySystems;
 
 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 TagSystem _tagSystem = default!;
     [Dependency] private readonly InventorySystem _invSystem = default!;
     [Dependency] private readonly SharedHandsSystem _handsSystem = default!;
 
-    [ValidatePrototypeId<TagPrototype>]
-    private const string HairTag = "HidesHair";
-
-    [ValidatePrototypeId<TagPrototype>]
-    private const string NoseTag = "HidesNose";
-
     public override void Initialize()
     {
         base.Initialize();
@@ -89,18 +82,22 @@ public abstract class ClothingSystem : EntitySystem
         }
     }
 
-    private void ToggleVisualLayer(EntityUid equipee, HumanoidVisualLayers layer, string tag)
+    private void ToggleVisualLayers(EntityUid equipee, HashSet<HumanoidVisualLayers> layers, HashSet<HumanoidVisualLayers> appearanceLayers)
     {
-        InventorySystem.InventorySlotEnumerator enumerator = _invSystem.GetSlotEnumerator(equipee, SlotFlags.HEAD ^ SlotFlags.MASK);
-        bool shouldLayerShow = true;
-
-        while (enumerator.NextItem(out EntityUid item))
+        foreach (HumanoidVisualLayers layer in layers)
         {
-            if (_tagSystem.HasTag(item, tag))
+            if (!appearanceLayers.Contains(layer))
+                break;
+
+            InventorySystem.InventorySlotEnumerator enumerator = _invSystem.GetSlotEnumerator(equipee);
+
+            bool shouldLayerShow = true;
+            while (enumerator.NextItem(out EntityUid item))
             {
-                if (tag == NoseTag) //Special check needs to be made for NoseTag, due to masks being toggleable
+                if (TryComp(item, out HideLayerClothingComponent? comp))
                 {
-                    if (TryComp(item, out MaskComponent? mask) && TryComp(item, out ClothingComponent? clothing))
+                    //Checks for mask toggling. TODO: Make a generic system for this
+                    if (comp.HideOnToggle && TryComp(item, out MaskComponent? mask) && TryComp(item, out ClothingComponent? clothing))
                     {
                         if (clothing.EquippedPrefix != mask.EquippedPrefix)
                         {
@@ -114,50 +111,49 @@ public abstract class ClothingSystem : EntitySystem
                         break;
                     }
                 }
-                else
-                {
-                    shouldLayerShow = false;
-                    break;
-                }
             }
+            _humanoidSystem.SetLayerVisibility(equipee, layer, shouldLayerShow);
         }
-        _humanoidSystem.SetLayerVisibility(equipee, layer, shouldLayerShow);
     }
 
     protected virtual void OnGotEquipped(EntityUid uid, ClothingComponent component, GotEquippedEvent args)
     {
         component.InSlot = args.Slot;
-        if ((new string[] { "head" }).Contains(args.Slot) && _tagSystem.HasTag(args.Equipment, HairTag))
-            ToggleVisualLayer(args.Equipee, HumanoidVisualLayers.Hair, HairTag);
-        if ((new string[] { "mask", "head" }).Contains(args.Slot) && _tagSystem.HasTag(args.Equipment, NoseTag))
-            ToggleVisualLayer(args.Equipee, HumanoidVisualLayers.Snout, NoseTag);
+        CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
     }
 
     protected virtual void OnGotUnequipped(EntityUid uid, ClothingComponent component, GotUnequippedEvent args)
     {
         component.InSlot = null;
-        if ((new string[] { "head" }).Contains(args.Slot) && _tagSystem.HasTag(args.Equipment, HairTag))
-            ToggleVisualLayer(args.Equipee, HumanoidVisualLayers.Hair, HairTag);
-        if ((new string[] { "mask", "head" }).Contains(args.Slot) && _tagSystem.HasTag(args.Equipment, NoseTag))
-            ToggleVisualLayer(args.Equipee, HumanoidVisualLayers.Snout, NoseTag);
+        CheckEquipmentForLayerHide(args.Equipment, args.Equipee);
     }
 
     private void OnGetState(EntityUid uid, ClothingComponent component, ref ComponentGetState args)
     {
         args.State = new ClothingComponentState(component.EquippedPrefix);
+        if (component.InSlot != null && _containerSys.TryGetContainingContainer(uid, out var container))
+        {
+            CheckEquipmentForLayerHide(uid, container.Owner);
+        }
     }
 
     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, out var container))
+            {
+                CheckEquipmentForLayerHide(uid, container.Owner);
+            }
+        }
     }
 
     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);
-        ToggleVisualLayer(args.Wearer, HumanoidVisualLayers.Snout, NoseTag);
+        CheckEquipmentForLayerHide(ent.Owner, args.Wearer);
     }
 
     private void OnEquipDoAfter(Entity<ClothingComponent> ent, ref ClothingEquipDoAfterEvent args)
@@ -176,6 +172,12 @@ public abstract class ClothingSystem : EntitySystem
             _handsSystem.TryPickup(args.User, ent);
     }
 
+    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)
index 82d6964522ca3dfa313deb25079804ff49ef4fa8..016ab64f1a7b05c219224b6de2ce2c44d3a968a6 100644 (file)
@@ -82,6 +82,12 @@ public sealed partial class HumanoidAppearanceComponent : Component
     /// </summary>
     [ViewVariables(VVAccess.ReadOnly)]
     public Color? CachedFacialHairColor;
+
+    /// <summary>
+    ///     Which layers of this humanoid that should be hidden on equipping a corresponding item..
+    /// </summary>
+    [DataField]
+    public HashSet<HumanoidVisualLayers> HideLayersOnEquip = [HumanoidVisualLayers.Hair];
 }
 
 [DataDefinition]
index d13b284ff292120cbf808539e065174644b631d7..e1e5ddc11fa11f109a42ebaf9e0d2a9633d56812 100644 (file)
     unequipSound: /Audio/Mecha/mechmove03.ogg
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
     - HelmetEVA
-    - HidesNose
   - type: IdentityBlocker
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 - type: entity
   abstract: true
   - type: IngestionBlocker
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
-    - HidesNose
   - type: IdentityBlocker
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 - type: entity
   abstract: true
   - type: TemperatureProtection
     coefficient: 0.7
   - type: GroupExamine
-  - type: Tag
-    tags:
-    - HidesHair
+  - type: HideLayerClothing
+    slots:
+    - Hair
index a35cf498f63da063416017fe550208e44c4f78a2..453f3d12679137b7c13307345ef89c0039bb3ecb 100644 (file)
@@ -17,9 +17,9 @@
     sprite: Clothing/Head/Hardsuits/basic.rsi
   - type: Clothing
     sprite: Clothing/Head/Hardsuits/basic.rsi
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 #Atmospherics Hardsuit
 - type: entity
index fba77d885ff06582efc5be4722cfbaa9177cf356..ec0921c5127b54bc12347f7cea29fa1f193bc9ee 100644 (file)
     sprite: Clothing/Head/Hats/plaguedoctor.rsi
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
     - ClothMade
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hats/witch.rsi
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
     - ClothMade
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
index f38efd5c8b973c27e612f953ee533b9673d9ae66..88070b430fd8d1a0d6fc2ee738e47ce4c01a2232 100644 (file)
         Blunt: 0.95
         Slash: 0.95
         Piercing: 0.95
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 #Janitorial Bombsuit Helmet
 - type: entity
     sprite: Clothing/Head/Helmets/spaceninja.rsi
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
   - type: IngestionBlocker
   - type: IdentityBlocker
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 #Templar Helmet
 - type: entity
   - type: IdentityBlocker
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 #Atmos Fire Helmet
 - type: entity
   - type: IdentityBlocker
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 #Chitinous Helmet
 - type: entity
index 68d50c5ab1ce1521a75fd483d0de6fc14a4bb09f..b62834dd98e0d615bf552eff7ac7bfdd64788bef 100644 (file)
   - type: IngestionBlocker
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 - type: entity
   parent: ClothingHeadHatHoodBioGeneral
     sprite: Clothing/Head/Hoods/chaplain.rsi
   - type: Tag
     tags:
-    - HidesHair
     - HamsterWearable
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hoods/cult.rsi
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hoods/nun.rsi
   - type: Tag
     tags:
-    - HidesHair
     - HamsterWearable
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
         Heat: 0.95
         Radiation: 0.65
   - type: BreathMask
-  - type: Tag
-    tags:
-    - HidesHair
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hoods/goliathcloak.rsi
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hoods/iansuit.rsi
   - type: Clothing
     sprite: Clothing/Head/Hoods/iansuit.rsi
-  - type: Tag
-    tags:
-    - HidesHair
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
     sprite: Clothing/Head/Hoods/carpsuit.rsi
   - type: Clothing
     sprite: Clothing/Head/Hoods/carpsuit.rsi
-  - type: Tag
-    tags:
-    - HidesHair
+  - type: HideLayerClothing
+    slots:
+    - Hair
 
 - type: entity
   parent: ClothingHeadBase
   - type: IdentityBlocker
   - type: Tag
     tags:
-    - HidesHair
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
 
 #Winter Coat Hoods
 - type: entity
index 93d9b1e084faff30083423d8fa9b5fdfc0aae749..cd5c63d7ec77d55c0e15228f3b1dac430a779519 100644 (file)
@@ -19,6 +19,9 @@
   - type: Tag
     tags:
     - WhitelistChameleon
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: WeldingMaskBase
index 246b47b80036b8cd819fc28ee6cf09739dd36c2f..f5ad2fb6c83939bcae4290ca56d78b225f80cf58 100644 (file)
   - type: Tag
     tags:
     - Bandana
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
+    hideOnToggle: true
 
 - type: entity
   parent: ClothingMaskBandanaBase
index 4d7351464ff4db907592b47cf120463c8f6b0770..850050b2d38582191bdca16a28258945aa41ad25 100644 (file)
     tags:
     - HamsterWearable
     - WhitelistChameleon
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
+    hideOnToggle: true
 
 - type: entity
   parent: ClothingMaskGas
     tags:
     - ClownMask
     - WhitelistChameleon
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskClownBase
     - ClownMask
     - HamsterWearable
     - WhitelistChameleon
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskClown
     sprite: Clothing/Mask/joy.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     tags:
     - HamsterWearable
     - WhitelistChameleon
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskPullableBase
   - type: BreathMask
   - type: IngestionBlocker
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
+    hideOnToggle: true
 
 - type: entity
   parent: ClothingMaskClownBase
   - type: Tag
     tags:
     - WhitelistChameleon
-    - HidesHair
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
+    hideOnToggle: true
 
 - type: entity
   parent: ClothingMaskGasExplorer
   - type: Tag
     tags:
     - WhitelistChameleon
-    - HidesHair
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Hair
+    - Snout
+    hideOnToggle: true
 
 - type: entity
   parent: ClothingMaskGasERT
     tags:
     - HamsterWearable
     - WhitelistChameleon
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
   - type: IdentityBlocker
 
 - type: entity
     sprite: Clothing/Mask/fox.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     sprite: Clothing/Mask/bee.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     sprite: Clothing/Mask/bear.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     sprite: Clothing/Mask/raven.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     sprite: Clothing/Mask/jackal.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
     sprite: Clothing/Mask/bat.rsi
   - type: BreathMask
   - type: IdentityBlocker
-  - type: Tag
-    tags:
-    - HidesNose
+  - type: HideLayerClothing
+    slots:
+    - Snout
 
 - type: entity
   parent: ClothingMaskBase
index d0e4e4d7e9f0601c3c53a3af9eb4e1c8b944d9e1..0a0fc68469a78a00949531f4dc89e1c085b7f0e0 100644 (file)
@@ -6,8 +6,7 @@
   suffix: Chameleon
   components:
     - type: Tag
-      tags: # ignore "WhitelistChameleon" tag
-        - HidesNose
+      tags: [] # ignore "WhitelistChameleon" tag
     - type: Sprite
       sprite: Clothing/Mask/gas.rsi
     - type: Clothing
@@ -21,6 +20,9 @@
       interfaces:
         - key: enum.ChameleonUiKey.Key
           type: ChameleonBoundUserInterface
+    - type: HideLayerClothing
+      slots:
+      - Snout
 
 - type: entity
   parent: ClothingMaskGasChameleon
@@ -28,3 +30,6 @@
   suffix: Voice Mask, Chameleon
   components:
     - type: VoiceMasker
+    - type: HideLayerClothing
+      slots:
+      - Snout
index 2c0ab1e15d78fc1a69f801e9c7797d5f1b2322af..5a54b56c48e0f3bfca795d04c869b9ded4d633db 100644 (file)
     accent: dwarf
   - type: Speech
     speechSounds: Bass
+  - type: HumanoidAppearance
+    species: Human
+    hideLayersOnEquip:
+    - Hair
+    - Snout
 
 - type: entity
   parent: BaseSpeciesDummy
index d469d6c60fb5c69f0acb26719859d34d64eab54c..6716d3902b82259f66aef1912191a8eeb5c4bf6c 100644 (file)
     spawned:
     - id: FoodMeatHuman
       amount: 5
+  - type: HumanoidAppearance
+    species: Human
+    hideLayersOnEquip:
+    - Hair
+    - Snout
 
 - type: entity
   parent: BaseSpeciesDummy