ECS Doors (#5887)
This commit is contained in:
8
Content.Client/Doors/AirlockComponent.cs
Normal file
8
Content.Client/Doors/AirlockComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Doors.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedAirlockComponent))]
|
||||
public sealed class AirlockComponent : SharedAirlockComponent { }
|
||||
5
Content.Client/Doors/AirlockSystem.cs
Normal file
5
Content.Client/Doors/AirlockSystem.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
using Content.Shared.Doors.Systems;
|
||||
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class AirlockSystem : SharedAirlockSystem { }
|
||||
@@ -1,6 +1,6 @@
|
||||
using System;
|
||||
using Content.Client.Wires.Visualizers;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Doors.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Client.Animations;
|
||||
using Robust.Client.GameObjects;
|
||||
@@ -8,6 +8,7 @@ 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
|
||||
{
|
||||
@@ -15,6 +16,7 @@ namespace Content.Client.Doors
|
||||
public class AirlockVisualizer : AppearanceVisualizer, ISerializationHooks
|
||||
{
|
||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
private const string AnimationKey = "airlock_animation";
|
||||
|
||||
@@ -122,15 +124,20 @@ namespace Content.Client.Doors
|
||||
|
||||
public override void OnChangeData(AppearanceComponent component)
|
||||
{
|
||||
// only start playing animations once.
|
||||
if (!_gameTiming.IsFirstTimePredicted)
|
||||
return;
|
||||
|
||||
base.OnChangeData(component);
|
||||
|
||||
var sprite = _entMan.GetComponent<ISpriteComponent>(component.Owner);
|
||||
var animPlayer = _entMan.GetComponent<AnimationPlayerComponent>(component.Owner);
|
||||
if (!component.TryGetData(DoorVisuals.VisualState, out DoorVisualState state))
|
||||
if (!component.TryGetData(DoorVisuals.State, out DoorState state))
|
||||
{
|
||||
state = DoorVisualState.Closed;
|
||||
state = DoorState.Closed;
|
||||
}
|
||||
|
||||
var door = _entMan.GetComponent<DoorComponent>(component.Owner);
|
||||
var unlitVisible = true;
|
||||
var boltedVisible = false;
|
||||
var weldedVisible = false;
|
||||
@@ -141,7 +148,7 @@ namespace Content.Client.Doors
|
||||
}
|
||||
switch (state)
|
||||
{
|
||||
case DoorVisualState.Open:
|
||||
case DoorState.Open:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "open");
|
||||
unlitVisible = _openUnlitVisible;
|
||||
if (_openUnlitVisible && !_simpleVisuals)
|
||||
@@ -149,7 +156,7 @@ namespace Content.Client.Doors
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseUnlit, "open_unlit");
|
||||
}
|
||||
break;
|
||||
case DoorVisualState.Closed:
|
||||
case DoorState.Closed:
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
@@ -157,17 +164,19 @@ namespace Content.Client.Doors
|
||||
sprite.LayerSetState(DoorVisualLayers.BaseBolted, "bolted_unlit");
|
||||
}
|
||||
break;
|
||||
case DoorVisualState.Opening:
|
||||
case DoorState.Opening:
|
||||
animPlayer.Play(OpenAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorVisualState.Closing:
|
||||
animPlayer.Play(CloseAnimation, AnimationKey);
|
||||
case DoorState.Closing:
|
||||
if (door.CurrentlyCrushing.Count == 0)
|
||||
animPlayer.Play(CloseAnimation, AnimationKey);
|
||||
else
|
||||
sprite.LayerSetState(DoorVisualLayers.Base, "closed");
|
||||
break;
|
||||
case DoorVisualState.Deny:
|
||||
if (!animPlayer.HasRunningAnimation(AnimationKey))
|
||||
animPlayer.Play(DenyAnimation, AnimationKey);
|
||||
case DoorState.Denying:
|
||||
animPlayer.Play(DenyAnimation, AnimationKey);
|
||||
break;
|
||||
case DoorVisualState.Welded:
|
||||
case DoorState.Welded:
|
||||
weldedVisible = true;
|
||||
break;
|
||||
default:
|
||||
@@ -185,7 +194,7 @@ namespace Content.Client.Doors
|
||||
|
||||
if (!_simpleVisuals)
|
||||
{
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible && state != DoorVisualState.Closed);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseUnlit, unlitVisible && state != DoorState.Closed && state != DoorState.Welded);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseWelded, weldedVisible);
|
||||
sprite.LayerSetVisible(DoorVisualLayers.BaseBolted, unlitVisible && boltedVisible);
|
||||
}
|
||||
|
||||
@@ -1,100 +0,0 @@
|
||||
using System;
|
||||
using Content.Shared.Doors;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
using DrawDepthTag = Robust.Shared.GameObjects.DrawDepth;
|
||||
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
|
||||
|
||||
|
||||
namespace Content.Client.Doors
|
||||
{
|
||||
/// <summary>
|
||||
/// Bare-bones client-side door component; used to stop door-based mispredicts.
|
||||
/// </summary>
|
||||
[UsedImplicitly]
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(SharedDoorComponent))]
|
||||
public class ClientDoorComponent : SharedDoorComponent
|
||||
{
|
||||
[DataField("openDrawDepth", customTypeSerializer: typeof(ConstantSerializer<DrawDepthTag>))]
|
||||
public int OpenDrawDepth = (int) DrawDepth.Doors;
|
||||
|
||||
[DataField("closedDrawDepth", customTypeSerializer: typeof(ConstantSerializer<DrawDepthTag>))]
|
||||
public int ClosedDrawDepth = (int) DrawDepth.Doors;
|
||||
|
||||
private bool _stateChangeHasProgressed = false;
|
||||
private TimeSpan _timeOffset;
|
||||
|
||||
public override DoorState State
|
||||
{
|
||||
protected set
|
||||
{
|
||||
if (State == value)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
base.State = value;
|
||||
|
||||
IoCManager.Resolve<IEntityManager>().EventBus.RaiseLocalEvent(Owner, new DoorStateChangedEvent(State), false);
|
||||
}
|
||||
}
|
||||
|
||||
public override void HandleComponentState(ComponentState? curState, ComponentState? nextState)
|
||||
{
|
||||
base.HandleComponentState(curState, nextState);
|
||||
|
||||
if (curState is not DoorComponentState doorCompState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
CurrentlyCrushing = doorCompState.CurrentlyCrushing;
|
||||
StateChangeStartTime = doorCompState.StartTime;
|
||||
State = doorCompState.DoorState;
|
||||
|
||||
if (StateChangeStartTime == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_timeOffset = State switch
|
||||
{
|
||||
DoorState.Opening => OpenTimeOne,
|
||||
DoorState.Closing => CloseTimeOne,
|
||||
_ => throw new ArgumentOutOfRangeException(),
|
||||
};
|
||||
|
||||
if (doorCompState.CurTime >= StateChangeStartTime + _timeOffset)
|
||||
{
|
||||
_stateChangeHasProgressed = true;
|
||||
return;
|
||||
}
|
||||
|
||||
_stateChangeHasProgressed = false;
|
||||
}
|
||||
|
||||
public void OnUpdate()
|
||||
{
|
||||
if (!_stateChangeHasProgressed)
|
||||
{
|
||||
if (GameTiming.CurTime < StateChangeStartTime + _timeOffset) return;
|
||||
|
||||
if (State == DoorState.Opening)
|
||||
{
|
||||
OnPartialOpen();
|
||||
}
|
||||
else
|
||||
{
|
||||
OnPartialClose();
|
||||
}
|
||||
|
||||
_stateChangeHasProgressed = true;
|
||||
Dirty();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,69 +1,35 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Shared.Doors;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors.Systems;
|
||||
using Robust.Client.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using static Content.Shared.Doors.SharedDoorComponent;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Client.Doors
|
||||
namespace Content.Client.Doors;
|
||||
|
||||
public sealed class DoorSystem : SharedDoorSystem
|
||||
{
|
||||
/// <summary>
|
||||
/// Used by the client to "predict" when doors will change how collideable they are as part of their opening / closing.
|
||||
/// </summary>
|
||||
internal 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)
|
||||
{
|
||||
/// <summary>
|
||||
/// List of doors that need to be periodically checked.
|
||||
/// </summary>
|
||||
private readonly List<ClientDoorComponent> _activeDoors = new();
|
||||
if (!Resolve(uid, ref door))
|
||||
return;
|
||||
|
||||
public override void Initialize()
|
||||
base.UpdateAppearance(uid, door);
|
||||
|
||||
if (TryComp(uid, out SpriteComponent? sprite))
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
UpdatesOutsidePrediction = true;
|
||||
|
||||
SubscribeLocalEvent<ClientDoorComponent, DoorStateChangedEvent>(OnDoorStateChanged);
|
||||
}
|
||||
|
||||
private void OnDoorStateChanged(EntityUid uid, ClientDoorComponent door, DoorStateChangedEvent args)
|
||||
{
|
||||
switch (args.State)
|
||||
{
|
||||
case DoorState.Closed:
|
||||
case DoorState.Open:
|
||||
_activeDoors.Remove(door);
|
||||
break;
|
||||
case DoorState.Closing:
|
||||
case DoorState.Opening:
|
||||
_activeDoors.Add(door);
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
|
||||
if (!EntityManager.TryGetComponent(uid, out SpriteComponent sprite))
|
||||
return;
|
||||
|
||||
// Update sprite draw depth. If the door is opening or closing, we will use the closed-draw depth.
|
||||
sprite.DrawDepth = (args.State == DoorState.Open)
|
||||
sprite.DrawDepth = (door.State == DoorState.Open)
|
||||
? door.OpenDrawDepth
|
||||
: door.ClosedDrawDepth;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
for (var i = _activeDoors.Count - 1; i >= 0; i--)
|
||||
{
|
||||
var comp = _activeDoors[i];
|
||||
if (comp.Deleted)
|
||||
{
|
||||
_activeDoors.RemoveAt(i);
|
||||
continue;
|
||||
}
|
||||
comp.OnUpdate();
|
||||
}
|
||||
}
|
||||
// TODO AUDIO PREDICT see comments in server-side PlaySound()
|
||||
protected override void PlaySound(EntityUid uid, string sound, AudioParams audioParams, EntityUid? predictingPlayer, bool predicted)
|
||||
{
|
||||
if (GameTiming.InPrediction && GameTiming.IsFirstTimePredicted)
|
||||
SoundSystem.Play(Filter.Local(), sound, uid, audioParams);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +44,6 @@ namespace Content.Client.Entry
|
||||
"ResearchPointSource",
|
||||
"ResearchClient",
|
||||
"IdCardConsole",
|
||||
"Airlock",
|
||||
"ThermalRegulator",
|
||||
"AtmosFixMarker",
|
||||
"CablePlacer",
|
||||
@@ -75,7 +74,6 @@ namespace Content.Client.Entry
|
||||
"Brain",
|
||||
"CommunicationsConsole",
|
||||
"BarSign",
|
||||
"DoorBumpOpener",
|
||||
"SolarPanel",
|
||||
"BodyScanner",
|
||||
"Stunbaton",
|
||||
|
||||
Reference in New Issue
Block a user