#nullable enable using System; using System.Collections.Generic; using Robust.Shared.GameObjects; using Robust.Shared.IoC; using Robust.Shared.Physics; using Robust.Shared.Serialization; using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Timing; using Robust.Shared.ViewVariables; namespace Content.Shared.GameObjects.Components.Doors { public abstract class SharedDoorComponent : Component, ICollideSpecial { public override string Name => "Door"; public override uint? NetID => ContentNetIDs.DOOR; [ComponentDependency] protected readonly SharedAppearanceComponent? AppearanceComponent = null; [ComponentDependency] protected readonly IPhysBody? PhysicsComponent = null; [ViewVariables] private DoorState _state = DoorState.Closed; /// /// The current state of the door -- whether it is open, closed, opening, or closing. /// public virtual DoorState State { get => _state; protected set { if (_state == value) { return; } _state = value; SetAppearance(State switch { DoorState.Open => DoorVisualState.Open, DoorState.Closed => DoorVisualState.Closed, DoorState.Opening => DoorVisualState.Opening, DoorState.Closing => DoorVisualState.Closing, _ => throw new ArgumentOutOfRangeException(), }); } } /// /// Closing time until impassable. /// [DataField("closeTimeOne")] protected TimeSpan CloseTimeOne = TimeSpan.FromSeconds(0.4f); /// /// Closing time until fully closed. /// [DataField("closeTimeTwo")] protected TimeSpan CloseTimeTwo = TimeSpan.FromSeconds(0.2f); /// /// Opening time until passable. /// [DataField("openTimeOne")] protected TimeSpan OpenTimeOne = TimeSpan.FromSeconds(0.4f); /// /// Opening time until fully open. /// [DataField("openTimeTwo")] protected TimeSpan OpenTimeTwo = TimeSpan.FromSeconds(0.2f); /// /// Time to finish denying. /// protected static TimeSpan DenyTime => TimeSpan.FromSeconds(0.45f); /// /// Used by ServerDoorComponent to get the CurTime for the client to use to know when to open, and by ClientDoorComponent to know the CurTime to correctly open. /// [Dependency] protected IGameTiming GameTiming = default!; /// /// The time the door began to open or close, if the door is opening or closing, or null if it is neither. /// protected TimeSpan? StateChangeStartTime = null; /// /// List of EntityUids of entities we're currently crushing. Cleared in OnPartialOpen(). /// protected List CurrentlyCrushing = new(); protected void SetAppearance(DoorVisualState state) { AppearanceComponent?.SetData(DoorVisuals.VisualState, state); } // stops us colliding with people we're crushing, to prevent hitbox clipping and jank public bool PreventCollide(IPhysBody collidedwith) { return CurrentlyCrushing.Contains(collidedwith.Entity.Uid); } /// /// Called when the door is partially opened. /// protected virtual void OnPartialOpen() { if (PhysicsComponent != null) { PhysicsComponent.CanCollide = false; } // we can't be crushing anyone anymore, since we're opening CurrentlyCrushing.Clear(); } /// /// Called when the door is partially closed. /// protected virtual void OnPartialClose() { if (PhysicsComponent != null) { PhysicsComponent.CanCollide = true; } } [Serializable, NetSerializable] public enum DoorState { Open, Closed, Opening, Closing, } } [Serializable, NetSerializable] public enum DoorVisualState { Open, Closed, Opening, Closing, Deny, Welded } [Serializable, NetSerializable] public enum DoorVisuals { VisualState, Powered, BoltLights } [Serializable, NetSerializable] public class DoorComponentState : ComponentState { public readonly SharedDoorComponent.DoorState DoorState; public readonly TimeSpan? StartTime; public readonly List CurrentlyCrushing; public readonly TimeSpan CurTime; public DoorComponentState(SharedDoorComponent.DoorState doorState, TimeSpan? startTime, List currentlyCrushing, TimeSpan curTime) : base(ContentNetIDs.DOOR) { DoorState = doorState; StartTime = startTime; CurrentlyCrushing = currentlyCrushing; CurTime = curTime; } } }