using Content.Shared.Light; using Robust.Client.Animations; using Robust.Client.GameObjects; using Robust.Shared.Animations; using Robust.Shared.Audio.Systems; using Robust.Shared.Random; namespace Content.Client.Light.Visualizers; public sealed class PoweredLightVisualizerSystem : VisualizerSystem { [Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly SharedAudioSystem _audio = default!; [Dependency] private readonly SpriteSystem _sprite = default!; public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnAnimationCompleted); } protected override void OnAppearanceChange(EntityUid uid, PoweredLightVisualsComponent comp, ref AppearanceChangeEvent args) { if (args.Sprite == null) return; if (!AppearanceSystem.TryGetData(uid, PoweredLightVisuals.BulbState, out var state, args.Component)) return; if (comp.SpriteStateMap.TryGetValue(state, out var spriteState)) _sprite.LayerSetRsiState((uid, args.Sprite), PoweredLightLayers.Base, spriteState); if (_sprite.LayerExists((uid, args.Sprite), PoweredLightLayers.Glow)) { if (TryComp(uid, out var light)) { _sprite.LayerSetColor((uid, args.Sprite), PoweredLightLayers.Glow, light.Color); } _sprite.LayerSetVisible((uid, args.Sprite), PoweredLightLayers.Glow, state == PoweredLightState.On); } SetBlinkingAnimation( uid, state == PoweredLightState.On && (AppearanceSystem.TryGetData(uid, PoweredLightVisuals.Blinking, out var isBlinking, args.Component) && isBlinking), comp ); } /// /// Loops the blinking animation until the light should stop blinking. /// private void OnAnimationCompleted(EntityUid uid, PoweredLightVisualsComponent comp, AnimationCompletedEvent args) { if (!TryComp(uid, out var animationPlayer)) return; if (args.Key != PoweredLightVisualsComponent.BlinkingAnimationKey) return; if (!comp.IsBlinking) return; AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); } /// /// Sets whether or not the given light should be blinking. /// Triggers or clears the blinking animation of the state changes. /// private void SetBlinkingAnimation(EntityUid uid, bool shouldBeBlinking, PoweredLightVisualsComponent comp) { if (shouldBeBlinking == comp.IsBlinking) return; comp.IsBlinking = shouldBeBlinking; var animationPlayer = EnsureComp(uid); if (shouldBeBlinking) { AnimationSystem.Play((uid, animationPlayer), BlinkingAnimation(comp), PoweredLightVisualsComponent.BlinkingAnimationKey); } else if (AnimationSystem.HasRunningAnimation(uid, animationPlayer, PoweredLightVisualsComponent.BlinkingAnimationKey)) { AnimationSystem.Stop(uid, animationPlayer, PoweredLightVisualsComponent.BlinkingAnimationKey); } } /// /// Generates a blinking animation. /// Essentially just flashes the light off and on over a random time interval. /// The resulting animation is looped indefinitely until the comp is set to stop blinking. /// private Animation BlinkingAnimation(PoweredLightVisualsComponent comp) { var randomTime = MathHelper.Lerp(comp.MinBlinkingAnimationCycleTime, comp.MaxBlinkingAnimationCycleTime, _random.NextFloat()); var blinkingAnim = new Animation() { Length = TimeSpan.FromSeconds(randomTime), AnimationTracks = { new AnimationTrackComponentProperty { ComponentType = typeof(PointLightComponent), InterpolationMode = AnimationInterpolationMode.Nearest, Property = nameof(PointLightComponent.AnimatedEnable), KeyFrames = { new AnimationTrackProperty.KeyFrame(false, 0), new AnimationTrackProperty.KeyFrame(true, 1) } }, new AnimationTrackSpriteFlick() { LayerKey = PoweredLightLayers.Base, KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.SpriteStateMap[PoweredLightState.Off], 0), new AnimationTrackSpriteFlick.KeyFrame(comp.SpriteStateMap[PoweredLightState.On], 0.5f) } } } }; if (comp.BlinkingSound != null) { var sound = _audio.ResolveSound(comp.BlinkingSound); blinkingAnim.AnimationTracks.Add(new AnimationTrackPlaySound() { KeyFrames = { new AnimationTrackPlaySound.KeyFrame(sound, 0.5f) } }); } return blinkingAnim; } }