--- /dev/null
+using Content.Shared.Singularity.Components;
+using Robust.Client.Animations;
+
+namespace Content.Client.Singularity.Visualizers;
+
+/// <summary>
+/// The component used to reflect the state of a radiation collector in its appearance.
+/// </summary>
+[RegisterComponent]
+[Access(typeof(RadiationCollectorSystem))]
+public sealed class RadiationCollectorComponent : Component
+{
+ /// <summary>
+ /// The key used to index the (de)activation animations played when turning a radiation collector on/off.
+ /// </summary>
+ [ViewVariables]
+ public const string AnimationKey = "radiationcollector_animation";
+
+ /// <summary>
+ /// The current visual state of the radiation collector.
+ /// </summary>
+ [ViewVariables]
+ public RadiationCollectorVisualState CurrentState = RadiationCollectorVisualState.Deactive;
+
+ /// <summary>
+ /// The RSI state used for the main sprite layer (<see cref="RadiationCollectorVisualLayers.Main"/>) when the radiation collector is active.
+ /// </summary>
+ [DataField("activeState")]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string ActiveState = "ca_on";
+
+ /// <summary>
+ /// The RSI state used for the main sprite layer (<see cref="RadiationCollectorVisualLayers.Main"/>) when the radiation collector is inactive.
+ /// </summary>
+ [DataField("inactiveState")]
+ [ViewVariables(VVAccess.ReadWrite)]
+ public string InactiveState = "ca_off";
+
+ /// <summary>
+ /// Used to build the <value cref="ActivateAnimation">activation animation</value> when the component is initialized.
+ /// </summary>
+ [DataField("activatingState")]
+ public string ActivatingState = "ca_active";
+
+ /// <summary>
+ /// Used to build the <see cref="DeactiveAnimation">deactivation animation</see> when the component is initialized.
+ /// </summary>
+ [DataField("deactivatingState")]
+ public string DeactivatingState = "ca_deactive";
+
+ /// <summary>
+ /// The animation used when turning on the radiation collector.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ public Animation ActivateAnimation = default!;
+
+ /// <summary>
+ /// The animation used when turning off the radiation collector.
+ /// </summary>
+ [ViewVariables(VVAccess.ReadWrite)]
+ public Animation DeactiveAnimation = default!;
+}
--- /dev/null
+using System;
+using Content.Shared.Singularity.Components;
+using Robust.Client.Animations;
+using Robust.Client.GameObjects;
+
+namespace Content.Client.Singularity.Visualizers;
+
+public sealed class RadiationCollectorSystem : VisualizerSystem<RadiationCollectorComponent>
+{
+ public override void Initialize()
+ {
+ base.Initialize();
+ SubscribeLocalEvent<RadiationCollectorComponent, ComponentInit>(OnComponentInit);
+ SubscribeLocalEvent<RadiationCollectorComponent, AnimationCompletedEvent>(OnAnimationCompleted);
+ }
+
+ private void OnComponentInit(EntityUid uid, RadiationCollectorComponent comp, ComponentInit args)
+ {
+ comp.ActivateAnimation = new Animation {
+ Length = TimeSpan.FromSeconds(0.8f),
+ AnimationTracks = {
+ new AnimationTrackSpriteFlick() {
+ LayerKey = RadiationCollectorVisualLayers.Main,
+ KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(comp.ActivatingState, 0f)}
+ }, // TODO: Make this play a sound when activating a radiation collector.
+ }
+ };
+
+ comp.DeactiveAnimation = new Animation {
+ Length = TimeSpan.FromSeconds(0.8f),
+ AnimationTracks = {
+ new AnimationTrackSpriteFlick() {
+ LayerKey = RadiationCollectorVisualLayers.Main,
+ KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(comp.DeactivatingState, 0f)}
+ }, // TODO: Make this play a sound when deactivating a radiation collector.
+ }
+ };
+ }
+
+ private void UpdateVisuals(EntityUid uid, RadiationCollectorVisualState state, RadiationCollectorComponent comp, SpriteComponent sprite, AnimationPlayerComponent? animPlayer = null)
+ {
+ if (state == comp.CurrentState)
+ return;
+ if (!Resolve(uid, ref animPlayer))
+ return;
+ if (AnimationSystem.HasRunningAnimation(uid, animPlayer, RadiationCollectorComponent.AnimationKey))
+ return;
+
+ var targetState = (RadiationCollectorVisualState) (state & RadiationCollectorVisualState.Active);
+ var destinationState = (RadiationCollectorVisualState) (comp.CurrentState & RadiationCollectorVisualState.Active);
+ if (targetState != destinationState) // If where we're going is not where we want to be then we must go there next.
+ targetState = (RadiationCollectorVisualState) (targetState | RadiationCollectorVisualState.Deactivating); // Convert to transition state.
+
+ comp.CurrentState = state;
+
+ switch (targetState)
+ {
+ case RadiationCollectorVisualState.Activating:
+ AnimationSystem.Play(uid, animPlayer, comp.ActivateAnimation, RadiationCollectorComponent.AnimationKey);
+ break;
+ case RadiationCollectorVisualState.Deactivating:
+ AnimationSystem.Play(uid, animPlayer, comp.DeactiveAnimation, RadiationCollectorComponent.AnimationKey);
+ break;
+
+ case RadiationCollectorVisualState.Active:
+ sprite.LayerSetState(RadiationCollectorVisualLayers.Main, comp.ActiveState);
+ break;
+ case RadiationCollectorVisualState.Deactive:
+ sprite.LayerSetState(RadiationCollectorVisualLayers.Main, comp.InactiveState);
+ break;
+ }
+ }
+
+ private void OnAnimationCompleted(EntityUid uid, RadiationCollectorComponent comp, AnimationCompletedEvent args)
+ {
+ if (args.Key != RadiationCollectorComponent.AnimationKey)
+ return;
+ if (!TryComp<SpriteComponent>(uid, out var sprite))
+ return;
+ if (!TryComp<AnimationPlayerComponent>(uid, out var animPlayer))
+ return; // Why doesn't AnimationCompletedEvent propagate the AnimationPlayerComponent? No idea, but it's in engine so I'm not touching it.
+
+ if (!AppearanceSystem.TryGetData<RadiationCollectorVisualState>(uid, RadiationCollectorVisuals.VisualState, out var state))
+ state = comp.CurrentState;
+
+ // Convert to terminal state.
+ var targetState = (RadiationCollectorVisualState) (state & RadiationCollectorVisualState.Active);
+
+ UpdateVisuals(uid, targetState, comp, sprite, animPlayer);
+ }
+
+ protected override void OnAppearanceChange(EntityUid uid, RadiationCollectorComponent comp, ref AppearanceChangeEvent args)
+ {
+ if (args.Sprite == null)
+ return;
+ if (!TryComp<AnimationPlayerComponent>(uid, out var animPlayer))
+ return;
+
+ if (!AppearanceSystem.TryGetData<RadiationCollectorVisualState>(uid, RadiationCollectorVisuals.VisualState, out var state, args.Component))
+ state = RadiationCollectorVisualState.Deactive;
+
+ UpdateVisuals(uid, state, comp, args.Sprite, animPlayer);
+ }
+}
+
+public enum RadiationCollectorVisualLayers : byte
+{
+ Main
+}
+++ /dev/null
-using System;
-using Content.Shared.Singularity.Components;
-using JetBrains.Annotations;
-using Robust.Client.Animations;
-using Robust.Client.GameObjects;
-using Robust.Shared.GameObjects;
-using Robust.Shared.IoC;
-using Robust.Shared.Serialization;
-
-namespace Content.Client.Singularity.Visualizers
-{
- [UsedImplicitly]
- public sealed class RadiationCollectorVisualizer : AppearanceVisualizer, ISerializationHooks
- {
- private const string AnimationKey = "radiationcollector_animation";
-
- private Animation ActivateAnimation = default!;
- private Animation DeactiveAnimation = default!;
-
- void ISerializationHooks.AfterDeserialization()
- {
- ActivateAnimation = new Animation {Length = TimeSpan.FromSeconds(0.8f)};
- {
- var flick = new AnimationTrackSpriteFlick();
- ActivateAnimation.AnimationTracks.Add(flick);
- flick.LayerKey = RadiationCollectorVisualLayers.Main;
- flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("ca_active", 0f));
-
- /*var sound = new AnimationTrackPlaySound();
- CloseAnimation.AnimationTracks.Add(sound);
- sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));*/
- }
-
- DeactiveAnimation = new Animation {Length = TimeSpan.FromSeconds(0.8f)};
- {
- var flick = new AnimationTrackSpriteFlick();
- DeactiveAnimation.AnimationTracks.Add(flick);
- flick.LayerKey = RadiationCollectorVisualLayers.Main;
- flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("ca_deactive", 0f));
-
- /*var sound = new AnimationTrackPlaySound();
- CloseAnimation.AnimationTracks.Add(sound);
- sound.KeyFrames.Add(new AnimationTrackPlaySound.KeyFrame(closeSound, 0));*/
- }
- }
-
- [Obsolete("Subscribe to your component being initialised instead.")]
- public override void InitializeEntity(EntityUid entity)
- {
- IoCManager.Resolve<IEntityManager>().EnsureComponent<AnimationPlayerComponent>(entity);
- }
-
- [Obsolete("Subscribe to AppearanceChangeEvent instead.")]
- public override void OnChangeData(AppearanceComponent component)
- {
- base.OnChangeData(component);
-
- var entities = IoCManager.Resolve<IEntityManager>();
- if (!entities.TryGetComponent(component.Owner, out SpriteComponent? sprite)) return;
- if (!entities.TryGetComponent(component.Owner, out AnimationPlayerComponent? animPlayer)) return;
- if (!component.TryGetData(RadiationCollectorVisuals.VisualState, out RadiationCollectorVisualState state))
- {
- state = RadiationCollectorVisualState.Deactive;
- }
-
- switch (state)
- {
- case RadiationCollectorVisualState.Active:
- sprite.LayerSetState(RadiationCollectorVisualLayers.Main, "ca_on");
- break;
- case RadiationCollectorVisualState.Activating:
- if (!animPlayer.HasRunningAnimation(AnimationKey))
- {
- animPlayer.Play(ActivateAnimation, AnimationKey);
- animPlayer.AnimationCompleted += _ =>
- component.SetData(RadiationCollectorVisuals.VisualState,
- RadiationCollectorVisualState.Active);
- }
- break;
- case RadiationCollectorVisualState.Deactivating:
- if (!animPlayer.HasRunningAnimation(AnimationKey))
- {
- animPlayer.Play(DeactiveAnimation, AnimationKey);
- animPlayer.AnimationCompleted += _ =>
- component.SetData(RadiationCollectorVisuals.VisualState,
- RadiationCollectorVisualState.Deactive);
- }
- break;
- case RadiationCollectorVisualState.Deactive:
- sprite.LayerSetState(RadiationCollectorVisualLayers.Main, "ca_off");
- break;
- }
- }
- }
-
- public enum RadiationCollectorVisualLayers : byte
- {
- Main
- }
-}
[NetSerializable, Serializable]
public enum RadiationCollectorVisualState
- {
- Active,
- Activating,
- Deactivating,
- Deactive
+ {
+ Active = (1<<0),
+ Activating = (1<<1) | Active,
+ Deactivating = (1<<1),
+ Deactive = 0
}
}
netsync: false
snapCardinals: true
layers:
- - state: ca_on
+ - state: ca_off
map: ["enum.RadiationCollectorVisualLayers.Main"]
- type: Appearance
- visuals:
- - type: RadiationCollectorVisualizer
+ - type: AnimationPlayer
- type: NodeContainer
examinable: true
nodes: