Machine Port Prototypes (#7659)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>
This commit is contained in:
Leon Friedrich
2022-05-12 20:46:20 +12:00
committed by GitHub
parent 32c2eb7a02
commit c00b459e31
29 changed files with 422 additions and 220 deletions

View File

@@ -197,7 +197,6 @@ namespace Content.Client.Entry
"SignalReceiver",
"SignalSwitch",
"SignalTransmitter",
"SignalButton",
"SignalLinker",
"ItemCabinet",
"FireExtinguisher",

View File

@@ -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<IPrototypeManager>();
}
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<TransmitterPortPrototype>(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<ReceiverPortPrototype>(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

View File

@@ -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

View File

@@ -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
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public ConveyorState State;
[DataField("forwardPort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string ForwardPort = "Forward";
[DataField("reversePort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string ReversePort = "Reverse";
[DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string OffPort = "Off";
}
}

View File

@@ -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<SignalReceiverComponent>(uid);
foreach (string port in Enum.GetNames<ConveyorState>())
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)

View File

@@ -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<ReceiverPortPrototype>))]
public string Port = "Trigger";
}
}

View File

@@ -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<TriggerOnSignalComponent,SignalReceivedEvent>(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<SignalReceiverComponent>(uid);
if (!receiver.Inputs.ContainsKey(TriggerOnSignalComponent.Port))
receiver.AddPort(TriggerOnSignalComponent.Port);
_signalSystem.EnsureReceiverPorts(uid, component.Port);
}
}
}

View File

@@ -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<ReceiverPortPrototype>))]
public string OnPort = "On";
[DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string OffPort = "Off";
[DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string TogglePort = "Toggle";
}
}

View File

@@ -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
{
/// <summary>
/// System for the PoweredLightComponens
/// System for the PoweredLightComponents
/// </summary>
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<ContainerSlot>("light_bulb");
var receiver = EnsureComp<SignalReceiverComponent>(uid);
foreach (string port in new[] { "On", "Off", "Toggle" })
if (!receiver.Inputs.ContainsKey(port))
receiver.AddPort(port);
light.LightBulbContainer = _containerSystem.EnsureContainer<ContainerSlot>(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);
}
/// <summary>

View File

@@ -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<ReceiverPortPrototype>))]
public string OpenPort = "Open";
[DataField("closePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string ClosePort = "Close";
[DataField("togglePort", customTypeSerializer: typeof(PrototypeIdSerializer<ReceiverPortPrototype>))]
public string TogglePort = "Toggle";
}
}

View File

@@ -1,9 +0,0 @@
using Robust.Shared.GameObjects;
namespace Content.Server.MachineLinking.Components
{
[RegisterComponent]
public sealed class SignalButtonComponent : Component
{
}
}

View File

@@ -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<string, List<PortIdentifier>> _inputs = new();
public void AddPort(string name)
{
_inputs.Add(name, new());
}
[ViewVariables]
public IReadOnlyDictionary<string, List<PortIdentifier>> Inputs => _inputs;
public Dictionary<string, List<PortIdentifier>> Inputs = new();
}
}

View File

@@ -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
{
/// <summary>
/// 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.
/// </summary>
[RegisterComponent]
public sealed class SignalSwitchComponent : Component
{
/// <summary>
/// The port that gets signaled when the switch turns on.
/// </summary>
[DataField("onPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string OnPort = "On";
/// <summary>
/// The port that gets signaled when the switch turns off.
/// </summary>
[DataField("offPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string OffPort = "Off";
[DataField("state")]
public bool State;
}
}

View File

@@ -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
{
/// <summary>
@@ -35,14 +31,6 @@ namespace Content.Server.MachineLinking.Components
public float TransmissionRange = 30f;
[DataField("outputs")]
private Dictionary<string, List<PortIdentifier>> _outputs = new();
[ViewVariables]
public IReadOnlyDictionary<string, List<PortIdentifier>> Outputs => _outputs;
public void AddPort(string name)
{
_outputs.Add(name, new());
}
public Dictionary<string, List<PortIdentifier>> Outputs = new();
}
}

View File

@@ -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";
/// <summary>
/// The port that gets signaled when the switch turns on.
/// </summary>
[DataField("port", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string Port = "Pressed";
}
}

View File

@@ -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<TransmitterPortPrototype>))]
public string LeftPort = "Left";
[DataField("rightPort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string RightPort = "Right";
[DataField("middlePort", customTypeSerializer: typeof(PrototypeIdSerializer<TransmitterPortPrototype>))]
public string MiddlePort = "Middle";
}
}

View File

@@ -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;
}
}
}

View File

@@ -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<SignalReceiverComponent>(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);
}
}
}

View File

@@ -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<SignalButtonComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SignalButtonComponent, ActivateInWorldEvent>(OnActivated);
}
private void OnInit(EntityUid uid, SignalButtonComponent component, ComponentInit args)
{
var transmitter = EnsureComp<SignalTransmitterComponent>(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;
}
}
}

View File

@@ -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<SignalTransmitterComponent, InvokePortEvent>(OnTransmitterInvokePort);
SubscribeLocalEvent<SignalTransmitterComponent, ComponentStartup>(OnTransmitterStartup);
SubscribeLocalEvent<SignalTransmitterComponent, ComponentRemove>(OnTransmitterRemoved);
SubscribeLocalEvent<SignalTransmitterComponent, InteractUsingEvent>(OnTransmitterInteractUsing);
@@ -52,6 +40,27 @@ namespace Content.Server.MachineLinking.System
SubscribeLocalEvent<SignalLinkerComponent, BoundUIClosedEvent>(OnLinkerUIClosed);
}
/// <summary>
/// Convenience function to add several ports to an entity.
/// </summary>
public void EnsureReceiverPorts(EntityUid uid, params string[] ports)
{
var comp = EnsureComp<SignalReceiverComponent>(uid);
foreach (var port in ports)
{
comp.Inputs.TryAdd(port, new());
}
}
public void EnsureTransmitterPorts(EntityUid uid, params string[] ports)
{
var comp = EnsureComp<SignalTransmitterComponent>(uid);
foreach (var port in ports)
{
comp.Outputs.TryAdd(port, new());
}
}
/// <summary>
/// Add an alt-click verb to allow users to link the default ports, without needing to open the UI.
/// </summary>
@@ -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<TransmitterPortPrototype>(args.TransmitterPort)),
("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(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<TransmitterPortPrototype>(args.TransmitterPort)),
("machine2", receiver.Owner), ("port2", PortName<ReceiverPortPrototype>(args.ReceiverPort))),
Filter.Entities(attached));
}
else
@@ -343,6 +366,19 @@ namespace Content.Server.MachineLinking.System
TryUpdateUI(linker, transmitter, receiver);
}
/// <summary>
/// Convenience function to retrieve the name of a port prototype.
/// </summary>
/// <param name="port"></param>
/// <returns></returns>
public string PortName<TPort>(string port) where TPort : MachinePortPrototype, IPrototype
{
if (!_protoMan.TryIndex<TPort>(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<TransmitterPortPrototype>(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;

View File

@@ -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<SignalSwitchComponent, ComponentInit>(OnInit);
SubscribeLocalEvent<SignalSwitchComponent, ActivateInWorldEvent>(OnActivated);
}
private void OnInit(EntityUid uid, SignalSwitchComponent component, ComponentInit args)
{
var transmitter = EnsureComp<SignalTransmitterComponent>(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;
}
}

View File

@@ -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,16 +17,14 @@ namespace Content.Server.MachineLinking.System
private void OnInit(EntityUid uid, SignallerComponent component, ComponentInit args)
{
var transmitter = EnsureComp<SignalTransmitterComponent>(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;
}
}

View File

@@ -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<SignalTransmitterComponent>(uid);
foreach (string state in Enum.GetNames<TwoWayLeverState>())
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<AppearanceComponent>(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;
}
}

View File

@@ -0,0 +1,44 @@
using Robust.Shared.Prototypes;
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype.Set;
namespace Content.Shared.MachineLinking;
/// <summary>
/// A prototype for a machine port, for use with machine linking.
/// </summary>
public abstract class MachinePortPrototype
{
/// <summary>
/// Localization string for the port name. Displayed in the linking UI.
/// </summary>
[DataField("name", required:true)]
public string Name = default!;
/// <summary>
/// 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.
/// </summary>
[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!;
/// <summary>
/// This is a set of receiver ports that this transmitter port will attempt to link to when using the
/// default-link functionality.
/// </summary>
[DataField("defaultLinks", customTypeSerializer: typeof(PrototypeIdHashSetSerializer<ReceiverPortPrototype>))]
public HashSet<string>? DefaultLinks;
}

View File

@@ -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.

View File

@@ -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.

View File

@@ -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

View File

@@ -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

View File

@@ -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]