diff --git a/Content.Client/GameObjects/Components/Doors/AirlockVisualizer2D.cs b/Content.Client/GameObjects/Components/Doors/AirlockVisualizer2D.cs index 03f0af16c5..04597fc3f9 100644 --- a/Content.Client/GameObjects/Components/Doors/AirlockVisualizer2D.cs +++ b/Content.Client/GameObjects/Components/Doors/AirlockVisualizer2D.cs @@ -120,7 +120,6 @@ namespace Content.Client.GameObjects.Components.Doors { animPlayer.Play(OpenAnimation, AnimationKey); } - break; case DoorVisualState.Open: sprite.LayerSetState(DoorVisualLayers.Base, "open"); diff --git a/Content.Client/GameObjects/Components/Power/AutolatheVisualizer2D.cs b/Content.Client/GameObjects/Components/Power/AutolatheVisualizer2D.cs new file mode 100644 index 0000000000..6ad061678b --- /dev/null +++ b/Content.Client/GameObjects/Components/Power/AutolatheVisualizer2D.cs @@ -0,0 +1,107 @@ +using System; +using Content.Shared.GameObjects.Components.Power; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Power +{ + public class AutolatheVisualizer2D : AppearanceVisualizer + { + private const string AnimationKey = "autolathe_animation"; + + private Animation _buildingAnimation; + private Animation _insertingMetalAnimation; + private Animation _insertingGlassAnimation; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + _buildingAnimation = PopulateAnimation("autolathe_building", "autolathe_building_unlit", 0.5f); + _insertingMetalAnimation = PopulateAnimation("autolathe_inserting_metal_plate", "autolathe_inserting_unlit", 0.9f); + _insertingGlassAnimation = PopulateAnimation("autolathe_inserting_glass_plate", "autolathe_inserting_unlit", 0.9f); + } + + private Animation PopulateAnimation(string sprite, string spriteUnlit, float length) + { + var animation = new Animation {Length = TimeSpan.FromSeconds(length)}; + + var flick = new AnimationTrackSpriteFlick(); + animation.AnimationTracks.Add(flick); + flick.LayerKey = AutolatheVisualLayers.Base; + flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(sprite, 0f)); + + var flickUnlit = new AnimationTrackSpriteFlick(); + animation.AnimationTracks.Add(flickUnlit); + flickUnlit.LayerKey = AutolatheVisualLayers.BaseUnlit; + flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(spriteUnlit, 0f)); + + return animation; + } + + public override void InitializeEntity(IEntity entity) + { + if (!entity.HasComponent()) + { + entity.AddComponent(); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + var sprite = component.Owner.GetComponent(); + var animPlayer = component.Owner.GetComponent(); + if (!component.TryGetData(PowerDeviceVisuals.VisualState, out LatheVisualState state)) + { + state = LatheVisualState.Idle; + } + + switch (state) + { + case LatheVisualState.Idle: + if (animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Stop(AnimationKey); + } + + sprite.LayerSetState(AutolatheVisualLayers.Base, "autolathe"); + sprite.LayerSetState(AutolatheVisualLayers.BaseUnlit, "autolathe_unlit"); + break; + case LatheVisualState.Producing: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_buildingAnimation, AnimationKey); + } + break; + case LatheVisualState.InsertingMetal: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_insertingMetalAnimation, AnimationKey); + } + break; + case LatheVisualState.InsertingGlass: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_insertingGlassAnimation, AnimationKey); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var glowingPartsVisible = !(component.TryGetData(PowerDeviceVisuals.Powered, out bool powered) && !powered); + sprite.LayerSetVisible(AutolatheVisualLayers.BaseUnlit, glowingPartsVisible); + } + public enum AutolatheVisualLayers + { + Base, + BaseUnlit + } + } +} diff --git a/Content.Client/GameObjects/Components/Power/ProtolatheVisualizer2D.cs b/Content.Client/GameObjects/Components/Power/ProtolatheVisualizer2D.cs new file mode 100644 index 0000000000..4547c1668c --- /dev/null +++ b/Content.Client/GameObjects/Components/Power/ProtolatheVisualizer2D.cs @@ -0,0 +1,104 @@ +using System; +using Content.Shared.GameObjects.Components.Power; +using Robust.Client.Animations; +using Robust.Client.GameObjects; +using Robust.Client.GameObjects.Components.Animations; +using Robust.Client.Interfaces.GameObjects.Components; +using Robust.Shared.Interfaces.GameObjects; +using YamlDotNet.RepresentationModel; + +namespace Content.Client.GameObjects.Components.Power +{ + public class ProtolatheVisualizer2D : AppearanceVisualizer + { + private const string AnimationKey = "protolathe_animation"; + + private Animation _buildingAnimation; + private Animation _insertingMetalAnimation; + private Animation _insertingGlassAnimation; + + public override void LoadData(YamlMappingNode node) + { + base.LoadData(node); + + _buildingAnimation = PopulateAnimation("protolathe_building", 0.9f); + _insertingMetalAnimation = PopulateAnimation("protolathe_metal", 0.9f); + _insertingGlassAnimation = PopulateAnimation("protolathe_glass", 0.9f); + } + + private Animation PopulateAnimation(string sprite, float length) + { + var animation = new Animation {Length = TimeSpan.FromSeconds(length)}; + + var flick = new AnimationTrackSpriteFlick(); + animation.AnimationTracks.Add(flick); + flick.LayerKey = ProtolatheVisualLayers.AnimationLayer; + flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame(sprite, 0f)); + + return animation; + } + + public override void InitializeEntity(IEntity entity) + { + if (!entity.HasComponent()) + { + entity.AddComponent(); + } + } + + public override void OnChangeData(AppearanceComponent component) + { + base.OnChangeData(component); + + var sprite = component.Owner.GetComponent(); + var animPlayer = component.Owner.GetComponent(); + if (!component.TryGetData(PowerDeviceVisuals.VisualState, out LatheVisualState state)) + { + state = LatheVisualState.Idle; + } + sprite.LayerSetVisible(ProtolatheVisualLayers.AnimationLayer, true); + switch (state) + { + case LatheVisualState.Idle: + if (animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Stop(AnimationKey); + } + + sprite.LayerSetState(ProtolatheVisualLayers.Base, "protolathe"); + sprite.LayerSetState(ProtolatheVisualLayers.BaseUnlit, "protolathe_unlit"); + sprite.LayerSetVisible(ProtolatheVisualLayers.AnimationLayer, false); + break; + case LatheVisualState.Producing: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_buildingAnimation, AnimationKey); + } + break; + case LatheVisualState.InsertingMetal: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_insertingMetalAnimation, AnimationKey); + } + break; + case LatheVisualState.InsertingGlass: + if (!animPlayer.HasRunningAnimation(AnimationKey)) + { + animPlayer.Play(_insertingGlassAnimation, AnimationKey); + } + break; + default: + throw new ArgumentOutOfRangeException(); + } + + var glowingPartsVisible = !(component.TryGetData(PowerDeviceVisuals.Powered, out bool powered) && !powered); + sprite.LayerSetVisible(ProtolatheVisualLayers.BaseUnlit, glowingPartsVisible); + } + public enum ProtolatheVisualLayers + { + Base, + BaseUnlit, + AnimationLayer + } + } +} diff --git a/Content.Server/GameObjects/Components/Research/LatheComponent.cs b/Content.Server/GameObjects/Components/Research/LatheComponent.cs index d08a471024..f1020eb091 100644 --- a/Content.Server/GameObjects/Components/Research/LatheComponent.cs +++ b/Content.Server/GameObjects/Components/Research/LatheComponent.cs @@ -1,18 +1,22 @@ // Only unused on .NET Core due to KeyValuePair.Deconstruct // ReSharper disable once RedundantUsingDirective + +using System; using Robust.Shared.Utility; using System.Collections.Generic; +using System.Linq; using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.Stack; using Content.Server.GameObjects.EntitySystems; using Content.Shared.GameObjects.Components.Materials; +using Content.Shared.GameObjects.Components.Power; using Content.Shared.GameObjects.Components.Research; using Content.Shared.Research; +using Robust.Server.GameObjects; using Robust.Server.GameObjects.Components.UserInterface; using Robust.Server.Interfaces.GameObjects; using Robust.Server.Interfaces.Player; using Robust.Shared.GameObjects; -using Robust.Shared.Map; using Robust.Shared.Timers; using Robust.Shared.ViewVariables; @@ -32,16 +36,28 @@ namespace Content.Server.GameObjects.Components.Research [ViewVariables] public bool Producing { get; private set; } = false; + private AppearanceComponent _appearance; + private LatheState _state = LatheState.Base; + + protected virtual LatheState State + { + get => _state; + set => _state = value; + } + private LatheRecipePrototype _producingRecipe = null; private PowerDeviceComponent _powerDevice; private bool Powered => _powerDevice.Powered; + private static readonly TimeSpan InsertionTime = TimeSpan.FromSeconds(0.9f); + public override void Initialize() { base.Initialize(); _userInterface = Owner.GetComponent().GetBoundUserInterface(LatheUiKey.Key); _userInterface.OnReceiveMessage += UserInterfaceOnOnReceiveMessage; _powerDevice = Owner.GetComponent(); + _appearance = Owner.GetComponent(); } private void UserInterfaceOnOnReceiveMessage(ServerBoundUserInterfaceMessage message) @@ -100,12 +116,17 @@ namespace Content.Server.GameObjects.Components.Research _userInterface.SendMessage(new LatheProducingRecipeMessage(recipe.ID)); + State = LatheState.Producing; + SetAppearance(LatheVisualState.Producing); + Timer.Spawn(recipe.CompleteTime, () => { Producing = false; _producingRecipe = null; Owner.EntityManager.SpawnEntity(recipe.Result, Owner.Transform.GridPosition); _userInterface.SendMessage(new LatheStoppedProducingRecipeMessage()); + State = LatheState.Base; + SetAppearance(LatheVisualState.Idle); }); return true; @@ -125,7 +146,6 @@ namespace Content.Server.GameObjects.Components.Research return; } OpenUserInterface(actor.playerSession); - return; } bool IAttackBy.AttackBy(AttackByEventArgs eventArgs) { @@ -151,15 +171,37 @@ namespace Content.Server.GameObjects.Components.Research foreach (var mat in material.MaterialTypes.Values) { - storage.InsertMaterial(mat.ID, VolumePerSheet * multiplier); } + State = LatheState.Inserting; + switch (material.MaterialTypes.Values.First().Name) + { + case "Steel": + SetAppearance(LatheVisualState.InsertingMetal); + break; + case "Glass": + SetAppearance(LatheVisualState.InsertingGlass); + break; + } + + Timer.Spawn(InsertionTime, async () => + { + State = LatheState.Base; + SetAppearance(LatheVisualState.Idle); + }); + eventArgs.AttackWith.Delete(); return false; } + private void SetAppearance(LatheVisualState state) + { + if (_appearance != null || Owner.TryGetComponent(out _appearance)) + _appearance.SetData(PowerDeviceVisuals.VisualState, state); + } + private Queue GetIDQueue() { var queue = new Queue(); @@ -170,5 +212,12 @@ namespace Content.Server.GameObjects.Components.Research return queue; } + + protected enum LatheState + { + Base, + Inserting, + Producing + } } } diff --git a/Content.Shared/GameObjects/Components/Power/SharedLatheComponent.cs b/Content.Shared/GameObjects/Components/Power/SharedLatheComponent.cs new file mode 100644 index 0000000000..21ab0d43a0 --- /dev/null +++ b/Content.Shared/GameObjects/Components/Power/SharedLatheComponent.cs @@ -0,0 +1,14 @@ +using System; +using Robust.Shared.Serialization; + +namespace Content.Shared.GameObjects.Components.Power +{ + [Serializable, NetSerializable] + public enum LatheVisualState + { + Idle, + Producing, + InsertingMetal, + InsertingGlass + } +} diff --git a/Content.Shared/GameObjects/Components/Power/SharedPowerDeviceComponent.cs b/Content.Shared/GameObjects/Components/Power/SharedPowerDeviceComponent.cs index 2f050760f4..67ee873851 100644 --- a/Content.Shared/GameObjects/Components/Power/SharedPowerDeviceComponent.cs +++ b/Content.Shared/GameObjects/Components/Power/SharedPowerDeviceComponent.cs @@ -6,6 +6,7 @@ namespace Content.Shared.GameObjects.Components.Power [Serializable, NetSerializable] public enum PowerDeviceVisuals { + VisualState, Powered } } diff --git a/Resources/Prototypes/Entities/buildings/lathe.yml b/Resources/Prototypes/Entities/buildings/lathe.yml index f49b58f89e..67a52069f2 100644 --- a/Resources/Prototypes/Entities/buildings/lathe.yml +++ b/Resources/Prototypes/Entities/buildings/lathe.yml @@ -25,9 +25,10 @@ sprite: Buildings/autolathe.rsi layers: - state: autolathe + map: ["enum.AutolatheVisualLayers.Base"] - state: autolathe_unlit shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] + map: ["enum.AutolatheVisualLayers.BaseUnlit"] - type: Icon sprite: Buildings/autolathe.rsi state: autolathe @@ -56,7 +57,7 @@ - Multitool - type: Appearance visuals: - - type: PowerDeviceVisualizer2D + - type: AutolatheVisualizer2D - type: entity parent: BaseLathe @@ -67,9 +68,12 @@ sprite: Buildings/research.rsi layers: - state: protolathe + map: ["enum.ProtolatheVisualLayers.Base"] - state: protolathe_unlit shader: unshaded - map: ["enum.PowerDeviceVisualLayers.Powered"] + map: ["enum.ProtolatheVisualLayers.BaseUnlit"] + - state: protolathe + map: ["enum.ProtolatheVisualLayers.AnimationLayer"] - type: Icon sprite: Buildings/research.rsi state: protolathe @@ -104,4 +108,4 @@ type: ResearchClientBoundUserInterface - type: Appearance visuals: - - type: PowerDeviceVisualizer2D + - type: ProtolatheVisualizer2D diff --git a/Resources/Textures/Buildings/autolathe.rsi/autolathe_r.png b/Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting_glass_plate.png similarity index 100% rename from Resources/Textures/Buildings/autolathe.rsi/autolathe_r.png rename to Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting_glass_plate.png diff --git a/Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting.png b/Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting_metal_plate.png similarity index 100% rename from Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting.png rename to Resources/Textures/Buildings/autolathe.rsi/autolathe_inserting_metal_plate.png diff --git a/Resources/Textures/Buildings/autolathe.rsi/meta.json b/Resources/Textures/Buildings/autolathe.rsi/meta.json index 3bb1cccea4..9f868d6a0c 100644 --- a/Resources/Textures/Buildings/autolathe.rsi/meta.json +++ b/Resources/Textures/Buildings/autolathe.rsi/meta.json @@ -28,15 +28,15 @@ "directions": 1, "delays": [ [ - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15 + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055 ] ] }, @@ -45,20 +45,37 @@ "directions": 1, "delays": [ [ - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15, - 0.15 + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055, + 0.055 ] ] }, { - "name": "autolathe_inserting", + "name": "autolathe_inserting_metal_plate", + "directions": 1, + "delays": [ + [ + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1, + 0.1 + ] + ] + }, + { + "name": "autolathe_inserting_glass_plate", "directions": 1, "delays": [ [ @@ -99,23 +116,6 @@ 1.0 ] ] - }, - { - "name": "autolathe_r", - "directions": 1, - "delays": [ - [ - 0.1, - 0.1, - 0.1, - 0.1, - 0.1, - 0.1, - 0.1, - 0.1, - 0.1 - ] - ] } ] -} \ No newline at end of file +} diff --git a/Resources/Textures/Buildings/research.rsi/meta.json b/Resources/Textures/Buildings/research.rsi/meta.json index 6eacf5723e..c41bbc75d9 100644 --- a/Resources/Textures/Buildings/research.rsi/meta.json +++ b/Resources/Textures/Buildings/research.rsi/meta.json @@ -259,7 +259,7 @@ ] }, { - "name": "protolathe_n", + "name": "protolathe_building", "directions": 1, "delays": [ [ @@ -314,7 +314,7 @@ ] }, { - "name": "protolathe_t", + "name": "protolathe_open", "directions": 1, "delays": [ [ diff --git a/Resources/Textures/Buildings/research.rsi/protolathe_n.png b/Resources/Textures/Buildings/research.rsi/protolathe_building.png similarity index 100% rename from Resources/Textures/Buildings/research.rsi/protolathe_n.png rename to Resources/Textures/Buildings/research.rsi/protolathe_building.png diff --git a/Resources/Textures/Buildings/research.rsi/protolathe_t.png b/Resources/Textures/Buildings/research.rsi/protolathe_open.png similarity index 100% rename from Resources/Textures/Buildings/research.rsi/protolathe_t.png rename to Resources/Textures/Buildings/research.rsi/protolathe_open.png