From eb82dd3daea7bd14bfe8b898a6a71662ce922331 Mon Sep 17 00:00:00 2001 From: 20kdc Date: Sun, 3 Oct 2021 00:43:47 +0100 Subject: [PATCH] Fix #4070, Visualizer Edition (#4730) * Transform the Cloning Pod visualizer into a generic enum visualizer for simple cases like it ...I find it helpful at times like these to remind myself that our true enemy is code. Code is what makes our programs run and generally is required for all of these machinations to function in any way at all... * Give the kitchen/meat spike a visualizer * GenericEnumVisualizer: Byteify enums, switch to TryGetComponent for sprite --- .../Cloning/CloningPodVisualizer.cs | 41 ------------ .../Visualizer/GenericEnumVisualizer.cs | 63 +++++++++++++++++++ .../Components/KitchenSpikeComponent.cs | 18 +++--- .../Cloning/SharedCloningPodComponent.cs | 4 +- .../Components/SharedKitchenSpikeComponent.cs | 15 +++++ .../Structures/Machines/cloning_machine.yml | 10 ++- .../Entities/Structures/meat_spike.yml | 9 +++ 7 files changed, 107 insertions(+), 53 deletions(-) delete mode 100644 Content.Client/Cloning/CloningPodVisualizer.cs create mode 100644 Content.Client/Visualizer/GenericEnumVisualizer.cs diff --git a/Content.Client/Cloning/CloningPodVisualizer.cs b/Content.Client/Cloning/CloningPodVisualizer.cs deleted file mode 100644 index 6a16cbe281..0000000000 --- a/Content.Client/Cloning/CloningPodVisualizer.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using JetBrains.Annotations; -using Robust.Client.GameObjects; -using static Content.Shared.Cloning.SharedCloningPodComponent; -using static Content.Shared.Cloning.SharedCloningPodComponent.CloningPodStatus; - -namespace Content.Client.Cloning -{ - [UsedImplicitly] - public class CloningPodVisualizer : AppearanceVisualizer - { - public override void OnChangeData(AppearanceComponent component) - { - base.OnChangeData(component); - - var sprite = component.Owner.GetComponent(); - if (!component.TryGetData(CloningPodVisuals.Status, out CloningPodStatus status)) return; - sprite.LayerSetState(CloningPodVisualLayers.Machine, StatusToMachineStateId(status)); - } - - private string StatusToMachineStateId(CloningPodStatus status) - { - //TODO: implement NoMind for if the mind is not yet in the body - //TODO: Find a use for GORE POD - switch (status) - { - case CloningPodStatus.Cloning: return "pod_1"; - case NoMind: return "pod_e"; - case Gore: return "pod_g"; - case Idle: return "pod_0"; - default: - throw new ArgumentOutOfRangeException(nameof(status), status, "unknown CloningPodStatus"); - } - } - - public enum CloningPodVisualLayers : byte - { - Machine, - } - } -} diff --git a/Content.Client/Visualizer/GenericEnumVisualizer.cs b/Content.Client/Visualizer/GenericEnumVisualizer.cs new file mode 100644 index 0000000000..27faff66b7 --- /dev/null +++ b/Content.Client/Visualizer/GenericEnumVisualizer.cs @@ -0,0 +1,63 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using JetBrains.Annotations; +using Robust.Client.GameObjects; +using Robust.Shared.IoC; +using Robust.Shared.Log; +using Robust.Shared.Reflection; +using Robust.Shared.Serialization; +using Robust.Shared.Serialization.Manager.Attributes; + +namespace Content.Client.Visualizer +{ + [UsedImplicitly] + public class GenericEnumVisualizer : AppearanceVisualizer, ISerializationHooks + { + public Enum Key { get; set; } = default!; + + public Dictionary States { get; set; } = default!; + + [DataField("layer")] + public int Layer { get; set; } = 0; + + [DataField("key", readOnly: true, required: true)] + private string _keyRaw = default!; + + [DataField("states", readOnly: true, required: true)] + private Dictionary _statesRaw { get; set; } = default!; + + void ISerializationHooks.AfterDeserialization() + { + var reflectionManager = IoCManager.Resolve(); + + object ResolveRef(string raw) + { + if (reflectionManager.TryParseEnumReference(raw, out var @enum)) + { + return @enum; + } + else + { + Logger.WarningS("c.c.v.genum", $"Unable to convert enum reference: {raw}"); + } + + return raw; + } + + // It's important that this conversion be done here so that it may "fail-fast". + Key = (Enum) ResolveRef(_keyRaw); + States = _statesRaw.ToDictionary(kvp => ResolveRef(kvp.Key), kvp => kvp.Value); + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + if (!component.Owner.TryGetComponent(out ISpriteComponent? sprite)) return; + if (!component.TryGetData(Key, out object status)) return; + if (!States.TryGetValue(status, out var val)) return; + sprite.LayerSetState(Layer, val); + } + } +} diff --git a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs index 8d7fa8593d..8f9b6b55d9 100644 --- a/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs +++ b/Content.Server/Kitchen/Components/KitchenSpikeComponent.cs @@ -55,10 +55,7 @@ namespace Content.Server.Kitchen.Components } else { - if (Owner.TryGetComponent(out sprite)) - { - sprite.LayerSetState(0, "spike"); - } + UpdateAppearance(); eventArgs.User.PopupMessage(_meatSource0); } @@ -73,6 +70,14 @@ namespace Content.Server.Kitchen.Components return true; } + private void UpdateAppearance() + { + if (Owner.TryGetComponent(out AppearanceComponent? appearance)) + { + appearance.SetData(KitchenSpikeVisuals.Status, (_meatParts > 0) ? KitchenSpikeStatus.Bloody : KitchenSpikeStatus.Empty); + } + } + private bool Spikeable(IEntity user, IEntity victim, [NotNullWhen(true)] out SharedButcherableComponent? butcherable) { butcherable = null; @@ -150,10 +155,7 @@ namespace Content.Server.Kitchen.Components _meatName = Loc.GetString("comp-kitchen-spike-meat-name", ("victim", victim)); // TODO: Visualizer - if (Owner.TryGetComponent(out var sprite)) - { - sprite.LayerSetState(0, "spikebloody"); - } + UpdateAppearance(); Owner.PopupMessageEveryone(Loc.GetString("comp-kitchen-spike-kill", ("user", user), ("victim", victim))); // TODO: Need to be able to leave them on the spike to do DoT, see ss13. diff --git a/Content.Shared/Cloning/SharedCloningPodComponent.cs b/Content.Shared/Cloning/SharedCloningPodComponent.cs index f90319bfe2..b592322424 100644 --- a/Content.Shared/Cloning/SharedCloningPodComponent.cs +++ b/Content.Shared/Cloning/SharedCloningPodComponent.cs @@ -44,13 +44,13 @@ namespace Content.Shared.Cloning } [Serializable, NetSerializable] - public enum CloningPodVisuals + public enum CloningPodVisuals : byte { Status } [Serializable, NetSerializable] - public enum CloningPodStatus + public enum CloningPodStatus : byte { Idle, Cloning, diff --git a/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs b/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs index ebfa220172..3649fdf8c9 100644 --- a/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs +++ b/Content.Shared/Kitchen/Components/SharedKitchenSpikeComponent.cs @@ -1,7 +1,9 @@ +using System; using Content.Shared.DragDrop; using Content.Shared.Nutrition.Components; using Content.Shared.Sound; using Robust.Shared.GameObjects; +using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.ViewVariables; @@ -31,5 +33,18 @@ namespace Content.Shared.Kitchen.Components } public abstract bool DragDropOn(DragDropEvent eventArgs); + + [Serializable, NetSerializable] + public enum KitchenSpikeVisuals : byte + { + Status + } + + [Serializable, NetSerializable] + public enum KitchenSpikeStatus : byte + { + Empty, + Bloody + } } } diff --git a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml index 0271ddadc6..e586ea094d 100644 --- a/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml +++ b/Resources/Prototypes/Entities/Structures/Machines/cloning_machine.yml @@ -10,7 +10,6 @@ sprite: Structures/Machines/cloning.rsi layers: - state: pod_0 - map: ["enum.CloningPodVisualLayers.Machine"] - type: Physics bodyType: Static fixtures: @@ -44,7 +43,14 @@ LayoutId: CloningPod - type: Appearance visuals: - - type: CloningPodVisualizer + - type: GenericEnumVisualizer + key: enum.CloningPodVisuals.Status + layer: 0 + states: + enum.CloningPodStatus.Cloning: pod_1 + enum.CloningPodStatus.NoMind: pod_e + enum.CloningPodStatus.Gore: pod_g + enum.CloningPodStatus.Idle: pod_0 - type: UserInterface interfaces: - key: enum.CloningPodUIKey.Key diff --git a/Resources/Prototypes/Entities/Structures/meat_spike.yml b/Resources/Prototypes/Entities/Structures/meat_spike.yml index 1a06698b2d..da8b281845 100644 --- a/Resources/Prototypes/Entities/Structures/meat_spike.yml +++ b/Resources/Prototypes/Entities/Structures/meat_spike.yml @@ -32,3 +32,12 @@ - type: KitchenSpike - type: Anchorable - type: Pullable + - type: Appearance + visuals: + - type: GenericEnumVisualizer + key: enum.KitchenSpikeVisuals.Status + layer: 0 + states: + enum.KitchenSpikeStatus.Empty: spike + enum.KitchenSpikeStatus.Bloody: spikebloody +