using Content.Shared.Doors.Components; using Content.Shared.Doors.Systems; using Content.Shared.SprayPainter.Prototypes; using Robust.Client.Animations; using Robust.Client.GameObjects; namespace Content.Client.Doors; /// public sealed partial class DoorSystem : SharedDoorSystem { [Dependency] private AnimationPlayerSystem _animationSystem = default!; [Dependency] private SpriteSystem _sprite = default!; /// protected override void OnComponentInit(Entity ent, ref ComponentInit args) { // Base call deferred until end of function. var comp = ent.Comp; comp.OpenSpriteStates = new(2); comp.ClosedSpriteStates = new(2); comp.OpenSpriteStates.Add((DoorVisualLayers.Base, comp.OpenSpriteState)); comp.ClosedSpriteStates.Add((DoorVisualLayers.Base, comp.ClosedSpriteState)); comp.OpeningAnimation = new Animation { Length = comp.OpeningAnimationTime, AnimationTracks = { new AnimationTrackSpriteFlick { LayerKey = DoorVisualLayers.Base, KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningSpriteState, 0f), }, }, }, }; comp.ClosingAnimation = new Animation { Length = comp.ClosingAnimationTime, AnimationTracks = { new AnimationTrackSpriteFlick { LayerKey = DoorVisualLayers.Base, KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingSpriteState, 0f), }, }, }, }; comp.EmaggingAnimation = new Animation { Length = comp.EmaggingAnimationTime, AnimationTracks = { new AnimationTrackSpriteFlick { LayerKey = DoorVisualLayers.BaseEmagging, KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.EmaggingSpriteState, 0f), }, }, }, }; base.OnComponentInit(ent, ref args); } [SubscribeLocalEvent] private void OnAnimationCompleted(Entity ent, ref AnimationCompletedEvent args) { if (args.Key != DoorComponent.OpenKey && args.Key != DoorComponent.CloseKey) return; if (!TryComp(ent, out var sprite)) return; switch (ent.Comp.State) { case DoorState.Open: foreach (var (layer, layerState) in ent.Comp.OpenSpriteStates) { _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); } break; case DoorState.Closed: foreach (var (layer, layerState) in ent.Comp.ClosedSpriteStates) { _sprite.LayerSetRsiState((ent.Owner, sprite), layer, layerState); } break; } } [SubscribeLocalEvent] private void OnAppearanceChange(Entity entity, ref AppearanceChangeEvent args) { if (args.Sprite == null) return; if (!args.TryGetData(DoorVisuals.State, out var state)) state = DoorState.Closed; if (args.TryGetData(PaintableVisuals.Prototype, out var prototype)) UpdateSpriteLayers((entity.Owner, args.Sprite), prototype); if (_sprite.LayerMapTryGet(entity.Owner, DoorVisualLayers.BaseEmagging, out var emaggingLayer, logMissing: false)) _sprite.LayerSetVisible(entity.Owner, emaggingLayer, state == DoorState.Emagging); UpdateAppearanceForDoorState(entity, args.Sprite, state); } private void UpdateAppearanceForDoorState(Entity entity, SpriteComponent sprite, DoorState state) { _sprite.SetDrawDepth((entity.Owner, sprite), state is DoorState.Open ? entity.Comp.OpenDrawDepth : entity.Comp.ClosedDrawDepth); switch (state) { case DoorState.Open: if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenKey)) return; if (_animationSystem.HasRunningAnimation(entity, DoorComponent.CloseKey)) { _animationSystem.Stop(entity, null, DoorComponent.CloseKey); _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenKey); } foreach (var (layer, layerState) in entity.Comp.OpenSpriteStates) { // Allow animations to play while it's open (e.g., pinion); // the animation unsets this so we gotta set it again. _sprite.LayerSetAutoAnimated((entity.Owner, sprite), layer, true); _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } return; case DoorState.Closed: if (_animationSystem.HasRunningAnimation(entity, DoorComponent.CloseKey)) return; if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenKey)) { _animationSystem.Stop(entity, null, DoorComponent.OpenKey); _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.CloseKey); } foreach (var (layer, layerState) in entity.Comp.ClosedSpriteStates) { _sprite.LayerSetAutoAnimated((entity.Owner, sprite), layer, true); _sprite.LayerSetRsiState((entity.Owner, sprite), layer, layerState); } return; case DoorState.Opening: if (entity.Comp.OpeningAnimationTime == TimeSpan.Zero) return; if (_animationSystem.HasRunningAnimation(entity, DoorComponent.OpenKey)) return; _animationSystem.Play(entity, (Animation)entity.Comp.OpeningAnimation, DoorComponent.OpenKey); return; case DoorState.Closing: if (entity.Comp.ClosingAnimationTime == TimeSpan.Zero) return; if (_animationSystem.HasRunningAnimation(entity, DoorComponent.CloseKey)) return; _animationSystem.Play(entity, (Animation)entity.Comp.ClosingAnimation, DoorComponent.CloseKey); return; case DoorState.Denying: if (_animationSystem.HasRunningAnimation(entity, DoorComponent.DenyKey)) return; _animationSystem.Play(entity, (Animation)entity.Comp.DenyingAnimation, DoorComponent.DenyKey); return; case DoorState.Emagging: if (_animationSystem.HasRunningAnimation(entity, DoorComponent.EmagKey)) return; // We are checking beforehand since some doors may not have an emagging visual layer. if (_sprite.TryGetLayer(entity.Owner, DoorVisualLayers.BaseEmagging, out var _, false)) _animationSystem.Play(entity, (Animation)entity.Comp.EmaggingAnimation, DoorComponent.EmagKey); return; } } private void UpdateSpriteLayers(Entity sprite, string targetProto) { if (!ProtoMan.Resolve(targetProto, out var target)) return; if (!target.TryComp(out SpriteComponent? targetSprite, Factory)) return; _sprite.SetBaseRsi(sprite.AsNullable(), targetSprite.BaseRSI); } }