Blast door/shutter, timer and or gate device linking fixes (#16347)
This commit is contained in:
@@ -0,0 +1,15 @@
|
||||
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class ActiveSignalTimerComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// The time the timer triggers.
|
||||
/// </summary>
|
||||
[DataField("triggerTime", customTypeSerializer: typeof(TimeOffsetSerializer))]
|
||||
public TimeSpan TriggerTime;
|
||||
}
|
||||
}
|
||||
34
Content.Server/DeviceLinking/Components/OrGateComponent.cs
Normal file
34
Content.Server/DeviceLinking/Components/OrGateComponent.cs
Normal file
@@ -0,0 +1,34 @@
|
||||
using Content.Server.MachineLinking.Events;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class OrGateComponent : Component
|
||||
{
|
||||
// Initial state
|
||||
[ViewVariables]
|
||||
public SignalState StateA1 = SignalState.Low;
|
||||
|
||||
[ViewVariables]
|
||||
public SignalState StateB1 = SignalState.Low;
|
||||
|
||||
[ViewVariables]
|
||||
public SignalState LastO1 = SignalState.Low;
|
||||
|
||||
[ViewVariables]
|
||||
public SignalState StateA2 = SignalState.Low;
|
||||
|
||||
[ViewVariables]
|
||||
public SignalState StateB2 = SignalState.Low;
|
||||
|
||||
[ViewVariables]
|
||||
public SignalState LastO2 = SignalState.Low;
|
||||
}
|
||||
|
||||
public enum SignalState
|
||||
{
|
||||
Momentary, // Instantaneous pulse high, compatibility behavior
|
||||
Low,
|
||||
High
|
||||
}
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
using Content.Shared.MachineLinking;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Components;
|
||||
|
||||
[RegisterComponent]
|
||||
public sealed class SignalTimerComponent : Component
|
||||
{
|
||||
[DataField("delay"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public double Delay = 5;
|
||||
|
||||
/// <summary>
|
||||
/// This shows the Label: text box in the UI.
|
||||
/// </summary>
|
||||
[DataField("canEditLabel"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanEditLabel = true;
|
||||
|
||||
/// <summary>
|
||||
/// The label, used for TextScreen visuals currently.
|
||||
/// </summary>
|
||||
[DataField("label"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string Label = "";
|
||||
|
||||
/// <summary>
|
||||
/// The port that gets signaled when the timer triggers.
|
||||
/// </summary>
|
||||
[DataField("triggerPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string TriggerPort = "Timer";
|
||||
|
||||
/// <summary>
|
||||
/// The port that gets signaled when the timer starts.
|
||||
/// </summary>
|
||||
[DataField("startPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>)), ViewVariables(VVAccess.ReadWrite)]
|
||||
public string StartPort = "Start";
|
||||
|
||||
/// <summary>
|
||||
/// If not null, this timer will play this sound when done.
|
||||
/// </summary>
|
||||
[DataField("doneSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier? DoneSound;
|
||||
}
|
||||
@@ -1,8 +1,6 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.MachineLinking.Events;
|
||||
using Content.Server.MachineLinking.System;
|
||||
using Content.Shared.Doors.Components;
|
||||
using Content.Shared.Doors;
|
||||
using JetBrains.Annotations;
|
||||
@@ -17,8 +15,6 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
|
||||
private const string DoorSignalState = "DoorState";
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
@@ -40,7 +36,7 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
return;
|
||||
|
||||
var state = SignalState.Momentary;
|
||||
args.Data?.TryGetValue(DoorSignalState, out state);
|
||||
args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state);
|
||||
|
||||
|
||||
if (args.Port == component.OpenPort)
|
||||
@@ -85,12 +81,12 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
{
|
||||
var data = new NetworkPayload()
|
||||
{
|
||||
{ DoorSignalState, SignalState.Momentary }
|
||||
{ DeviceNetworkConstants.LogicState, SignalState.Momentary }
|
||||
};
|
||||
|
||||
if (args.State == DoorState.Closed)
|
||||
{
|
||||
data[DoorSignalState] = SignalState.Low;
|
||||
data[DeviceNetworkConstants.LogicState] = SignalState.Low;
|
||||
_signalSystem.InvokePort(uid, door.OutOpen, data);
|
||||
}
|
||||
else if (args.State == DoorState.Open
|
||||
@@ -98,7 +94,7 @@ namespace Content.Server.DeviceLinking.Systems
|
||||
|| args.State == DoorState.Closing
|
||||
|| args.State == DoorState.Emagging)
|
||||
{
|
||||
data[DoorSignalState] = SignalState.High;
|
||||
data[DeviceNetworkConstants.LogicState] = SignalState.High;
|
||||
_signalSystem.InvokePort(uid, door.OutOpen, data);
|
||||
}
|
||||
}
|
||||
|
||||
84
Content.Server/DeviceLinking/Systems/OrGateSystem.cs
Normal file
84
Content.Server/DeviceLinking/Systems/OrGateSystem.cs
Normal file
@@ -0,0 +1,84 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.DeviceNetwork;
|
||||
using Content.Server.MachineLinking.Events;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Utility;
|
||||
using SignalReceivedEvent = Content.Server.DeviceLinking.Events.SignalReceivedEvent;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Systems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class OrGateSystem : EntitySystem
|
||||
{
|
||||
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
SubscribeLocalEvent<OrGateComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<OrGateComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, OrGateComponent component, ComponentInit args)
|
||||
{
|
||||
_signalSystem.EnsureSinkPorts(uid, "A1", "B1", "A2", "B2");
|
||||
_signalSystem.EnsureSourcePorts(uid, "O1", "O2");
|
||||
}
|
||||
|
||||
private void OnSignalReceived(EntityUid uid, OrGateComponent component, ref SignalReceivedEvent args)
|
||||
{
|
||||
var state = SignalState.Momentary;
|
||||
args.Data?.TryGetValue(DeviceNetworkConstants.LogicState, out state);
|
||||
|
||||
switch (args.Port)
|
||||
{
|
||||
case "A1":
|
||||
component.StateA1 = state;
|
||||
break;
|
||||
case "B1":
|
||||
component.StateB1 = state;
|
||||
break;
|
||||
case "A2":
|
||||
component.StateA2 = state;
|
||||
break;
|
||||
case "B2":
|
||||
component.StateB2 = state;
|
||||
break;
|
||||
}
|
||||
|
||||
// O1 = A1 || B1
|
||||
var v1 = SignalState.Low;
|
||||
if (component.StateA1 == SignalState.High || component.StateB1 == SignalState.High)
|
||||
v1 = SignalState.High;
|
||||
|
||||
if (v1 != component.LastO1)
|
||||
{
|
||||
var data = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.LogicState] = v1
|
||||
};
|
||||
|
||||
_signalSystem.InvokePort(uid, "O1", data);
|
||||
}
|
||||
|
||||
component.LastO1 = v1;
|
||||
|
||||
// O2 = A2 || B2
|
||||
var v2 = SignalState.Low;
|
||||
if (component.StateA2 == SignalState.High || component.StateB2 == SignalState.High)
|
||||
v2 = SignalState.High;
|
||||
|
||||
if (v2 != component.LastO2)
|
||||
{
|
||||
var data = new NetworkPayload
|
||||
{
|
||||
[DeviceNetworkConstants.LogicState] = v2
|
||||
};
|
||||
|
||||
_signalSystem.InvokePort(uid, "O2", data);
|
||||
}
|
||||
component.LastO2 = v2;
|
||||
}
|
||||
}
|
||||
}
|
||||
162
Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs
Normal file
162
Content.Server/DeviceLinking/Systems/SignalTimerSystem.cs
Normal file
@@ -0,0 +1,162 @@
|
||||
using Content.Server.DeviceLinking.Components;
|
||||
using Content.Server.Interaction;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.Access.Systems;
|
||||
using Content.Shared.MachineLinking;
|
||||
using Content.Shared.TextScreen;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.DeviceLinking.Systems;
|
||||
|
||||
public sealed class SignalTimerSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
[Dependency] private readonly DeviceLinkSystem _signalSystem = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearanceSystem = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly AccessReaderSystem _accessReader = default!;
|
||||
[Dependency] private readonly InteractionSystem _interaction = default!;
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
SubscribeLocalEvent<SignalTimerComponent, ComponentInit>(OnInit);
|
||||
SubscribeLocalEvent<SignalTimerComponent, AfterActivatableUIOpenEvent>(OnAfterActivatableUIOpen);
|
||||
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerTextChangedMessage>(OnTextChangedMessage);
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerDelayChangedMessage>(OnDelayChangedMessage);
|
||||
SubscribeLocalEvent<SignalTimerComponent, SignalTimerStartMessage>(OnTimerStartMessage);
|
||||
}
|
||||
|
||||
private void OnInit(EntityUid uid, SignalTimerComponent component, ComponentInit args)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label);
|
||||
}
|
||||
|
||||
private void OnAfterActivatableUIOpen(EntityUid uid, SignalTimerComponent component, AfterActivatableUIOpenEvent args)
|
||||
{
|
||||
var time = TryComp<ActiveSignalTimerComponent>(uid, out var active) ? active.TriggerTime : TimeSpan.Zero;
|
||||
|
||||
if (_ui.TryGetUi(uid, SignalTimerUiKey.Key, out var bui))
|
||||
{
|
||||
_ui.SetUiState(bui, new SignalTimerBoundUserInterfaceState(component.Label,
|
||||
TimeSpan.FromSeconds(component.Delay).Minutes.ToString("D2"),
|
||||
TimeSpan.FromSeconds(component.Delay).Seconds.ToString("D2"),
|
||||
component.CanEditLabel,
|
||||
time,
|
||||
active != null,
|
||||
_accessReader.IsAllowed(args.User, uid)));
|
||||
}
|
||||
}
|
||||
|
||||
public void Trigger(EntityUid uid, SignalTimerComponent signalTimer)
|
||||
{
|
||||
RemComp<ActiveSignalTimerComponent>(uid);
|
||||
|
||||
_signalSystem.InvokePort(uid, signalTimer.TriggerPort);
|
||||
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Text);
|
||||
|
||||
if (_ui.TryGetUi(uid, SignalTimerUiKey.Key, out var bui))
|
||||
{
|
||||
_ui.SetUiState(bui, new SignalTimerBoundUserInterfaceState(signalTimer.Label,
|
||||
TimeSpan.FromSeconds(signalTimer.Delay).Minutes.ToString("D2"),
|
||||
TimeSpan.FromSeconds(signalTimer.Delay).Seconds.ToString("D2"),
|
||||
signalTimer.CanEditLabel,
|
||||
TimeSpan.Zero,
|
||||
false,
|
||||
true));
|
||||
}
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
UpdateTimer();
|
||||
}
|
||||
|
||||
private void UpdateTimer()
|
||||
{
|
||||
var query = EntityQueryEnumerator<ActiveSignalTimerComponent, SignalTimerComponent>();
|
||||
while (query.MoveNext(out var uid, out var active, out var timer))
|
||||
{
|
||||
if (active.TriggerTime > _gameTiming.CurTime)
|
||||
continue;
|
||||
|
||||
Trigger(uid, timer);
|
||||
|
||||
if (timer.DoneSound == null)
|
||||
continue;
|
||||
_audio.PlayPvs(timer.DoneSound, uid);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a UI <paramref name="message"/> is allowed to be sent by the user.
|
||||
/// </summary>
|
||||
/// <param name="uid">The entity that is interacted with.</param>
|
||||
/// <param name="message"></param>
|
||||
private bool IsMessageValid(EntityUid uid, BoundUserInterfaceMessage message)
|
||||
{
|
||||
if (message.Session.AttachedEntity is not { Valid: true } mob)
|
||||
return false;
|
||||
|
||||
if (!_accessReader.IsAllowed(mob, uid))
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void OnTextChangedMessage(EntityUid uid, SignalTimerComponent component, SignalTimerTextChangedMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
component.Label = args.Text[..Math.Min(5,args.Text.Length)];
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label);
|
||||
}
|
||||
|
||||
private void OnDelayChangedMessage(EntityUid uid, SignalTimerComponent component, SignalTimerDelayChangedMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
component.Delay = args.Delay.TotalSeconds;
|
||||
}
|
||||
|
||||
private void OnTimerStartMessage(EntityUid uid, SignalTimerComponent component, SignalTimerStartMessage args)
|
||||
{
|
||||
if (!IsMessageValid(uid, args))
|
||||
return;
|
||||
|
||||
TryComp<AppearanceComponent>(uid, out var appearance);
|
||||
|
||||
if (!HasComp<ActiveSignalTimerComponent>(uid))
|
||||
{
|
||||
var activeTimer = EnsureComp<ActiveSignalTimerComponent>(uid);
|
||||
activeTimer.TriggerTime = _gameTiming.CurTime + TimeSpan.FromSeconds(component.Delay);
|
||||
|
||||
if (appearance != null)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Timer, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.TargetTime, activeTimer.TriggerTime, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label, appearance);
|
||||
}
|
||||
|
||||
_signalSystem.InvokePort(uid, component.StartPort);
|
||||
}
|
||||
else
|
||||
{
|
||||
RemComp<ActiveSignalTimerComponent>(uid);
|
||||
|
||||
if (appearance != null)
|
||||
{
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.Mode, TextScreenMode.Text, appearance);
|
||||
_appearanceSystem.SetData(uid, TextScreenVisuals.ScreenText, component.Label, appearance);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user