machine linking refactor to ecs (#4323)
* started work * some more work, ui working (somewhat) * stuff * reorganization * some more reorg * conveyors * conveyors working * finalized (dis)connection added linkattempt added feedback text work on conveyors * removed command add rangecheck * fixed inrange check * handling * ui no longer kanser, ship it * adresses reviews * reformats file * reformats file Co-authored-by: Paul <ritter.paul1+git@googlemail.com>
This commit is contained in:
@@ -0,0 +1,50 @@
|
|||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Client.MachineLinking.UI
|
||||||
|
{
|
||||||
|
public class SignalPortSelectorBoundUserInterface : BoundUserInterface
|
||||||
|
{
|
||||||
|
private SignalPortSelectorMenu? _menu;
|
||||||
|
|
||||||
|
public SignalPortSelectorBoundUserInterface([NotNull] ClientUserInterfaceComponent owner, [NotNull] object uiKey) : base(owner, uiKey)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Open()
|
||||||
|
{
|
||||||
|
base.Open();
|
||||||
|
|
||||||
|
_menu = new SignalPortSelectorMenu(this);
|
||||||
|
_menu.OnClose += Close;
|
||||||
|
_menu.OpenCentered();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
|
{
|
||||||
|
base.UpdateState(state);
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case SignalPortsState data:
|
||||||
|
_menu?.UpdateState(data);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OnPortSelected(string port)
|
||||||
|
{
|
||||||
|
SendMessage(new SignalPortSelected(port));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Dispose(bool disposing)
|
||||||
|
{
|
||||||
|
base.Dispose(disposing);
|
||||||
|
if (!disposing)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_menu?.Dispose();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
<customControls:SS14Window xmlns:customControls="https://spacestation14.io"
|
||||||
|
Title="Port Selector" MinSize="200 200">
|
||||||
|
<customControls:ItemList Name="ButtonContainer" VerticalExpand="True" HorizontalExpand="True" SelectMode="Button"/>
|
||||||
|
</customControls:SS14Window>
|
||||||
@@ -0,0 +1,36 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.CustomControls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
|
||||||
|
namespace Content.Client.MachineLinking.UI
|
||||||
|
{
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public partial class SignalPortSelectorMenu : SS14Window
|
||||||
|
{
|
||||||
|
private SignalPortSelectorBoundUserInterface _bui;
|
||||||
|
|
||||||
|
public SignalPortSelectorMenu(SignalPortSelectorBoundUserInterface boundUserInterface)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_bui = boundUserInterface;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(SignalPortsState state)
|
||||||
|
{
|
||||||
|
ButtonContainer.Clear();
|
||||||
|
foreach (var port in state.Ports)
|
||||||
|
{
|
||||||
|
var portBtn = new ItemList.Item(ButtonContainer)
|
||||||
|
{
|
||||||
|
Text = port.Key,
|
||||||
|
Disabled = !port.Value
|
||||||
|
};
|
||||||
|
portBtn.OnSelected += _ => _bui.OnPortSelected(port.Key);
|
||||||
|
ButtonContainer.Add(portBtn);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -14,7 +14,7 @@ using Robust.Shared.ViewVariables;
|
|||||||
namespace Content.Server.Conveyor
|
namespace Content.Server.Conveyor
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class ConveyorComponent : Component, ISignalReceiver<TwoWayLeverSignal>, ISignalReceiver<bool>
|
public class ConveyorComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "Conveyor";
|
public override string Name => "Conveyor";
|
||||||
|
|
||||||
@@ -150,7 +150,7 @@ namespace Content.Server.Conveyor
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerSignal(TwoWayLeverSignal signal)
|
public void SetState(TwoWayLeverSignal signal)
|
||||||
{
|
{
|
||||||
State = signal switch
|
State = signal switch
|
||||||
{
|
{
|
||||||
@@ -160,10 +160,5 @@ namespace Content.Server.Conveyor
|
|||||||
_ => ConveyorState.Off
|
_ => ConveyorState.Off
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerSignal(bool signal)
|
|
||||||
{
|
|
||||||
State = signal ? ConveyorState.Forward : ConveyorState.Off;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
51
Content.Server/Conveyor/ConveyorSystem.cs
Normal file
51
Content.Server/Conveyor/ConveyorSystem.cs
Normal file
@@ -0,0 +1,51 @@
|
|||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Server.MachineLinking.Models;
|
||||||
|
using Content.Server.Stunnable.Components;
|
||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using Content.Shared.Notification.Managers;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
|
||||||
|
namespace Content.Server.Conveyor
|
||||||
|
{
|
||||||
|
public class ConveyorSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<ConveyorComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||||
|
SubscribeLocalEvent<ConveyorComponent, PortDisconnectedEvent>(OnPortDisconnected);
|
||||||
|
SubscribeLocalEvent<ConveyorComponent, LinkAttemptEvent>(OnLinkAttempt);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnLinkAttempt(EntityUid uid, ConveyorComponent component, LinkAttemptEvent args)
|
||||||
|
{
|
||||||
|
if (args.TransmitterComponent.Outputs.GetPort(args.TransmitterPort).Signal is TwoWayLeverSignal signal &&
|
||||||
|
signal != TwoWayLeverSignal.Middle)
|
||||||
|
{
|
||||||
|
args.Cancel();
|
||||||
|
if (args.Attemptee.TryGetComponent<StunnableComponent>(out var stunnableComponent))
|
||||||
|
{
|
||||||
|
stunnableComponent.Paralyze(2);
|
||||||
|
component.Owner.PopupMessage(args.Attemptee, Loc.GetString("conveyor-component-failed-link"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPortDisconnected(EntityUid uid, ConveyorComponent component, PortDisconnectedEvent args)
|
||||||
|
{
|
||||||
|
component.SetState(TwoWayLeverSignal.Middle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSignalReceived(EntityUid uid, ConveyorComponent component, SignalReceivedEvent args)
|
||||||
|
{
|
||||||
|
switch (args.Port)
|
||||||
|
{
|
||||||
|
case "state":
|
||||||
|
component.SetState((TwoWayLeverSignal) args.Value!);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,8 +3,6 @@ using System.Threading.Tasks;
|
|||||||
using Content.Server.Ghost;
|
using Content.Server.Ghost;
|
||||||
using Content.Server.Hands.Components;
|
using Content.Server.Hands.Components;
|
||||||
using Content.Server.Items;
|
using Content.Server.Items;
|
||||||
using Content.Server.MachineLinking.Components;
|
|
||||||
using Content.Server.MachineLinking.Signals;
|
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Temperature.Components;
|
using Content.Server.Temperature.Components;
|
||||||
using Content.Shared.Actions.Behaviors;
|
using Content.Shared.Actions.Behaviors;
|
||||||
@@ -12,7 +10,6 @@ using Content.Shared.Damage;
|
|||||||
using Content.Shared.Damage.Components;
|
using Content.Shared.Damage.Components;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Light;
|
using Content.Shared.Light;
|
||||||
using Content.Shared.Notification;
|
|
||||||
using Content.Shared.Notification.Managers;
|
using Content.Shared.Notification.Managers;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -33,7 +30,7 @@ namespace Content.Server.Light.Components
|
|||||||
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
|
/// Component that represents a wall light. It has a light bulb that can be replaced when broken.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, ISignalReceiver<bool>, ISignalReceiver<ToggleSignal>, IGhostBooAffected
|
public class PoweredLightComponent : Component, IInteractHand, IInteractUsing, IMapInit, IGhostBooAffected
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||||
|
|
||||||
@@ -309,13 +306,7 @@ namespace Content.Server.Light.Components
|
|||||||
UpdateLight();
|
UpdateLight();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void TriggerSignal(bool signal)
|
public void ToggleLight()
|
||||||
{
|
|
||||||
_on = signal;
|
|
||||||
UpdateLight();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TriggerSignal(ToggleSignal signal)
|
|
||||||
{
|
{
|
||||||
_on = !_on;
|
_on = !_on;
|
||||||
UpdateLight();
|
UpdateLight();
|
||||||
|
|||||||
26
Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Normal file
26
Content.Server/Light/EntitySystems/PoweredLightSystem.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Server.Light.Components;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Light.EntitySystems
|
||||||
|
{
|
||||||
|
public class PoweredLightSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSignalReceived(EntityUid uid, PoweredLightComponent component, SignalReceivedEvent args)
|
||||||
|
{
|
||||||
|
switch (args.Port)
|
||||||
|
{
|
||||||
|
case "toggle":
|
||||||
|
component.ToggleLight();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
namespace Content.Server.MachineLinking.Components
|
|
||||||
{
|
|
||||||
public interface ISignalReceiver<in T>
|
|
||||||
{
|
|
||||||
void TriggerSignal(T signal);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,45 +1,10 @@
|
|||||||
using Content.Server.MachineLinking.Signals;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Notification;
|
|
||||||
using Content.Shared.Notification.Managers;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking.Components
|
namespace Content.Server.MachineLinking.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class SignalButtonComponent : Component, IActivate, IInteractHand
|
public class SignalButtonComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "SignalButton";
|
public override string Name => "SignalButton";
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
TransmitSignal(eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
TransmitSignal(eventArgs.User);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void TransmitSignal(IEntity user)
|
|
||||||
{
|
|
||||||
if (!Owner.TryGetComponent<SignalTransmitterComponent>(out var transmitter))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transmitter.TransmitSignal(new ToggleSignal()))
|
|
||||||
{
|
|
||||||
// Since the button doesn't have an animation, I'm going to use a popup message
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-button-component-transmit-signal-success-message"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-button-component-transmit-signal-no-receivers-message"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,13 +9,6 @@ namespace Content.Server.MachineLinking.Components
|
|||||||
public override string Name => "SignalLinker";
|
public override string Name => "SignalLinker";
|
||||||
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public SignalTransmitterComponent? Link { get; set; }
|
public (SignalTransmitterComponent transmitter, string port)? Port;
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
Link = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,125 +1,21 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using Content.Server.MachineLinking.Models;
|
||||||
using Content.Server.Tools.Components;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Notification;
|
|
||||||
using Content.Shared.Notification.Managers;
|
|
||||||
using Content.Shared.Tool;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking.Components
|
namespace Content.Server.MachineLinking.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class SignalReceiverComponent : Component, IInteractUsing
|
public class SignalReceiverComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "SignalReceiver";
|
public override string Name => "SignalReceiver";
|
||||||
|
|
||||||
private readonly List<SignalTransmitterComponent> _transmitters = new();
|
[DataField("inputs")]
|
||||||
|
private List<SignalPort> _inputs = new();
|
||||||
|
|
||||||
[DataField("maxTransmitters")]
|
[ViewVariables]
|
||||||
private int? _maxTransmitters = default;
|
public IReadOnlyList<SignalPort> Inputs => _inputs;
|
||||||
|
|
||||||
public void DistributeSignal<T>(T state)
|
|
||||||
{
|
|
||||||
foreach (var comp in Owner.GetAllComponents<ISignalReceiver<T>>())
|
|
||||||
{
|
|
||||||
comp.TriggerSignal(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool Subscribe(SignalTransmitterComponent transmitter)
|
|
||||||
{
|
|
||||||
if (_transmitters.Contains(transmitter))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transmitters.Count >= _maxTransmitters) return false;
|
|
||||||
|
|
||||||
transmitter.Subscribe(this);
|
|
||||||
_transmitters.Add(transmitter);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unsubscribe(SignalTransmitterComponent transmitter)
|
|
||||||
{
|
|
||||||
transmitter.Unsubscribe(this);
|
|
||||||
_transmitters.Remove(transmitter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UnsubscribeAll()
|
|
||||||
{
|
|
||||||
for (var i = _transmitters.Count-1; i >= 0; i--)
|
|
||||||
{
|
|
||||||
var transmitter = _transmitters[i];
|
|
||||||
if (transmitter.Deleted)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
transmitter.Unsubscribe(this);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Subscribes/Unsubscribes a transmitter to this component. Returns whether it was successful.
|
|
||||||
/// </summary>
|
|
||||||
/// <param name="user"></param>
|
|
||||||
/// <param name="transmitter"></param>
|
|
||||||
/// <returns></returns>
|
|
||||||
public bool Interact(IEntity user, SignalTransmitterComponent? transmitter)
|
|
||||||
{
|
|
||||||
if (transmitter == null)
|
|
||||||
{
|
|
||||||
user.PopupMessage(Loc.GetString("signal-receiver-component-interact-no-transmitter-message"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transmitters.Contains(transmitter))
|
|
||||||
{
|
|
||||||
Unsubscribe(transmitter);
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-receiver-component-interact-unlinked"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (transmitter.Range > 0 && !Owner.Transform.Coordinates.InRange(Owner.EntityManager, transmitter.Owner.Transform.Coordinates, transmitter.Range))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-receiver-component-interact-out-of-range"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!Subscribe(transmitter))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-receiver-component-interact-max-transmitters-limit"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-receiver-component-interact-success"));
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (tool.HasQuality(ToolQuality.Multitool)
|
|
||||||
&& eventArgs.Using.TryGetComponent<SignalLinkerComponent>(out var linker))
|
|
||||||
{
|
|
||||||
return Interact(eventArgs.User, linker.Link);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Shutdown()
|
|
||||||
{
|
|
||||||
base.Shutdown();
|
|
||||||
|
|
||||||
UnsubscribeAll();
|
|
||||||
|
|
||||||
_transmitters.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,86 +1,12 @@
|
|||||||
using Content.Shared.ActionBlocker;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Interaction.Events;
|
|
||||||
using Content.Shared.MachineLinking;
|
|
||||||
using Content.Shared.Notification.Managers;
|
|
||||||
using Content.Shared.Verbs;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking.Components
|
namespace Content.Server.MachineLinking.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class SignalSwitchComponent : Component, IInteractHand, IActivate
|
public class SignalSwitchComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "SignalSwitch";
|
public override string Name => "SignalSwitch";
|
||||||
|
|
||||||
[DataField("on")]
|
public bool State;
|
||||||
private bool _on;
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
UpdateSprite();
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
TransmitSignal(eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
TransmitSignal(eventArgs.User);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void TransmitSignal(IEntity user)
|
|
||||||
{
|
|
||||||
_on = !_on;
|
|
||||||
|
|
||||||
UpdateSprite();
|
|
||||||
|
|
||||||
if (!Owner.TryGetComponent<SignalTransmitterComponent>(out var transmitter))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!transmitter.TransmitSignal(_on))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-switch-component-transmit-no-receivers-connected"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateSprite()
|
|
||||||
{
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(SignalSwitchVisuals.On, _on);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[Verb]
|
|
||||||
private sealed class ToggleSwitchVerb : Verb<SignalSwitchComponent>
|
|
||||||
{
|
|
||||||
protected override void Activate(IEntity user, SignalSwitchComponent component)
|
|
||||||
{
|
|
||||||
component.TransmitSignal(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void GetData(IEntity user, SignalSwitchComponent component, VerbData data)
|
|
||||||
{
|
|
||||||
if (!EntitySystem.Get<ActionBlockerSystem>().CanInteract(user))
|
|
||||||
{
|
|
||||||
data.Visibility = VerbVisibility.Invisible;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
data.Text = Loc.GetString("toggle-switch-verb-get-data-text");
|
|
||||||
data.Visibility = VerbVisibility.Visible;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,159 +1,21 @@
|
|||||||
|
using System;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using Content.Server.MachineLinking.Models;
|
||||||
using Content.Server.Tools.Components;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Notification;
|
|
||||||
using Content.Shared.Notification.Managers;
|
|
||||||
using Content.Shared.Tool;
|
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.Localization;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking.Components
|
namespace Content.Server.MachineLinking.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public class SignalTransmitterComponent : Component, IInteractUsing, ISerializationHooks
|
public class SignalTransmitterComponent : Component
|
||||||
{
|
{
|
||||||
public override string Name => "SignalTransmitter";
|
public override string Name => "SignalTransmitter";
|
||||||
|
|
||||||
private List<SignalReceiverComponent>? _unresolvedReceivers = new();
|
[DataField("outputs")]
|
||||||
private List<SignalReceiverComponent> _receivers = new();
|
private List<SignalPort> _outputs = new();
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// 0 is unlimited range
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
[DataField("range")]
|
public IReadOnlyList<SignalPort> Outputs => _outputs;
|
||||||
public float Range { get; private set; } = 10;
|
|
||||||
|
|
||||||
[DataField("signalReceivers")] private List<EntityUid> _receiverIds = new();
|
|
||||||
|
|
||||||
void ISerializationHooks.BeforeSerialization()
|
|
||||||
{
|
|
||||||
var entityList = new List<EntityUid>();
|
|
||||||
|
|
||||||
foreach (var receiver in _receivers)
|
|
||||||
{
|
|
||||||
if (receiver.Deleted)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
entityList.Add(receiver.Owner.Uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
_receiverIds = entityList;
|
|
||||||
}
|
|
||||||
|
|
||||||
void ISerializationHooks.AfterDeserialization()
|
|
||||||
{
|
|
||||||
_unresolvedReceivers = new List<SignalReceiverComponent>();
|
|
||||||
|
|
||||||
foreach (var id in _receiverIds)
|
|
||||||
{
|
|
||||||
if (!Owner.EntityManager.TryGetEntity(id, out var entity) ||
|
|
||||||
!entity.TryGetComponent<SignalReceiverComponent>(out var receiver))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_unresolvedReceivers.Add(receiver);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
_receivers = new List<SignalReceiverComponent>();
|
|
||||||
|
|
||||||
if (_unresolvedReceivers != null)
|
|
||||||
{
|
|
||||||
foreach (var receiver in _unresolvedReceivers)
|
|
||||||
{
|
|
||||||
receiver.Subscribe(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_unresolvedReceivers = null;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool TransmitSignal<T>(T signal)
|
|
||||||
{
|
|
||||||
if (_receivers.Count == 0)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var receiver in _receivers)
|
|
||||||
{
|
|
||||||
if (Range > 0 && !Owner.Transform.Coordinates.InRange(Owner.EntityManager, receiver.Owner.Transform.Coordinates, Range))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
receiver.DistributeSignal(signal);
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Subscribe(SignalReceiverComponent receiver)
|
|
||||||
{
|
|
||||||
if (_receivers.Contains(receiver))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_receivers.Add(receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void Unsubscribe(SignalReceiverComponent receiver)
|
|
||||||
{
|
|
||||||
_receivers.Remove(receiver);
|
|
||||||
}
|
|
||||||
|
|
||||||
public SignalTransmitterComponent GetSignal(IEntity? user)
|
|
||||||
{
|
|
||||||
if (user != null)
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-transmitter-component-get-signal-success"));
|
|
||||||
}
|
|
||||||
|
|
||||||
return this;
|
|
||||||
}
|
|
||||||
|
|
||||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
if (!eventArgs.Using.TryGetComponent<ToolComponent>(out var tool))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
if (tool.HasQuality(ToolQuality.Multitool)
|
|
||||||
&& eventArgs.Using.TryGetComponent<SignalLinkerComponent>(out var linker))
|
|
||||||
{
|
|
||||||
linker.Link = GetSignal(eventArgs.User);
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Shutdown()
|
|
||||||
{
|
|
||||||
base.Shutdown();
|
|
||||||
|
|
||||||
for (var i = _receivers.Count-1; i >= 0; i++)
|
|
||||||
{
|
|
||||||
var receiver = _receivers[i];
|
|
||||||
if (receiver.Deleted)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
receiver.Unsubscribe(this);
|
|
||||||
}
|
|
||||||
|
|
||||||
_receivers.Clear();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,64 +0,0 @@
|
|||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.MachineLinking;
|
|
||||||
using Content.Shared.Notification;
|
|
||||||
using Content.Shared.Notification.Managers;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Localization;
|
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking.Components
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
[ComponentReference(typeof(IActivate))]
|
|
||||||
public class SignalTwoWayLeverComponent : SignalTransmitterComponent, IInteractHand, IActivate
|
|
||||||
{
|
|
||||||
public override string Name => "TwoWayLever";
|
|
||||||
|
|
||||||
private TwoWayLeverSignal _state = TwoWayLeverSignal.Middle;
|
|
||||||
|
|
||||||
private bool _nextForward = true;
|
|
||||||
|
|
||||||
public TwoWayLeverSignal State
|
|
||||||
{
|
|
||||||
get => _state;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
_state = value;
|
|
||||||
|
|
||||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(TwoWayLeverVisuals.State, value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void NextState(IEntity user)
|
|
||||||
{
|
|
||||||
State = State switch
|
|
||||||
{
|
|
||||||
TwoWayLeverSignal.Left => TwoWayLeverSignal.Middle,
|
|
||||||
TwoWayLeverSignal.Middle => _nextForward ? TwoWayLeverSignal.Right : TwoWayLeverSignal.Left,
|
|
||||||
TwoWayLeverSignal.Right => TwoWayLeverSignal.Middle,
|
|
||||||
_ => TwoWayLeverSignal.Middle
|
|
||||||
};
|
|
||||||
|
|
||||||
if (State == TwoWayLeverSignal.Left || State == TwoWayLeverSignal.Right) _nextForward = !_nextForward;
|
|
||||||
|
|
||||||
if (!TransmitSignal(State))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("signal-two-way-lever-component-next-state-no-receivers-connected"));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
bool IInteractHand.InteractHand(InteractHandEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
NextState(eventArgs.User);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
|
||||||
{
|
|
||||||
NextState(eventArgs.User);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Components
|
||||||
|
{
|
||||||
|
[RegisterComponent]
|
||||||
|
public class TwoWayLeverComponent : Component
|
||||||
|
{
|
||||||
|
public override string Name => "TwoWayLever";
|
||||||
|
|
||||||
|
public TwoWayLeverSignal State;
|
||||||
|
|
||||||
|
public bool NextSignalLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Content.Server/MachineLinking/Events/InvokePortEvent.cs
Normal file
16
Content.Server/MachineLinking/Events/InvokePortEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Events
|
||||||
|
{
|
||||||
|
public class InvokePortEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly string Port;
|
||||||
|
public readonly object? Value;
|
||||||
|
|
||||||
|
public InvokePortEvent(string port, object? value = null)
|
||||||
|
{
|
||||||
|
Port = port;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Content.Server/MachineLinking/Events/LinkAttemptEvent.cs
Normal file
23
Content.Server/MachineLinking/Events/LinkAttemptEvent.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Events
|
||||||
|
{
|
||||||
|
public class LinkAttemptEvent : CancellableEntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly IEntity Attemptee;
|
||||||
|
public readonly SignalTransmitterComponent TransmitterComponent;
|
||||||
|
public readonly string TransmitterPort;
|
||||||
|
public readonly SignalReceiverComponent ReceiverComponent;
|
||||||
|
public readonly string ReceiverPort;
|
||||||
|
|
||||||
|
public LinkAttemptEvent(IEntity attemptee, SignalTransmitterComponent transmitterComponent, string transmitterPort, SignalReceiverComponent receiverComponent, string receiverPort)
|
||||||
|
{
|
||||||
|
TransmitterComponent = transmitterComponent;
|
||||||
|
this.TransmitterPort = transmitterPort;
|
||||||
|
ReceiverComponent = receiverComponent;
|
||||||
|
this.ReceiverPort = receiverPort;
|
||||||
|
Attemptee = attemptee;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Events
|
||||||
|
{
|
||||||
|
public class PortDisconnectedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly string Port;
|
||||||
|
|
||||||
|
public PortDisconnectedEvent(string port)
|
||||||
|
{
|
||||||
|
Port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
16
Content.Server/MachineLinking/Events/SignalReceivedEvent.cs
Normal file
16
Content.Server/MachineLinking/Events/SignalReceivedEvent.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Events
|
||||||
|
{
|
||||||
|
public class SignalReceivedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly string Port;
|
||||||
|
public readonly object? Value;
|
||||||
|
|
||||||
|
public SignalReceivedEvent(string port, object? value)
|
||||||
|
{
|
||||||
|
Port = port;
|
||||||
|
Value = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Events
|
||||||
|
{
|
||||||
|
public class SignalValueRequestedEvent : HandledEntityEventArgs
|
||||||
|
{
|
||||||
|
public readonly string Port;
|
||||||
|
public readonly Type Type;
|
||||||
|
|
||||||
|
public object? Signal;
|
||||||
|
|
||||||
|
public SignalValueRequestedEvent(string port, Type type)
|
||||||
|
{
|
||||||
|
Port = port;
|
||||||
|
Type = type;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Exceptions
|
||||||
|
{
|
||||||
|
public class InvalidPortValueException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Exceptions
|
||||||
|
{
|
||||||
|
public class LinkAlreadyRegisteredException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Exceptions
|
||||||
|
{
|
||||||
|
public class NoSignalValueProvidedException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using System;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Exceptions
|
||||||
|
{
|
||||||
|
public class PortNotFoundException : Exception
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Content.Server/MachineLinking/Models/SignalLink.cs
Normal file
20
Content.Server/MachineLinking/Models/SignalLink.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Models
|
||||||
|
{
|
||||||
|
public class SignalLink
|
||||||
|
{
|
||||||
|
public readonly SignalTransmitterComponent TransmitterComponent;
|
||||||
|
public readonly SignalReceiverComponent ReceiverComponent;
|
||||||
|
public readonly SignalPort Transmitterport;
|
||||||
|
public readonly SignalPort Receiverport;
|
||||||
|
|
||||||
|
public SignalLink(SignalTransmitterComponent transmitterComponent, string transmitterPort, SignalReceiverComponent receiverComponent, string receiverPort)
|
||||||
|
{
|
||||||
|
TransmitterComponent = transmitterComponent;
|
||||||
|
ReceiverComponent = receiverComponent;
|
||||||
|
Transmitterport = TransmitterComponent.Outputs.GetPort(transmitterPort);
|
||||||
|
Receiverport = ReceiverComponent.Inputs.GetPort(receiverPort);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
131
Content.Server/MachineLinking/Models/SignalLinkCollection.cs
Normal file
131
Content.Server/MachineLinking/Models/SignalLinkCollection.cs
Normal file
@@ -0,0 +1,131 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Content.Server.MachineLinking.Exceptions;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Models
|
||||||
|
{
|
||||||
|
public class SignalLinkCollection
|
||||||
|
{
|
||||||
|
private Dictionary<SignalTransmitterComponent, List<SignalLink>> _transmitterDict = new();
|
||||||
|
private Dictionary<SignalReceiverComponent, List<SignalLink>> _receiverDict = new();
|
||||||
|
|
||||||
|
public SignalLink AddLink(SignalTransmitterComponent transmitterComponent, string transmitterPort,
|
||||||
|
SignalReceiverComponent receiverComponent, string receiverPort)
|
||||||
|
{
|
||||||
|
if (LinkExists(transmitterComponent, transmitterPort, receiverComponent, receiverPort))
|
||||||
|
{
|
||||||
|
throw new LinkAlreadyRegisteredException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_transmitterDict.ContainsKey(transmitterComponent))
|
||||||
|
{
|
||||||
|
_transmitterDict[transmitterComponent] = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_receiverDict.ContainsKey(receiverComponent))
|
||||||
|
{
|
||||||
|
_receiverDict[receiverComponent] = new();
|
||||||
|
}
|
||||||
|
|
||||||
|
var link = new SignalLink(transmitterComponent, transmitterPort, receiverComponent, receiverPort);
|
||||||
|
_transmitterDict[transmitterComponent].Add(link);
|
||||||
|
_receiverDict[receiverComponent].Add(link);
|
||||||
|
|
||||||
|
return link;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool LinkExists(SignalTransmitterComponent transmitterComponent, string transmitterPort,
|
||||||
|
SignalReceiverComponent receiverComponent, string receiverPort)
|
||||||
|
{
|
||||||
|
if (!_transmitterDict.ContainsKey(transmitterComponent) || !_receiverDict.ContainsKey(receiverComponent))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var link in _transmitterDict[transmitterComponent])
|
||||||
|
{
|
||||||
|
if (link.Transmitterport.Name == transmitterPort && link.Receiverport.Name == receiverPort &&
|
||||||
|
link.ReceiverComponent == receiverComponent)
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool RemoveLink(SignalTransmitterComponent transmitterComponent, string transmitterPort,
|
||||||
|
SignalReceiverComponent receiverComponent, string receiverPort)
|
||||||
|
{
|
||||||
|
if (!_transmitterDict.ContainsKey(transmitterComponent) || !_receiverDict.ContainsKey(receiverComponent))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
SignalLink? theLink = null;
|
||||||
|
foreach (var link in _transmitterDict[transmitterComponent])
|
||||||
|
{
|
||||||
|
if (link.Transmitterport.Name == transmitterPort && link.Receiverport.Name == receiverPort &&
|
||||||
|
link.ReceiverComponent == receiverComponent)
|
||||||
|
{
|
||||||
|
theLink = link;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (theLink == null) return false;
|
||||||
|
|
||||||
|
_transmitterDict[transmitterComponent].Remove(theLink);
|
||||||
|
if (_transmitterDict[transmitterComponent].Count == 0) _transmitterDict.Remove(transmitterComponent);
|
||||||
|
_receiverDict[receiverComponent].Remove(theLink);
|
||||||
|
if (_receiverDict[receiverComponent].Count == 0) _receiverDict.Remove(receiverComponent);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public int LinkCount(SignalTransmitterComponent comp) =>
|
||||||
|
_transmitterDict.ContainsKey(comp) ? _transmitterDict[comp].Count : 0;
|
||||||
|
|
||||||
|
public int LinkCount(SignalReceiverComponent comp) =>
|
||||||
|
_receiverDict.ContainsKey(comp) ? _receiverDict[comp].Count : 0;
|
||||||
|
|
||||||
|
public void RemoveLinks(SignalTransmitterComponent component)
|
||||||
|
{
|
||||||
|
if (!_transmitterDict.ContainsKey(component))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var link in _transmitterDict[component])
|
||||||
|
{
|
||||||
|
_receiverDict[link.ReceiverComponent].Remove(link);
|
||||||
|
if (_receiverDict[link.ReceiverComponent].Count == 0) _receiverDict.Remove(link.ReceiverComponent);
|
||||||
|
}
|
||||||
|
|
||||||
|
_transmitterDict.Remove(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void RemoveLinks(SignalReceiverComponent component)
|
||||||
|
{
|
||||||
|
if (!_receiverDict.ContainsKey(component))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var link in _receiverDict[component])
|
||||||
|
{
|
||||||
|
_transmitterDict[link.TransmitterComponent].Remove(link);
|
||||||
|
if (_transmitterDict[link.TransmitterComponent].Count == 0)
|
||||||
|
_transmitterDict.Remove(link.TransmitterComponent);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public IEnumerable<SignalLink> GetLinks(SignalTransmitterComponent component, string port)
|
||||||
|
{
|
||||||
|
if (!_transmitterDict.ContainsKey(component)) yield break;
|
||||||
|
|
||||||
|
foreach (var link in _transmitterDict[component])
|
||||||
|
{
|
||||||
|
if (link.Transmitterport.Name != port) continue;
|
||||||
|
yield return link;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
81
Content.Server/MachineLinking/Models/SignalPort.cs
Normal file
81
Content.Server/MachineLinking/Models/SignalPort.cs
Normal file
@@ -0,0 +1,81 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Server.MachineLinking.Exceptions;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.Models
|
||||||
|
{
|
||||||
|
[DataDefinition]
|
||||||
|
public class SignalPort
|
||||||
|
{
|
||||||
|
[DataField("name", required: true)] public string Name { get; } = default!;
|
||||||
|
[DataField("type")] public Type? Type { get; }
|
||||||
|
/// <summary>
|
||||||
|
/// Maximum connections of the port. 0 means infinite.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("maxConnections")] public int MaxConnections { get; } = 0;
|
||||||
|
|
||||||
|
public object? Signal;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class PortPrototypeExtensions{
|
||||||
|
public static bool ContainsPort(this IReadOnlyList<SignalPort> ports, string port)
|
||||||
|
{
|
||||||
|
foreach (var portPrototype in ports)
|
||||||
|
{
|
||||||
|
if (portPrototype.Name == port)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<string> GetPortStrings(this IReadOnlyList<SignalPort> ports)
|
||||||
|
{
|
||||||
|
foreach (var portPrototype in ports)
|
||||||
|
{
|
||||||
|
yield return portPrototype.Name;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static IEnumerable<KeyValuePair<string, bool>> GetValidatedPorts(this IReadOnlyList<SignalPort> ports, Type? validType)
|
||||||
|
{
|
||||||
|
foreach (var portPrototype in ports)
|
||||||
|
{
|
||||||
|
yield return new KeyValuePair<string, bool>(portPrototype.Name, portPrototype.Type == validType);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool TryGetPort(this IReadOnlyList<SignalPort> ports, string name, [NotNullWhen(true)] out SignalPort? port)
|
||||||
|
{
|
||||||
|
foreach (var portPrototype in ports)
|
||||||
|
{
|
||||||
|
if (portPrototype.Name == name)
|
||||||
|
{
|
||||||
|
port = portPrototype;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
port = null;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static SignalPort GetPort(this IReadOnlyList<SignalPort> ports, string name)
|
||||||
|
{
|
||||||
|
foreach (var portPrototype in ports)
|
||||||
|
{
|
||||||
|
if (portPrototype.Name == name)
|
||||||
|
{
|
||||||
|
return portPrototype;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new PortNotFoundException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
using Content.Server.Administration;
|
|
||||||
using Content.Shared.Administration;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.Console;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking
|
|
||||||
{
|
|
||||||
[AdminCommand(AdminFlags.Debug)]
|
|
||||||
public class SignalLinkerCommand : IConsoleCommand
|
|
||||||
{
|
|
||||||
public string Command => "signallink";
|
|
||||||
|
|
||||||
public string Description => "Turns on signal linker mode. Click a transmitter to tune that signal and then click on each receiver to tune them to the transmitter signal.";
|
|
||||||
|
|
||||||
public string Help => "signallink (on/off)";
|
|
||||||
|
|
||||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
|
||||||
{
|
|
||||||
var player = (IPlayerSession?) shell.Player;
|
|
||||||
|
|
||||||
if (player == null)
|
|
||||||
{
|
|
||||||
shell.WriteError("This command cannot be run locally.");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool? enable = null;
|
|
||||||
if (args.Length > 0)
|
|
||||||
{
|
|
||||||
if (args[0] == "on")
|
|
||||||
enable = true;
|
|
||||||
else if (args[0] == "off")
|
|
||||||
enable = false;
|
|
||||||
else if (bool.TryParse(args[0], out var boolean))
|
|
||||||
enable = boolean;
|
|
||||||
else if (int.TryParse(args[0], out var num))
|
|
||||||
{
|
|
||||||
if (num == 1)
|
|
||||||
enable = true;
|
|
||||||
else if (num == 0)
|
|
||||||
enable = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!IoCManager.Resolve<IEntitySystemManager>().TryGetEntitySystem<SignalLinkerSystem>(out var system))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var ret = system.SignalLinkerKeybind(player.UserId, enable);
|
|
||||||
shell.WriteLine(ret ? "Enabled" : "Disabled");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,92 +0,0 @@
|
|||||||
using System.Collections.Generic;
|
|
||||||
using Content.Server.Interaction;
|
|
||||||
using Content.Server.MachineLinking.Components;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.Input;
|
|
||||||
using Robust.Shared.Input.Binding;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Network;
|
|
||||||
using Robust.Shared.Players;
|
|
||||||
|
|
||||||
namespace Content.Server.MachineLinking
|
|
||||||
{
|
|
||||||
public class SignalLinkerSystem : EntitySystem
|
|
||||||
{
|
|
||||||
private readonly Dictionary<NetUserId, SignalTransmitterComponent?> _transmitters = new();
|
|
||||||
|
|
||||||
public bool SignalLinkerKeybind(NetUserId id, bool? enable)
|
|
||||||
{
|
|
||||||
enable ??= !_transmitters.ContainsKey(id);
|
|
||||||
|
|
||||||
if (enable.Value)
|
|
||||||
{
|
|
||||||
if (_transmitters.ContainsKey(id))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_transmitters.Count == 0)
|
|
||||||
{
|
|
||||||
CommandBinds.Builder
|
|
||||||
.BindBefore(EngineKeyFunctions.Use,
|
|
||||||
new PointerInputCmdHandler(HandleUse),
|
|
||||||
typeof(InteractionSystem))
|
|
||||||
.Register<SignalLinkerSystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
_transmitters.Add(id, null);
|
|
||||||
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (!_transmitters.ContainsKey(id))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_transmitters.Remove(id);
|
|
||||||
if (_transmitters.Count == 0)
|
|
||||||
{
|
|
||||||
CommandBinds.Unregister<SignalLinkerSystem>();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return enable.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool HandleUse(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
|
|
||||||
{
|
|
||||||
if (session?.AttachedEntity == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_transmitters.TryGetValue(session.UserId, out var signalTransmitter))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EntityManager.TryGetEntity(uid, out var entity))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent<SignalReceiverComponent>(out var signalReceiver))
|
|
||||||
{
|
|
||||||
if (signalReceiver.Interact(session.AttachedEntity, signalTransmitter))
|
|
||||||
{
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (entity.TryGetComponent<SignalTransmitterComponent>(out var transmitter))
|
|
||||||
{
|
|
||||||
_transmitters[session.UserId] = transmitter.GetSignal(session.AttachedEntity);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
namespace Content.Server.MachineLinking.Signals
|
|
||||||
{
|
|
||||||
public struct ToggleSignal {}
|
|
||||||
}
|
|
||||||
23
Content.Server/MachineLinking/System/SignalButtonSystem.cs
Normal file
23
Content.Server/MachineLinking/System/SignalButtonSystem.cs
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.System
|
||||||
|
{
|
||||||
|
public class SignalButtonSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SignalButtonComponent, InteractHandEvent>(OnInteractHand);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteractHand(EntityUid uid, SignalButtonComponent component, InteractHandEvent args)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(uid, new InvokePortEvent("pressed"), false);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
296
Content.Server/MachineLinking/System/SignalLinkerSystem.cs
Normal file
296
Content.Server/MachineLinking/System/SignalLinkerSystem.cs
Normal file
@@ -0,0 +1,296 @@
|
|||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Content.Server.Hands.Components;
|
||||||
|
using Content.Server.Interaction;
|
||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Server.MachineLinking.Exceptions;
|
||||||
|
using Content.Server.MachineLinking.Models;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Power.EntitySystems;
|
||||||
|
using Content.Server.UserInterface;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using Content.Shared.Notification.Managers;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Localization;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.System
|
||||||
|
{
|
||||||
|
public class SignalLinkerSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private IComponentManager _componentManager = default!;
|
||||||
|
private InteractionSystem _interaction = default!;
|
||||||
|
|
||||||
|
private SignalLinkCollection _linkCollection = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_interaction = Get<InteractionSystem>();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SignalTransmitterComponent, ComponentStartup>(TransmitterStartupHandler);
|
||||||
|
SubscribeLocalEvent<SignalTransmitterComponent, InteractUsingEvent>(TransmitterInteractUsingHandler);
|
||||||
|
SubscribeLocalEvent<SignalTransmitterComponent, InvokePortEvent>(OnTransmitterInvokePort);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SignalReceiverComponent, ComponentStartup>(OnReceiverStartup);
|
||||||
|
SubscribeLocalEvent<SignalReceiverComponent, InteractUsingEvent>(OnReceiverInteractUsing);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SignalReceiverComponent, ComponentRemove>(OnReceiverRemoved);
|
||||||
|
SubscribeLocalEvent<SignalTransmitterComponent, ComponentRemove>(OnTransmitterRemoved);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTransmitterRemoved(EntityUid uid, SignalTransmitterComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
_linkCollection.RemoveLinks(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReceiverRemoved(EntityUid uid, SignalReceiverComponent component, ComponentRemove args)
|
||||||
|
{
|
||||||
|
_linkCollection.RemoveLinks(component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTransmitterInvokePort(EntityUid uid, SignalTransmitterComponent component, InvokePortEvent args)
|
||||||
|
{
|
||||||
|
if (!component.Outputs.TryGetPort(args.Port, out var port)) throw new PortNotFoundException();
|
||||||
|
|
||||||
|
if (args.Value == null)
|
||||||
|
{
|
||||||
|
if (port.Type != null && !port.Type.IsNullable()) throw new InvalidPortValueException();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (port.Type == null || !args.Value.GetType().IsAssignableTo(port.Type))
|
||||||
|
throw new InvalidPortValueException();
|
||||||
|
}
|
||||||
|
|
||||||
|
port.Signal = args.Value;
|
||||||
|
|
||||||
|
foreach (var link in _linkCollection.GetLinks(component, port.Name))
|
||||||
|
{
|
||||||
|
if (!IsInRange(component, link.ReceiverComponent)) continue;
|
||||||
|
|
||||||
|
RaiseLocalEvent(link.ReceiverComponent.Owner.Uid,
|
||||||
|
new SignalReceivedEvent(link.Receiverport.Name, args.Value));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReceiverInteractUsing(EntityUid uid, SignalReceiverComponent component, InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled) return;
|
||||||
|
|
||||||
|
if (!args.Used.TryGetComponent<SignalLinkerComponent>(out var linker) || !linker.Port.HasValue ||
|
||||||
|
!args.User.TryGetComponent(out ActorComponent? actor) ||
|
||||||
|
!linker.Port.Value.transmitter.Outputs.TryGetPort(linker.Port.Value.port, out var port))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Inputs.Count == 1)
|
||||||
|
{
|
||||||
|
LinkerInteraction(args.User, linker.Port.Value.transmitter, linker.Port.Value.port, component,
|
||||||
|
component.Inputs[0].Name);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bui = component.Owner.GetUIOrNull(SignalReceiverUiKey.Key);
|
||||||
|
if (bui == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
bui.Open(actor.PlayerSession);
|
||||||
|
bui.SetState(
|
||||||
|
new SignalPortsState(new Dictionary<string, bool>(component.Inputs.GetValidatedPorts(port.Type))));
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReceiverStartup(EntityUid uid, SignalReceiverComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (component.Owner.GetUIOrNull(SignalReceiverUiKey.Key) is { } ui)
|
||||||
|
ui.OnReceiveMessage += msg => OnReceiverUIMessage(uid, component, msg);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnReceiverUIMessage(EntityUid uid, SignalReceiverComponent component,
|
||||||
|
ServerBoundUserInterfaceMessage msg)
|
||||||
|
{
|
||||||
|
switch (msg.Message)
|
||||||
|
{
|
||||||
|
case SignalPortSelected portSelected:
|
||||||
|
if (msg.Session.AttachedEntity == null ||
|
||||||
|
!msg.Session.AttachedEntity.TryGetComponent(out HandsComponent? hands) ||
|
||||||
|
!hands.TryGetActiveHeldEntity(out var heldEntity) ||
|
||||||
|
!heldEntity.TryGetComponent(out SignalLinkerComponent? signalLinkerComponent) ||
|
||||||
|
!_interaction.InRangeUnobstructed(msg.Session.AttachedEntity, component.Owner) ||
|
||||||
|
!signalLinkerComponent.Port.HasValue ||
|
||||||
|
!signalLinkerComponent.Port.Value.transmitter.Outputs.ContainsPort(signalLinkerComponent.Port
|
||||||
|
.Value.port) || !component.Inputs.ContainsPort(portSelected.Port))
|
||||||
|
return;
|
||||||
|
LinkerInteraction(msg.Session.AttachedEntity, signalLinkerComponent.Port.Value.transmitter,
|
||||||
|
signalLinkerComponent.Port.Value.port, component, portSelected.Port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TransmitterStartupHandler(EntityUid uid, SignalTransmitterComponent component,
|
||||||
|
ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (component.Owner.GetUIOrNull(SignalTransmitterUiKey.Key) is { } ui)
|
||||||
|
ui.OnReceiveMessage += msg => OnTransmitterUIMessage(uid, component, msg);
|
||||||
|
|
||||||
|
foreach (var portPrototype in component.Outputs)
|
||||||
|
{
|
||||||
|
if (portPrototype.Type == null) continue;
|
||||||
|
|
||||||
|
var valueRequest = new SignalValueRequestedEvent(portPrototype.Name, portPrototype.Type);
|
||||||
|
RaiseLocalEvent(uid, valueRequest, false);
|
||||||
|
|
||||||
|
if (!valueRequest.Handled) throw new NoSignalValueProvidedException();
|
||||||
|
|
||||||
|
portPrototype.Signal = valueRequest.Signal;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnTransmitterUIMessage(EntityUid uid, SignalTransmitterComponent component,
|
||||||
|
ServerBoundUserInterfaceMessage msg)
|
||||||
|
{
|
||||||
|
switch (msg.Message)
|
||||||
|
{
|
||||||
|
case SignalPortSelected portSelected:
|
||||||
|
if (msg.Session.AttachedEntity == null ||
|
||||||
|
!msg.Session.AttachedEntity.TryGetComponent(out HandsComponent? hands) ||
|
||||||
|
!hands.TryGetActiveHeldEntity(out var heldEntity) ||
|
||||||
|
!heldEntity.TryGetComponent(out SignalLinkerComponent? signalLinkerComponent) ||
|
||||||
|
!_interaction.InRangeUnobstructed(msg.Session.AttachedEntity, component.Owner))
|
||||||
|
return;
|
||||||
|
LinkerSaveInteraction(msg.Session.AttachedEntity, signalLinkerComponent, component,
|
||||||
|
portSelected.Port);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TransmitterInteractUsingHandler(EntityUid uid, SignalTransmitterComponent component,
|
||||||
|
InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if (args.Handled) return;
|
||||||
|
|
||||||
|
if (!args.Used.TryGetComponent<SignalLinkerComponent>(out var linker) ||
|
||||||
|
!args.User.TryGetComponent(out ActorComponent? actor))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.Outputs.Count == 1)
|
||||||
|
{
|
||||||
|
var port = component.Outputs.First();
|
||||||
|
LinkerSaveInteraction(args.User, linker, component, port.Name);
|
||||||
|
args.Handled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var bui = component.Owner.GetUIOrNull(SignalTransmitterUiKey.Key);
|
||||||
|
if (bui == null) return;
|
||||||
|
bui.Open(actor.PlayerSession);
|
||||||
|
bui.SetState(new SignalPortsState(component.Outputs.GetPortStrings().ToArray()));
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LinkerInteraction(IEntity entity, SignalTransmitterComponent transmitter, string transmitterPort,
|
||||||
|
SignalReceiverComponent receiver, string receiverPort)
|
||||||
|
{
|
||||||
|
if (_linkCollection.LinkExists(transmitter, transmitterPort, receiver, receiverPort))
|
||||||
|
{
|
||||||
|
if (_linkCollection.RemoveLink(transmitter, transmitterPort, receiver, receiverPort))
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(receiver.Owner.Uid, new PortDisconnectedEvent(receiverPort));
|
||||||
|
RaiseLocalEvent(transmitter.Owner.Uid, new PortDisconnectedEvent(transmitterPort));
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-unlinked-port",
|
||||||
|
("port", receiverPort), ("machine", receiver)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var tport = transmitter.Outputs.GetPort(transmitterPort);
|
||||||
|
var rport = receiver.Inputs.GetPort(receiverPort);
|
||||||
|
|
||||||
|
if (!IsInRange(transmitter, receiver))
|
||||||
|
{
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-out-of-range"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tport.MaxConnections != 0 && tport.MaxConnections >= _linkCollection.LinkCount(transmitter))
|
||||||
|
{
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-max-connections-transmitter"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rport.MaxConnections != 0 && rport.MaxConnections <= _linkCollection.LinkCount(receiver))
|
||||||
|
{
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-max-connections-receiver"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tport.Type != rport.Type)
|
||||||
|
{
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-type-mismatch"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var linkAttempt = new LinkAttemptEvent(entity, transmitter, transmitterPort, receiver, receiverPort);
|
||||||
|
RaiseLocalEvent(receiver.Owner.Uid, linkAttempt);
|
||||||
|
RaiseLocalEvent(transmitter.Owner.Uid, linkAttempt);
|
||||||
|
|
||||||
|
if (linkAttempt.Cancelled) return;
|
||||||
|
|
||||||
|
var link = _linkCollection.AddLink(transmitter, transmitterPort, receiver, receiverPort);
|
||||||
|
if (link.Transmitterport.Signal != null)
|
||||||
|
RaiseLocalEvent(receiver.Owner.Uid,
|
||||||
|
new SignalReceivedEvent(receiverPort, link.Transmitterport.Signal));
|
||||||
|
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-linked-port", ("port", receiverPort),
|
||||||
|
("machine", receiver)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void LinkerSaveInteraction(IEntity entity, SignalLinkerComponent linkerComponent,
|
||||||
|
SignalTransmitterComponent transmitterComponent, string transmitterPort)
|
||||||
|
{
|
||||||
|
if (SavePortInSignalLinker(linkerComponent, transmitterComponent, transmitterPort))
|
||||||
|
{
|
||||||
|
entity.PopupMessageCursor(Loc.GetString("signal-linker-component-saved-port", ("port", transmitterPort),
|
||||||
|
("machine", transmitterComponent.Owner)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool SavePortInSignalLinker(SignalLinkerComponent linker, SignalTransmitterComponent transmitter,
|
||||||
|
string port)
|
||||||
|
{
|
||||||
|
if (!transmitter.Outputs.ContainsPort(port)) return false;
|
||||||
|
linker.Port = (transmitter, port);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool IsInRange(SignalTransmitterComponent transmitterComponent,
|
||||||
|
SignalReceiverComponent receiverComponent)
|
||||||
|
{
|
||||||
|
if (transmitterComponent.Owner.TryGetComponent<ApcPowerReceiverComponent>(
|
||||||
|
out var transmitterPowerReceiverComponent) &&
|
||||||
|
receiverComponent.Owner.TryGetComponent<ApcPowerReceiverComponent>(
|
||||||
|
out var receiverPowerReceiverComponent)
|
||||||
|
) //&& todo are they on the same powernet?
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return transmitterComponent.Owner.Transform.MapPosition.InRange(
|
||||||
|
receiverComponent.Owner.Transform.MapPosition, 30f);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
35
Content.Server/MachineLinking/System/SignalSwitchSystem.cs
Normal file
35
Content.Server/MachineLinking/System/SignalSwitchSystem.cs
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.System
|
||||||
|
{
|
||||||
|
public class SignalSwitchSystem : EntitySystem
|
||||||
|
{
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<SignalSwitchComponent, InteractHandEvent>(OnInteracted);
|
||||||
|
SubscribeLocalEvent<SignalSwitchComponent, SignalValueRequestedEvent>(OnSignalValueRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSignalValueRequested(EntityUid uid, SignalSwitchComponent component, SignalValueRequestedEvent args)
|
||||||
|
{
|
||||||
|
if (args.Port == "state")
|
||||||
|
{
|
||||||
|
args.Handled = true;
|
||||||
|
args.Signal = component.State;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteracted(EntityUid uid, SignalSwitchComponent component, InteractHandEvent args)
|
||||||
|
{
|
||||||
|
component.State = !component.State;
|
||||||
|
RaiseLocalEvent(uid, new InvokePortEvent("state", component.State), false);
|
||||||
|
RaiseLocalEvent(uid, new InvokePortEvent("stateChange"), false);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Content.Server/MachineLinking/System/TwoWayLeverSystem.cs
Normal file
54
Content.Server/MachineLinking/System/TwoWayLeverSystem.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using System;
|
||||||
|
using Content.Server.MachineLinking.Components;
|
||||||
|
using Content.Server.MachineLinking.Events;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.MachineLinking;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
|
||||||
|
namespace Content.Server.MachineLinking.System
|
||||||
|
{
|
||||||
|
public class TwoWayLeverSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IComponentManager _componentManager = default!;
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<TwoWayLeverComponent, InteractHandEvent>(OnInteractHand);
|
||||||
|
SubscribeLocalEvent<TwoWayLeverComponent, SignalValueRequestedEvent>(OnSignalValueRequested);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnSignalValueRequested(EntityUid uid, TwoWayLeverComponent component, SignalValueRequestedEvent args)
|
||||||
|
{
|
||||||
|
args.Signal = component.State;
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteractHand(EntityUid uid, TwoWayLeverComponent component, InteractHandEvent args)
|
||||||
|
{
|
||||||
|
component.State = component.State switch
|
||||||
|
{
|
||||||
|
TwoWayLeverSignal.Middle => component.NextSignalLeft ? TwoWayLeverSignal.Left : TwoWayLeverSignal.Right,
|
||||||
|
TwoWayLeverSignal.Right => TwoWayLeverSignal.Middle,
|
||||||
|
TwoWayLeverSignal.Left => TwoWayLeverSignal.Middle,
|
||||||
|
_ => throw new ArgumentOutOfRangeException()
|
||||||
|
};
|
||||||
|
|
||||||
|
if (component.State == TwoWayLeverSignal.Middle)
|
||||||
|
{
|
||||||
|
component.NextSignalLeft = !component.NextSignalLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_componentManager.TryGetComponent<AppearanceComponent>(uid, out var appearanceComponent))
|
||||||
|
{
|
||||||
|
appearanceComponent.SetData(TwoWayLeverVisuals.State, component.State);
|
||||||
|
}
|
||||||
|
|
||||||
|
RaiseLocalEvent(uid, new InvokePortEvent("state", component.State));
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@ namespace Content.Shared.MachineLinking
|
|||||||
public enum TwoWayLeverSignal : byte
|
public enum TwoWayLeverSignal : byte
|
||||||
{
|
{
|
||||||
Middle,
|
Middle,
|
||||||
Left,
|
Right,
|
||||||
Right
|
Left
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
17
Content.Shared/MachineLinking/UIKeys.cs
Normal file
17
Content.Shared/MachineLinking/UIKeys.cs
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
using System;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.MachineLinking
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum SignalTransmitterUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum SignalReceiverUiKey
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
}
|
||||||
37
Content.Shared/MachineLinking/UIMessages.cs
Normal file
37
Content.Shared/MachineLinking/UIMessages.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.MachineLinking
|
||||||
|
{
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class SignalPortsState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// A Dictionary containing all ports and wether or not they can be selected.
|
||||||
|
/// </summary>
|
||||||
|
public readonly Dictionary<string, bool> Ports;
|
||||||
|
|
||||||
|
public SignalPortsState(string[] ports) : this(ports.ToDictionary(s => s, _ => true))
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public SignalPortsState(Dictionary<string, bool> ports)
|
||||||
|
{
|
||||||
|
Ports = ports;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class SignalPortSelected : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly string Port;
|
||||||
|
|
||||||
|
public SignalPortSelected(string port)
|
||||||
|
{
|
||||||
|
Port = port;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Resources/Locale/en-US/conveyors/conveyor-component.ftl
Normal file
1
Resources/Locale/en-US/conveyors/conveyor-component.ftl
Normal file
@@ -0,0 +1 @@
|
|||||||
|
conveyor-component-failed-link = The port shocks you as you try to connect to it!
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
signal-button-component-transmit-signal-success-message = Click
|
|
||||||
signal-button-component-transmit-signal-no-receivers-message = No receivers connected.
|
|
||||||
@@ -0,0 +1,10 @@
|
|||||||
|
signal-linker-component-saved-port = Successfully saved port '{$port}' of {$machine}!
|
||||||
|
signal-linker-component-linked-port = Successfully linked saved port to port '{$port}' of {$machine}!
|
||||||
|
signal-linker-component-unlinked-port = Successfully unlinked saved port from port '{$port}' of {$machine}!
|
||||||
|
|
||||||
|
signal-linker-component-max-connections-receiver = Maximum connections reached on the receiver!
|
||||||
|
signal-linker-component-max-connections-transmitter = Maximum connections reached on the transmitter!
|
||||||
|
|
||||||
|
signal-linker-component-type-mismatch = The port's type does not match the type of the saved port!
|
||||||
|
|
||||||
|
signal-linker-component-out-of-range = Connection is out of range!
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
signal-receiver-component-interact-no-transmitter-message = Signal not set.
|
|
||||||
signal-receiver-component-interact-unlinked = Unlinked.
|
|
||||||
signal-receiver-component-interact-out-of-range = Out of range.
|
|
||||||
signal-receiver-component-interact-max-transmitters-limit = Max Transmitters reached!
|
|
||||||
signal-receiver-component-interact-success = Linked!
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
signal-switch-component-transmit-no-receivers-connected = No receivers connected.
|
|
||||||
|
|
||||||
# ToggleSwitchVerb
|
|
||||||
toggle-switch-verb-get-data-text = Toggle Switch
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
signal-transmitter-component-get-signal-success = Signal fetched.
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
signal-two-way-lever-component-next-state-no-receivers-connected = No receivers connected.
|
|
||||||
@@ -28,6 +28,12 @@
|
|||||||
softness: 1.1
|
softness: 1.1
|
||||||
offset: "0, -0.5"
|
offset: "0, -0.5"
|
||||||
- type: SignalReceiver
|
- type: SignalReceiver
|
||||||
|
inputs:
|
||||||
|
- name: toggle
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.SignalReceiverUiKey.Key
|
||||||
|
type: SignalPortSelectorBoundUserInterface
|
||||||
- type: Damageable
|
- type: Damageable
|
||||||
resistances: metallicResistances
|
resistances: metallicResistances
|
||||||
- type: Destructible
|
- type: Destructible
|
||||||
|
|||||||
@@ -12,6 +12,14 @@
|
|||||||
- type: SignalSwitch
|
- type: SignalSwitch
|
||||||
- type: Rotatable
|
- type: Rotatable
|
||||||
- type: SignalTransmitter
|
- type: SignalTransmitter
|
||||||
|
outputs:
|
||||||
|
- name: state
|
||||||
|
type: bool
|
||||||
|
- name: stateChange
|
||||||
|
- type: UserInterface
|
||||||
|
interfaces:
|
||||||
|
- key: enum.SignalTransmitterUiKey.Key
|
||||||
|
type: SignalPortSelectorBoundUserInterface
|
||||||
placement:
|
placement:
|
||||||
snap:
|
snap:
|
||||||
- Wallmount
|
- Wallmount
|
||||||
@@ -30,6 +38,8 @@
|
|||||||
- type: SignalButton
|
- type: SignalButton
|
||||||
- type: Rotatable
|
- type: Rotatable
|
||||||
- type: SignalTransmitter
|
- type: SignalTransmitter
|
||||||
|
outputs:
|
||||||
|
- name: pressed
|
||||||
placement:
|
placement:
|
||||||
snap:
|
snap:
|
||||||
- Wallmount
|
- Wallmount
|
||||||
|
|||||||
@@ -26,7 +26,10 @@
|
|||||||
state: conveyor_started_cw
|
state: conveyor_started_cw
|
||||||
drawdepth: FloorObjects
|
drawdepth: FloorObjects
|
||||||
- type: SignalReceiver
|
- type: SignalReceiver
|
||||||
maxTransmitters: 1
|
inputs:
|
||||||
|
- name: state
|
||||||
|
type: Content.Shared.MachineLinking.TwoWayLeverSignal
|
||||||
|
maxConnections: 1
|
||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
- type: Conveyor
|
- type: Conveyor
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
@@ -69,6 +72,10 @@
|
|||||||
sprite: Structures/conveyor.rsi
|
sprite: Structures/conveyor.rsi
|
||||||
state: switch-off
|
state: switch-off
|
||||||
- type: TwoWayLever
|
- type: TwoWayLever
|
||||||
|
- type: SignalTransmitter
|
||||||
|
outputs:
|
||||||
|
- name: state
|
||||||
|
type: Content.Shared.MachineLinking.TwoWayLeverSignal
|
||||||
- type: Appearance
|
- type: Appearance
|
||||||
visuals:
|
visuals:
|
||||||
- type: TwoWayLeverVisualizer
|
- type: TwoWayLeverVisualizer
|
||||||
|
|||||||
Reference in New Issue
Block a user