Resolves AirlockVisualizer is Obsolete (#13884)
This commit is contained in:
parent
8f8b71f75b
commit
7523ed4c17
|
|
@ -1,5 +1,112 @@
|
|||
using Content.Client.Wires.Visualizers;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class AirlockSystem : SharedAirlockSystem { }
|
||||
public sealed class AirlockSystem : SharedAirlockSystem
|
||||
{
|
||||
[Dependency] private readonly AppearanceSystem _appearanceSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<AirlockComponent, ComponentStartup>(OnComponentStartup);
|
||||
SubscribeLocalEvent<AirlockComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
private void OnComponentStartup(EntityUid uid, AirlockComponent comp, ComponentStartup args)
|
||||
{
|
||||
// Has to be on component startup because we don't know what order components initialize in and running this before DoorComponent inits _will_ crash.
|
||||
if(!TryComp<DoorComponent>(uid, out var door))
|
||||
return;
|
||||
|
||||
if (comp.OpenUnlitVisible) // Otherwise there are flashes of the fallback sprite between clicking on the door and the door closing animation starting.
|
||||
{
|
||||
door.OpenSpriteStates.Add((DoorVisualLayers.BaseUnlit, comp.OpenSpriteState));
|
||||
door.ClosedSpriteStates.Add((DoorVisualLayers.BaseUnlit, comp.ClosedSpriteState));
|
||||
}
|
||||
|
||||
((Animation)door.OpeningAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.BaseUnlit,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningSpriteState, 0f) },
|
||||
}
|
||||
);
|
||||
|
||||
((Animation)door.ClosingAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.BaseUnlit,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingSpriteState, 0f) },
|
||||
}
|
||||
);
|
||||
|
||||
door.DenyingAnimation = new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(comp.DenyAnimationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.BaseUnlit,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.DenySpriteState, 0f) },
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
if(!comp.AnimatePanel)
|
||||
return;
|
||||
|
||||
((Animation)door.OpeningAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = WiresVisualLayers.MaintenancePanel,
|
||||
KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningPanelSpriteState, 0f)},
|
||||
});
|
||||
|
||||
((Animation)door.ClosingAnimation).AnimationTracks.Add(new AnimationTrackSpriteFlick
|
||||
{
|
||||
LayerKey = WiresVisualLayers.MaintenancePanel,
|
||||
KeyFrames = {new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingPanelSpriteState, 0f)},
|
||||
});
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, AirlockComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null)
|
||||
return;
|
||||
|
||||
var boltedVisible = false;
|
||||
var emergencyLightsVisible = false;
|
||||
var unlitVisible = false;
|
||||
|
||||
if (!_appearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||
state = DoorState.Closed;
|
||||
|
||||
if (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.Powered, out var powered, args.Component) && powered)
|
||||
{
|
||||
boltedVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.BoltLights, out var lights, args.Component) && lights;
|
||||
emergencyLightsVisible = _appearanceSystem.TryGetData<bool>(uid, DoorVisuals.EmergencyLights, out var eaLights, args.Component) && eaLights;
|
||||
unlitVisible =
|
||||
state == DoorState.Closing
|
||||
|| state == DoorState.Opening
|
||||
|| state == DoorState.Denying
|
||||
|| (state == DoorState.Open && comp.OpenUnlitVisible)
|
||||
|| (_appearanceSystem.TryGetData<bool>(uid, DoorVisuals.ClosedLights, out var closedLights, args.Component) && closedLights);
|
||||
}
|
||||
|
||||
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
||||
args.Sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, boltedVisible);
|
||||
if (comp.EmergencyAccessLayer)
|
||||
{
|
||||
args.Sprite.LayerSetVisible(
|
||||
DoorVisualLayers.BaseEmergencyAccess,
|
||||
emergencyLightsVisible
|
||||
&& state != DoorState.Open
|
||||
&& state != DoorState.Opening
|
||||
&& state != DoorState.Closing
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,256 +0,0 @@
|
|||
using System;
|
||||
using Content.Client.Wires.Visualizers;
|
||||
using Content.Shared.Doors.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Doors
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class AirlockVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
private const string AnimationKey = "airlock_animation";
|
||||
|
||||
[DataField("animationTime")]
|
||||
private float _delay = 0.8f;
|
||||
|
||||
[DataField("denyAnimationTime")]
|
||||
private float _denyDelay = 0.3f;
|
||||
|
||||
|
||||
[DataField("emagAnimationTime")]
|
||||
private float _delayEmag = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the maintenance panel is animated or stays static.
|
||||
/// False for windoors.
|
||||
/// </summary>
|
||||
[DataField("animatedPanel")]
|
||||
private bool _animatedPanel = true;
|
||||
|
||||
/// <summary>
|
||||
/// Means the door is simply open / closed / opening / closing. No wires or access.
|
||||
/// </summary>
|
||||
[DataField("simpleVisuals")]
|
||||
private bool _simpleVisuals = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the BaseUnlit layer should still be visible when the airlock
|
||||
/// is opened.
|
||||
/// </summary>
|
||||
[DataField("openUnlitVisible")]
|
||||
private bool _openUnlitVisible = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door should have an emergency access layer
|
||||
/// </summary>
|
||||
[DataField("emergencyAccessLayer")]
|
||||
private bool _emergencyAccessLayer = true;
|
||||
|
||||
private Animation CloseAnimation = default!;
|
||||
private Animation OpenAnimation = default!;
|
||||
private Animation DenyAnimation = default!;
|
||||
private Animation EmaggingAnimation = default!;
|
||||
|
||||
void ISerializationHooks.AfterDeserialization()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
CloseAnimation = new Animation { Length = TimeSpan.FromSeconds(_delay) };
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing", 0f));
|
||||
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("closing_unlit", 0f));
|
||||
|
||||
if (_animatedPanel)
|
||||
{
|
||||
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
|
||||
CloseAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||
flickMaintenancePanel.LayerKey = WiresVisualLayers.MaintenancePanel;
|
||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_closing", 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OpenAnimation = new Animation { Length = TimeSpan.FromSeconds(_delay) };
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.Base;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening", 0f));
|
||||
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("opening_unlit", 0f));
|
||||
|
||||
if (_animatedPanel)
|
||||
{
|
||||
var flickMaintenancePanel = new AnimationTrackSpriteFlick();
|
||||
OpenAnimation.AnimationTracks.Add(flickMaintenancePanel);
|
||||
flickMaintenancePanel.LayerKey = WiresVisualLayers.MaintenancePanel;
|
||||
flickMaintenancePanel.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("panel_opening", 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
EmaggingAnimation = new Animation { Length = TimeSpan.FromSeconds(_delay) };
|
||||
{
|
||||
var flickUnlit = new AnimationTrackSpriteFlick();
|
||||
EmaggingAnimation.AnimationTracks.Add(flickUnlit);
|
||||
flickUnlit.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flickUnlit.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("sparks", 0f));
|
||||
}
|
||||
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
DenyAnimation = new Animation { Length = TimeSpan.FromSeconds(_denyDelay) };
|
||||
{
|
||||
var flick = new AnimationTrackSpriteFlick();
|
||||
DenyAnimation.AnimationTracks.Add(flick);
|
||||
flick.LayerKey = DoorVisualLayers.BaseUnlit;
|
||||
flick.KeyFrames.Add(new AnimationTrackSpriteFlick.KeyFrame("deny_unlit", 0f));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Subscribe to your component being initialised instead.")]
|
||||
public override void InitializeEntity(EntityUid entity)
|
||||
{
|
||||
if (!_entMan.HasComponent<AnimationPlayerComponent>(entity))
|
||||
{
|
||||
_entMan.AddComponent<AnimationPlayerComponent>(entity);
|
||||
}
|
||||
}
|
||||
|
||||
[Obsolete("Subscribe to AppearanceChangeEvent instead.")]
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
// only start playing animations once.
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = _entMan.GetComponent<SpriteComponent>(component.Owner);
|
||||
var animPlayer = _entMan.GetComponent<AnimationPlayerComponent>(component.Owner);
|
||||
if (!component.TryGetData(DoorVisuals.State, out DoorState state))
|
||||
{
|
||||
state = DoorState.Closed;
|
||||
}
|
||||
|
||||
var door = _entMan.GetComponent<DoorComponent>(component.Owner);
|
||||
|
||||
if (component.TryGetData(DoorVisuals.BaseRSI, out string baseRsi))
|
||||
{
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(SharedSpriteComponent.TextureRoot / baseRsi, out var res))
|
||||
{
|
||||
Logger.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||
}
|
||||
foreach (ISpriteLayer layer in sprite.AllLayers)
|
||||
{
|
||||
layer.Rsi = res?.RSI;
|
||||
}
|
||||
}
|
||||
|
||||
if (animPlayer.HasRunningAnimation(AnimationKey))
|
||||
{
|
||||
animPlayer.Stop(AnimationKey);
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case DoorState.Open:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "open");
|
||||
if (_openUnlitVisible && !_simpleVisuals)
|
||||
{
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "open_unlit");
|
||||
}
|
||||
break;
|
||||
case DoorState.Closed:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "closed_unlit");
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit");
|
||||
}
|
||||
break;
|
||||
case DoorState.Opening:
|
||||
animPlayer.Play(OpenAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorState.Closing:
|
||||
if (door.CurrentlyCrushing.Count == 0)
|
||||
animPlayer.Play(CloseAnimation, AnimationKey);
|
||||
else
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
break;
|
||||
case DoorState.Denying:
|
||||
animPlayer.Play(DenyAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorState.Welded:
|
||||
break;
|
||||
case DoorState.Emagging:
|
||||
animPlayer.Play(EmaggingAnimation, AnimationKey);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (_simpleVisuals)
|
||||
return;
|
||||
|
||||
var boltedVisible = false;
|
||||
var emergencyLightsVisible = false;
|
||||
var unlitVisible = false;
|
||||
|
||||
if (component.TryGetData(DoorVisuals.Powered, out bool powered) && powered)
|
||||
{
|
||||
boltedVisible = component.TryGetData(DoorVisuals.BoltLights, out bool lights) && lights;
|
||||
emergencyLightsVisible = component.TryGetData(DoorVisuals.EmergencyLights, out bool eaLights) && eaLights;
|
||||
unlitVisible = state == DoorState.Closing
|
||||
|| state == DoorState.Opening
|
||||
|| state == DoorState.Denying
|
||||
|| state == DoorState.Open && _openUnlitVisible
|
||||
|| (component.TryGetData(DoorVisuals.ClosedLights, out bool closedLights) && closedLights);
|
||||
}
|
||||
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, boltedVisible);
|
||||
if (_emergencyAccessLayer)
|
||||
{
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseEmergencyAccess,
|
||||
emergencyLightsVisible
|
||||
&& state != DoorState.Open
|
||||
&& state != DoorState.Opening
|
||||
&& state != DoorState.Closing);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum DoorVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
BaseUnlit,
|
||||
BaseBolted,
|
||||
BaseEmergencyAccess,
|
||||
}
|
||||
}
|
||||
|
|
@ -1,28 +1,135 @@
|
|||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Client.ResourceManagement;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
// Gotta love it when both the client-side and server-side sprite components both have a draw depth, but for
|
||||
// whatever bloody reason the shared component doesn't.
|
||||
protected override void UpdateAppearance(EntityUid uid, DoorComponent? door = null)
|
||||
[Dependency] private readonly AnimationPlayerSystem _animationSystem = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly IResourceCache _resourceCache = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
if (!Resolve(uid, ref door))
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<DoorComponent, AppearanceChangeEvent>(OnAppearanceChange);
|
||||
}
|
||||
|
||||
protected override void OnComponentInit(EntityUid uid, DoorComponent comp, ComponentInit args)
|
||||
{
|
||||
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 = TimeSpan.FromSeconds(comp.OpeningAnimationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.Base,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.OpeningSpriteState, 0f) }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
comp.ClosingAnimation = new Animation()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(comp.ClosingAnimationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.Base,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.ClosingSpriteState, 0f) }
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
comp.EmaggingAnimation = new Animation ()
|
||||
{
|
||||
Length = TimeSpan.FromSeconds(comp.EmaggingAnimationTime),
|
||||
AnimationTracks =
|
||||
{
|
||||
new AnimationTrackSpriteFlick()
|
||||
{
|
||||
LayerKey = DoorVisualLayers.BaseUnlit,
|
||||
KeyFrames = { new AnimationTrackSpriteFlick.KeyFrame(comp.EmaggingSpriteState, 0f) }
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
private void OnAppearanceChange(EntityUid uid, DoorComponent comp, ref AppearanceChangeEvent args)
|
||||
{
|
||||
if (args.Sprite == null || !_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
base.UpdateAppearance(uid, door);
|
||||
if(!AppearanceSystem.TryGetData<DoorState>(uid, DoorVisuals.State, out var state, args.Component))
|
||||
state = DoorState.Closed;
|
||||
|
||||
if (TryComp(uid, out SpriteComponent? sprite))
|
||||
if (AppearanceSystem.TryGetData<string>(uid, DoorVisuals.BaseRSI, out var baseRsi, args.Component))
|
||||
{
|
||||
sprite.DrawDepth = (door.State == DoorState.Open)
|
||||
? door.OpenDrawDepth
|
||||
: door.ClosedDrawDepth;
|
||||
if (!_resourceCache.TryGetResource<RSIResource>(SharedSpriteComponent.TextureRoot / baseRsi, out var res))
|
||||
{
|
||||
Logger.Error("Unable to load RSI '{0}'. Trace:\n{1}", baseRsi, Environment.StackTrace);
|
||||
}
|
||||
foreach (ISpriteLayer layer in args.Sprite.AllLayers)
|
||||
{
|
||||
layer.Rsi = res?.RSI;
|
||||
}
|
||||
}
|
||||
|
||||
TryComp<AnimationPlayerComponent>(uid, out var animPlayer);
|
||||
if (_animationSystem.HasRunningAnimation(uid, animPlayer, DoorComponent.AnimationKey))
|
||||
_animationSystem.Stop(uid, animPlayer, DoorComponent.AnimationKey); // Halt all running anomations.
|
||||
|
||||
args.Sprite.DrawDepth = comp.ClosedDrawDepth;
|
||||
switch(state)
|
||||
{
|
||||
case DoorState.Open:
|
||||
args.Sprite.DrawDepth = comp.OpenDrawDepth;
|
||||
foreach(var (layer, layerState) in comp.OpenSpriteStates)
|
||||
{
|
||||
args.Sprite.LayerSetState(layer, layerState);
|
||||
}
|
||||
break;
|
||||
case DoorState.Closed:
|
||||
foreach(var (layer, layerState) in comp.ClosedSpriteStates)
|
||||
{
|
||||
args.Sprite.LayerSetState(layer, layerState);
|
||||
}
|
||||
break;
|
||||
case DoorState.Opening:
|
||||
if (animPlayer != null && comp.OpeningAnimation != default)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.OpeningAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Closing:
|
||||
if (animPlayer != null && comp.ClosingAnimation != default && comp.CurrentlyCrushing.Count == 0)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.ClosingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Denying:
|
||||
if (animPlayer != null && comp.DenyingAnimation != default)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.DenyingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
case DoorState.Welded:
|
||||
break;
|
||||
case DoorState.Emagging:
|
||||
if (animPlayer != null && comp.EmaggingAnimation != default)
|
||||
_animationSystem.Play(uid, animPlayer, (Animation)comp.EmaggingAnimation, DoorComponent.AnimationKey);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException($"Invalid door visual state {state}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,4 +1,3 @@
|
|||
using System.Threading;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.MachineLinking;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
@ -56,8 +55,16 @@ public sealed class AirlockComponent : Component
|
|||
[DataField("keepOpenIfClicked")]
|
||||
public bool KeepOpenIfClicked = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door bolts are currently deployed.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool BoltsDown;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the bolt lights are currently enabled.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool BoltLightsEnabled = true;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -86,10 +93,80 @@ public sealed class AirlockComponent : Component
|
|||
public float AutoCloseDelayModifier = 1.0f;
|
||||
|
||||
/// <summary>
|
||||
/// The receiver port for turning off automatic closing.
|
||||
/// The receiver port for turning off automatic closing.
|
||||
/// </summary>
|
||||
[DataField("autoClosePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
|
||||
public string AutoClosePort = "AutoClose";
|
||||
|
||||
#region Graphics
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door lights should be visible.
|
||||
/// </summary>
|
||||
[DataField("openUnlitVisible")]
|
||||
public bool OpenUnlitVisible = false;
|
||||
|
||||
/// <summary>
|
||||
/// Whether the door should display emergency access lights.
|
||||
/// </summary>
|
||||
[DataField("emergencyAccessLayer")]
|
||||
public bool EmergencyAccessLayer = true;
|
||||
|
||||
/// <summary>
|
||||
/// Whether or not to animate the panel when the door opens or closes.
|
||||
/// </summary>
|
||||
[DataField("animatePanel")]
|
||||
public bool AnimatePanel = true;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used to animate the airlock frame when the airlock opens.
|
||||
/// </summary>
|
||||
[DataField("openingSpriteState")]
|
||||
public string OpeningSpriteState = "opening_unlit";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used to animate the airlock panel when the airlock opens.
|
||||
/// </summary>
|
||||
[DataField("openingPanelSpriteState")]
|
||||
public string OpeningPanelSpriteState = "panel_opening";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used to animate the airlock frame when the airlock closes.
|
||||
/// </summary>
|
||||
[DataField("closingSpriteState")]
|
||||
public string ClosingSpriteState = "closing_unlit";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used to animate the airlock panel when the airlock closes.
|
||||
/// </summary>
|
||||
[DataField("closingPanelSpriteState")]
|
||||
public string ClosingPanelSpriteState = "panel_closing";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the open airlock lights.
|
||||
/// </summary>
|
||||
[DataField("openSpriteState")]
|
||||
public string OpenSpriteState = "open_unlit";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the closed airlock lights.
|
||||
/// </summary>
|
||||
[DataField("closedSpriteState")]
|
||||
public string ClosedSpriteState = "closed_unlit";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the 'access denied' lights animation.
|
||||
/// </summary>
|
||||
[DataField("denySpriteState")]
|
||||
public string DenySpriteState = "deny_unlit";
|
||||
|
||||
/// <summary>
|
||||
/// How long the animation played when the airlock denies access is in seconds.
|
||||
/// </summary>
|
||||
[DataField("denyAnimationTime")]
|
||||
public float DenyAnimationTime = 0.3f;
|
||||
|
||||
#endregion Graphics
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
|
|
|
|||
|
|
@ -1,4 +1,6 @@
|
|||
using System.Runtime.InteropServices;
|
||||
using Content.Shared.Damage;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Content.Shared.Tools;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
|
|
@ -13,16 +15,17 @@ namespace Content.Shared.Doors.Components;
|
|||
|
||||
[NetworkedComponent]
|
||||
[RegisterComponent]
|
||||
public sealed class DoorComponent : Component, ISerializationHooks
|
||||
public sealed class DoorComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The current state of the door -- whether it is open, closed, opening, or closing.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This should never be set directly.
|
||||
/// This should never be set directly, use <see cref="SharedDoorSystem.SetState(EntityUid, DoorState, DoorComponent?)"/> instead.
|
||||
/// </remarks>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("state")]
|
||||
[Access(typeof(SharedDoorSystem))]
|
||||
public DoorState State = DoorState.Closed;
|
||||
|
||||
#region Timing
|
||||
|
|
@ -139,6 +142,97 @@ public sealed class DoorComponent : Component, ISerializationHooks
|
|||
public HashSet<EntityUid> CurrentlyCrushing = new();
|
||||
#endregion
|
||||
|
||||
#region Graphics
|
||||
|
||||
/// <summary>
|
||||
/// The key used when playing door opening/closing/emagging/deny animations.
|
||||
/// </summary>
|
||||
public const string AnimationKey = "door_animation";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's open.
|
||||
/// </summary>
|
||||
[DataField("openSpriteState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string OpenSpriteState = "open";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite states used for the door while it's open.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<(DoorVisualLayers, string)> OpenSpriteStates = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's closed.
|
||||
/// </summary>
|
||||
[DataField("closedSpriteState")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public string ClosedSpriteState = "closed";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite states used for the door while it's closed.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadOnly)]
|
||||
public List<(DoorVisualLayers, string)> ClosedSpriteStates = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's opening.
|
||||
/// </summary>
|
||||
[DataField("openingSpriteState")]
|
||||
public string OpeningSpriteState = "opening";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's closing.
|
||||
/// </summary>
|
||||
[DataField("closingSpriteState")]
|
||||
public string ClosingSpriteState = "closing";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's being emagged.
|
||||
/// </summary>
|
||||
[DataField("emaggingSpriteState")]
|
||||
public string EmaggingSpriteState = "emagging";
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's open.
|
||||
/// </summary>
|
||||
[DataField("openingAnimationTime")]
|
||||
public float OpeningAnimationTime = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's open.
|
||||
/// </summary>
|
||||
[DataField("closingAnimationTime")]
|
||||
public float ClosingAnimationTime = 0.8f;
|
||||
|
||||
/// <summary>
|
||||
/// The sprite state used for the door when it's open.
|
||||
/// </summary>
|
||||
[DataField("emaggingAnimationTime")]
|
||||
public float EmaggingAnimationTime = 1.5f;
|
||||
|
||||
/// <summary>
|
||||
/// The animation used when the door opens.
|
||||
/// </summary>
|
||||
public object OpeningAnimation = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The animation used when the door closes.
|
||||
/// </summary>
|
||||
public object ClosingAnimation = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The animation used when the door denies access.
|
||||
/// </summary>
|
||||
public object DenyingAnimation = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The animation used when the door is emagged.
|
||||
/// </summary>
|
||||
public object EmaggingAnimation = default!;
|
||||
|
||||
#endregion Graphics
|
||||
|
||||
#region Serialization
|
||||
/// <summary>
|
||||
/// Time until next state change. Because apparently <see cref="IGameTiming.CurTime"/> might not get saved/restored.
|
||||
|
|
@ -209,7 +303,7 @@ public sealed class DoorComponent : Component, ISerializationHooks
|
|||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DoorState
|
||||
public enum DoorState : byte
|
||||
{
|
||||
Closed,
|
||||
Closing,
|
||||
|
|
@ -221,7 +315,7 @@ public enum DoorState
|
|||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public enum DoorVisuals
|
||||
public enum DoorVisuals : byte
|
||||
{
|
||||
State,
|
||||
Powered,
|
||||
|
|
@ -231,6 +325,14 @@ public enum DoorVisuals
|
|||
BaseRSI,
|
||||
}
|
||||
|
||||
public enum DoorVisualLayers : byte
|
||||
{
|
||||
Base,
|
||||
BaseUnlit,
|
||||
BaseBolted,
|
||||
BaseEmergencyAccess,
|
||||
}
|
||||
|
||||
[Serializable, NetSerializable]
|
||||
public sealed class DoorComponentState : ComponentState
|
||||
{
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
[Dependency] protected readonly TagSystem Tags = default!;
|
||||
[Dependency] protected readonly SharedAudioSystem Audio = default!;
|
||||
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] protected readonly SharedAppearanceSystem AppearanceSystem = default!;
|
||||
[Dependency] private readonly OccluderSystem _occluder = default!;
|
||||
|
||||
/// <summary>
|
||||
|
|
@ -49,7 +49,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<DoorComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<DoorComponent, ComponentInit>(OnComponentInit);
|
||||
SubscribeLocalEvent<DoorComponent, ComponentRemove>(OnRemove);
|
||||
|
||||
SubscribeLocalEvent<DoorComponent, ComponentGetState>(OnGetState);
|
||||
|
|
@ -61,7 +61,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
SubscribeLocalEvent<DoorComponent, PreventCollideEvent>(PreventCollision);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, DoorComponent door, ComponentInit args)
|
||||
protected virtual void OnComponentInit(EntityUid uid, DoorComponent door, ComponentInit args)
|
||||
{
|
||||
if (door.NextStateChange != null)
|
||||
_activeDoors.Add(door);
|
||||
|
|
@ -88,7 +88,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
|| door.State == DoorState.Opening && !door.Partial;
|
||||
|
||||
SetCollidable(uid, collidable, door);
|
||||
UpdateAppearance(uid, door);
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
|
||||
}
|
||||
|
||||
private void OnRemove(EntityUid uid, DoorComponent door, ComponentRemove args)
|
||||
|
|
@ -123,7 +123,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
_activeDoors.Add(door);
|
||||
|
||||
RaiseLocalEvent(uid, new DoorStateChangedEvent(door.State), false);
|
||||
UpdateAppearance(uid, door);
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
|
||||
}
|
||||
|
||||
protected void SetState(EntityUid uid, DoorState state, DoorComponent? door = null)
|
||||
|
|
@ -167,19 +167,9 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
door.State = state;
|
||||
Dirty(door);
|
||||
RaiseLocalEvent(uid, new DoorStateChangedEvent(state), false);
|
||||
UpdateAppearance(uid, door);
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, door.State);
|
||||
}
|
||||
|
||||
protected virtual void UpdateAppearance(EntityUid uid, DoorComponent? door = null)
|
||||
{
|
||||
if (!Resolve(uid, ref door))
|
||||
return;
|
||||
|
||||
if (!TryComp(uid, out AppearanceComponent? appearance))
|
||||
return;
|
||||
|
||||
_appearance.SetData(uid, DoorVisuals.State, door.State);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Interactions
|
||||
|
|
@ -365,7 +355,7 @@ public abstract class SharedDoorSystem : EntitySystem
|
|||
{
|
||||
door.NextStateChange = GameTiming.CurTime + door.OpenTimeTwo;
|
||||
door.State = DoorState.Opening;
|
||||
UpdateAppearance(uid, door);
|
||||
AppearanceSystem.SetData(uid, DoorVisuals.State, DoorState.Opening);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
layers:
|
||||
- state: closed
|
||||
map: ["enum.DoorVisualLayers.Base"]
|
||||
- type: AnimationPlayer
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
|
|
@ -36,9 +37,6 @@
|
|||
closeSound:
|
||||
path: /Audio/Effects/curtain_openclose.ogg
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
simpleVisuals: true
|
||||
- type: Damageable
|
||||
damageContainer: Inorganic
|
||||
damageModifierSet: Wood
|
||||
|
|
|
|||
|
|
@ -104,6 +104,7 @@
|
|||
enabled: false
|
||||
- type: Sprite
|
||||
sprite: Structures/Doors/Airlocks/Glass/glass.rsi
|
||||
- type: AnimationPlayer
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
|
|
|
|||
|
|
@ -30,6 +30,7 @@
|
|||
shader: unshaded
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
|
|
@ -65,8 +66,6 @@
|
|||
time: 3
|
||||
- type: Airlock
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
- type: WiresVisuals
|
||||
- type: ApcPowerReceiver
|
||||
powerLoad: 20
|
||||
|
|
|
|||
|
|
@ -18,8 +18,6 @@
|
|||
- type: Sprite
|
||||
sprite: Structures/Doors/Airlocks/Standard/external.rsi
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
- type: WiresVisuals
|
||||
- type: PaintableAirlock
|
||||
group: External
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@
|
|||
shader: unshaded
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
|
|
@ -56,8 +57,6 @@
|
|||
time: 10
|
||||
- type: Airlock
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
- type: WiresVisuals
|
||||
- type: ApcPowerReceiver
|
||||
powerLoad: 20
|
||||
|
|
|
|||
|
|
@ -37,13 +37,16 @@
|
|||
- state: closed_unlit
|
||||
shader: unshaded
|
||||
map: ["enum.DoorVisualLayers.BaseUnlit"]
|
||||
visible: false
|
||||
- state: welded
|
||||
map: ["enum.WeldableLayers.BaseWelded"]
|
||||
- state: bolted_unlit
|
||||
shader: unshaded
|
||||
map: ["enum.DoorVisualLayers.BaseBolted"]
|
||||
visible: false
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- type: AnimationPlayer
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
- shape:
|
||||
|
|
@ -72,15 +75,13 @@
|
|||
path: /Audio/Machines/airlock_close.ogg
|
||||
denySound:
|
||||
path: /Audio/Machines/airlock_deny.ogg
|
||||
openingAnimationTime: 0.6
|
||||
closingAnimationTime: 0.6
|
||||
- type: Weldable
|
||||
fuel: 3
|
||||
time: 3
|
||||
- type: Firelock
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
animationTime: 0.6
|
||||
emergencyAccessLayer: false
|
||||
- type: WiresVisuals
|
||||
- type: Wires
|
||||
BoardName: "Firelock Control"
|
||||
|
|
|
|||
|
|
@ -12,6 +12,7 @@
|
|||
layers:
|
||||
- state: closed
|
||||
map: ["enum.DoorVisualLayers.Base"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
|
|
@ -31,15 +32,13 @@
|
|||
closeTimeTwo: 0.6
|
||||
openTimeOne: 0.6
|
||||
openTimeTwo: 0.2
|
||||
openingAnimationTime: 1.2
|
||||
closingAnimationTime: 1.2
|
||||
openSound:
|
||||
path: /Audio/Effects/stonedoor_openclose.ogg
|
||||
closeSound:
|
||||
path: /Audio/Effects/stonedoor_openclose.ogg
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
simpleVisuals: true
|
||||
animationTime: 1.2
|
||||
- type: Airtight
|
||||
fixVacuum: true
|
||||
- type: Damageable
|
||||
|
|
|
|||
|
|
@ -15,16 +15,14 @@
|
|||
closeTimeTwo: 0.4
|
||||
openTimeOne: 0.4
|
||||
openTimeTwo: 0.4
|
||||
openingAnimationTime: 1.0
|
||||
closingAnimationTime: 1.0
|
||||
pryTime: -1
|
||||
crushDamage:
|
||||
types:
|
||||
Blunt: 25 # yowch
|
||||
- type: Occluder
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
simpleVisuals: true
|
||||
animationTime: 1.0
|
||||
- type: RadiationBlocker
|
||||
resistance: 8
|
||||
|
||||
|
|
|
|||
|
|
@ -14,6 +14,7 @@
|
|||
layers:
|
||||
- state: closed
|
||||
map: ["enum.DoorVisualLayers.Base"]
|
||||
- type: AnimationPlayer
|
||||
- type: Physics
|
||||
- type: Fixtures
|
||||
fixtures:
|
||||
|
|
@ -38,6 +39,8 @@
|
|||
closeTimeTwo: 1.2
|
||||
openTimeOne: 1.2
|
||||
openTimeTwo: 0.2
|
||||
openingAnimationTime: 1.4
|
||||
closingAnimationTime: 1.4
|
||||
crushDamage:
|
||||
types:
|
||||
Blunt: 5 # getting shutters closed on you probably doesn't hurt that much
|
||||
|
|
@ -49,10 +52,6 @@
|
|||
fuel: 3
|
||||
time: 3
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
simpleVisuals: true
|
||||
animationTime: 1.4
|
||||
- type: UserInterface
|
||||
interfaces:
|
||||
- key: enum.WiresUiKey.Key
|
||||
|
|
|
|||
|
|
@ -41,6 +41,7 @@
|
|||
map: ["enum.DoorVisualLayers.BaseEmergencyAccess"]
|
||||
- state: panel_open
|
||||
map: ["enum.WiresVisualLayers.MaintenancePanel"]
|
||||
- type: AnimationPlayer
|
||||
- type: ApcPowerReceiver
|
||||
- type: ExtensionCableReceiver
|
||||
- type: DoorSignalControl
|
||||
|
|
@ -78,11 +79,6 @@
|
|||
- !type:DoActsBehavior
|
||||
acts: [ "Destruction" ]
|
||||
- type: AccessReader
|
||||
- type: Airlock
|
||||
keepOpenIfClicked: true
|
||||
openPanelVisible: true
|
||||
# needed so that windoors will close regardless of whether there are people in it; it doesn't crush after all
|
||||
safety: false
|
||||
- type: ContainerFill
|
||||
containers:
|
||||
board: [ DoorElectronics ]
|
||||
|
|
@ -91,12 +87,22 @@
|
|||
board: !type:Container
|
||||
- type: Door
|
||||
canCrush: false
|
||||
openingAnimationTime: 0.9
|
||||
closingAnimationTime: 0.9
|
||||
openSound:
|
||||
path: /Audio/Machines/windoor_open.ogg
|
||||
closeSound:
|
||||
path: /Audio/Machines/windoor_open.ogg
|
||||
denySound:
|
||||
path: /Audio/Machines/airlock_deny.ogg
|
||||
- type: Airlock
|
||||
keepOpenIfClicked: true
|
||||
openPanelVisible: true
|
||||
denyAnimationTime: 0.4
|
||||
animatePanel: false
|
||||
openUnlitVisible: true
|
||||
# needed so that windoors will close regardless of whether there are people in it; it doesn't crush after all
|
||||
safety: false
|
||||
- type: Electrified
|
||||
enabled: false
|
||||
usesApcPower: true
|
||||
|
|
@ -108,12 +114,6 @@
|
|||
- key: enum.WiresUiKey.Key
|
||||
type: WiresBoundUserInterface
|
||||
- type: Appearance
|
||||
visuals:
|
||||
- type: AirlockVisualizer
|
||||
animationTime: 0.9
|
||||
denyAnimationTime: 0.4
|
||||
animatedPanel: false
|
||||
openUnlitVisible: true
|
||||
- type: WiresVisuals
|
||||
- type: Airtight
|
||||
fixVacuum: true
|
||||
|
|
|
|||
Loading…
Reference in New Issue