]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Allow Vulps With Human Hair To Be Shaved Without Clyde Joining The Circus (#40171)
authorHannah Giovanna Dawson <karakkaraz@gmail.com>
Sun, 7 Sep 2025 14:36:38 +0000 (15:36 +0100)
committerGitHub <noreply@github.com>
Sun, 7 Sep 2025 14:36:38 +0000 (16:36 +0200)
* Revert "Disable vulpkanin human hair (#40144)"

This reverts commit d02aa1a4e2e106b9bfd8e9516464c9dbd86df7ca.

* You can once again shave your pet Vulp

* I can see the ass, I'm safe

* Rectified docstring as I am a good person

* I am doing this instead of playing Silksong please help

* Fix forgetting to re-add shader overriding

Content.Client/DisplacementMap/DisplacementMapSystem.cs
Content.Client/Humanoid/HumanoidAppearanceSystem.cs
Resources/Prototypes/Species/vulpkanin.yml

index 94dbc7f00cf06c76470c17a587f33e115eeade0b..6986e1c8682d61dd3e769bd5aaae49f172e8466f 100644 (file)
@@ -1,3 +1,4 @@
+using System.Diagnostics.CodeAnalysis;
 using Content.Shared.DisplacementMap;
 using Robust.Client.GameObjects;
 using Robust.Client.Graphics;
@@ -10,6 +11,11 @@ public sealed class DisplacementMapSystem : EntitySystem
     [Dependency] private readonly ISerializationManager _serialization = default!;
     [Dependency] private readonly SpriteSystem _sprite = default!;
 
+    private static string? BuildDisplacementLayerKey(object key)
+    {
+        return key.ToString() is null ? null : $"{key}-displacement";
+    }
+
     /// <summary>
     /// Attempting to apply a displacement map to a specific layer of SpriteComponent
     /// </summary>
@@ -19,21 +25,22 @@ public sealed class DisplacementMapSystem : EntitySystem
     /// <param name="key">Unique layer key, which will determine which layer to apply displacement map to</param>
     /// <param name="displacementKey">The key of the new displacement map layer added by this function.</param>
     /// <returns></returns>
-    public bool TryAddDisplacement(DisplacementData data,
+    public bool TryAddDisplacement(
+        DisplacementData data,
         Entity<SpriteComponent> sprite,
         int index,
         object key,
-        out string displacementKey)
+        [NotNullWhen(true)] out string? displacementKey
+    )
     {
-        displacementKey = $"{key}-displacement";
-
-        if (key.ToString() is null)
+        displacementKey = BuildDisplacementLayerKey(key);
+        if (displacementKey is null)
             return false;
 
-        if (data.ShaderOverride != null)
-            sprite.Comp.LayerSetShader(index, data.ShaderOverride);
+        EnsureDisplacementIsNotOnSprite(sprite, key);
 
-        _sprite.RemoveLayer(sprite.AsNullable(), displacementKey, false);
+        if (data.ShaderOverride is not null)
+            sprite.Comp.LayerSetShader(index, data.ShaderOverride);
 
         //allows you not to write it every time in the YML
         foreach (var pair in data.SizeMaps)
@@ -70,7 +77,11 @@ public sealed class DisplacementMapSystem : EntitySystem
         }
 
         var displacementLayer = _serialization.CreateCopy(displacementDataLayer, notNullableOverride: true);
-        displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString() ?? "this is impossible";
+
+        // This previously assigned a string reading "this is impossible" if key.ToString eval'd to false.
+        // However, for the sake of sanity, we've changed this to assert non-null - !.
+        // If this throws an error, we're not sorry. Nanotrasen thanks you for your service fixing this bug.
+        displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString()!;
 
         _sprite.AddLayer(sprite.AsNullable(), displacementLayer, index);
         _sprite.LayerMapSet(sprite.AsNullable(), displacementKey, index);
@@ -78,14 +89,18 @@ public sealed class DisplacementMapSystem : EntitySystem
         return true;
     }
 
-    /// <inheritdoc cref="TryAddDisplacement"/>
-    [Obsolete("Use the Entity<SpriteComponent> overload")]
-    public bool TryAddDisplacement(DisplacementData data,
-        SpriteComponent sprite,
-        int index,
-        object key,
-        out string displacementKey)
+    /// <summary>
+    /// Ensures that the displacement map associated with the given layer key is not in the Sprite's LayerMap.
+    /// </summary>
+    /// <param name="sprite">The sprite to remove the displacement layer from.</param>
+    /// <param name="key">The key of the layer that is referenced by the displacement layer we want to remove.</param>
+    /// <param name="logMissing">Whether to report an error if the displacement map isn't on the sprite.</param>
+    public void EnsureDisplacementIsNotOnSprite(Entity<SpriteComponent> sprite, object key)
     {
-        return TryAddDisplacement(data, (sprite.Owner, sprite), index, key, out displacementKey);
+        var displacementLayerKey = BuildDisplacementLayerKey(key);
+        if (displacementLayerKey is null)
+            return;
+
+        _sprite.RemoveLayer(sprite.AsNullable(), displacementLayerKey, false);
     }
 }
index 6700cf2a18d762546aa84a32e33bc73803177ee7..54c2801e33f502f04cfb3c7a9441ba45292c72a4 100644 (file)
@@ -289,25 +289,26 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
     private void RemoveMarking(Marking marking, Entity<SpriteComponent> spriteEnt)
     {
         if (!_markingManager.TryGetMarking(marking, out var prototype))
-        {
             return;
-        }
 
         foreach (var sprite in prototype.Sprites)
         {
             if (sprite is not SpriteSpecifier.Rsi rsi)
-            {
                 continue;
-            }
 
             var layerId = $"{marking.MarkingId}-{rsi.RsiState}";
             if (!_sprite.LayerMapTryGet(spriteEnt.AsNullable(), layerId, out var index, false))
-            {
                 continue;
-            }
 
             _sprite.LayerMapRemove(spriteEnt.AsNullable(), layerId);
             _sprite.RemoveLayer(spriteEnt.AsNullable(), index);
+
+            // If this marking is one that can be displaced, we need to remove the displacement as well; otherwise
+            // altering a marking at runtime can lead to the renderer falling over.
+            // The Vulps must be shaved.
+            // (https://github.com/space-wizards/space-station-14/issues/40135).
+            if (prototype.CanBeDisplaced)
+                _displacement.EnsureDisplacementIsNotOnSprite(spriteEnt, layerId);
         }
     }
 
@@ -346,9 +347,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
         var sprite = entity.Comp2;
 
         if (!_sprite.LayerMapTryGet((entity.Owner, sprite), markingPrototype.BodyPart, out var targetLayer, false))
-        {
             return;
-        }
 
         visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
         visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
@@ -359,9 +358,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
             var markingSprite = markingPrototype.Sprites[j];
 
             if (markingSprite is not SpriteSpecifier.Rsi rsi)
-            {
-                continue;
-            }
+                return;
 
             var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
 
@@ -375,26 +372,18 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
             _sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
 
             if (!visible || setting == null) // this is kinda implied
-            {
                 continue;
-            }
 
             // Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
             // and we need to check the index is correct.
             // So if that happens just default to white?
             if (colors != null && j < colors.Count)
-            {
                 _sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
-            }
             else
-            {
                 _sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
-            }
 
             if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
-            {
                 _displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
-            }
         }
     }
 
index bf83807c56a56533f4ea5982c070c0087eb3dc29..5d2b4418c8a1242ce221f5996c79cc92326d627c 100644 (file)
@@ -41,7 +41,6 @@
   points:
     Hair:
       points: 1
-      onlyWhitelisted: true # TODO: Vulps are meant to use human hair, however something causes hair to break if affected by a displacement map and removed. Allow human hair again when #40135 is resolved.
       required: false
     FacialHair:
       points: 1