2022-05-05 19:35:06 -07:00
|
|
|
|
using System.Diagnostics.CodeAnalysis;
|
|
|
|
|
|
using System.Linq;
|
|
|
|
|
|
using System.Threading;
|
2023-10-05 22:15:03 -05:00
|
|
|
|
using Content.Server.Construction;
|
|
|
|
|
|
using Content.Server.Construction.Components;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
using Content.Server.Power.Components;
|
2024-02-02 23:24:33 -05:00
|
|
|
|
using Content.Server.UserInterface;
|
2023-04-07 11:21:12 -07:00
|
|
|
|
using Content.Shared.DoAfter;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
using Content.Shared.GameTicking;
|
2023-04-07 11:21:12 -07:00
|
|
|
|
using Content.Shared.Hands.Components;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
using Content.Shared.Interaction;
|
|
|
|
|
|
using Content.Shared.Popups;
|
|
|
|
|
|
using Content.Shared.Tools.Components;
|
2024-02-02 23:24:33 -05:00
|
|
|
|
using Content.Shared.UserInterface;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
using Content.Shared.Wires;
|
|
|
|
|
|
using Robust.Server.GameObjects;
|
2023-10-28 09:59:53 +11:00
|
|
|
|
using Robust.Shared.Player;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
using Robust.Shared.Prototypes;
|
|
|
|
|
|
using Robust.Shared.Random;
|
|
|
|
|
|
|
|
|
|
|
|
namespace Content.Server.Wires;
|
|
|
|
|
|
|
2023-03-24 03:09:45 +03:00
|
|
|
|
public sealed class WiresSystem : SharedWiresSystem
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
2023-08-14 19:34:23 -04:00
|
|
|
|
[Dependency] private readonly ActivatableUISystem _activatableUI = default!;
|
2023-04-03 13:13:48 +12:00
|
|
|
|
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
|
|
|
|
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
2022-10-17 02:44:23 +11:00
|
|
|
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
2023-04-03 13:13:48 +12:00
|
|
|
|
[Dependency] private readonly IRobustRandom _random = default!;
|
2023-10-05 22:15:03 -05:00
|
|
|
|
[Dependency] private readonly ConstructionSystem _construction = default!;
|
2022-05-08 21:51:06 -07:00
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
// This is where all the wire layouts are stored.
|
|
|
|
|
|
[ViewVariables] private readonly Dictionary<string, WireLayout> _layouts = new();
|
|
|
|
|
|
|
2022-06-16 16:14:34 +10:00
|
|
|
|
private float _toolTime = 0f;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
#region Initialization
|
|
|
|
|
|
public override void Initialize()
|
|
|
|
|
|
{
|
2023-03-24 03:09:45 +03:00
|
|
|
|
base.Initialize();
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
|
|
|
|
|
|
|
|
|
|
|
// this is a broadcast event
|
2024-02-02 23:24:33 -05:00
|
|
|
|
SubscribeLocalEvent<WiresComponent, PanelChangedEvent>(OnPanelChanged);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
SubscribeLocalEvent<WiresComponent, WiresActionMessage>(OnWiresActionMessage);
|
|
|
|
|
|
SubscribeLocalEvent<WiresComponent, InteractUsingEvent>(OnInteractUsing);
|
|
|
|
|
|
SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnMapInit);
|
|
|
|
|
|
SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire);
|
|
|
|
|
|
SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered);
|
2023-04-03 13:13:48 +12:00
|
|
|
|
SubscribeLocalEvent<WiresComponent, WireDoAfterEvent>(OnDoAfter);
|
2023-08-12 17:39:58 -04:00
|
|
|
|
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, ActivatableUIOpenAttemptEvent>(OnAttemptOpenActivatableUI);
|
2023-08-13 03:09:30 -04:00
|
|
|
|
SubscribeLocalEvent<ActivatableUIRequiresPanelComponent, PanelChangedEvent>(OnActivatableUIPanelChanged);
|
2023-10-05 22:15:03 -05:00
|
|
|
|
SubscribeLocalEvent<WiresPanelSecurityComponent, WiresPanelSecurityEvent>(SetWiresPanelSecurity);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
2024-02-02 23:24:33 -05:00
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
WireLayout? layout = null;
|
|
|
|
|
|
List<Wire>? wireSet = null;
|
2022-08-01 14:39:37 +02:00
|
|
|
|
if (!wires.AlwaysRandomize)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-08-01 14:39:37 +02:00
|
|
|
|
TryGetLayout(wires.LayoutId, out layout);
|
|
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2022-08-02 06:42:05 -07:00
|
|
|
|
List<IWireAction> wireActions = new();
|
|
|
|
|
|
var dummyWires = 0;
|
|
|
|
|
|
|
|
|
|
|
|
if (!_protoMan.TryIndex(wires.LayoutId, out WireLayoutPrototype? layoutPrototype))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dummyWires += layoutPrototype.DummyWires;
|
|
|
|
|
|
|
|
|
|
|
|
if (layoutPrototype.Wires != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
wireActions.AddRange(layoutPrototype.Wires);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-08-01 14:39:37 +02:00
|
|
|
|
// does the prototype have a parent (and are the wires empty?) if so, we just create
|
|
|
|
|
|
// a new layout based on that
|
2022-08-02 06:42:05 -07:00
|
|
|
|
foreach (var parentLayout in _protoMan.EnumerateParents<WireLayoutPrototype>(wires.LayoutId))
|
2022-08-01 14:39:37 +02:00
|
|
|
|
{
|
2022-08-02 06:42:05 -07:00
|
|
|
|
if (parentLayout.Wires != null)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-08-02 06:42:05 -07:00
|
|
|
|
wireActions.AddRange(parentLayout.Wires);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
dummyWires += parentLayout.DummyWires;
|
|
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2022-08-02 06:42:05 -07:00
|
|
|
|
if (wireActions.Count > 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var wire in wireActions)
|
|
|
|
|
|
{
|
|
|
|
|
|
wire.Initialize();
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
2022-08-02 06:42:05 -07:00
|
|
|
|
|
|
|
|
|
|
wireSet = CreateWireSet(uid, layout, wireActions, dummyWires);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (wireSet == null || wireSet.Count == 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wires.WiresList.AddRange(wireSet);
|
|
|
|
|
|
|
2022-08-01 14:39:37 +02:00
|
|
|
|
var types = new Dictionary<object, int>();
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
if (layout != null)
|
|
|
|
|
|
{
|
|
|
|
|
|
for (var i = 0; i < wireSet.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
wires.WiresList[layout.Specifications[i].Position] = wireSet[i];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var id = 0;
|
|
|
|
|
|
foreach (var wire in wires.WiresList)
|
|
|
|
|
|
{
|
2023-01-21 12:51:02 +13:00
|
|
|
|
wire.Id = id++;
|
|
|
|
|
|
if (wire.Action == null)
|
|
|
|
|
|
continue;
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
var wireType = wire.Action.GetType();
|
|
|
|
|
|
if (types.ContainsKey(wireType))
|
|
|
|
|
|
{
|
|
|
|
|
|
types[wireType] += 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
types.Add(wireType, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// don't care about the result, this should've
|
|
|
|
|
|
// been handled in layout creation
|
|
|
|
|
|
wire.Action.AddWire(wire, types[wireType]);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
var enumeratedList = new List<(int, Wire)>();
|
|
|
|
|
|
var data = new Dictionary<int, WireLayout.WireData>();
|
|
|
|
|
|
for (int i = 0; i < wireSet.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
enumeratedList.Add((i, wireSet[i]));
|
|
|
|
|
|
}
|
|
|
|
|
|
_random.Shuffle(enumeratedList);
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 0; i < enumeratedList.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
(int id, Wire d) = enumeratedList[i];
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
if (d.Action != null)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2023-01-21 12:51:02 +13:00
|
|
|
|
var actionType = d.Action.GetType();
|
|
|
|
|
|
if (types.ContainsKey(actionType))
|
|
|
|
|
|
types[actionType] += 1;
|
|
|
|
|
|
else
|
|
|
|
|
|
types.Add(actionType, 1);
|
|
|
|
|
|
|
|
|
|
|
|
if (!d.Action.AddWire(d, types[actionType]))
|
|
|
|
|
|
d.Action = null;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
2023-05-01 20:40:30 -04:00
|
|
|
|
d.Id = i;
|
2023-02-02 17:34:53 +01:00
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
data.Add(id, new WireLayout.WireData(d.Letter, d.Color, i));
|
|
|
|
|
|
wires.WiresList[i] = wireSet[id];
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!wires.AlwaysRandomize && !string.IsNullOrEmpty(wires.LayoutId))
|
|
|
|
|
|
{
|
|
|
|
|
|
AddLayout(wires.LayoutId, new WireLayout(data));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private List<Wire>? CreateWireSet(EntityUid uid, WireLayout? layout, List<IWireAction> wires, int dummyWires)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (wires.Count == 0)
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
List<WireColor> colors =
|
|
|
|
|
|
new((WireColor[]) Enum.GetValues(typeof(WireColor)));
|
|
|
|
|
|
|
|
|
|
|
|
List<WireLetter> letters =
|
|
|
|
|
|
new((WireLetter[]) Enum.GetValues(typeof(WireLetter)));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
var wireSet = new List<Wire>();
|
|
|
|
|
|
for (var i = 0; i < wires.Count; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
wireSet.Add(CreateWire(uid, wires[i], i, layout, colors, letters));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 1; i <= dummyWires; i++)
|
|
|
|
|
|
{
|
2023-01-21 12:51:02 +13:00
|
|
|
|
wireSet.Add(CreateWire(uid, null, wires.Count + i, layout, colors, letters));
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return wireSet;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
private Wire CreateWire(EntityUid uid, IWireAction? action, int position, WireLayout? layout, List<WireColor> colors, List<WireLetter> letters)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
WireLetter letter;
|
|
|
|
|
|
WireColor color;
|
|
|
|
|
|
|
|
|
|
|
|
if (layout != null
|
|
|
|
|
|
&& layout.Specifications.TryGetValue(position, out var spec))
|
|
|
|
|
|
{
|
|
|
|
|
|
color = spec.Color;
|
|
|
|
|
|
letter = spec.Letter;
|
|
|
|
|
|
colors.Remove(color);
|
|
|
|
|
|
letters.Remove(letter);
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
color = colors.Count == 0 ? WireColor.Red : _random.PickAndTake(colors);
|
|
|
|
|
|
letter = letters.Count == 0 ? WireLetter.α : _random.PickAndTake(letters);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return new Wire(
|
|
|
|
|
|
uid,
|
|
|
|
|
|
false,
|
|
|
|
|
|
color,
|
|
|
|
|
|
letter,
|
2022-07-24 21:53:30 -07:00
|
|
|
|
position,
|
2022-05-05 19:35:06 -07:00
|
|
|
|
action);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region DoAfters
|
|
|
|
|
|
private void OnTimedWire(EntityUid uid, WiresComponent component, TimedWireEvent args)
|
|
|
|
|
|
{
|
|
|
|
|
|
args.Delegate(args.Wire);
|
|
|
|
|
|
UpdateUserInterface(uid);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Tries to cancel an active wire action via the given key that it's stored in.
|
|
|
|
|
|
/// </summary>
|
2023-01-19 03:56:45 +01:00
|
|
|
|
/// <param name="key">The key used to cancel the action.</param>
|
2022-05-05 19:35:06 -07:00
|
|
|
|
public bool TryCancelWireAction(EntityUid owner, object key)
|
|
|
|
|
|
{
|
2023-02-02 17:34:53 +01:00
|
|
|
|
if (TryGetData<CancellationTokenSource?>(owner, key, out var token))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
token.Cancel();
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Starts a timed action for this entity.
|
|
|
|
|
|
/// </summary>
|
2023-01-19 03:56:45 +01:00
|
|
|
|
/// <param name="delay">How long this takes to finish</param>
|
|
|
|
|
|
/// <param name="key">The key used to cancel the action</param>
|
|
|
|
|
|
/// <param name="onFinish">The event that is sent out when the wire is finished <see cref="TimedWireEvent" /></param>
|
2022-05-05 19:35:06 -07:00
|
|
|
|
public void StartWireAction(EntityUid owner, float delay, object key, TimedWireEvent onFinish)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!HasComp<WiresComponent>(owner))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!_activeWires.ContainsKey(owner))
|
|
|
|
|
|
{
|
|
|
|
|
|
_activeWires.Add(owner, new());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
CancellationTokenSource tokenSource = new();
|
|
|
|
|
|
|
|
|
|
|
|
// Starting an already started action will do nothing.
|
|
|
|
|
|
if (HasData(owner, key))
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
SetData(owner, key, tokenSource);
|
|
|
|
|
|
|
|
|
|
|
|
_activeWires[owner].Add(new ActiveWireAction
|
|
|
|
|
|
(
|
|
|
|
|
|
key,
|
|
|
|
|
|
delay,
|
|
|
|
|
|
tokenSource.Token,
|
|
|
|
|
|
onFinish
|
|
|
|
|
|
));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private Dictionary<EntityUid, List<ActiveWireAction>> _activeWires = new();
|
|
|
|
|
|
private List<(EntityUid, ActiveWireAction)> _finishedWires = new();
|
|
|
|
|
|
|
|
|
|
|
|
public override void Update(float frameTime)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var (owner, activeWires) in _activeWires)
|
|
|
|
|
|
{
|
2023-01-22 03:49:35 +13:00
|
|
|
|
if (!HasComp<WiresComponent>(owner))
|
|
|
|
|
|
_activeWires.Remove(owner);
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
foreach (var wire in activeWires)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (wire.CancelToken.IsCancellationRequested)
|
|
|
|
|
|
{
|
2022-06-22 09:53:41 +10:00
|
|
|
|
RaiseLocalEvent(owner, wire.OnFinish, true);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
_finishedWires.Add((owner, wire));
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
wire.TimeLeft -= frameTime;
|
|
|
|
|
|
if (wire.TimeLeft <= 0)
|
|
|
|
|
|
{
|
2022-06-22 09:53:41 +10:00
|
|
|
|
RaiseLocalEvent(owner, wire.OnFinish, true);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
_finishedWires.Add((owner, wire));
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (_finishedWires.Count != 0)
|
|
|
|
|
|
{
|
|
|
|
|
|
foreach (var (owner, wireAction) in _finishedWires)
|
|
|
|
|
|
{
|
2023-03-22 19:53:29 -07:00
|
|
|
|
if (!_activeWires.TryGetValue(owner, out var activeWire))
|
|
|
|
|
|
{
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
activeWire.RemoveAll(action => action.CancelToken == wireAction.CancelToken);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2023-03-22 19:53:29 -07:00
|
|
|
|
if (activeWire.Count == 0)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
_activeWires.Remove(owner);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
RemoveData(owner, wireAction.Id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
_finishedWires.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private class ActiveWireAction
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The wire action's ID. This is so that once the action is finished,
|
|
|
|
|
|
/// any related data can be removed from the state dictionary.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public object Id;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// How much time is left in this action before it finishes.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public float TimeLeft;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The token used to cancel the action.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public CancellationToken CancelToken;
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The event called once the action finishes.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public TimedWireEvent OnFinish;
|
|
|
|
|
|
|
|
|
|
|
|
public ActiveWireAction(object identifier, float time, CancellationToken cancelToken, TimedWireEvent onFinish)
|
|
|
|
|
|
{
|
|
|
|
|
|
Id = identifier;
|
|
|
|
|
|
TimeLeft = time;
|
|
|
|
|
|
CancelToken = cancelToken;
|
|
|
|
|
|
OnFinish = onFinish;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Event Handling
|
2022-10-15 15:08:15 +11:00
|
|
|
|
private void OnWiresPowered(EntityUid uid, WiresComponent component, ref PowerChangedEvent args)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
UpdateUserInterface(uid);
|
|
|
|
|
|
foreach (var wire in component.WiresList)
|
|
|
|
|
|
{
|
2023-01-21 12:51:02 +13:00
|
|
|
|
wire.Action?.Update(wire);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnWiresActionMessage(EntityUid uid, WiresComponent component, WiresActionMessage args)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (args.Session.AttachedEntity == null)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
var player = (EntityUid) args.Session.AttachedEntity;
|
|
|
|
|
|
|
|
|
|
|
|
if (!EntityManager.TryGetComponent(player, out HandsComponent? handsComponent))
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupEntity(Loc.GetString("wires-component-ui-on-receive-message-no-hands"), uid, player);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (!_interactionSystem.InRangeUnobstructed(player, uid))
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupEntity(Loc.GetString("wires-component-ui-on-receive-message-cannot-reach"), uid, player);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
var activeHand = handsComponent.ActiveHand;
|
|
|
|
|
|
|
|
|
|
|
|
if (activeHand == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (activeHand.HeldEntity == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
var activeHandEntity = activeHand.HeldEntity.Value;
|
|
|
|
|
|
if (!EntityManager.TryGetComponent(activeHandEntity, out ToolComponent? tool))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
TryDoWireAction(uid, player, activeHandEntity, args.Id, args.Action, component, tool);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-03 13:13:48 +12:00
|
|
|
|
private void OnDoAfter(EntityUid uid, WiresComponent component, WireDoAfterEvent args)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2023-02-24 19:01:25 -05:00
|
|
|
|
if (args.Cancelled)
|
|
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
component.WiresQueue.Remove(args.Id);
|
2023-02-24 19:01:25 -05:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2023-02-24 19:01:25 -05:00
|
|
|
|
if (args.Handled || args.Args.Target == null || args.Args.Used == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-04-03 13:13:48 +12:00
|
|
|
|
UpdateWires(args.Args.Target.Value, args.Args.User, args.Args.Used.Value, args.Id, args.Action, component);
|
2023-02-24 19:01:25 -05:00
|
|
|
|
|
|
|
|
|
|
args.Handled = true;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args)
|
|
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
if (args.Handled)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!TryComp<ToolComponent>(args.Used, out var tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
2023-04-03 13:13:48 +12:00
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!IsPanelOpen(uid))
|
|
|
|
|
|
return;
|
2023-10-06 16:26:02 -05:00
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (Tool.HasQuality(args.Used, "Cutting", tool) ||
|
|
|
|
|
|
Tool.HasQuality(args.Used, "Pulsing", tool))
|
|
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
if (TryComp(args.User, out ActorComponent? actor))
|
2023-04-05 12:19:28 +12:00
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
_uiSystem.TryOpen(uid, WiresUiKey.Key, actor.PlayerSession);
|
2023-04-05 12:19:28 +12:00
|
|
|
|
args.Handled = true;
|
|
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
private void OnPanelChanged(Entity<WiresComponent> ent, ref PanelChangedEvent args)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (args.Open)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
2024-02-02 23:24:33 -05:00
|
|
|
|
_uiSystem.TryCloseAll(ent, WiresUiKey.Key);
|
2022-12-21 15:41:06 +01:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-12 17:39:58 -04:00
|
|
|
|
private void OnAttemptOpenActivatableUI(EntityUid uid, ActivatableUIRequiresPanelComponent component, ActivatableUIOpenAttemptEvent args)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (args.Cancelled || !TryComp<WiresPanelComponent>(uid, out var wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (component.RequireOpen != wires.Open)
|
|
|
|
|
|
args.Cancel();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-08-13 03:09:30 -04:00
|
|
|
|
private void OnActivatableUIPanelChanged(EntityUid uid, ActivatableUIRequiresPanelComponent component, ref PanelChangedEvent args)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (args.Open == component.RequireOpen)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-08-14 19:34:23 -04:00
|
|
|
|
_activatableUI.CloseAll(uid);
|
2023-08-13 03:09:30 -04:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args)
|
|
|
|
|
|
{
|
2023-05-22 15:45:36 +12:00
|
|
|
|
if (!string.IsNullOrEmpty(component.LayoutId))
|
|
|
|
|
|
SetOrCreateWireLayout(uid, component);
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
if (component.SerialNumber == null)
|
|
|
|
|
|
GenerateSerialNumber(uid, component);
|
|
|
|
|
|
|
|
|
|
|
|
if (component.WireSeed == 0)
|
|
|
|
|
|
component.WireSeed = _random.Next(1, int.MaxValue);
|
2023-05-22 15:45:36 +12:00
|
|
|
|
|
2023-10-05 22:15:03 -05:00
|
|
|
|
// Update the construction graph to make sure that it starts on the node specified by WiresPanelSecurityComponent
|
|
|
|
|
|
if (TryComp<WiresPanelSecurityComponent>(uid, out var wiresPanelSecurity) &&
|
|
|
|
|
|
!string.IsNullOrEmpty(wiresPanelSecurity.SecurityLevel) &&
|
|
|
|
|
|
TryComp<ConstructionComponent>(uid, out var construction))
|
|
|
|
|
|
{
|
|
|
|
|
|
_construction.ChangeNode(uid, null, wiresPanelSecurity.SecurityLevel, true, construction);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-05-22 15:45:36 +12:00
|
|
|
|
UpdateUserInterface(uid);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Entity API
|
|
|
|
|
|
private void GenerateSerialNumber(EntityUid uid, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
Span<char> data = stackalloc char[9];
|
|
|
|
|
|
data[4] = '-';
|
|
|
|
|
|
|
|
|
|
|
|
if (_random.Prob(0.01f))
|
|
|
|
|
|
{
|
|
|
|
|
|
for (var i = 0; i < 4; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Cyrillic Letters
|
|
|
|
|
|
data[i] = (char) _random.Next(0x0410, 0x0430);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
|
|
|
|
|
for (var i = 0; i < 4; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Letters
|
|
|
|
|
|
data[i] = (char) _random.Next(0x41, 0x5B);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
for (var i = 5; i < 9; i++)
|
|
|
|
|
|
{
|
|
|
|
|
|
// Digits
|
|
|
|
|
|
data[i] = (char) _random.Next(0x30, 0x3A);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wires.SerialNumber = new string(data);
|
|
|
|
|
|
UpdateUserInterface(uid);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-09-11 21:20:46 +10:00
|
|
|
|
private void UpdateUserInterface(EntityUid uid, WiresComponent? wires = null, UserInterfaceComponent? ui = null)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires, ref ui, false)) // logging this means that we get a bunch of errors
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
var clientList = new List<ClientWire>();
|
|
|
|
|
|
foreach (var entry in wires.WiresList)
|
|
|
|
|
|
{
|
|
|
|
|
|
clientList.Add(new ClientWire(entry.Id, entry.IsCut, entry.Color,
|
|
|
|
|
|
entry.Letter));
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
var statusData = entry.Action?.GetStatusLightData(entry);
|
|
|
|
|
|
if (statusData != null && entry.Action?.StatusKey != null)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-07-24 21:53:30 -07:00
|
|
|
|
wires.Statuses[entry.Action.StatusKey] = (entry.OriginalPosition, statusData);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
var statuses = new List<(int position, object key, object value)>();
|
|
|
|
|
|
foreach (var (key, value) in wires.Statuses)
|
|
|
|
|
|
{
|
|
|
|
|
|
var valueCast = ((int position, StatusLightData? value)) value;
|
|
|
|
|
|
statuses.Add((valueCast.position, key, valueCast.value!));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
statuses.Sort((a, b) => a.position.CompareTo(b.position));
|
|
|
|
|
|
|
2022-10-17 02:44:23 +11:00
|
|
|
|
_uiSystem.TrySetUiState(uid, WiresUiKey.Key, new WiresBoundUserInterfaceState(
|
|
|
|
|
|
clientList.ToArray(),
|
|
|
|
|
|
statuses.Select(p => new StatusEntry(p.key, p.value)).ToArray(),
|
2023-08-27 13:41:43 +03:00
|
|
|
|
Loc.GetString(wires.BoardName),
|
2022-10-17 02:44:23 +11:00
|
|
|
|
wires.SerialNumber,
|
|
|
|
|
|
wires.WireSeed), ui: ui);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-28 09:59:53 +11:00
|
|
|
|
public void OpenUserInterface(EntityUid uid, ICommonSession player)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-10-17 02:44:23 +11:00
|
|
|
|
if (_uiSystem.TryGetUi(uid, WiresUiKey.Key, out var ui))
|
|
|
|
|
|
_uiSystem.OpenUi(ui, player);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Tries to get a wire on this entity by its integer id.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>The wire if found, otherwise null</returns>
|
|
|
|
|
|
public Wire? TryGetWire(EntityUid uid, int id, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return null;
|
|
|
|
|
|
|
|
|
|
|
|
return id >= 0 && id < wires.WiresList.Count
|
|
|
|
|
|
? wires.WiresList[id]
|
|
|
|
|
|
: null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Tries to get all the wires on this entity by the wire action type.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
/// <returns>Enumerator of all wires in this entity according to the given type.</returns>
|
|
|
|
|
|
public IEnumerable<Wire> TryGetWires<T>(EntityUid uid, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
yield break;
|
|
|
|
|
|
|
|
|
|
|
|
foreach (var wire in wires.WiresList)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (wire.GetType() == typeof(T))
|
|
|
|
|
|
{
|
|
|
|
|
|
yield return wire;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-10-05 22:15:03 -05:00
|
|
|
|
public void SetWiresPanelSecurity(EntityUid uid, WiresPanelSecurityComponent component, WiresPanelSecurityEvent args)
|
2023-08-10 03:33:03 -05:00
|
|
|
|
{
|
2023-10-05 22:15:03 -05:00
|
|
|
|
component.Examine = args.Examine;
|
|
|
|
|
|
component.WiresAccessible = args.WiresAccessible;
|
|
|
|
|
|
|
2023-09-14 00:54:49 -05:00
|
|
|
|
Dirty(uid, component);
|
2023-08-10 03:33:03 -05:00
|
|
|
|
|
2023-10-05 22:15:03 -05:00
|
|
|
|
if (!args.WiresAccessible)
|
2023-08-10 03:33:03 -05:00
|
|
|
|
{
|
|
|
|
|
|
_uiSystem.TryCloseAll(uid, WiresUiKey.Key);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-04-03 13:13:48 +12:00
|
|
|
|
private void TryDoWireAction(EntityUid target, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
if (!Resolve(target, ref wires)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|| !Resolve(toolEntity, ref tool))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (wires.WiresQueue.Contains(id))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2023-04-03 13:13:48 +12:00
|
|
|
|
var wire = TryGetWire(target, id, wires);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
if (wire == null)
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
switch (action)
|
|
|
|
|
|
{
|
|
|
|
|
|
case WiresAction.Cut:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Cutting", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), user);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-cut-cut-wire"), user);
|
2022-07-24 21:53:30 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
break;
|
|
|
|
|
|
case WiresAction.Mend:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Cutting", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), user);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (!wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-mend-uncut-wire"), user);
|
2022-07-24 21:53:30 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
break;
|
|
|
|
|
|
case WiresAction.Pulse:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Pulsing", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-multitool"), user);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-pulse-cut-wire"), user);
|
2022-07-24 21:53:30 -07:00
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
wires.WiresQueue.Add(id);
|
|
|
|
|
|
|
2022-06-16 16:14:34 +10:00
|
|
|
|
if (_toolTime > 0f)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2023-09-11 09:42:41 +10:00
|
|
|
|
var args = new DoAfterArgs(EntityManager, user, _toolTime, new WireDoAfterEvent(action, id), target, target: target, used: toolEntity)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-06-16 16:14:34 +10:00
|
|
|
|
NeedHand = true,
|
|
|
|
|
|
BreakOnDamage = true,
|
2023-02-24 19:01:25 -05:00
|
|
|
|
BreakOnUserMove = true
|
2022-06-16 16:14:34 +10:00
|
|
|
|
};
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2023-04-03 13:13:48 +12:00
|
|
|
|
_doAfter.TryStartDoAfter(args);
|
2022-06-16 16:14:34 +10:00
|
|
|
|
}
|
|
|
|
|
|
else
|
|
|
|
|
|
{
|
2023-04-03 13:13:48 +12:00
|
|
|
|
UpdateWires(target, user, toolEntity, id, action, wires);
|
2022-06-16 16:14:34 +10:00
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void UpdateWires(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
|
|
|
|
|
|
{
|
2022-05-23 16:00:51 -07:00
|
|
|
|
if (!Resolve(used, ref wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (!wires.WiresQueue.Contains(id))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
2022-05-23 16:00:51 -07:00
|
|
|
|
if (!Resolve(toolEntity, ref tool))
|
|
|
|
|
|
{
|
|
|
|
|
|
wires.WiresQueue.Remove(id);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
2022-05-23 16:00:51 -07:00
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
var wire = TryGetWire(used, id, wires);
|
|
|
|
|
|
|
|
|
|
|
|
if (wire == null)
|
2022-05-23 16:00:51 -07:00
|
|
|
|
{
|
|
|
|
|
|
wires.WiresQueue.Remove(id);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
return;
|
2022-05-23 16:00:51 -07:00
|
|
|
|
}
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
switch (action)
|
|
|
|
|
|
{
|
|
|
|
|
|
case WiresAction.Cut:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Cutting", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), user);
|
2022-05-23 16:00:51 -07:00
|
|
|
|
break;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-cut-cut-wire"), user);
|
2022-07-24 21:53:30 -07:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
Tool.PlayToolSound(toolEntity, tool, user);
|
2023-01-21 12:51:02 +13:00
|
|
|
|
if (wire.Action == null || wire.Action.Cut(user, wire))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
wire.IsCut = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateUserInterface(used);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case WiresAction.Mend:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Cutting", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), user);
|
2022-05-23 16:00:51 -07:00
|
|
|
|
break;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
if (!wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-mend-uncut-wire"), user);
|
2022-07-24 21:53:30 -07:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2024-02-02 23:24:33 -05:00
|
|
|
|
Tool.PlayToolSound(toolEntity, tool, user);
|
2023-01-21 12:51:02 +13:00
|
|
|
|
if (wire.Action == null || wire.Action.Mend(user, wire))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
wire.IsCut = false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
UpdateUserInterface(used);
|
|
|
|
|
|
break;
|
|
|
|
|
|
case WiresAction.Pulse:
|
2024-02-02 23:24:33 -05:00
|
|
|
|
if (!Tool.HasQuality(toolEntity, "Pulsing", tool))
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-multitool"), user);
|
2022-05-23 16:00:51 -07:00
|
|
|
|
break;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (wire.IsCut)
|
|
|
|
|
|
{
|
2022-12-19 10:41:47 +13:00
|
|
|
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-pulse-cut-wire"), user);
|
2022-05-23 16:00:51 -07:00
|
|
|
|
break;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
wire.Action?.Pulse(user, wire);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
|
|
|
|
|
UpdateUserInterface(used);
|
2024-02-02 23:24:33 -05:00
|
|
|
|
Audio.PlayPvs(wires.PulseSound, used);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
wire.Action?.Update(wire);
|
2022-05-05 19:35:06 -07:00
|
|
|
|
wires.WiresQueue.Remove(id);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Tries to get the stateful data stored in this entity's WiresComponent.
|
|
|
|
|
|
/// </summary>
|
2023-01-19 03:56:45 +01:00
|
|
|
|
/// <param name="identifier">The key that stores the data in the WiresComponent.</param>
|
2022-05-05 19:35:06 -07:00
|
|
|
|
public bool TryGetData<T>(EntityUid uid, object identifier, [NotNullWhen(true)] out T? data, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
data = default(T);
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
wires.StateData.TryGetValue(identifier, out var result);
|
|
|
|
|
|
|
|
|
|
|
|
if (result is not T)
|
|
|
|
|
|
{
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
data = (T) result;
|
|
|
|
|
|
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Sets data in the entity's WiresComponent state dictionary by key.
|
|
|
|
|
|
/// </summary>
|
2023-01-19 03:56:45 +01:00
|
|
|
|
/// <param name="identifier">The key that stores the data in the WiresComponent.</param>
|
|
|
|
|
|
/// <param name="data">The data to store using the given identifier.</param>
|
2022-05-05 19:35:06 -07:00
|
|
|
|
public void SetData(EntityUid uid, object identifier, object data, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
if (wires.StateData.TryGetValue(identifier, out var storedMessage))
|
|
|
|
|
|
{
|
|
|
|
|
|
if (storedMessage == data)
|
|
|
|
|
|
{
|
|
|
|
|
|
return;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
wires.StateData[identifier] = data;
|
|
|
|
|
|
UpdateUserInterface(uid, wires);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// If this entity has data stored via this key in the WiresComponent it has
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool HasData(EntityUid uid, object identifier, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return false;
|
|
|
|
|
|
|
|
|
|
|
|
return wires.StateData.ContainsKey(identifier);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Removes data from this entity stored in the given key from the entity's WiresComponent.
|
|
|
|
|
|
/// </summary>
|
2023-01-19 03:56:45 +01:00
|
|
|
|
/// <param name="identifier">The key that stores the data in the WiresComponent.</param>
|
2022-05-05 19:35:06 -07:00
|
|
|
|
public void RemoveData(EntityUid uid, object identifier, WiresComponent? wires = null)
|
|
|
|
|
|
{
|
|
|
|
|
|
if (!Resolve(uid, ref wires))
|
|
|
|
|
|
return;
|
|
|
|
|
|
|
|
|
|
|
|
wires.StateData.Remove(identifier);
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
|
|
|
|
#region Layout Handling
|
|
|
|
|
|
private bool TryGetLayout(string id, [NotNullWhen(true)] out WireLayout? layout)
|
|
|
|
|
|
{
|
|
|
|
|
|
return _layouts.TryGetValue(id, out layout);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void AddLayout(string id, WireLayout layout)
|
|
|
|
|
|
{
|
|
|
|
|
|
_layouts.Add(id, layout);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
private void Reset(RoundRestartCleanupEvent args)
|
|
|
|
|
|
{
|
|
|
|
|
|
_layouts.Clear();
|
|
|
|
|
|
}
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class Wire
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The entity that registered the wire.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public EntityUid Owner { get; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Whether the wire is cut.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public bool IsCut { get; set; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// Used in client-server communication to identify a wire without telling the client what the wire does.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ViewVariables]
|
|
|
|
|
|
public int Id { get; set; }
|
|
|
|
|
|
|
2022-07-24 21:53:30 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The original position of this wire in the prototype.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ViewVariables]
|
|
|
|
|
|
public int OriginalPosition { get; set; }
|
|
|
|
|
|
|
2022-05-05 19:35:06 -07:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The color of the wire.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ViewVariables]
|
|
|
|
|
|
public WireColor Color { get; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The greek letter shown below the wire.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
[ViewVariables]
|
|
|
|
|
|
public WireLetter Letter { get; }
|
|
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The action that this wire performs when mended, cut or puled. This also determines the status lights that this wire adds.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public IWireAction? Action { get; set; }
|
2022-05-05 19:35:06 -07:00
|
|
|
|
|
2023-01-21 12:51:02 +13:00
|
|
|
|
public Wire(EntityUid owner, bool isCut, WireColor color, WireLetter letter, int position, IWireAction? action)
|
2022-05-05 19:35:06 -07:00
|
|
|
|
{
|
|
|
|
|
|
Owner = owner;
|
|
|
|
|
|
IsCut = isCut;
|
|
|
|
|
|
Color = color;
|
2022-07-24 21:53:30 -07:00
|
|
|
|
OriginalPosition = position;
|
2022-05-05 19:35:06 -07:00
|
|
|
|
Letter = letter;
|
|
|
|
|
|
Action = action;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// this is here so that when a DoAfter event is called,
|
|
|
|
|
|
// WiresSystem can call the action in question after the
|
|
|
|
|
|
// doafter is finished (either through cancellation
|
|
|
|
|
|
// or completion - this is implementation dependent)
|
|
|
|
|
|
public delegate void WireActionDelegate(Wire wire);
|
|
|
|
|
|
|
|
|
|
|
|
// callbacks over the event bus,
|
|
|
|
|
|
// because async is banned
|
|
|
|
|
|
public sealed class TimedWireEvent : EntityEventArgs
|
|
|
|
|
|
{
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The function to be called once
|
|
|
|
|
|
/// the timed event is complete.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public WireActionDelegate Delegate { get; }
|
|
|
|
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
|
|
/// The wire tied to this timed wire event.
|
|
|
|
|
|
/// </summary>
|
|
|
|
|
|
public Wire Wire { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public TimedWireEvent(WireActionDelegate @delegate, Wire wire)
|
|
|
|
|
|
{
|
|
|
|
|
|
Delegate = @delegate;
|
|
|
|
|
|
Wire = wire;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class WireLayout
|
|
|
|
|
|
{
|
|
|
|
|
|
// why is this an <int, WireData>?
|
|
|
|
|
|
// List<T>.Insert panics,
|
|
|
|
|
|
// and I needed a uniquer key for wires
|
|
|
|
|
|
// which allows me to have a unified identifier
|
|
|
|
|
|
[ViewVariables] public IReadOnlyDictionary<int, WireData> Specifications { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public WireLayout(IReadOnlyDictionary<int, WireData> specifications)
|
|
|
|
|
|
{
|
|
|
|
|
|
Specifications = specifications;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
public sealed class WireData
|
|
|
|
|
|
{
|
|
|
|
|
|
public WireLetter Letter { get; }
|
|
|
|
|
|
public WireColor Color { get; }
|
|
|
|
|
|
public int Position { get; }
|
|
|
|
|
|
|
|
|
|
|
|
public WireData(WireLetter letter, WireColor color, int position)
|
|
|
|
|
|
{
|
|
|
|
|
|
Letter = letter;
|
|
|
|
|
|
Color = color;
|
|
|
|
|
|
Position = position;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|