diff --git a/Content.Client/Entry/IgnoredComponents.cs b/Content.Client/Entry/IgnoredComponents.cs index 973e9bd0cf..997bffad24 100644 --- a/Content.Client/Entry/IgnoredComponents.cs +++ b/Content.Client/Entry/IgnoredComponents.cs @@ -197,7 +197,6 @@ namespace Content.Client.Entry "SignalReceiver", "SignalSwitch", "SignalTransmitter", - "SignalButton", "SignalLinker", "ItemCabinet", "FireExtinguisher", diff --git a/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml.cs b/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml.cs index 84d5bc2646..ee498019c5 100644 --- a/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml.cs +++ b/Content.Client/MachineLinking/UI/SignalPortSelectorMenu.xaml.cs @@ -5,6 +5,7 @@ using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.XAML; using Robust.Client.Graphics; +using Robust.Shared.Prototypes; namespace Content.Client.MachineLinking.UI { @@ -12,18 +13,20 @@ namespace Content.Client.MachineLinking.UI public sealed partial class SignalPortSelectorMenu : DefaultWindow { private SignalPortSelectorBoundUserInterface _bui; - private LinksRender links; + private LinksRender _links; - private ButtonGroup buttonGroup = new(); + private ButtonGroup _buttonGroup = new(); + private IPrototypeManager _protoMan; public SignalPortSelectorMenu(SignalPortSelectorBoundUserInterface boundUserInterface) { RobustXamlLoader.Load(this); _bui = boundUserInterface; - links = new(ButtonContainerLeft, ButtonContainerRight); - ContainerMiddle.AddChild(links); + _links = new(ButtonContainerLeft, ButtonContainerRight); + ContainerMiddle.AddChild(_links); ButtonClear.OnPressed += _ => _bui.OnClearPressed(); ButtonLinkDefault.OnPressed += _ => _bui.OnLinkDefaultPressed(); + _protoMan = IoCManager.Resolve(); } public void UpdateState(SignalPortsState state) @@ -32,7 +35,14 @@ namespace Content.Client.MachineLinking.UI ButtonContainerLeft.DisposeAllChildren(); foreach (var port in state.TransmitterPorts) { - var portButton = new Button() { Text = port, ToggleMode = true, Group = buttonGroup }; + var proto = _protoMan.Index(port); + var portButton = new Button() + { + Text = Loc.GetString(proto.Name), + ToolTip = Loc.GetString(proto.Description), + ToggleMode = true, + Group = _buttonGroup + }; portButton.OnPressed += _ => _bui.OnTransmitterPortSelected(port); ButtonContainerLeft.AddChild(portButton); } @@ -41,12 +51,19 @@ namespace Content.Client.MachineLinking.UI ButtonContainerRight.DisposeAllChildren(); foreach (var port in state.ReceiverPorts) { - var portButton = new Button() { Text = port, ToggleMode = true, Group = buttonGroup }; + var proto = _protoMan.Index(port); + var portButton = new Button() + { + Text = Loc.GetString(proto.Name), + ToolTip = Loc.GetString(proto.Description), + ToggleMode = true, + Group = _buttonGroup + }; portButton.OnPressed += _ => _bui.OnReceiverPortSelected(port); ButtonContainerRight.AddChild(portButton); } - links.Links = state.Links; + _links.Links = state.Links; } private sealed class LinksRender : Control diff --git a/Content.Client/Popups/PopupSystem.cs b/Content.Client/Popups/PopupSystem.cs index b027b48415..4875442f99 100644 --- a/Content.Client/Popups/PopupSystem.cs +++ b/Content.Client/Popups/PopupSystem.cs @@ -66,7 +66,7 @@ namespace Content.Client.Popups _userInterfaceManager.PopupRoot.AddChild(label); label.Measure(Vector2.Infinity); - var mapCoordinates = _eyeManager.ScreenToMap(coordinates.Position / label.UIScale - label.DesiredSize / 2); + var mapCoordinates = _eyeManager.ScreenToMap(coordinates.Position); label.InitialPos = mapCoordinates; LayoutContainer.SetPosition(label, label.InitialPos.Position); _aliveLabels.Add(label); @@ -204,7 +204,7 @@ namespace Content.Client.Popups Vector2 position; if (Entity == null) - position = InitialPos.Position; + position = _eyeManager.WorldToScreen(InitialPos.Position) / UIScale - DesiredSize / 2; else if (_entityManager.TryGetComponent(Entity.Value, out TransformComponent xform)) position = (_eyeManager.CoordinatesToScreen(xform.Coordinates).Position / UIScale) - DesiredSize / 2; else diff --git a/Content.Server/Conveyor/ConveyorComponent.cs b/Content.Server/Conveyor/ConveyorComponent.cs index 12ee830c72..657dc1f529 100644 --- a/Content.Server/Conveyor/ConveyorComponent.cs +++ b/Content.Server/Conveyor/ConveyorComponent.cs @@ -1,9 +1,6 @@ using Content.Shared.Conveyor; -using Robust.Shared.Analyzers; -using Robust.Shared.GameObjects; -using Robust.Shared.Maths; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Conveyor { @@ -30,5 +27,14 @@ namespace Content.Server.Conveyor /// [ViewVariables(VVAccess.ReadWrite)] public ConveyorState State; + + [DataField("forwardPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ForwardPort = "Forward"; + + [DataField("reversePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ReversePort = "Reverse"; + + [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OffPort = "Off"; } } diff --git a/Content.Server/Conveyor/ConveyorSystem.cs b/Content.Server/Conveyor/ConveyorSystem.cs index 1a9a3cbb2b..08411110f3 100644 --- a/Content.Server/Conveyor/ConveyorSystem.cs +++ b/Content.Server/Conveyor/ConveyorSystem.cs @@ -1,25 +1,19 @@ -using System; -using System.Collections.Generic; using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Events; +using Content.Server.MachineLinking.System; using Content.Server.Power.Components; using Content.Server.Recycling; using Content.Server.Recycling.Components; using Content.Shared.Conveyor; using Content.Shared.Item; -using Content.Shared.Movement.Components; -using Content.Shared.Popups; -using Robust.Shared.Containers; -using Robust.Shared.GameObjects; -using Robust.Shared.Localization; -using Robust.Shared.Maths; -using Robust.Shared.Physics; namespace Content.Server.Conveyor { public sealed class ConveyorSystem : EntitySystem { [Dependency] private RecyclerSystem _recycler = default!; + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + public override void Initialize() { base.Initialize(); @@ -31,13 +25,9 @@ namespace Content.Server.Conveyor private void OnInit(EntityUid uid, ConveyorComponent component, ComponentInit args) { - var receiver = EnsureComp(uid); - foreach (string port in Enum.GetNames()) - if (!receiver.Inputs.ContainsKey(port)) - receiver.AddPort(port); + _signalSystem.EnsureReceiverPorts(uid, component.ReversePort, component.ForwardPort, component.OffPort); } - private void OnPowerChanged(EntityUid uid, ConveyorComponent component, PowerChangedEvent args) { UpdateAppearance(component); @@ -60,8 +50,12 @@ namespace Content.Server.Conveyor private void OnSignalReceived(EntityUid uid, ConveyorComponent component, SignalReceivedEvent args) { - if (Enum.TryParse(args.Port, out ConveyorState state)) - SetState(component, state); + if (args.Port == component.OffPort) + SetState(component, ConveyorState.Off); + else if (args.Port == component.ForwardPort) + SetState(component, ConveyorState.Forward); + else if (args.Port == component.ReversePort) + SetState(component, ConveyorState.Reverse); } private void SetState(ConveyorComponent component, ConveyorState state) diff --git a/Content.Server/Explosion/Components/TriggerOnSignalComponent.cs b/Content.Server/Explosion/Components/TriggerOnSignalComponent.cs index cbb1db093d..2017e4fee8 100644 --- a/Content.Server/Explosion/Components/TriggerOnSignalComponent.cs +++ b/Content.Server/Explosion/Components/TriggerOnSignalComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.GameObjects; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.Explosion.Components { @@ -8,6 +9,7 @@ namespace Content.Server.Explosion.Components [RegisterComponent] public sealed class TriggerOnSignalComponent : Component { - public const string Port = "Trigger"; + [DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Port = "Trigger"; } -} \ No newline at end of file +} diff --git a/Content.Server/Explosion/EntitySystems/TriggerSystem.Signal.cs b/Content.Server/Explosion/EntitySystems/TriggerSystem.Signal.cs index c484c78c8d..f7787fdaa5 100644 --- a/Content.Server/Explosion/EntitySystems/TriggerSystem.Signal.cs +++ b/Content.Server/Explosion/EntitySystems/TriggerSystem.Signal.cs @@ -1,11 +1,14 @@ using Content.Server.Explosion.Components; using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.Components; +using Content.Server.MachineLinking.System; namespace Content.Server.Explosion.EntitySystems { public sealed partial class TriggerSystem { + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + private void InitializeSignal() { SubscribeLocalEvent(OnSignalReceived); @@ -14,16 +17,14 @@ namespace Content.Server.Explosion.EntitySystems private void OnSignalReceived(EntityUid uid, TriggerOnSignalComponent component, SignalReceivedEvent args) { - if (args.Port != TriggerOnSignalComponent.Port) + if (args.Port != component.Port) return; Trigger(uid); } private void OnInit(EntityUid uid, TriggerOnSignalComponent component, ComponentInit args) { - var receiver = EnsureComp(uid); - if (!receiver.Inputs.ContainsKey(TriggerOnSignalComponent.Port)) - receiver.AddPort(TriggerOnSignalComponent.Port); + _signalSystem.EnsureReceiverPorts(uid, component.Port); } } -} \ No newline at end of file +} diff --git a/Content.Server/Light/Components/PoweredLightComponent.cs b/Content.Server/Light/Components/PoweredLightComponent.cs index 9fb6fa71b6..98f807a2c1 100644 --- a/Content.Server/Light/Components/PoweredLightComponent.cs +++ b/Content.Server/Light/Components/PoweredLightComponent.cs @@ -10,6 +10,7 @@ using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; using Robust.Shared.Prototypes; using Robust.Shared.ViewVariables; +using Content.Shared.MachineLinking; namespace Content.Server.Light.Components { @@ -61,5 +62,14 @@ namespace Content.Server.Light.Components public TimeSpan LastThunk; [ViewVariables] public TimeSpan? LastGhostBlink; + + [DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OnPort = "On"; + + [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OffPort = "Off"; + + [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string TogglePort = "Toggle"; } } diff --git a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs index b46db6a51a..51ab820735 100644 --- a/Content.Server/Light/EntitySystems/PoweredLightSystem.cs +++ b/Content.Server/Light/EntitySystems/PoweredLightSystem.cs @@ -1,17 +1,15 @@ -using System; using Content.Server.Administration.Logs; using Content.Server.DeviceNetwork; using Content.Server.DeviceNetwork.Systems; using Content.Server.Ghost; using Content.Server.Light.Components; using Content.Server.MachineLinking.Events; -using Content.Server.MachineLinking.Components; +using Content.Server.MachineLinking.System; using Content.Server.Power.Components; using Content.Server.Temperature.Components; using Content.Shared.Audio; using Content.Shared.Damage; using Content.Shared.Database; -using Content.Shared.Hands.Components; using Content.Shared.Hands.EntitySystems; using Content.Shared.Interaction; using Content.Shared.Light; @@ -25,7 +23,7 @@ using Robust.Shared.Timing; namespace Content.Server.Light.EntitySystems { /// - /// System for the PoweredLightComponens + /// System for the PoweredLightComponents /// public sealed class PoweredLightSystem : EntitySystem { @@ -36,8 +34,11 @@ namespace Content.Server.Light.EntitySystems [Dependency] private readonly SharedPopupSystem _popupSystem = default!; [Dependency] private readonly AdminLogSystem _logSystem = default!; [Dependency] private readonly SharedHandsSystem _handsSystem = default!; + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + [Dependency] private readonly SharedContainerSystem _containerSystem = default!; private static readonly TimeSpan ThunkDelay = TimeSpan.FromSeconds(2); + public const string LightBulbContainer = "light_bulb"; public override void Initialize() { @@ -58,12 +59,8 @@ namespace Content.Server.Light.EntitySystems private void OnInit(EntityUid uid, PoweredLightComponent light, ComponentInit args) { - light.LightBulbContainer = light.Owner.EnsureContainer("light_bulb"); - - var receiver = EnsureComp(uid); - foreach (string port in new[] { "On", "Off", "Toggle" }) - if (!receiver.Inputs.ContainsKey(port)) - receiver.AddPort(port); + light.LightBulbContainer = _containerSystem.EnsureContainer(uid, LightBulbContainer); + _signalSystem.EnsureReceiverPorts(uid, light.OnPort, light.OffPort, light.TogglePort); } private void OnMapInit(EntityUid uid, PoweredLightComponent light, MapInitEvent args) @@ -335,12 +332,12 @@ namespace Content.Server.Light.EntitySystems private void OnSignalReceived(EntityUid uid, PoweredLightComponent component, SignalReceivedEvent args) { - switch (args.Port) - { - case "On": SetState(uid, true, component); break; - case "Off": SetState(uid, false, component); break; - case "Toggle": ToggleLight(uid, component); break; - } + if (args.Port == component.OffPort) + SetState(uid, false, component); + else if (args.Port == component.OnPort) + SetState(uid, true, component); + else if (args.Port == component.TogglePort) + ToggleLight(uid, component); } /// diff --git a/Content.Server/MachineLinking/Components/DoorSignalControlComponent.cs b/Content.Server/MachineLinking/Components/DoorSignalControlComponent.cs index 3100e61157..1d5b92f243 100644 --- a/Content.Server/MachineLinking/Components/DoorSignalControlComponent.cs +++ b/Content.Server/MachineLinking/Components/DoorSignalControlComponent.cs @@ -1,9 +1,18 @@ -using Robust.Shared.GameObjects; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.MachineLinking.Components { [RegisterComponent] public sealed class DoorSignalControlComponent : Component { + [DataField("openPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OpenPort = "Open"; + + [DataField("closePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string ClosePort = "Close"; + + [DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string TogglePort = "Toggle"; } } diff --git a/Content.Server/MachineLinking/Components/SignalButtonComponent.cs b/Content.Server/MachineLinking/Components/SignalButtonComponent.cs deleted file mode 100644 index cd2226e2d5..0000000000 --- a/Content.Server/MachineLinking/Components/SignalButtonComponent.cs +++ /dev/null @@ -1,9 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.MachineLinking.Components -{ - [RegisterComponent] - public sealed class SignalButtonComponent : Component - { - } -} diff --git a/Content.Server/MachineLinking/Components/SignalReceiverComponent.cs b/Content.Server/MachineLinking/Components/SignalReceiverComponent.cs index 8fde62a003..a690833375 100644 --- a/Content.Server/MachineLinking/Components/SignalReceiverComponent.cs +++ b/Content.Server/MachineLinking/Components/SignalReceiverComponent.cs @@ -1,23 +1,12 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; +using Content.Server.MachineLinking.System; namespace Content.Server.MachineLinking.Components { [RegisterComponent] + [Friend(typeof(SignalLinkerSystem))] public sealed class SignalReceiverComponent : Component { [DataField("inputs")] - private Dictionary> _inputs = new(); - - public void AddPort(string name) - { - _inputs.Add(name, new()); - } - - [ViewVariables] - public IReadOnlyDictionary> Inputs => _inputs; + public Dictionary> Inputs = new(); } } diff --git a/Content.Server/MachineLinking/Components/SignalSwitchComponent.cs b/Content.Server/MachineLinking/Components/SignalSwitchComponent.cs index c8b8aa8286..ec79d083eb 100644 --- a/Content.Server/MachineLinking/Components/SignalSwitchComponent.cs +++ b/Content.Server/MachineLinking/Components/SignalSwitchComponent.cs @@ -1,10 +1,28 @@ -using Robust.Shared.GameObjects; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.MachineLinking.Components { + /// + /// Simple switch that will fire ports when toggled on or off. A button is jsut a switch that signals on the + /// same port regardless of its state. + /// [RegisterComponent] public sealed class SignalSwitchComponent : Component { + /// + /// The port that gets signaled when the switch turns on. + /// + [DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OnPort = "On"; + + /// + /// The port that gets signaled when the switch turns off. + /// + [DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string OffPort = "Off"; + + [DataField("state")] public bool State; } } diff --git a/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs b/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs index 61d00e5de4..13e80d6628 100644 --- a/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs +++ b/Content.Server/MachineLinking/Components/SignalTransmitterComponent.cs @@ -1,8 +1,3 @@ -using System; -using System.Collections.Generic; -using Robust.Shared.GameObjects; -using Robust.Shared.Serialization.Manager.Attributes; -using Robust.Shared.ViewVariables; using Content.Server.MachineLinking.System; namespace Content.Server.MachineLinking.Components @@ -24,6 +19,7 @@ namespace Content.Server.MachineLinking.Components } [RegisterComponent] + [Friend(typeof(SignalLinkerSystem))] public sealed class SignalTransmitterComponent : Component { /// @@ -35,14 +31,6 @@ namespace Content.Server.MachineLinking.Components public float TransmissionRange = 30f; [DataField("outputs")] - private Dictionary> _outputs = new(); - - [ViewVariables] - public IReadOnlyDictionary> Outputs => _outputs; - - public void AddPort(string name) - { - _outputs.Add(name, new()); - } + public Dictionary> Outputs = new(); } } diff --git a/Content.Server/MachineLinking/Components/SignallerComponent.cs b/Content.Server/MachineLinking/Components/SignallerComponent.cs index 5397eeb30d..e4d7140136 100644 --- a/Content.Server/MachineLinking/Components/SignallerComponent.cs +++ b/Content.Server/MachineLinking/Components/SignallerComponent.cs @@ -1,4 +1,5 @@ -using Robust.Shared.GameObjects; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.MachineLinking.Components { @@ -8,6 +9,10 @@ namespace Content.Server.MachineLinking.Components [RegisterComponent] public sealed class SignallerComponent : Component { - public const string Port = "Pressed"; + /// + /// The port that gets signaled when the switch turns on. + /// + [DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string Port = "Pressed"; } } diff --git a/Content.Server/MachineLinking/Components/TwoWayLeverComponent.cs b/Content.Server/MachineLinking/Components/TwoWayLeverComponent.cs index 3ce05f314b..7420fb191d 100644 --- a/Content.Server/MachineLinking/Components/TwoWayLeverComponent.cs +++ b/Content.Server/MachineLinking/Components/TwoWayLeverComponent.cs @@ -1,13 +1,24 @@ -using Content.Shared.MachineLinking; -using Robust.Shared.GameObjects; +using Content.Shared.MachineLinking; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype; namespace Content.Server.MachineLinking.Components { [RegisterComponent] public sealed class TwoWayLeverComponent : Component { + [DataField("state")] public TwoWayLeverState State; + [DataField("nextSignalLeft")] public bool NextSignalLeft; + + [DataField("leftPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string LeftPort = "Left"; + + [DataField("rightPort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string RightPort = "Right"; + + [DataField("middlePort", customTypeSerializer: typeof(PrototypeIdSerializer))] + public string MiddlePort = "Middle"; } } diff --git a/Content.Server/MachineLinking/Events/InvokePortEvent.cs b/Content.Server/MachineLinking/Events/InvokePortEvent.cs deleted file mode 100644 index cf690311a1..0000000000 --- a/Content.Server/MachineLinking/Events/InvokePortEvent.cs +++ /dev/null @@ -1,14 +0,0 @@ -using Robust.Shared.GameObjects; - -namespace Content.Server.MachineLinking.Events -{ - public sealed class InvokePortEvent : EntityEventArgs - { - public readonly string Port; - - public InvokePortEvent(string port) - { - Port = port; - } - } -} diff --git a/Content.Server/MachineLinking/System/DoorSignalControlSystem.cs b/Content.Server/MachineLinking/System/DoorSignalControlSystem.cs index 0928c0f9c3..e8b2f17c3c 100644 --- a/Content.Server/MachineLinking/System/DoorSignalControlSystem.cs +++ b/Content.Server/MachineLinking/System/DoorSignalControlSystem.cs @@ -2,9 +2,7 @@ using Content.Server.MachineLinking.Components; using Content.Server.MachineLinking.Events; using Content.Server.Doors.Systems; using Content.Shared.Doors.Components; -using Content.Shared.Interaction; using JetBrains.Annotations; -using Robust.Shared.GameObjects; namespace Content.Server.MachineLinking.System { @@ -12,6 +10,8 @@ namespace Content.Server.MachineLinking.System public sealed class DoorSignalControlSystem : EntitySystem { [Dependency] private readonly DoorSystem _doorSystem = default!; + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + public override void Initialize() { base.Initialize(); @@ -21,20 +21,27 @@ namespace Content.Server.MachineLinking.System private void OnInit(EntityUid uid, DoorSignalControlComponent component, ComponentInit args) { - var receiver = EnsureComp(uid); - foreach (string port in new[] { "Open", "Close", "Toggle" }) - if (!receiver.Inputs.ContainsKey(port)) - receiver.AddPort(port); + _signalSystem.EnsureReceiverPorts(uid, component.OpenPort, component.ClosePort, component.TogglePort); } private void OnSignalReceived(EntityUid uid, DoorSignalControlComponent component, SignalReceivedEvent args) { - if (!TryComp(uid, out DoorComponent? door)) return; - switch (args.Port) + if (!TryComp(uid, out DoorComponent? door)) + return; + + if (args.Port == component.OpenPort) { - case "Open": if (door.State != DoorState.Open) _doorSystem.TryOpen(uid, door); break; - case "Close": if (door.State != DoorState.Closed) _doorSystem.TryClose(uid, door); break; - case "Toggle": _doorSystem.TryToggleDoor(uid); break; + if (door.State != DoorState.Open) + _doorSystem.TryOpen(uid, door); + } + else if (args.Port == component.ClosePort) + { + if (door.State != DoorState.Closed) + _doorSystem.TryClose(uid, door); + } + else if (args.Port == component.TogglePort) + { + _doorSystem.TryToggleDoor(uid, door); } } } diff --git a/Content.Server/MachineLinking/System/SignalButtonSystem.cs b/Content.Server/MachineLinking/System/SignalButtonSystem.cs deleted file mode 100644 index 6afbeb2157..0000000000 --- a/Content.Server/MachineLinking/System/SignalButtonSystem.cs +++ /dev/null @@ -1,33 +0,0 @@ -using Content.Server.MachineLinking.Components; -using Content.Server.MachineLinking.Events; -using Content.Shared.Interaction; -using JetBrains.Annotations; -using Robust.Shared.GameObjects; -using Content.Shared.Interaction.Events; - -namespace Content.Server.MachineLinking.System -{ - [UsedImplicitly] - public sealed class SignalButtonSystem : EntitySystem - { - public override void Initialize() - { - base.Initialize(); - SubscribeLocalEvent(OnInit); - SubscribeLocalEvent(OnActivated); - } - - private void OnInit(EntityUid uid, SignalButtonComponent component, ComponentInit args) - { - var transmitter = EnsureComp(uid); - if (!transmitter.Outputs.ContainsKey("Pressed")) - transmitter.AddPort("Pressed"); - } - - private void OnActivated(EntityUid uid, SignalButtonComponent component, ActivateInWorldEvent args) - { - RaiseLocalEvent(uid, new InvokePortEvent("Pressed"), false); - args.Handled = true; - } - } -} diff --git a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs index 478dd42f2d..8e9ff6abee 100644 --- a/Content.Server/MachineLinking/System/SignalLinkerSystem.cs +++ b/Content.Server/MachineLinking/System/SignalLinkerSystem.cs @@ -7,9 +7,9 @@ using Content.Shared.Interaction; using Content.Shared.MachineLinking; using Content.Shared.Popups; using Robust.Server.GameObjects; -using Robust.Shared.Utility; using Robust.Shared.Player; using Content.Shared.Verbs; +using Robust.Shared.Prototypes; using Content.Shared.MachineLinking.Events; namespace Content.Server.MachineLinking.System @@ -18,24 +18,12 @@ namespace Content.Server.MachineLinking.System { [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly SharedPopupSystem _popupSystem = default!; - - private static readonly (string, string)[][] _defaultMappings = - { - new [] { ("Pressed", "Toggle") }, - new [] { ("On", "On"), ("Off", "Off") }, - new [] { ("On", "Open"), ("Off", "Close") }, - new [] { ("On", "Forward"), ("Off", "Off") }, - new [] { ("Left", "On"), ("Right", "On"), ("Middle", "Off") }, - new [] { ("Left", "Open"), ("Right", "Open"), ("Middle", "Close") }, - new [] { ("Left", "Forward"), ("Right", "Reverse"), ("Middle", "Off") }, - }; + [Dependency] private readonly IPrototypeManager _protoMan = default!; public override void Initialize() { base.Initialize(); - SubscribeLocalEvent(OnTransmitterInvokePort); - SubscribeLocalEvent(OnTransmitterStartup); SubscribeLocalEvent(OnTransmitterRemoved); SubscribeLocalEvent(OnTransmitterInteractUsing); @@ -52,6 +40,27 @@ namespace Content.Server.MachineLinking.System SubscribeLocalEvent(OnLinkerUIClosed); } + /// + /// Convenience function to add several ports to an entity. + /// + public void EnsureReceiverPorts(EntityUid uid, params string[] ports) + { + var comp = EnsureComp(uid); + foreach (var port in ports) + { + comp.Inputs.TryAdd(port, new()); + } + } + + public void EnsureTransmitterPorts(EntityUid uid, params string[] ports) + { + var comp = EnsureComp(uid); + foreach (var port in ports) + { + comp.Outputs.TryAdd(port, new()); + } + } + /// /// Add an alt-click verb to allow users to link the default ports, without needing to open the UI. /// @@ -117,9 +126,15 @@ namespace Content.Server.MachineLinking.System verb.Message = Loc.GetString("signal-linking-verb-disabled-no-receiver"); } - private void OnTransmitterInvokePort(EntityUid uid, SignalTransmitterComponent component, InvokePortEvent args) + public void InvokePort(EntityUid uid, string port, SignalTransmitterComponent? component = null) { - foreach (var receiver in component.Outputs[args.Port]) + if (!Resolve(uid, ref component)) + return; + + if (!component.Outputs.TryGetValue(port, out var receivers)) + return; + + foreach (var receiver in receivers) RaiseLocalEvent(receiver.Uid, new SignalReceivedEvent(receiver.Port), false); } @@ -264,10 +279,18 @@ namespace Content.Server.MachineLinking.System private bool TryLink(SignalTransmitterComponent transmitter, SignalReceiverComponent receiver, SignalPortSelected args, EntityUid user, bool quiet = false, bool checkRange = true) { - if (!transmitter.Outputs.TryGetValue(args.TransmitterPort, out var receivers) || - !receiver.Inputs.TryGetValue(args.ReceiverPort, out var transmitters)) + if (!transmitter.Outputs.TryGetValue(args.TransmitterPort, out var linkedReceivers) || + !receiver.Inputs.TryGetValue(args.ReceiverPort, out var linkedTransmitters)) return false; + // Does the link already exist? Under the assumption that nothing has broken, lets only check the + // transmitter ports. + foreach (var identifier in linkedTransmitters) + { + if (identifier.Uid == transmitter.Owner && identifier.Port == args.TransmitterPort) + return true; + } + if (checkRange && !IsInRange(transmitter, receiver)) { if (!quiet) @@ -295,12 +318,12 @@ namespace Content.Server.MachineLinking.System return false; } - receivers.Add(new(receiver.Owner, args.ReceiverPort)); - transmitters.Add(new(transmitter.Owner, args.TransmitterPort)); + linkedReceivers.Add(new(receiver.Owner, args.ReceiverPort)); + linkedTransmitters.Add(new(transmitter.Owner, args.TransmitterPort)); if (!quiet) _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-linked-port", - ("machine1", transmitter.Owner), ("port1", args.TransmitterPort), - ("machine2", receiver.Owner), ("port2", args.ReceiverPort)), + ("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)), + ("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))), Filter.Entities(user)); return true; @@ -326,8 +349,8 @@ namespace Content.Server.MachineLinking.System RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(args.ReceiverPort)); RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(args.TransmitterPort)); _popupSystem.PopupCursor(Loc.GetString("signal-linker-component-unlinked-port", - ("machine1", transmitter.Owner), ("port1", args.TransmitterPort), - ("machine2", receiver.Owner), ("port2", args.ReceiverPort)), + ("machine1", transmitter.Owner), ("port1", PortName(args.TransmitterPort)), + ("machine2", receiver.Owner), ("port2", PortName(args.ReceiverPort))), Filter.Entities(attached)); } else @@ -343,6 +366,19 @@ namespace Content.Server.MachineLinking.System TryUpdateUI(linker, transmitter, receiver); } + /// + /// Convenience function to retrieve the name of a port prototype. + /// + /// + /// + public string PortName(string port) where TPort : MachinePortPrototype, IPrototype + { + if (!_protoMan.TryIndex(port, out var proto)) + return port; + + return Loc.GetString(proto.Name); + } + private void OnLinkerClearSelected(EntityUid uid, SignalLinkerComponent linker, LinkerClearSelected args) { if (!TryComp(linker.SavedTransmitter, out SignalTransmitterComponent? transmitter) || @@ -388,19 +424,27 @@ namespace Content.Server.MachineLinking.System var allLinksSucceeded = true; - if (_defaultMappings.TryFirstOrDefault(map => !map.ExceptBy(transmitter.Outputs.Keys, item => item.Item1).Any() && - !map.ExceptBy(receiver.Inputs.Keys, item => item.Item2).Any(), out var mapping)) + // First, disconnect existing links. + foreach (var (port, receivers) in transmitter.Outputs) + if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) + RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port)); + + foreach (var (port, transmitters) in receiver.Inputs) + if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) + RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port)); + + // Then make any valid default connections. + foreach (var outPort in transmitter.Outputs.Keys) { - foreach (var (port, receivers) in transmitter.Outputs) - if (receivers.RemoveAll(id => id.Uid == receiver.Owner) > 0) - RaiseLocalEvent(transmitter.Owner, new PortDisconnectedEvent(port)); + var prototype = _protoMan.Index(outPort); + if (prototype.DefaultLinks == null) + continue; - foreach (var (port, transmitters) in receiver.Inputs) - if (transmitters.RemoveAll(id => id.Uid == transmitter.Owner) > 0) - RaiseLocalEvent(receiver.Owner, new PortDisconnectedEvent(port)); - - foreach (var (t, r) in mapping) - allLinksSucceeded &= !TryLink(transmitter, receiver, new(t, r), user, quiet: true, checkRange: false); + foreach (var inPort in prototype.DefaultLinks) + { + if (receiver.Inputs.ContainsKey(inPort)) + allLinksSucceeded &= TryLink(transmitter, receiver, new(outPort, inPort), user, quiet: true, checkRange: false); + } } return allLinksSucceeded; diff --git a/Content.Server/MachineLinking/System/SignalSwitchSystem.cs b/Content.Server/MachineLinking/System/SignalSwitchSystem.cs index e3dab5e2f1..b254bd2f2c 100644 --- a/Content.Server/MachineLinking/System/SignalSwitchSystem.cs +++ b/Content.Server/MachineLinking/System/SignalSwitchSystem.cs @@ -1,32 +1,33 @@ using Content.Server.MachineLinking.Components; -using Content.Server.MachineLinking.Events; using Content.Shared.Interaction; -using Robust.Shared.GameObjects; namespace Content.Server.MachineLinking.System { public sealed class SignalSwitchSystem : EntitySystem { + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + public override void Initialize() { base.Initialize(); + SubscribeLocalEvent(OnInit); SubscribeLocalEvent(OnActivated); } private void OnInit(EntityUid uid, SignalSwitchComponent component, ComponentInit args) { - var transmitter = EnsureComp(uid); - foreach (string port in new[] { "On", "Off" }) - if (!transmitter.Outputs.ContainsKey(port)) - transmitter.AddPort(port); - + _signalSystem.EnsureTransmitterPorts(uid, component.OnPort, component.OffPort); } private void OnActivated(EntityUid uid, SignalSwitchComponent component, ActivateInWorldEvent args) { + if (args.Handled) + return; + component.State = !component.State; - RaiseLocalEvent(uid, new InvokePortEvent(component.State ? "On" : "Off"), false); + _signalSystem.InvokePort(uid, component.State ? component.OnPort : component.OffPort); + args.Handled = true; } } diff --git a/Content.Server/MachineLinking/System/SignallerSystem.cs b/Content.Server/MachineLinking/System/SignallerSystem.cs index c839732e2b..3637119958 100644 --- a/Content.Server/MachineLinking/System/SignallerSystem.cs +++ b/Content.Server/MachineLinking/System/SignallerSystem.cs @@ -1,8 +1,5 @@ using Content.Server.MachineLinking.Components; -using Content.Server.MachineLinking.Events; -using Content.Shared.Interaction; using JetBrains.Annotations; -using Robust.Shared.GameObjects; using Content.Shared.Interaction.Events; namespace Content.Server.MachineLinking.System @@ -10,6 +7,7 @@ namespace Content.Server.MachineLinking.System [UsedImplicitly] public sealed class SignallerSystem : EntitySystem { + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; public override void Initialize() { base.Initialize(); @@ -19,17 +17,15 @@ namespace Content.Server.MachineLinking.System private void OnInit(EntityUid uid, SignallerComponent component, ComponentInit args) { - var transmitter = EnsureComp(uid); - if (!transmitter.Outputs.ContainsKey(SignallerComponent.Port)) - transmitter.AddPort(SignallerComponent.Port); + _signalSystem.EnsureTransmitterPorts(uid, component.Port); } private void OnUseInHand(EntityUid uid, SignallerComponent component, UseInHandEvent args) { if (args.Handled) return; - RaiseLocalEvent(uid, new InvokePortEvent(SignallerComponent.Port), false); + _signalSystem.InvokePort(uid, component.Port); args.Handled = true; } } -} \ No newline at end of file +} diff --git a/Content.Server/MachineLinking/System/TwoWayLeverSystem.cs b/Content.Server/MachineLinking/System/TwoWayLeverSystem.cs index 944f3ca961..49ca1a3b24 100644 --- a/Content.Server/MachineLinking/System/TwoWayLeverSystem.cs +++ b/Content.Server/MachineLinking/System/TwoWayLeverSystem.cs @@ -1,14 +1,14 @@ using System; using Content.Server.MachineLinking.Components; -using Content.Server.MachineLinking.Events; using Content.Shared.Interaction; using Content.Shared.MachineLinking; -using Robust.Shared.GameObjects; namespace Content.Server.MachineLinking.System { public sealed class TwoWayLeverSystem : EntitySystem { + [Dependency] private readonly SignalLinkerSystem _signalSystem = default!; + public override void Initialize() { base.Initialize(); @@ -18,14 +18,14 @@ namespace Content.Server.MachineLinking.System private void OnInit(EntityUid uid, TwoWayLeverComponent component, ComponentInit args) { - var transmitter = EnsureComp(uid); - foreach (string state in Enum.GetNames()) - if (!transmitter.Outputs.ContainsKey(state)) - transmitter.AddPort(state); + _signalSystem.EnsureTransmitterPorts(uid, component.LeftPort, component.RightPort, component.MiddlePort); } private void OnActivated(EntityUid uid, TwoWayLeverComponent component, ActivateInWorldEvent args) { + if (args.Handled) + return; + component.State = component.State switch { TwoWayLeverState.Middle => component.NextSignalLeft ? TwoWayLeverState.Left : TwoWayLeverState.Right, @@ -35,16 +35,20 @@ namespace Content.Server.MachineLinking.System }; if (component.State == TwoWayLeverState.Middle) - { component.NextSignalLeft = !component.NextSignalLeft; - } - if (EntityManager.TryGetComponent(uid, out var appearanceComponent)) - { + if (TryComp(uid, out AppearanceComponent? appearanceComponent)) appearanceComponent.SetData(TwoWayLeverVisuals.State, component.State); - } - RaiseLocalEvent(uid, new InvokePortEvent(component.State.ToString())); + var port = component.State switch + { + TwoWayLeverState.Left => component.LeftPort, + TwoWayLeverState.Right => component.RightPort, + TwoWayLeverState.Middle => component.MiddlePort, + _ => throw new ArgumentOutOfRangeException() + }; + + _signalSystem.InvokePort(uid, port); args.Handled = true; } } diff --git a/Content.Shared/MachineLinking/MachinePortPrototype.cs b/Content.Shared/MachineLinking/MachinePortPrototype.cs new file mode 100644 index 0000000000..7607159222 --- /dev/null +++ b/Content.Shared/MachineLinking/MachinePortPrototype.cs @@ -0,0 +1,44 @@ +using Robust.Shared.Prototypes; +using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set; + +namespace Content.Shared.MachineLinking; + +/// +/// A prototype for a machine port, for use with machine linking. +/// +public abstract class MachinePortPrototype +{ + /// + /// Localization string for the port name. Displayed in the linking UI. + /// + [DataField("name", required:true)] + public string Name = default!; + + /// + /// Localization string for a description of the ports functionality. Should either indicate when a transmitter + /// port is fired, or what function a receiver port serves. Displayed as a tooltip in the linking UI. + /// + [DataField("description", required: true)] + public string Description = default!; +} + +[Prototype("receiverPort")] +public sealed class ReceiverPortPrototype : MachinePortPrototype, IPrototype +{ + [IdDataField] + public string ID { get; } = default!; +} + +[Prototype("transmitterPort")] +public sealed class TransmitterPortPrototype : MachinePortPrototype, IPrototype +{ + [IdDataField] + public string ID { get; } = default!; + + /// + /// This is a set of receiver ports that this transmitter port will attempt to link to when using the + /// default-link functionality. + /// + [DataField("defaultLinks", customTypeSerializer: typeof(PrototypeIdHashSetSerializer))] + public HashSet? DefaultLinks; +} diff --git a/Resources/Locale/en-US/machine-linking/receiver_ports.ftl b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl new file mode 100644 index 0000000000..1cb2deb31f --- /dev/null +++ b/Resources/Locale/en-US/machine-linking/receiver_ports.ftl @@ -0,0 +1,23 @@ +signal-port-name-toggle = Toggle +signal-port-description-toggle = Toggles the state of a device. + +signal-port-name-on-receiver = On +signal-port-description-on-receiver = Turns a device on. + +signal-port-name-off-receiver = Off +signal-port-description-off-receiver = Turns a device off. + +signal-port-name-forward = Forward +signal-port-description-forward = Makes a device (e.g. conveyer) operate in the normal direction. + +signal-port-name-reverse = Reverse +signal-port-description-reverse = Makes a device (e.g. conveyer) operate in the reverse direction. + +signal-port-name-open = Open +signal-port-description-open = Opens a device. + +signal-port-name-close = Close +signal-port-description-close = Closes a device. + +signal-port-name-trigger = Trigger +signal-port-description-trigger = Triggers some mechanism on the device. \ No newline at end of file diff --git a/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl new file mode 100644 index 0000000000..db0599cb77 --- /dev/null +++ b/Resources/Locale/en-US/machine-linking/transmitter_ports.ftl @@ -0,0 +1,17 @@ +signal-port-name-pressed = Pressed +signal-port-description-pressed = This port is invoked whenever the transmitter is activated. + +signal-port-name-on-transmitter = On +signal-port-description-on-transmitter = This port is invoked whenever the transmitter is turned on. + +signal-port-name-off-transmitter = Off +signal-port-description-off-transmitter = This port is invoked whenever the transmitter is turned off. + +signal-port-name-left = Left +signal-port-description-left = This port is invoked whenever the lever is moved to the leftmost position. + +signal-port-name-right = Right +signal-port-description-right = This port is invoked whenever the lever is moved to the rightmost position. + +signal-port-name-middle = Middle +signal-port-description-middle = This port is invoked whenever the lever is moved to the neutral position. diff --git a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml index 8520d28fc4..93ab99751c 100644 --- a/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml +++ b/Resources/Prototypes/Entities/Structures/Wallmounts/switch.yml @@ -40,9 +40,11 @@ - type: Sprite sprite: Structures/Wallmounts/switch.rsi state: dead - - type: SignalButton - type: UseDelay delay: 0.5 # prevent light-toggling auto-clickers. + - type: SignalSwitch + onPort: Pressed + offPort: Pressed - type: Rotatable - type: Construction graph: SignalButtonGraph diff --git a/Resources/Prototypes/MachineLinking/receiver_ports.yml b/Resources/Prototypes/MachineLinking/receiver_ports.yml new file mode 100644 index 0000000000..298e7fbbda --- /dev/null +++ b/Resources/Prototypes/MachineLinking/receiver_ports.yml @@ -0,0 +1,39 @@ +- type: receiverPort + id: Toggle + name: signal-port-name-toggle + description: signal-port-description-toggle + +- type: receiverPort + id: On + name: signal-port-name-on-receiver + description: signal-port-description-on-receiver + +- type: receiverPort + id: Off + name: signal-port-name-off-receiver + description: signal-port-description-off-receiver + +- type: receiverPort + id: Forward + name: signal-port-name-forward + description: signal-port-description-forward + +- type: receiverPort + id: Reverse + name: signal-port-name-reverse + description: signal-port-description-reverse + +- type: receiverPort + id: Open + name: signal-port-name-open + description: signal-port-description-open + +- type: receiverPort + id: Close + name: signal-port-name-close + description: signal-port-description-close + +- type: receiverPort + id: Trigger + name: signal-port-name-trigger + description: signal-port-description-trigger \ No newline at end of file diff --git a/Resources/Prototypes/MachineLinking/transmitter_ports.yml b/Resources/Prototypes/MachineLinking/transmitter_ports.yml new file mode 100644 index 0000000000..3e908ee035 --- /dev/null +++ b/Resources/Prototypes/MachineLinking/transmitter_ports.yml @@ -0,0 +1,35 @@ +- type: transmitterPort + id: Pressed + name: signal-port-name-pressed + description: signal-port-description-pressed + defaultLinks: [ Toggle, Trigger ] + +- type: transmitterPort + id: On + name: signal-port-name-on-transmitter + description: signal-port-description-on-transmitter + defaultLinks: [ On, Open, Forward, Trigger ] + +- type: transmitterPort + id: Off + name: signal-port-name-off-transmitter + description: signal-port-description-off-transmitter + defaultLinks: [ Off, Close] + +- type: transmitterPort + id: Left + name: signal-port-name-left + description: signal-port-description-left + defaultLinks: [ On, Open, Forward, Trigger] + +- type: transmitterPort + id: Right + name: signal-port-name-right + description: signal-port-description-right + defaultLinks: [ On, Open, Reverse, Trigger] + +- type: transmitterPort + id: Middle + name: signal-port-name-middle + description: signal-port-description-middle + defaultLinks: [ Off, Close]