Wires refactor (#7699)
Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com> Co-authored-by: Kara <lunarautomaton6@gmail.com>
This commit is contained in:
@@ -77,7 +77,6 @@ namespace Content.Client.Entry
|
|||||||
factory.RegisterClass<SharedLatheComponent>();
|
factory.RegisterClass<SharedLatheComponent>();
|
||||||
factory.RegisterClass<SharedSpawnPointComponent>();
|
factory.RegisterClass<SharedSpawnPointComponent>();
|
||||||
factory.RegisterClass<SharedVendingMachineComponent>();
|
factory.RegisterClass<SharedVendingMachineComponent>();
|
||||||
factory.RegisterClass<SharedWiresComponent>();
|
|
||||||
factory.RegisterClass<SharedCargoConsoleComponent>();
|
factory.RegisterClass<SharedCargoConsoleComponent>();
|
||||||
factory.RegisterClass<SharedReagentDispenserComponent>();
|
factory.RegisterClass<SharedReagentDispenserComponent>();
|
||||||
factory.RegisterClass<SharedChemMasterComponent>();
|
factory.RegisterClass<SharedChemMasterComponent>();
|
||||||
@@ -105,6 +104,7 @@ namespace Content.Client.Entry
|
|||||||
prototypes.RegisterIgnore("worldSpell");
|
prototypes.RegisterIgnore("worldSpell");
|
||||||
prototypes.RegisterIgnore("entitySpell");
|
prototypes.RegisterIgnore("entitySpell");
|
||||||
prototypes.RegisterIgnore("instantSpell");
|
prototypes.RegisterIgnore("instantSpell");
|
||||||
|
prototypes.RegisterIgnore("wireLayout");
|
||||||
|
|
||||||
ClientContentIoC.Register();
|
ClientContentIoC.Register();
|
||||||
|
|
||||||
|
|||||||
@@ -344,7 +344,8 @@ namespace Content.Client.Entry
|
|||||||
"EnergySword",
|
"EnergySword",
|
||||||
"DoorRemote",
|
"DoorRemote",
|
||||||
"InteractionPopup",
|
"InteractionPopup",
|
||||||
"HealthAnalyzer"
|
"HealthAnalyzer",
|
||||||
|
"Wires"
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -13,12 +13,11 @@ namespace Content.Client.Lathe
|
|||||||
{
|
{
|
||||||
if (TryComp(uid, out SpriteComponent? sprite))
|
if (TryComp(uid, out SpriteComponent? sprite))
|
||||||
{
|
{
|
||||||
if (args.Component.TryGetData(PowerDeviceVisuals.Powered, out bool powered)
|
if (args.Component.TryGetData(PowerDeviceVisuals.Powered, out bool powered))
|
||||||
&& sprite.LayerMapTryGet(PowerDeviceVisualLayers.Powered, out var poweredLayer))
|
sprite.LayerSetVisible(PowerDeviceVisualLayers.Powered, powered);
|
||||||
sprite.LayerSetVisible(poweredLayer, powered);
|
if (args.Component.TryGetData(WiresVisuals.MaintenancePanelState, out bool panel)
|
||||||
if (args.Component.TryGetData(SharedWiresComponent.WiresVisuals.MaintenancePanelState, out bool panel)
|
|
||||||
&& sprite.LayerMapTryGet(WiresVisualizer.WiresVisualLayers.MaintenancePanel, out var panelLayer))
|
&& sprite.LayerMapTryGet(WiresVisualizer.WiresVisualLayers.MaintenancePanel, out var panelLayer))
|
||||||
sprite.LayerSetVisible(panelLayer, panel);
|
sprite.LayerSetVisible(WiresVisualizer.WiresVisualLayers.MaintenancePanel, panel);
|
||||||
// Lathe specific stuff
|
// Lathe specific stuff
|
||||||
if (args.Component.TryGetData(LatheVisuals.IsRunning, out bool isRunning))
|
if (args.Component.TryGetData(LatheVisuals.IsRunning, out bool isRunning))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Robust.Client.GameObjects;
|
using Content.Shared.Wires;
|
||||||
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Wires.UI
|
namespace Content.Client.Wires.UI
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ using Robust.Shared.Input;
|
|||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
using Robust.Shared.Random;
|
||||||
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
using static Robust.Client.UserInterface.Controls.BoxContainer;
|
||||||
|
|
||||||
namespace Content.Client.Wires.UI
|
namespace Content.Client.Wires.UI
|
||||||
@@ -267,7 +267,16 @@ namespace Content.Client.Wires.UI
|
|||||||
|
|
||||||
|
|
||||||
_statusContainer.RemoveAllChildren();
|
_statusContainer.RemoveAllChildren();
|
||||||
foreach (var status in state.Statuses)
|
var originalStatuses = new List<StatusEntry>(state.Statuses); // TODO: maybe not this way?
|
||||||
|
var shuffledStatuses = new List<StatusEntry>();
|
||||||
|
for (var i = originalStatuses.Count; i > 0; i--)
|
||||||
|
{
|
||||||
|
var index = random.Next(originalStatuses.Count);
|
||||||
|
shuffledStatuses.Add(originalStatuses[index]);
|
||||||
|
originalStatuses.RemoveAt(index);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var status in shuffledStatuses)
|
||||||
{
|
{
|
||||||
if (status.Value is StatusLightData statusLightData)
|
if (status.Value is StatusLightData statusLightData)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
|
using Content.Shared.Wires;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
|
|
||||||
namespace Content.Client.Wires.Visualizers
|
namespace Content.Client.Wires.Visualizers
|
||||||
{
|
{
|
||||||
|
|||||||
95
Content.Server/Access/AccessWireAction.cs
Normal file
95
Content.Server/Access/AccessWireAction.cs
Normal file
@@ -0,0 +1,95 @@
|
|||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Access;
|
||||||
|
using Content.Shared.Access.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Access;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public class AccessWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Green;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "ACC";
|
||||||
|
|
||||||
|
[DataField("pulseTimeout")]
|
||||||
|
private int _pulseTimeout = 30;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
StatusLightState lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner)
|
||||||
|
&& EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
|
||||||
|
{
|
||||||
|
if (access.Enabled)
|
||||||
|
{
|
||||||
|
lightState = StatusLightState.On;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AccessWireActionKey.Status;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
access.Enabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
|
||||||
|
{
|
||||||
|
access.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
|
||||||
|
{
|
||||||
|
access.Enabled = false;
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, _pulseTimeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitPulseCancel, wire));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(Wire wire)
|
||||||
|
{
|
||||||
|
if (!IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AwaitPulseCancel(Wire wire)
|
||||||
|
{
|
||||||
|
if (!wire.IsCut)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AccessReaderComponent>(wire.Owner, out var access))
|
||||||
|
{
|
||||||
|
access.Enabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PulseTimeoutKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Server.VendingMachines;
|
using Content.Server.VendingMachines;
|
||||||
using Content.Server.WireHacking;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Arcade;
|
using Content.Shared.Arcade;
|
||||||
using Content.Shared.Interaction;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
@@ -16,7 +16,7 @@ using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototy
|
|||||||
namespace Content.Server.Arcade.Components
|
namespace Content.Server.Arcade.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class SpaceVillainArcadeComponent : SharedSpaceVillainArcadeComponent, IWires
|
public sealed class SpaceVillainArcadeComponent : SharedSpaceVillainArcadeComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IRobustRandom _random = null!;
|
[Dependency] private readonly IRobustRandom _random = null!;
|
||||||
|
|
||||||
@@ -24,9 +24,9 @@ namespace Content.Server.Arcade.Components
|
|||||||
private bool Powered => _entityManager.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var powerReceiverComponent) && powerReceiverComponent.Powered;
|
private bool Powered => _entityManager.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var powerReceiverComponent) && powerReceiverComponent.Powered;
|
||||||
|
|
||||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(SpaceVillainArcadeUiKey.Key);
|
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(SpaceVillainArcadeUiKey.Key);
|
||||||
[ViewVariables] private bool _overflowFlag;
|
[ViewVariables] public bool OverflowFlag;
|
||||||
[ViewVariables] private bool _playerInvincibilityFlag;
|
[ViewVariables] public bool PlayerInvincibilityFlag;
|
||||||
[ViewVariables] private bool _enemyInvincibilityFlag;
|
[ViewVariables] public bool EnemyInvincibilityFlag;
|
||||||
[ViewVariables] public SpaceVillainGame Game = null!;
|
[ViewVariables] public SpaceVillainGame Game = null!;
|
||||||
|
|
||||||
[DataField("newGameSound")] private SoundSpecifier _newGameSound = new SoundPathSpecifier("/Audio/Effects/Arcade/newgame.ogg");
|
[DataField("newGameSound")] private SoundSpecifier _newGameSound = new SoundPathSpecifier("/Audio/Effects/Arcade/newgame.ogg");
|
||||||
@@ -62,7 +62,6 @@ namespace Content.Server.Arcade.Components
|
|||||||
"ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton"
|
"ToyPhazon", "ToyFireRipley", "ToyReticence", "ToyRipley", "ToySeraph", "ToyDurand", "ToySkeleton"
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
protected override void Initialize()
|
protected override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -110,71 +109,6 @@ namespace Content.Server.Arcade.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum Wires
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Disables Max Health&Mana for both Enemy and Player.
|
|
||||||
/// </summary>
|
|
||||||
Overflow,
|
|
||||||
/// <summary>
|
|
||||||
/// Makes Player Invincible.
|
|
||||||
/// </summary>
|
|
||||||
PlayerInvincible,
|
|
||||||
/// <summary>
|
|
||||||
/// Makes Enemy Invincible.
|
|
||||||
/// </summary>
|
|
||||||
EnemyInvincible
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterWires(WiresComponent.WiresBuilder builder)
|
|
||||||
{
|
|
||||||
builder.CreateWire(Wires.Overflow);
|
|
||||||
builder.CreateWire(Wires.PlayerInvincible);
|
|
||||||
builder.CreateWire(Wires.EnemyInvincible);
|
|
||||||
builder.CreateWire(4);
|
|
||||||
builder.CreateWire(5);
|
|
||||||
builder.CreateWire(6);
|
|
||||||
IndicatorUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WiresUpdate(WiresUpdateEventArgs args)
|
|
||||||
{
|
|
||||||
var wire = (Wires) args.Identifier;
|
|
||||||
var value = args.Action != SharedWiresComponent.WiresAction.Mend;
|
|
||||||
switch (wire)
|
|
||||||
{
|
|
||||||
case Wires.Overflow:
|
|
||||||
_overflowFlag = value;
|
|
||||||
break;
|
|
||||||
case Wires.PlayerInvincible:
|
|
||||||
_playerInvincibilityFlag = value;
|
|
||||||
break;
|
|
||||||
case Wires.EnemyInvincible:
|
|
||||||
_enemyInvincibilityFlag = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
IndicatorUpdate();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void IndicatorUpdate()
|
|
||||||
{
|
|
||||||
if (!_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent)) return;
|
|
||||||
|
|
||||||
wiresComponent.SetStatus(Indicators.HealthManager,
|
|
||||||
new SharedWiresComponent.StatusLightData(Color.Purple,
|
|
||||||
_playerInvincibilityFlag || _enemyInvincibilityFlag
|
|
||||||
? SharedWiresComponent.StatusLightState.BlinkingSlow
|
|
||||||
: SharedWiresComponent.StatusLightState.On,
|
|
||||||
"MNGR"));
|
|
||||||
wiresComponent.SetStatus(Indicators.HealthLimiter,
|
|
||||||
new SharedWiresComponent.StatusLightData(Color.Red,
|
|
||||||
_overflowFlag
|
|
||||||
? SharedWiresComponent.StatusLightState.BlinkingSlow
|
|
||||||
: SharedWiresComponent.StatusLightState.On,
|
|
||||||
"LIMT"));
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Called when the user wins the game.
|
/// Called when the user wins the game.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -247,7 +181,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private void ValidateVars()
|
private void ValidateVars()
|
||||||
{
|
{
|
||||||
if (_owner._overflowFlag) return;
|
if (_owner.OverflowFlag) return;
|
||||||
|
|
||||||
if (_playerHp > _playerHpMax) _playerHp = _playerHpMax;
|
if (_playerHp > _playerHpMax) _playerHp = _playerHpMax;
|
||||||
if (_playerMp > _playerMpMax) _playerMp = _playerMpMax;
|
if (_playerMp > _playerMpMax) _playerMp = _playerMpMax;
|
||||||
@@ -271,7 +205,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
("enemyName", _enemyName),
|
("enemyName", _enemyName),
|
||||||
("attackAmount", attackAmount));
|
("attackAmount", attackAmount));
|
||||||
SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerAttackSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f));
|
SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerAttackSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f));
|
||||||
if (!_owner._enemyInvincibilityFlag)
|
if (!_owner.EnemyInvincibilityFlag)
|
||||||
_enemyHp -= attackAmount;
|
_enemyHp -= attackAmount;
|
||||||
_turtleTracker -= _turtleTracker > 0 ? 1 : 0;
|
_turtleTracker -= _turtleTracker > 0 ? 1 : 0;
|
||||||
break;
|
break;
|
||||||
@@ -282,7 +216,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
("magicPointAmount", pointAmount),
|
("magicPointAmount", pointAmount),
|
||||||
("healAmount", healAmount));
|
("healAmount", healAmount));
|
||||||
SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerHealSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f));
|
SoundSystem.Play(Filter.Pvs(_owner.Owner), _owner._playerHealSound.GetSound(), _owner.Owner, AudioParams.Default.WithVolume(-4f));
|
||||||
if (!_owner._playerInvincibilityFlag)
|
if (!_owner.PlayerInvincibilityFlag)
|
||||||
_playerMp -= pointAmount;
|
_playerMp -= pointAmount;
|
||||||
_playerHp += healAmount;
|
_playerHp += healAmount;
|
||||||
_turtleTracker++;
|
_turtleTracker++;
|
||||||
@@ -380,7 +314,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
_latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-throws-bomb-message",
|
_latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-throws-bomb-message",
|
||||||
("enemyName", _enemyName),
|
("enemyName", _enemyName),
|
||||||
("damageReceived", boomAmount));
|
("damageReceived", boomAmount));
|
||||||
if (_owner._playerInvincibilityFlag) return;
|
if (_owner.PlayerInvincibilityFlag) return;
|
||||||
_playerHp -= boomAmount;
|
_playerHp -= boomAmount;
|
||||||
_turtleTracker--;
|
_turtleTracker--;
|
||||||
}
|
}
|
||||||
@@ -390,7 +324,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
_latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-steals-player-power-message",
|
_latestEnemyActionMessage = Loc.GetString("space-villain-game-enemy-steals-player-power-message",
|
||||||
("enemyName", _enemyName),
|
("enemyName", _enemyName),
|
||||||
("stolenAmount", stealAmount));
|
("stolenAmount", stealAmount));
|
||||||
if (_owner._playerInvincibilityFlag) return;
|
if (_owner.PlayerInvincibilityFlag) return;
|
||||||
_playerMp -= stealAmount;
|
_playerMp -= stealAmount;
|
||||||
_enemyMp += stealAmount;
|
_enemyMp += stealAmount;
|
||||||
}
|
}
|
||||||
@@ -409,7 +343,7 @@ namespace Content.Server.Arcade.Components
|
|||||||
Loc.GetString("space-villain-game-enemy-attacks-message",
|
Loc.GetString("space-villain-game-enemy-attacks-message",
|
||||||
("enemyName", _enemyName),
|
("enemyName", _enemyName),
|
||||||
("damageDealt", attackAmount));
|
("damageDealt", attackAmount));
|
||||||
if (_owner._playerInvincibilityFlag) return;
|
if (_owner.PlayerInvincibilityFlag) return;
|
||||||
_playerHp -= attackAmount;
|
_playerHp -= attackAmount;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,74 @@
|
|||||||
|
using Content.Server.Arcade.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Arcade;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class ArcadePlayerInvincibleWireAction : BaseToggleWireAction
|
||||||
|
{
|
||||||
|
private string _text = "MNGR";
|
||||||
|
private Color _color = Color.Purple;
|
||||||
|
|
||||||
|
public override object? StatusKey { get; } = SharedSpaceVillainArcadeComponent.Indicators.HealthManager;
|
||||||
|
|
||||||
|
public override void ToggleValue(EntityUid owner, bool setting)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade))
|
||||||
|
{
|
||||||
|
arcade.PlayerInvincibilityFlag = !setting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetValue(EntityUid owner)
|
||||||
|
{
|
||||||
|
return EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade)
|
||||||
|
&& !arcade.PlayerInvincibilityFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
|
||||||
|
if (IsPowered(wire.Owner) && EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(wire.Owner, out var arcade))
|
||||||
|
{
|
||||||
|
lightState = arcade.PlayerInvincibilityFlag || arcade.EnemyInvincibilityFlag
|
||||||
|
? StatusLightState.BlinkingSlow
|
||||||
|
: StatusLightState.On;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_color,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class ArcadeEnemyInvincibleWireAction : BaseToggleWireAction
|
||||||
|
{
|
||||||
|
public override object? StatusKey { get; } = null;
|
||||||
|
|
||||||
|
public override void ToggleValue(EntityUid owner, bool setting)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade))
|
||||||
|
{
|
||||||
|
arcade.PlayerInvincibilityFlag = !setting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetValue(EntityUid owner)
|
||||||
|
{
|
||||||
|
return EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade)
|
||||||
|
&& !arcade.PlayerInvincibilityFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire) => null;
|
||||||
|
}
|
||||||
|
|
||||||
|
public enum ArcadeInvincibilityWireActionKeys : short
|
||||||
|
{
|
||||||
|
Player,
|
||||||
|
Enemy
|
||||||
|
}
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
using Content.Server.Arcade.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Arcade;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Arcade;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class ArcadeOverflowWireAction : BaseToggleWireAction
|
||||||
|
{
|
||||||
|
private Color _color = Color.Red;
|
||||||
|
private string _text = "LMTR";
|
||||||
|
|
||||||
|
public override object? StatusKey { get; } = SharedSpaceVillainArcadeComponent.Indicators.HealthLimiter;
|
||||||
|
|
||||||
|
public override void ToggleValue(EntityUid owner, bool setting)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade))
|
||||||
|
{
|
||||||
|
arcade.OverflowFlag = !setting;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool GetValue(EntityUid owner)
|
||||||
|
{
|
||||||
|
return EntityManager.TryGetComponent<SpaceVillainArcadeComponent>(owner, out var arcade)
|
||||||
|
&& !arcade.OverflowFlag;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
|
||||||
|
if (IsPowered(wire.Owner) && EntityManager.HasComponent<SpaceVillainArcadeComponent>(wire.Owner))
|
||||||
|
{
|
||||||
|
lightState = !GetValue(wire.Owner)
|
||||||
|
? StatusLightState.BlinkingSlow
|
||||||
|
: StatusLightState.On;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_color,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -2,23 +2,17 @@ using System;
|
|||||||
using System.Threading;
|
using System.Threading;
|
||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.Atmos.Monitor.Systems;
|
using Content.Server.Atmos.Monitor.Systems;
|
||||||
using Content.Server.DeviceNetwork.Components;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.VendingMachines; // TODO: Move this out of vending machines???
|
|
||||||
using Content.Server.WireHacking;
|
|
||||||
using Content.Shared.Atmos.Monitor.Components;
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
using Robust.Shared.GameObjects;
|
using Robust.Shared.GameObjects;
|
||||||
using Robust.Shared.IoC;
|
using Robust.Shared.IoC;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Network;
|
using Robust.Shared.Network;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
using static Content.Shared.Wires.SharedWiresComponent.WiresAction;
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Monitor.Components
|
namespace Content.Server.Atmos.Monitor.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class AirAlarmComponent : Component, IWires
|
public sealed class AirAlarmComponent : Component
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
|
|
||||||
@@ -33,188 +27,6 @@ namespace Content.Server.Atmos.Monitor.Components
|
|||||||
|
|
||||||
public HashSet<NetUserId> ActivePlayers = new();
|
public HashSet<NetUserId> ActivePlayers = new();
|
||||||
|
|
||||||
public bool FullAccess = false;
|
|
||||||
public bool CanSync = true;
|
public bool CanSync = true;
|
||||||
|
|
||||||
// <-- Wires -->
|
|
||||||
|
|
||||||
private CancellationTokenSource _powerPulsedCancel = new();
|
|
||||||
private int PowerPulsedTimeout = 30;
|
|
||||||
|
|
||||||
private enum Wires
|
|
||||||
{
|
|
||||||
// Cutting this kills power.
|
|
||||||
// Pulsing it disrupts power.
|
|
||||||
Power,
|
|
||||||
// Cutting this allows full access.
|
|
||||||
// Pulsing this does nothing.
|
|
||||||
Access,
|
|
||||||
// Cutting/Remending this resets ONLY from panic mode.
|
|
||||||
// Pulsing this sets panic mode.
|
|
||||||
Panic,
|
|
||||||
// Cutting this clears sync'd devices, and makes
|
|
||||||
// the alarm unable to resync.
|
|
||||||
// Pulsing this resyncs all devices (ofc current
|
|
||||||
// implementation just auto-does this anyways)
|
|
||||||
DeviceSync,
|
|
||||||
// This does nothing. (placeholder for AI wire,
|
|
||||||
// if that ever gets implemented)
|
|
||||||
Dummy
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterWires(WiresComponent.WiresBuilder builder)
|
|
||||||
{
|
|
||||||
foreach (var wire in Enum.GetValues<Wires>())
|
|
||||||
builder.CreateWire(wire);
|
|
||||||
|
|
||||||
UpdateWires();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateWires()
|
|
||||||
{
|
|
||||||
if (_airAlarmSystem == null)
|
|
||||||
_airAlarmSystem = EntitySystem.Get<AirAlarmSystem>();
|
|
||||||
|
|
||||||
if (!_entMan.TryGetComponent<WiresComponent>(Owner, out var wires)) return;
|
|
||||||
|
|
||||||
var pwrLightState = (PowerPulsed, PowerCut) switch {
|
|
||||||
(true, false) => StatusLightState.BlinkingFast,
|
|
||||||
(_, true) => StatusLightState.Off,
|
|
||||||
(_, _) => StatusLightState.On
|
|
||||||
};
|
|
||||||
|
|
||||||
var powerLight = new StatusLightData(Color.Yellow, pwrLightState, "POWR");
|
|
||||||
|
|
||||||
var accessLight = new StatusLightData(
|
|
||||||
Color.Green,
|
|
||||||
wires.IsWireCut(Wires.Access) ? StatusLightState.Off : StatusLightState.On,
|
|
||||||
"ACC"
|
|
||||||
);
|
|
||||||
|
|
||||||
var panicLight = new StatusLightData(
|
|
||||||
Color.Red,
|
|
||||||
CurrentMode == AirAlarmMode.Panic ? StatusLightState.On : StatusLightState.Off,
|
|
||||||
"PAN"
|
|
||||||
);
|
|
||||||
|
|
||||||
var syncLightState = StatusLightState.BlinkingSlow;
|
|
||||||
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent) && !atmosMonitorComponent.NetEnabled)
|
|
||||||
syncLightState = StatusLightState.Off;
|
|
||||||
else if (DeviceData.Count != 0)
|
|
||||||
syncLightState = StatusLightState.On;
|
|
||||||
|
|
||||||
var syncLight = new StatusLightData(Color.Orange, syncLightState, "NET");
|
|
||||||
|
|
||||||
wires.SetStatus(AirAlarmWireStatus.Power, powerLight);
|
|
||||||
wires.SetStatus(AirAlarmWireStatus.Access, accessLight);
|
|
||||||
wires.SetStatus(AirAlarmWireStatus.Panic, panicLight);
|
|
||||||
wires.SetStatus(AirAlarmWireStatus.DeviceSync, syncLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _powerCut;
|
|
||||||
private bool PowerCut
|
|
||||||
{
|
|
||||||
get => _powerCut;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_powerCut = value;
|
|
||||||
SetPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _powerPulsed;
|
|
||||||
private bool PowerPulsed
|
|
||||||
{
|
|
||||||
get => _powerPulsed && !_powerCut;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_powerPulsed = value;
|
|
||||||
SetPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPower()
|
|
||||||
{
|
|
||||||
if (_entMan.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var receiverComponent)
|
|
||||||
&& _entMan.HasComponent<WiresComponent>(Owner))
|
|
||||||
receiverComponent.PowerDisabled = PowerPulsed || PowerCut;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WiresUpdate(WiresUpdateEventArgs args)
|
|
||||||
{
|
|
||||||
if (!_entMan.TryGetComponent<DeviceNetworkComponent>(Owner, out var deviceNetworkComponent)) return;
|
|
||||||
|
|
||||||
if (_airAlarmSystem == null)
|
|
||||||
_airAlarmSystem = EntitySystem.Get<AirAlarmSystem>();
|
|
||||||
|
|
||||||
switch (args.Action)
|
|
||||||
{
|
|
||||||
case Pulse:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Power:
|
|
||||||
PowerPulsed = true;
|
|
||||||
_powerPulsedCancel.Cancel();
|
|
||||||
_powerPulsedCancel = new CancellationTokenSource();
|
|
||||||
Owner.SpawnTimer(TimeSpan.FromSeconds(PowerPulsedTimeout),
|
|
||||||
() => PowerPulsed = false,
|
|
||||||
_powerPulsedCancel.Token);
|
|
||||||
break;
|
|
||||||
case Wires.Panic:
|
|
||||||
if (CurrentMode != AirAlarmMode.Panic)
|
|
||||||
_airAlarmSystem.SetMode(Owner, deviceNetworkComponent.Address, AirAlarmMode.Panic, true, false);
|
|
||||||
break;
|
|
||||||
case Wires.DeviceSync:
|
|
||||||
_airAlarmSystem.SyncAllDevices(Owner);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Mend:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Power:
|
|
||||||
_powerPulsedCancel.Cancel();
|
|
||||||
PowerPulsed = false;
|
|
||||||
PowerCut = false;
|
|
||||||
break;
|
|
||||||
case Wires.Panic:
|
|
||||||
if (CurrentMode == AirAlarmMode.Panic)
|
|
||||||
_airAlarmSystem.SetMode(Owner, deviceNetworkComponent.Address, AirAlarmMode.Filtering, true, false);
|
|
||||||
break;
|
|
||||||
case Wires.Access:
|
|
||||||
FullAccess = false;
|
|
||||||
break;
|
|
||||||
case Wires.DeviceSync:
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
atmosMonitorComponent.NetEnabled = true;
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case Cut:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.DeviceSync:
|
|
||||||
DeviceData.Clear();
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
{
|
|
||||||
atmosMonitorComponent.NetworkAlarmStates.Clear();
|
|
||||||
atmosMonitorComponent.NetEnabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Wires.Power:
|
|
||||||
PowerCut = true;
|
|
||||||
break;
|
|
||||||
case Wires.Access:
|
|
||||||
FullAccess = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateWires();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,165 +1,7 @@
|
|||||||
using System;
|
|
||||||
using System.Threading;
|
|
||||||
using Content.Server.Atmos.Monitor.Systems;
|
|
||||||
using Content.Server.Power.Components;
|
|
||||||
using Content.Server.VendingMachines; // TODO: Move this out of vending machines???
|
|
||||||
using Content.Server.WireHacking;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Atmos.Monitor;
|
|
||||||
using Content.Shared.Atmos.Monitor.Components;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.GameObjects;
|
|
||||||
using Robust.Shared.IoC;
|
|
||||||
using Robust.Shared.Log;
|
|
||||||
using Robust.Shared.Maths;
|
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
using static Content.Shared.Wires.SharedWiresComponent.WiresAction;
|
|
||||||
|
|
||||||
|
|
||||||
namespace Content.Server.Atmos.Monitor.Components
|
namespace Content.Server.Atmos.Monitor.Components
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class FireAlarmComponent : Component, IWires
|
public sealed class FireAlarmComponent : Component
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
|
||||||
|
|
||||||
private AtmosMonitorSystem? _atmosMonitorSystem;
|
|
||||||
private CancellationTokenSource _powerPulsedCancel = new();
|
|
||||||
private int PowerPulsedTimeout = 30;
|
|
||||||
|
|
||||||
// Much more simpler than the air alarm wire set.
|
|
||||||
private enum Wires
|
|
||||||
{
|
|
||||||
// Cutting this kills power,
|
|
||||||
// pulsing it disrupts.
|
|
||||||
Power,
|
|
||||||
// Cutting this disables network
|
|
||||||
// connectivity,
|
|
||||||
// pulsing it sets off an alarm.
|
|
||||||
Alarm,
|
|
||||||
Dummy1,
|
|
||||||
Dummy2,
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _powerCut;
|
|
||||||
private bool PowerCut
|
|
||||||
{
|
|
||||||
get => _powerCut;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_powerCut = value;
|
|
||||||
SetPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _powerPulsed;
|
|
||||||
private bool PowerPulsed
|
|
||||||
{
|
|
||||||
get => _powerPulsed && !_powerCut;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_powerPulsed = value;
|
|
||||||
SetPower();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetPower()
|
|
||||||
{
|
|
||||||
if (_entMan.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var receiverComponent) && _entMan.HasComponent<WiresComponent>(Owner))
|
|
||||||
receiverComponent.PowerDisabled = PowerPulsed || PowerCut;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
public void RegisterWires(WiresComponent.WiresBuilder builder)
|
|
||||||
{
|
|
||||||
builder.CreateWire(Wires.Power);
|
|
||||||
builder.CreateWire(Wires.Alarm);
|
|
||||||
builder.CreateWire(Wires.Dummy1);
|
|
||||||
builder.CreateWire(Wires.Dummy2);
|
|
||||||
|
|
||||||
UpdateWires();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateWires()
|
|
||||||
{
|
|
||||||
if (!_entMan.TryGetComponent<WiresComponent>(Owner, out var wiresComponent)) return;
|
|
||||||
|
|
||||||
var powerLight = new StatusLightData(Color.Yellow, StatusLightState.On, "POWR");
|
|
||||||
|
|
||||||
if (PowerPulsed)
|
|
||||||
powerLight = new StatusLightData(Color.Yellow, StatusLightState.BlinkingFast, "POWR");
|
|
||||||
else if (PowerCut)
|
|
||||||
powerLight = new StatusLightData(Color.Yellow, StatusLightState.Off, "POWR");
|
|
||||||
|
|
||||||
var syncLight = new StatusLightData(Color.Orange, StatusLightState.On, "NET");
|
|
||||||
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
if (!atmosMonitorComponent.NetEnabled)
|
|
||||||
syncLight = new StatusLightData(Color.Orange, StatusLightState.Off, "NET");
|
|
||||||
else if (atmosMonitorComponent.HighestAlarmInNetwork == AtmosMonitorAlarmType.Danger)
|
|
||||||
syncLight = new StatusLightData(Color.Orange, StatusLightState.BlinkingFast, "NET");
|
|
||||||
|
|
||||||
wiresComponent.SetStatus(FireAlarmWireStatus.Power, powerLight);
|
|
||||||
wiresComponent.SetStatus(FireAlarmWireStatus.Alarm, syncLight);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WiresUpdate(WiresUpdateEventArgs args)
|
|
||||||
{
|
|
||||||
if (_atmosMonitorSystem == null)
|
|
||||||
_atmosMonitorSystem = EntitySystem.Get<AtmosMonitorSystem>();
|
|
||||||
|
|
||||||
switch (args.Action)
|
|
||||||
{
|
|
||||||
case Pulse:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Power:
|
|
||||||
PowerPulsed = true;
|
|
||||||
_powerPulsedCancel.Cancel();
|
|
||||||
_powerPulsedCancel = new CancellationTokenSource();
|
|
||||||
Owner.SpawnTimer(TimeSpan.FromSeconds(PowerPulsedTimeout),
|
|
||||||
() => PowerPulsed = false,
|
|
||||||
_powerPulsedCancel.Token);
|
|
||||||
break;
|
|
||||||
case Wires.Alarm:
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
_atmosMonitorSystem.Alert(Owner, AtmosMonitorAlarmType.Danger, monitor: atmosMonitorComponent);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Mend:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Power:
|
|
||||||
_powerPulsedCancel.Cancel();
|
|
||||||
PowerPulsed = false;
|
|
||||||
PowerCut = false;
|
|
||||||
break;
|
|
||||||
case Wires.Alarm:
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
atmosMonitorComponent.NetEnabled = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Cut:
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Power:
|
|
||||||
PowerCut = true;
|
|
||||||
break;
|
|
||||||
case Wires.Alarm:
|
|
||||||
if (_entMan.TryGetComponent<AtmosMonitorComponent>(Owner, out var atmosMonitorComponent))
|
|
||||||
atmosMonitorComponent.NetEnabled = false;
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateWires();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ using Content.Server.DeviceNetwork.Components;
|
|||||||
using Content.Server.DeviceNetwork.Systems;
|
using Content.Server.DeviceNetwork.Systems;
|
||||||
using Content.Server.Popups;
|
using Content.Server.Popups;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.Wires;
|
||||||
using Content.Shared.Access.Components;
|
using Content.Shared.Access.Components;
|
||||||
using Content.Shared.Access.Systems;
|
using Content.Shared.Access.Systems;
|
||||||
using Content.Shared.Atmos;
|
using Content.Shared.Atmos;
|
||||||
@@ -263,7 +263,7 @@ namespace Content.Server.Atmos.Monitor.Systems
|
|||||||
if (!EntityManager.TryGetComponent(uid, out AccessReaderComponent reader) || user == null)
|
if (!EntityManager.TryGetComponent(uid, out AccessReaderComponent reader) || user == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (!_accessSystem.IsAllowed(reader, user.Value) && !component.FullAccess)
|
if (!_accessSystem.IsAllowed(reader, user.Value))
|
||||||
{
|
{
|
||||||
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, Filter.Entities(user.Value));
|
_popup.PopupEntity(Loc.GetString("air-alarm-ui-access-denied"), user.Value, Filter.Entities(user.Value));
|
||||||
return false;
|
return false;
|
||||||
@@ -401,7 +401,7 @@ namespace Content.Server.Atmos.Monitor.Systems
|
|||||||
// _airAlarmDataSystem.UpdateDeviceData(uid, args.SenderAddress, data);
|
// _airAlarmDataSystem.UpdateDeviceData(uid, args.SenderAddress, data);
|
||||||
//
|
//
|
||||||
_uiSystem.TrySendUiMessage(uid, SharedAirAlarmInterfaceKey.Key, new AirAlarmUpdateDeviceDataMessage(args.SenderAddress, data));
|
_uiSystem.TrySendUiMessage(uid, SharedAirAlarmInterfaceKey.Key, new AirAlarmUpdateDeviceDataMessage(args.SenderAddress, data));
|
||||||
if (HasComp<WiresComponent>(uid)) controller.UpdateWires();
|
// if (HasComp<WiresComponent>(uid)) controller.UpdateWires();
|
||||||
if (!controller.DeviceData.TryAdd(args.SenderAddress, data))
|
if (!controller.DeviceData.TryAdd(args.SenderAddress, data))
|
||||||
controller.DeviceData[args.SenderAddress] = data;
|
controller.DeviceData[args.SenderAddress] = data;
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,75 @@
|
|||||||
|
using Content.Server.Atmos.Monitor.Components;
|
||||||
|
using Content.Server.Atmos.Monitor.Systems;
|
||||||
|
using Content.Server.DeviceNetwork.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Atmos.Monitor.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Monitor;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class AirAlarmPanicWire : BaseWireAction
|
||||||
|
{
|
||||||
|
private string _text = "PANC";
|
||||||
|
private Color _color = Color.Red;
|
||||||
|
|
||||||
|
private AirAlarmSystem _airAlarmSystem = default!;
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AirAlarmWireStatus.Panic;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner) && EntityManager.TryGetComponent<AirAlarmComponent>(wire.Owner, out var alarm))
|
||||||
|
{
|
||||||
|
lightState = alarm.CurrentMode == AirAlarmMode.Panic
|
||||||
|
? StatusLightState.On
|
||||||
|
: StatusLightState.Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_color,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_airAlarmSystem = EntitySystem.Get<AirAlarmSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<DeviceNetworkComponent>(wire.Owner, out var devNet))
|
||||||
|
{
|
||||||
|
_airAlarmSystem.SetMode(wire.Owner, devNet.Address, AirAlarmMode.Panic, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<DeviceNetworkComponent>(wire.Owner, out var devNet)
|
||||||
|
&& EntityManager.TryGetComponent<AirAlarmComponent>(wire.Owner, out var alarm)
|
||||||
|
&& alarm.CurrentMode == AirAlarmMode.Panic)
|
||||||
|
{
|
||||||
|
_airAlarmSystem.SetMode(wire.Owner, devNet.Address, AirAlarmMode.Filtering, true, false, alarm);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<DeviceNetworkComponent>(wire.Owner, out var devNet))
|
||||||
|
{
|
||||||
|
_airAlarmSystem.SetMode(wire.Owner, devNet.Address, AirAlarmMode.Panic, true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,77 @@
|
|||||||
|
using Content.Server.Atmos.Monitor.Components;
|
||||||
|
using Content.Server.Atmos.Monitor.Systems;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Atmos.Monitor;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Atmos.Monitor;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class AtmosMonitorDeviceNetWire : BaseWireAction
|
||||||
|
{
|
||||||
|
// whether or not this wire will send out an alarm upon
|
||||||
|
// being pulsed
|
||||||
|
[DataField("alarmOnPulse")]
|
||||||
|
private bool _alarmOnPulse = false;
|
||||||
|
|
||||||
|
private string _text = "NETW";
|
||||||
|
private Color _color = Color.Orange;
|
||||||
|
|
||||||
|
private AtmosMonitorSystem _atmosMonitorSystem = default!;
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AtmosMonitorAlarmWireActionKeys.Network;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
|
||||||
|
if (IsPowered(wire.Owner) && EntityManager.TryGetComponent<AtmosMonitorComponent>(wire.Owner, out var monitor))
|
||||||
|
{
|
||||||
|
lightState = monitor.HighestAlarmInNetwork == AtmosMonitorAlarmType.Danger
|
||||||
|
? StatusLightState.BlinkingFast
|
||||||
|
: StatusLightState.On;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_color,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_atmosMonitorSystem = EntitySystem.Get<AtmosMonitorSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AtmosMonitorComponent>(wire.Owner, out var monitor))
|
||||||
|
{
|
||||||
|
monitor.NetEnabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AtmosMonitorComponent>(wire.Owner, out var monitor))
|
||||||
|
{
|
||||||
|
monitor.NetEnabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (_alarmOnPulse)
|
||||||
|
{
|
||||||
|
_atmosMonitorSystem.Alert(wire.Owner, AtmosMonitorAlarmType.Danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.Wires;
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using System.Collections.Generic;
|
using System.Collections.Generic;
|
||||||
using System.Threading.Tasks;
|
using System.Threading.Tasks;
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.Wires;
|
||||||
using Content.Shared.Construction;
|
using Content.Shared.Construction;
|
||||||
using Content.Shared.Examine;
|
using Content.Shared.Examine;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ using System.Threading;
|
|||||||
using Content.Server.Doors.Systems;
|
using Content.Server.Doors.Systems;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.VendingMachines;
|
using Content.Server.VendingMachines;
|
||||||
using Content.Server.WireHacking;
|
// using Content.Server.WireHacking;
|
||||||
using Content.Shared.Doors;
|
using Content.Shared.Doors;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
@@ -14,8 +14,8 @@ using Robust.Shared.Maths;
|
|||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
// using static Content.Shared.Wires.SharedWiresComponent;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent.WiresAction;
|
// using static Content.Shared.Wires.SharedWiresComponent.WiresAction;
|
||||||
|
|
||||||
namespace Content.Server.Doors.Components
|
namespace Content.Server.Doors.Components
|
||||||
{
|
{
|
||||||
@@ -24,7 +24,7 @@ namespace Content.Server.Doors.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
[ComponentReference(typeof(SharedAirlockComponent))]
|
[ComponentReference(typeof(SharedAirlockComponent))]
|
||||||
public sealed class AirlockComponent : SharedAirlockComponent, IWires
|
public sealed class AirlockComponent : SharedAirlockComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entityManager = default!;
|
[Dependency] private readonly IEntityManager _entityManager = default!;
|
||||||
|
|
||||||
@@ -65,8 +65,8 @@ namespace Content.Server.Doors.Components
|
|||||||
set
|
set
|
||||||
{
|
{
|
||||||
_powerWiresPulsed = value;
|
_powerWiresPulsed = value;
|
||||||
UpdateWiresStatus();
|
// UpdateWiresStatus();
|
||||||
UpdatePowerCutStatus();
|
// UpdatePowerCutStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -83,16 +83,26 @@ namespace Content.Server.Doors.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool _boltLightsWirePulsed = true;
|
private bool _boltLightsEnabled = true;
|
||||||
|
|
||||||
|
public bool BoltLightsEnabled
|
||||||
|
{
|
||||||
|
get => _boltLightsEnabled;
|
||||||
|
set
|
||||||
|
{
|
||||||
|
_boltLightsEnabled = value;
|
||||||
|
UpdateBoltLightStatus();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
private bool BoltLightsVisible
|
public bool BoltLightsVisible
|
||||||
{
|
{
|
||||||
get => _boltLightsWirePulsed && BoltsDown && IsPowered()
|
get => _boltLightsEnabled && BoltsDown && IsPowered()
|
||||||
&& _entityManager.TryGetComponent<DoorComponent>(Owner, out var doorComponent) && doorComponent.State == DoorState.Closed;
|
&& _entityManager.TryGetComponent<DoorComponent>(Owner, out var doorComponent) && doorComponent.State == DoorState.Closed;
|
||||||
set
|
set
|
||||||
{
|
{
|
||||||
_boltLightsWirePulsed = value;
|
_boltLightsEnabled = value;
|
||||||
UpdateBoltLightStatus();
|
UpdateBoltLightStatus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -144,228 +154,6 @@ namespace Content.Server.Doors.Components
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateWiresStatus()
|
|
||||||
{
|
|
||||||
if (!_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent)) return;
|
|
||||||
|
|
||||||
var mainPowerCut = wiresComponent.IsWireCut(Wires.MainPower);
|
|
||||||
var backupPowerCut = wiresComponent.IsWireCut(Wires.BackupPower);
|
|
||||||
var statusLightState = PowerWiresPulsed ? StatusLightState.BlinkingFast : StatusLightState.On;
|
|
||||||
StatusLightData powerLight;
|
|
||||||
if (mainPowerCut && backupPowerCut)
|
|
||||||
{
|
|
||||||
powerLight = new StatusLightData(Color.DarkGoldenrod, StatusLightState.Off, "POWER");
|
|
||||||
}
|
|
||||||
else if (mainPowerCut != backupPowerCut)
|
|
||||||
{
|
|
||||||
powerLight = new StatusLightData(Color.Gold, statusLightState, "POWER");
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
powerLight = new StatusLightData(Color.Yellow, statusLightState, "POWER");
|
|
||||||
}
|
|
||||||
|
|
||||||
var boltStatus =
|
|
||||||
new StatusLightData(Color.Red, BoltsDown ? StatusLightState.On : StatusLightState.Off, "BOLT");
|
|
||||||
var boltLightsStatus = new StatusLightData(Color.Lime,
|
|
||||||
_boltLightsWirePulsed ? StatusLightState.On : StatusLightState.Off, "BOLT LED");
|
|
||||||
|
|
||||||
var timingStatus =
|
|
||||||
new StatusLightData(Color.Orange, (AutoCloseDelayModifier <= 0) ? StatusLightState.Off :
|
|
||||||
!MathHelper.CloseToPercent(AutoCloseDelayModifier, 1.0f) ? StatusLightState.BlinkingSlow :
|
|
||||||
StatusLightState.On,
|
|
||||||
"TIME");
|
|
||||||
|
|
||||||
var safetyStatus =
|
|
||||||
new StatusLightData(Color.Red, Safety ? StatusLightState.On : StatusLightState.Off, "SAFETY");
|
|
||||||
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.PowerIndicator, powerLight);
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.BoltIndicator, boltStatus);
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.BoltLightIndicator, boltLightsStatus);
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.AIControlIndicator, new StatusLightData(Color.Purple, StatusLightState.BlinkingSlow, "AI CTRL"));
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.TimingIndicator, timingStatus);
|
|
||||||
wiresComponent.SetStatus(AirlockWireStatus.SafetyIndicator, safetyStatus);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdatePowerCutStatus()
|
|
||||||
{
|
|
||||||
if (!_entityManager.TryGetComponent<ApcPowerReceiverComponent>(Owner, out var receiverComponent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (PowerWiresPulsed)
|
|
||||||
{
|
|
||||||
receiverComponent.PowerDisabled = true;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_entityManager.TryGetComponent<WiresComponent>(Owner, out var wiresComponent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
receiverComponent.PowerDisabled =
|
|
||||||
wiresComponent.IsWireCut(Wires.MainPower) &&
|
|
||||||
wiresComponent.IsWireCut(Wires.BackupPower);
|
|
||||||
}
|
|
||||||
|
|
||||||
private enum Wires
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Pulsing turns off power for <see cref="AirlockComponent.PowerWiresTimeout"/>.
|
|
||||||
/// Cutting turns off power permanently if <see cref="BackupPower"/> is also cut.
|
|
||||||
/// Mending restores power.
|
|
||||||
/// </summary>
|
|
||||||
MainPower,
|
|
||||||
|
|
||||||
/// <see cref="MainPower"/>
|
|
||||||
BackupPower,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pulsing causes for bolts to toggle (but only raise if power is on)
|
|
||||||
/// Cutting causes Bolts to drop
|
|
||||||
/// Mending does nothing
|
|
||||||
/// </summary>
|
|
||||||
Bolts,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pulsing causes light to toggle
|
|
||||||
/// Cutting causes light to go out
|
|
||||||
/// Mending causes them to go on again
|
|
||||||
/// </summary>
|
|
||||||
BoltLight,
|
|
||||||
|
|
||||||
// Placeholder for when AI is implemented
|
|
||||||
// aaaaany day now.
|
|
||||||
AIControl,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pulsing causes door to close faster
|
|
||||||
/// Cutting disables door timer, causing door to stop closing automatically
|
|
||||||
/// Mending restores door timer
|
|
||||||
/// </summary>
|
|
||||||
Timing,
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Pulsing toggles safety
|
|
||||||
/// Cutting disables safety
|
|
||||||
/// Mending enables safety
|
|
||||||
/// </summary>
|
|
||||||
Safety,
|
|
||||||
}
|
|
||||||
|
|
||||||
public void RegisterWires(WiresComponent.WiresBuilder builder)
|
|
||||||
{
|
|
||||||
builder.CreateWire(Wires.MainPower);
|
|
||||||
builder.CreateWire(Wires.BackupPower);
|
|
||||||
builder.CreateWire(Wires.Bolts);
|
|
||||||
builder.CreateWire(Wires.BoltLight);
|
|
||||||
builder.CreateWire(Wires.Timing);
|
|
||||||
builder.CreateWire(Wires.Safety);
|
|
||||||
|
|
||||||
UpdateWiresStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WiresUpdate(WiresUpdateEventArgs args)
|
|
||||||
{
|
|
||||||
if (!_entityManager.TryGetComponent<DoorComponent>(Owner, out var doorComponent))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.Action == Pulse)
|
|
||||||
{
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.MainPower:
|
|
||||||
case Wires.BackupPower:
|
|
||||||
PowerWiresPulsed = true;
|
|
||||||
_powerWiresPulsedTimerCancel.Cancel();
|
|
||||||
_powerWiresPulsedTimerCancel = new CancellationTokenSource();
|
|
||||||
Owner.SpawnTimer(TimeSpan.FromSeconds(PowerWiresTimeout),
|
|
||||||
() => PowerWiresPulsed = false,
|
|
||||||
_powerWiresPulsedTimerCancel.Token);
|
|
||||||
break;
|
|
||||||
case Wires.Bolts:
|
|
||||||
if (!BoltsDown)
|
|
||||||
{
|
|
||||||
SetBoltsWithAudio(true);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (IsPowered()) // only raise again if powered
|
|
||||||
{
|
|
||||||
SetBoltsWithAudio(false);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
case Wires.BoltLight:
|
|
||||||
// we need to change the property here to set the appearance again
|
|
||||||
BoltLightsVisible = !_boltLightsWirePulsed;
|
|
||||||
break;
|
|
||||||
case Wires.Timing:
|
|
||||||
// This is permanent, until the wire gets cut & mended.
|
|
||||||
AutoCloseDelayModifier = 0.5f;
|
|
||||||
EntitySystem.Get<AirlockSystem>().UpdateAutoClose(Owner, this);
|
|
||||||
break;
|
|
||||||
case Wires.Safety:
|
|
||||||
Safety = !Safety;
|
|
||||||
Dirty();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (args.Action == Mend)
|
|
||||||
{
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.MainPower:
|
|
||||||
case Wires.BackupPower:
|
|
||||||
// mending power wires instantly restores power
|
|
||||||
_powerWiresPulsedTimerCancel?.Cancel();
|
|
||||||
PowerWiresPulsed = false;
|
|
||||||
break;
|
|
||||||
case Wires.BoltLight:
|
|
||||||
BoltLightsVisible = true;
|
|
||||||
break;
|
|
||||||
case Wires.Timing:
|
|
||||||
AutoCloseDelayModifier = 1;
|
|
||||||
EntitySystem.Get<AirlockSystem>().UpdateAutoClose(Owner, this);
|
|
||||||
break;
|
|
||||||
case Wires.Safety:
|
|
||||||
Safety = true;
|
|
||||||
Dirty();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (args.Action == Cut)
|
|
||||||
{
|
|
||||||
switch (args.Identifier)
|
|
||||||
{
|
|
||||||
case Wires.Bolts:
|
|
||||||
SetBoltsWithAudio(true);
|
|
||||||
break;
|
|
||||||
case Wires.BoltLight:
|
|
||||||
BoltLightsVisible = false;
|
|
||||||
break;
|
|
||||||
case Wires.Timing:
|
|
||||||
AutoCloseDelayModifier = 0; // disable auto close
|
|
||||||
EntitySystem.Get<AirlockSystem>().UpdateAutoClose(Owner, this);
|
|
||||||
break;
|
|
||||||
case Wires.Safety:
|
|
||||||
Safety = false;
|
|
||||||
Dirty();
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
UpdateWiresStatus();
|
|
||||||
UpdatePowerCutStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetBoltsWithAudio(bool newBolts)
|
public void SetBoltsWithAudio(bool newBolts)
|
||||||
{
|
{
|
||||||
if (newBolts == BoltsDown)
|
if (newBolts == BoltsDown)
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
using Content.Server.Doors.Components;
|
using Content.Server.Doors.Components;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.Wires;
|
||||||
using Content.Shared.Doors;
|
using Content.Shared.Doors;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
using Content.Shared.Doors.Systems;
|
using Content.Shared.Doors.Systems;
|
||||||
@@ -15,6 +15,8 @@ namespace Content.Server.Doors.Systems
|
|||||||
{
|
{
|
||||||
public sealed class AirlockSystem : SharedAirlockSystem
|
public sealed class AirlockSystem : SharedAirlockSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly WiresSystem _wiresSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
@@ -128,7 +130,7 @@ namespace Content.Server.Doors.Systems
|
|||||||
if (TryComp<WiresComponent>(uid, out var wiresComponent) && wiresComponent.IsPanelOpen &&
|
if (TryComp<WiresComponent>(uid, out var wiresComponent) && wiresComponent.IsPanelOpen &&
|
||||||
EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||||
{
|
{
|
||||||
wiresComponent.OpenInterface(actor.PlayerSession);
|
_wiresSystem.OpenUserInterface(uid, actor.PlayerSession);
|
||||||
args.Handled = true;
|
args.Handled = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Content.Server.Doors;
|
||||||
|
|
||||||
|
public enum AirlockWireIdentifier : byte
|
||||||
|
{
|
||||||
|
Bolt,
|
||||||
|
BoltLight,
|
||||||
|
Timing,
|
||||||
|
Safety,
|
||||||
|
}
|
||||||
65
Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs
Normal file
65
Content.Server/Doors/WireActions/DoorBoltLightWireAction.cs
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
using Content.Server.Doors.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Doors;
|
||||||
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Doors;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public class DoorBoltLightWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Lime;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "BLIT";
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
StatusLightState lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner) && EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
lightState = door.BoltLightsEnabled
|
||||||
|
? StatusLightState.On
|
||||||
|
: StatusLightState.Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AirlockWireStatus.BoltLightIndicator;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.BoltLightsVisible = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.BoltLightsVisible = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.BoltLightsVisible = !door.BoltLightsEnabled;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Content.Server/Doors/WireActions/DoorBoltWireAction.cs
Normal file
66
Content.Server/Doors/WireActions/DoorBoltWireAction.cs
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
using Content.Server.Doors.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Doors;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Doors;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public class DoorBoltWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Red;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "BOLT";
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
StatusLightState lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner)
|
||||||
|
&& EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
if (door.BoltsDown)
|
||||||
|
{
|
||||||
|
lightState = StatusLightState.On;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AirlockWireStatus.BoltIndicator;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
if (!door.BoltsDown)
|
||||||
|
{
|
||||||
|
door.SetBoltsWithAudio(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// does nothing
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (IsPowered(wire.Owner)
|
||||||
|
&& EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.SetBoltsWithAudio(!door.BoltsDown);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
94
Content.Server/Doors/WireActions/DoorSafetyWireAction.cs
Normal file
94
Content.Server/Doors/WireActions/DoorSafetyWireAction.cs
Normal file
@@ -0,0 +1,94 @@
|
|||||||
|
using Content.Server.Doors.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Doors;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Doors;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public class DoorSafetyWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Red;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "SAFE";
|
||||||
|
|
||||||
|
[DataField("timeout")]
|
||||||
|
private int _timeout = 30;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner)
|
||||||
|
&& EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
lightState = door.Safety
|
||||||
|
? StatusLightState.On
|
||||||
|
: StatusLightState.Off;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AirlockWireStatus.SafetyIndicator;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
door.Safety = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.Safety = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.Safety = false;
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, _timeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitSafetyTimerFinish, wire));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(Wire wire)
|
||||||
|
{
|
||||||
|
if (!IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AwaitSafetyTimerFinish(Wire wire)
|
||||||
|
{
|
||||||
|
if (!wire.IsCut)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.Safety = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PulseTimeoutKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
}
|
||||||
105
Content.Server/Doors/WireActions/DoorTimingWireAction.cs
Normal file
105
Content.Server/Doors/WireActions/DoorTimingWireAction.cs
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
using Content.Server.Doors.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Doors;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Doors;
|
||||||
|
|
||||||
|
[DataDefinition]
|
||||||
|
public class DoorTimingWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Orange;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "TIMR";
|
||||||
|
|
||||||
|
[DataField("timeout")]
|
||||||
|
private int _timeout = 30;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
var lightState = StatusLightState.Off;
|
||||||
|
if (IsPowered(wire.Owner)
|
||||||
|
&& EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
switch (door.AutoCloseDelayModifier)
|
||||||
|
{
|
||||||
|
case 0f:
|
||||||
|
lightState = StatusLightState.Off;
|
||||||
|
break;
|
||||||
|
case <= 0.5f:
|
||||||
|
lightState = StatusLightState.BlinkingSlow;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
lightState = StatusLightState.On;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = AirlockWireStatus.TimingIndicator;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
door.AutoCloseDelayModifier = 0f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.AutoCloseDelayModifier = 1f;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.AutoCloseDelayModifier = 0.5f;
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, _timeout, PulseTimeoutKey.Key, new TimedWireEvent(AwaitTimingTimerFinish, wire));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(Wire wire)
|
||||||
|
{
|
||||||
|
if (!IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PulseTimeoutKey.Key);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// timing timer??? ???
|
||||||
|
private void AwaitTimingTimerFinish(Wire wire)
|
||||||
|
{
|
||||||
|
if (!wire.IsCut)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<AirlockComponent>(wire.Owner, out var door))
|
||||||
|
{
|
||||||
|
door.AutoCloseDelayModifier = 1f;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PulseTimeoutKey : byte
|
||||||
|
{
|
||||||
|
Key
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -6,12 +6,15 @@ using Content.Server.Power.Components;
|
|||||||
using Content.Server.Power.EntitySystems;
|
using Content.Server.Power.EntitySystems;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Server.VendingMachines;
|
using Content.Server.VendingMachines;
|
||||||
using Content.Server.WireHacking;
|
// using Content.Server.WireHacking;
|
||||||
|
using Content.Shared.ActionBlocker;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Singularity.Components;
|
using Content.Shared.Singularity.Components;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
using Robust.Shared.ViewVariables;
|
||||||
|
// using static Content.Shared.Wires.SharedWiresComponent;
|
||||||
using Timer = Robust.Shared.Timing.Timer;
|
using Timer = Robust.Shared.Timing.Timer;
|
||||||
|
|
||||||
namespace Content.Server.ParticleAccelerator.Components
|
namespace Content.Server.ParticleAccelerator.Components
|
||||||
@@ -23,7 +26,7 @@ namespace Content.Server.ParticleAccelerator.Components
|
|||||||
/// Also contains primary logic for actual PA behavior, part scanning, etc...
|
/// Also contains primary logic for actual PA behavior, part scanning, etc...
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class ParticleAcceleratorControlBoxComponent : ParticleAcceleratorPartComponent, IWires
|
public sealed class ParticleAcceleratorControlBoxComponent : ParticleAcceleratorPartComponent
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entMan = default!;
|
[Dependency] private readonly IEntityManager _entMan = default!;
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
@@ -103,7 +106,7 @@ namespace Content.Server.ParticleAccelerator.Components
|
|||||||
{
|
{
|
||||||
base.Startup();
|
base.Startup();
|
||||||
|
|
||||||
UpdateWireStatus();
|
// UpdateWireStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
// This is the power state for the PA control box itself.
|
// This is the power state for the PA control box itself.
|
||||||
@@ -185,12 +188,14 @@ namespace Content.Server.ParticleAccelerator.Components
|
|||||||
|
|
||||||
UserInterface?.SetState(state);
|
UserInterface?.SetState(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void OnRemove()
|
protected override void OnRemove()
|
||||||
{
|
{
|
||||||
UserInterface?.CloseAll();
|
UserInterface?.CloseAll();
|
||||||
base.OnRemove();
|
base.OnRemove();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
void IWires.RegisterWires(WiresComponent.WiresBuilder builder)
|
void IWires.RegisterWires(WiresComponent.WiresBuilder builder)
|
||||||
{
|
{
|
||||||
builder.CreateWire(ParticleAcceleratorControlBoxWires.Toggle);
|
builder.CreateWire(ParticleAcceleratorControlBoxWires.Toggle);
|
||||||
@@ -308,6 +313,7 @@ namespace Content.Server.ParticleAccelerator.Components
|
|||||||
wires.SetStatus(ParticleAcceleratorWireStatus.Limiter, limiterLight);
|
wires.SetStatus(ParticleAcceleratorWireStatus.Limiter, limiterLight);
|
||||||
wires.SetStatus(ParticleAcceleratorWireStatus.Strength, strengthLight);
|
wires.SetStatus(ParticleAcceleratorWireStatus.Strength, strengthLight);
|
||||||
}
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
public void RescanParts()
|
public void RescanParts()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -45,7 +45,10 @@ namespace Content.Server.Power.Components
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
[ViewVariables(VVAccess.ReadWrite)]
|
||||||
[DataField("powerDisabled")]
|
[DataField("powerDisabled")]
|
||||||
public bool PowerDisabled { get => !NetworkLoad.Enabled; set => NetworkLoad.Enabled = !value; }
|
public bool PowerDisabled {
|
||||||
|
get => !NetworkLoad.Enabled;
|
||||||
|
set => NetworkLoad.Enabled = !value;
|
||||||
|
}
|
||||||
|
|
||||||
public bool? PoweredLastUpdate;
|
public bool? PoweredLastUpdate;
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Server.WireHacking;
|
using Content.Server.Wires;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
|
|
||||||
namespace Content.Server.Power.EntitySystems
|
namespace Content.Server.Power.EntitySystems
|
||||||
|
|||||||
@@ -5,7 +5,6 @@ using Content.Server.NodeContainer.Nodes;
|
|||||||
using Content.Server.Power.Components;
|
using Content.Server.Power.Components;
|
||||||
using Content.Server.Power.NodeGroups;
|
using Content.Server.Power.NodeGroups;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Server.WireHacking;
|
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
|
|
||||||
@@ -99,4 +98,3 @@ internal sealed class PowerMonitoringConsoleSystem : EntitySystem
|
|||||||
return -x.Size.CompareTo(y.Size);
|
return -x.Size.CompareTo(y.Size);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
266
Content.Server/Power/PowerWireAction.cs
Normal file
266
Content.Server/Power/PowerWireAction.cs
Normal file
@@ -0,0 +1,266 @@
|
|||||||
|
using System;
|
||||||
|
using System.Threading;
|
||||||
|
using System.Threading.Tasks;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Electrocution;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Wires;
|
||||||
|
using Content.Shared.Power;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.Maths;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Power;
|
||||||
|
|
||||||
|
// Generic power wire action. Use on anything
|
||||||
|
// that requires power.
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class PowerWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
[DataField("color")]
|
||||||
|
private Color _statusColor = Color.Red;
|
||||||
|
|
||||||
|
[DataField("name")]
|
||||||
|
private string _text = "POWR";
|
||||||
|
|
||||||
|
[DataField("pulseTimeout")]
|
||||||
|
private int _pulseTimeout = 30;
|
||||||
|
|
||||||
|
private ElectrocutionSystem _electrocutionSystem = default!;
|
||||||
|
|
||||||
|
public override object StatusKey { get; } = PowerWireActionKey.Status;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire)
|
||||||
|
{
|
||||||
|
StatusLightState lightState = StatusLightState.Off;
|
||||||
|
if (WiresSystem.TryGetData(wire.Owner, PowerWireActionInternalKeys.MainWire, out int main)
|
||||||
|
&& main != wire.Id)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
if (WiresSystem.TryGetData(wire.Owner, PowerWireActionKey.Pulsed, out bool pulsed)
|
||||||
|
&& pulsed)
|
||||||
|
{
|
||||||
|
lightState = StatusLightState.BlinkingSlow;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
lightState = (AllWiresCut(wire.Owner))
|
||||||
|
? StatusLightState.Off
|
||||||
|
: StatusLightState.On;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return new StatusLightData(
|
||||||
|
_statusColor,
|
||||||
|
lightState,
|
||||||
|
_text);
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool AllWiresCut(EntityUid owner)
|
||||||
|
{
|
||||||
|
return WiresSystem.TryGetData(owner, PowerWireActionInternalKeys.CutWires, out int? cut)
|
||||||
|
&& WiresSystem.TryGetData(owner, PowerWireActionInternalKeys.WireCount, out int? count)
|
||||||
|
&& count == cut;
|
||||||
|
}
|
||||||
|
|
||||||
|
// I feel like these two should be within ApcPowerReceiverComponent at this point.
|
||||||
|
// Getting it from a dictionary is significantly more expensive.
|
||||||
|
private void SetPower(EntityUid owner, bool pulsed)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(owner, out ApcPowerReceiverComponent? power))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (pulsed)
|
||||||
|
{
|
||||||
|
power.PowerDisabled = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WiresSystem.TryGetData(owner, PowerWireActionInternalKeys.CutWires, out int? cut)
|
||||||
|
&& WiresSystem.TryGetData(owner, PowerWireActionInternalKeys.WireCount, out int? count))
|
||||||
|
{
|
||||||
|
if (AllWiresCut(owner))
|
||||||
|
{
|
||||||
|
power.PowerDisabled = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (WiresSystem.TryGetData(owner, PowerWireActionKey.Pulsed, out bool isPulsed)
|
||||||
|
&& isPulsed)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
power.PowerDisabled = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetWireCuts(EntityUid owner, bool isCut)
|
||||||
|
{
|
||||||
|
if (WiresSystem.TryGetData(owner, PowerWireActionInternalKeys.CutWires, out int? cut))
|
||||||
|
{
|
||||||
|
cut = isCut ? cut + 1 : cut - 1;
|
||||||
|
WiresSystem.SetData(owner, PowerWireActionInternalKeys.CutWires, cut);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetElectrified(EntityUid used, bool setting, ElectrifiedComponent? electrified = null)
|
||||||
|
{
|
||||||
|
if (electrified == null
|
||||||
|
&& !EntityManager.TryGetComponent(used, out electrified))
|
||||||
|
return;
|
||||||
|
|
||||||
|
electrified.Enabled = setting;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <returns>false if failed, true otherwise</returns>
|
||||||
|
private bool TrySetElectrocution(EntityUid user, Wire wire, bool timed = false)
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent<ApcPowerReceiverComponent>(wire.Owner, out var power)
|
||||||
|
&& EntityManager.TryGetComponent<ElectrifiedComponent>(wire.Owner, out var electrified))
|
||||||
|
{
|
||||||
|
// always set this to true
|
||||||
|
SetElectrified(wire.Owner, true, electrified);
|
||||||
|
|
||||||
|
// if we were electrified, then return false
|
||||||
|
var electrifiedAttempt = _electrocutionSystem.TryDoElectrifiedAct(wire.Owner, user);
|
||||||
|
|
||||||
|
// if this is timed, we set up a doAfter so that the
|
||||||
|
// electrocution continues - unless cancelled
|
||||||
|
//
|
||||||
|
// if the power is disabled however, just don't bother
|
||||||
|
if (timed && IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, _pulseTimeout, PowerWireActionKey.ElectrifiedCancel, new TimedWireEvent(AwaitElectrifiedCancel, wire));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SetElectrified(wire.Owner, false, electrified);
|
||||||
|
}
|
||||||
|
|
||||||
|
return !electrifiedAttempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
|
||||||
|
_electrocutionSystem = EntitySystem.Get<ElectrocutionSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
// This should add a wire into the entity's state, whether it be
|
||||||
|
// in WiresComponent or ApcPowerReceiverComponent.
|
||||||
|
public override bool AddWire(Wire wire, int count)
|
||||||
|
{
|
||||||
|
if (!WiresSystem.HasData(wire.Owner, PowerWireActionInternalKeys.CutWires))
|
||||||
|
{
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionInternalKeys.CutWires, 0);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (count == 1)
|
||||||
|
{
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionInternalKeys.MainWire, wire.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionInternalKeys.WireCount, count);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (!TrySetElectrocution(user, wire))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
SetWireCuts(wire.Owner, true);
|
||||||
|
|
||||||
|
SetPower(wire.Owner, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
if (!TrySetElectrocution(user, wire))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// Mending any power wire restores shorts.
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PowerWireActionKey.PulseCancel);
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PowerWireActionKey.ElectrifiedCancel);
|
||||||
|
|
||||||
|
SetWireCuts(wire.Owner, false);
|
||||||
|
|
||||||
|
SetPower(wire.Owner, false);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PowerWireActionKey.ElectrifiedCancel);
|
||||||
|
|
||||||
|
if (!TrySetElectrocution(user, wire, true))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
// disrupted power shouldn't re-disrupt
|
||||||
|
if (WiresSystem.TryGetData(wire.Owner, PowerWireActionKey.Pulsed, out bool pulsedKey)
|
||||||
|
&& pulsedKey)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionKey.Pulsed, true);
|
||||||
|
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, _pulseTimeout, PowerWireActionKey.PulseCancel, new TimedWireEvent(AwaitPulseCancel, wire));
|
||||||
|
|
||||||
|
SetPower(wire.Owner, true);
|
||||||
|
|
||||||
|
// AwaitPulseCancel(wire.Owner, wire, _doAfterSystem.WaitDoAfter(doAfter));
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(Wire wire)
|
||||||
|
{
|
||||||
|
if (!IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
if (!WiresSystem.TryGetData(wire.Owner, PowerWireActionKey.Pulsed, out bool pulsed)
|
||||||
|
|| !pulsed)
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PowerWireActionKey.ElectrifiedCancel);
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, PowerWireActionKey.PulseCancel);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AwaitElectrifiedCancel(Wire wire)
|
||||||
|
{
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionKey.Electrified, false);
|
||||||
|
SetElectrified(wire.Owner, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AwaitPulseCancel(Wire wire)
|
||||||
|
{
|
||||||
|
WiresSystem.SetData(wire.Owner, PowerWireActionKey.Pulsed, false);
|
||||||
|
SetPower(wire.Owner, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private enum PowerWireActionInternalKeys : byte
|
||||||
|
{
|
||||||
|
MainWire,
|
||||||
|
WireCount,
|
||||||
|
CutWires
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
using System;
|
using System;
|
||||||
using Content.Server.UserInterface;
|
using Content.Server.UserInterface;
|
||||||
using Content.Server.WireHacking;
|
using Content.Shared.Interaction;
|
||||||
using Content.Shared.Sound;
|
using Content.Shared.Sound;
|
||||||
using Content.Shared.VendingMachines;
|
using Content.Shared.VendingMachines;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
@@ -8,13 +8,12 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Serialization.Manager.Attributes;
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
using Robust.Shared.ViewVariables;
|
using Robust.Shared.ViewVariables;
|
||||||
using Content.Server.VendingMachines.systems;
|
using Content.Server.VendingMachines.systems;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
namespace Content.Server.VendingMachines
|
namespace Content.Server.VendingMachines
|
||||||
{
|
{
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed class VendingMachineComponent : SharedVendingMachineComponent, IWires
|
public sealed class VendingMachineComponent : SharedVendingMachineComponent
|
||||||
{
|
{
|
||||||
public bool Ejecting;
|
public bool Ejecting;
|
||||||
public bool Emagged = false;
|
public bool Emagged = false;
|
||||||
@@ -37,45 +36,5 @@ namespace Content.Server.VendingMachines
|
|||||||
[ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(VendingMachineUiKey.Key);
|
[ViewVariables] public BoundUserInterface? UserInterface => Owner.GetUIOrNull(VendingMachineUiKey.Key);
|
||||||
public float NonLimitedEjectForce = 7.5f;
|
public float NonLimitedEjectForce = 7.5f;
|
||||||
public float NonLimitedEjectRange = 5f;
|
public float NonLimitedEjectRange = 5f;
|
||||||
|
|
||||||
public enum Wires
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Shoots a random item when pulsed.
|
|
||||||
/// </summary>
|
|
||||||
Limiter
|
|
||||||
}
|
|
||||||
public void RegisterWires(WiresComponent.WiresBuilder builder)
|
|
||||||
{
|
|
||||||
builder.CreateWire(Wires.Limiter);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void WiresUpdate(WiresUpdateEventArgs args)
|
|
||||||
{
|
|
||||||
var identifier = (Wires) args.Identifier;
|
|
||||||
if (identifier == Wires.Limiter && args.Action == WiresAction.Pulse)
|
|
||||||
{
|
|
||||||
EntitySystem.Get<VendingMachineSystem>().EjectRandom(this.Owner, true, this);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WiresUpdateEventArgs : EventArgs
|
|
||||||
{
|
|
||||||
public readonly object Identifier;
|
|
||||||
public readonly WiresAction Action;
|
|
||||||
|
|
||||||
public WiresUpdateEventArgs(object identifier, WiresAction action)
|
|
||||||
{
|
|
||||||
Identifier = identifier;
|
|
||||||
Action = action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public interface IWires
|
|
||||||
{
|
|
||||||
void RegisterWires(WiresComponent.WiresBuilder builder);
|
|
||||||
void WiresUpdate(WiresUpdateEventArgs args);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|||||||
@@ -1,266 +0,0 @@
|
|||||||
using Content.Server.Tools;
|
|
||||||
using Content.Server.VendingMachines;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Examine;
|
|
||||||
using Content.Shared.Tools.Components;
|
|
||||||
using Content.Shared.GameTicking;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Shared.Audio;
|
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
|
|
||||||
namespace Content.Server.WireHacking
|
|
||||||
{
|
|
||||||
public sealed class WireHackingSystem : EntitySystem
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
[Dependency] private readonly ToolSystem _tools = default!;
|
|
||||||
|
|
||||||
[ViewVariables] private readonly Dictionary<string, WireLayout> _layouts =
|
|
||||||
new();
|
|
||||||
|
|
||||||
public const float ScrewTime = 2.5f;
|
|
||||||
|
|
||||||
public override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
SubscribeLocalEvent<WiresComponent, ComponentStartup>(OnWiresStartup);
|
|
||||||
SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnWiresMapInit);
|
|
||||||
SubscribeLocalEvent<WiresComponent, ExaminedEvent>(OnWiresExamine);
|
|
||||||
SubscribeLocalEvent<WiresComponent, InteractUsingEvent>(OnInteractUsing);
|
|
||||||
|
|
||||||
// Hacking DoAfters
|
|
||||||
SubscribeLocalEvent<WiresComponent, WiresComponent.WiresCutEvent>(OnWiresCut);
|
|
||||||
SubscribeLocalEvent<WiresComponent, WiresComponent.WiresMendedEvent>(OnWiresMended);
|
|
||||||
SubscribeLocalEvent<WiresComponent, WiresComponent.WiresPulsedEvent>(OnWiresPulsed);
|
|
||||||
SubscribeLocalEvent<WiresComponent, WiresComponent.WiresCancelledEvent>(OnWiresCancelled);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresCancelled(EntityUid uid, WiresComponent component, WiresComponent.WiresCancelledEvent args)
|
|
||||||
{
|
|
||||||
component.PendingDoAfters.Remove(args.Wire.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void HackingInteract(WiresComponent component, WiresComponent.Wire wire)
|
|
||||||
{
|
|
||||||
component.PendingDoAfters.Remove(wire.Id);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresCut(EntityUid uid, WiresComponent component, WiresComponent.WiresCutEvent args)
|
|
||||||
{
|
|
||||||
HackingInteract(component, args.Wire);
|
|
||||||
|
|
||||||
// Re-validate
|
|
||||||
// Deletion for user + wires should already be handled by do-after and tool is checked once at end in active-hand anyway.
|
|
||||||
if (!component.CanWiresInteract(args.User, out var tool)) return;
|
|
||||||
|
|
||||||
if (!tool.Qualities.Contains(component.CuttingQuality)) return;
|
|
||||||
|
|
||||||
var wire = args.Wire;
|
|
||||||
|
|
||||||
_tools.PlayToolSound(args.Tool.Owner, args.Tool);
|
|
||||||
wire.IsCut = true;
|
|
||||||
component.UpdateUserInterface();
|
|
||||||
|
|
||||||
wire.Owner.WiresUpdate(new WiresUpdateEventArgs(wire.Identifier, WiresAction.Cut));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresMended(EntityUid uid, WiresComponent component, WiresComponent.WiresMendedEvent args)
|
|
||||||
{
|
|
||||||
HackingInteract(component, args.Wire);
|
|
||||||
|
|
||||||
if (!component.CanWiresInteract(args.User, out var tool)) return;
|
|
||||||
|
|
||||||
if (!tool.Qualities.Contains(component.CuttingQuality)) return;
|
|
||||||
|
|
||||||
var wire = args.Wire;
|
|
||||||
|
|
||||||
_tools.PlayToolSound(args.Tool.Owner, args.Tool);
|
|
||||||
wire.IsCut = false;
|
|
||||||
component.UpdateUserInterface();
|
|
||||||
|
|
||||||
wire.Owner.WiresUpdate(new WiresUpdateEventArgs(wire.Identifier, WiresAction.Mend));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresPulsed(EntityUid uid, WiresComponent component, WiresComponent.WiresPulsedEvent args)
|
|
||||||
{
|
|
||||||
HackingInteract(component, args.Wire);
|
|
||||||
|
|
||||||
if (args.Wire.IsCut || !component.CanWiresInteract(args.User, out var tool)) return;
|
|
||||||
|
|
||||||
if (!tool.Qualities.Contains(component.PulsingQuality)) return;
|
|
||||||
|
|
||||||
var wire = args.Wire;
|
|
||||||
SoundSystem.Play(Filter.Pvs(uid), component.PulseSound.GetSound(), uid);
|
|
||||||
wire.Owner.WiresUpdate(new WiresUpdateEventArgs(wire.Identifier, WiresAction.Pulse));
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresExamine(EntityUid uid, WiresComponent component, ExaminedEvent args)
|
|
||||||
{
|
|
||||||
args.PushMarkup(Loc.GetString(component.IsPanelOpen
|
|
||||||
? "wires-component-on-examine-panel-open"
|
|
||||||
: "wires-component-on-examine-panel-closed"));
|
|
||||||
}
|
|
||||||
|
|
||||||
private async void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args)
|
|
||||||
{
|
|
||||||
if (!TryComp<ToolComponent?>(args.Used, out var tool))
|
|
||||||
return;
|
|
||||||
|
|
||||||
// opens the wires ui if using a tool with cutting or multitool quality on it
|
|
||||||
if (component.IsPanelOpen &&
|
|
||||||
(tool.Qualities.Contains(component.CuttingQuality) ||
|
|
||||||
tool.Qualities.Contains(component.PulsingQuality)))
|
|
||||||
{
|
|
||||||
if (TryComp(args.User, out ActorComponent? actor))
|
|
||||||
{
|
|
||||||
component.OpenInterface(actor.PlayerSession);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// screws the panel open if the tool can do so
|
|
||||||
else if (await _tools.UseTool(tool.Owner, args.User, uid,
|
|
||||||
0f, WireHackingSystem.ScrewTime, component.ScrewingQuality, toolComponent:tool))
|
|
||||||
{
|
|
||||||
component.InvertPanel();
|
|
||||||
if (component.IsPanelOpen)
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(uid), component.ScrewdriverOpenSound.GetSound(), uid);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
SoundSystem.Play(Filter.Pvs(uid), component.ScrewdriverCloseSound.GetSound(), uid);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresStartup(EntityUid uid, WiresComponent component, ComponentStartup args)
|
|
||||||
{
|
|
||||||
WireLayout? layout = null;
|
|
||||||
if (component.LayoutId != null)
|
|
||||||
{
|
|
||||||
_layouts.TryGetValue(component.LayoutId, out layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var wiresProvider in EntityManager.GetComponents<IWires>(uid))
|
|
||||||
{
|
|
||||||
var builder = new WiresComponent.WiresBuilder(component, wiresProvider, layout);
|
|
||||||
wiresProvider.RegisterWires(builder);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (layout != null)
|
|
||||||
{
|
|
||||||
component.WiresList.Sort((a, b) =>
|
|
||||||
{
|
|
||||||
var pA = layout.Specifications[a.Identifier].Position;
|
|
||||||
var pB = layout.Specifications[b.Identifier].Position;
|
|
||||||
|
|
||||||
return pA.CompareTo(pB);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_random.Shuffle(component.WiresList);
|
|
||||||
|
|
||||||
if (component.LayoutId != null)
|
|
||||||
{
|
|
||||||
var dict = new Dictionary<object, WireLayout.WireData>();
|
|
||||||
for (var i = 0; i < component.WiresList.Count; i++)
|
|
||||||
{
|
|
||||||
var d = component.WiresList[i];
|
|
||||||
dict.Add(d.Identifier, new WireLayout.WireData(d.Letter, d.Color, i));
|
|
||||||
}
|
|
||||||
|
|
||||||
_layouts.Add(component.LayoutId, new WireLayout(dict));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
var id = 0;
|
|
||||||
foreach (var wire in component.WiresList)
|
|
||||||
{
|
|
||||||
wire.Id = ++id;
|
|
||||||
}
|
|
||||||
|
|
||||||
component.UpdateUserInterface();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Reset(RoundRestartCleanupEvent ev)
|
|
||||||
{
|
|
||||||
_layouts.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnWiresMapInit(EntityUid uid, WiresComponent component, MapInitEvent args)
|
|
||||||
{
|
|
||||||
if (component.SerialNumber == null)
|
|
||||||
{
|
|
||||||
GenerateSerialNumber(component);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (component.WireSeed == 0)
|
|
||||||
{
|
|
||||||
component.WireSeed = _random.Next(1, int.MaxValue);
|
|
||||||
component.UpdateUserInterface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void GenerateSerialNumber(WiresComponent component)
|
|
||||||
{
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
|
|
||||||
component.SerialNumber = new string(data);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WireLayout
|
|
||||||
{
|
|
||||||
[ViewVariables] public IReadOnlyDictionary<object, WireData> Specifications { get; }
|
|
||||||
|
|
||||||
public WireLayout(IReadOnlyDictionary<object, 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;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,509 +0,0 @@
|
|||||||
using System.Diagnostics.CodeAnalysis;
|
|
||||||
using System.Linq;
|
|
||||||
using Content.Server.DoAfter;
|
|
||||||
using Content.Server.Hands.Components;
|
|
||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Server.VendingMachines;
|
|
||||||
using Content.Shared.Interaction;
|
|
||||||
using Content.Shared.Popups;
|
|
||||||
using Content.Shared.Sound;
|
|
||||||
using Content.Shared.Tools;
|
|
||||||
using Content.Shared.Tools.Components;
|
|
||||||
using Content.Shared.Wires;
|
|
||||||
using Robust.Server.GameObjects;
|
|
||||||
using Robust.Server.Player;
|
|
||||||
using Robust.Shared.Random;
|
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
|
||||||
|
|
||||||
namespace Content.Server.WireHacking
|
|
||||||
{
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed class WiresComponent : SharedWiresComponent
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IRobustRandom _random = default!;
|
|
||||||
[Dependency] private readonly IEntityManager _entities = default!;
|
|
||||||
private bool _isPanelOpen;
|
|
||||||
|
|
||||||
[DataField("cuttingTime")] public float CuttingTime = 1f;
|
|
||||||
|
|
||||||
[DataField("mendTime")] public float MendTime = 1f;
|
|
||||||
|
|
||||||
[DataField("pulseTime")] public float PulseTime = 3f;
|
|
||||||
|
|
||||||
[DataField("screwingQuality", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
|
||||||
public string ScrewingQuality = "Screwing";
|
|
||||||
|
|
||||||
[DataField("cuttingQuality", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
|
||||||
public string CuttingQuality = "Cutting";
|
|
||||||
|
|
||||||
[DataField("pulsingQuality", customTypeSerializer:typeof(PrototypeIdSerializer<ToolQualityPrototype>))]
|
|
||||||
public string PulsingQuality = "Pulsing";
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Make do_afters for hacking unique per wire so we can't spam a single wire.
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<int> PendingDoAfters = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Opening the maintenance panel (typically with a screwdriver) changes this.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool IsPanelOpen
|
|
||||||
{
|
|
||||||
get => _isPanelOpen;
|
|
||||||
private set
|
|
||||||
{
|
|
||||||
if (_isPanelOpen == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isPanelOpen = value;
|
|
||||||
|
|
||||||
if (!_isPanelOpen)
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private bool _isPanelVisible = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Components can set this to prevent the maintenance panel overlay from showing even if it's open
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public bool IsPanelVisible
|
|
||||||
{
|
|
||||||
get => _isPanelVisible;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
if (_isPanelVisible == value)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
_isPanelVisible = value;
|
|
||||||
UpdateAppearance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public string BoardName
|
|
||||||
{
|
|
||||||
get => _boardName;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_boardName = value;
|
|
||||||
UpdateUserInterface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite)]
|
|
||||||
public string? SerialNumber
|
|
||||||
{
|
|
||||||
get => _serialNumber;
|
|
||||||
set
|
|
||||||
{
|
|
||||||
_serialNumber = value;
|
|
||||||
UpdateUserInterface();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateAppearance()
|
|
||||||
{
|
|
||||||
if (_entities.TryGetComponent(Owner, out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(WiresVisuals.MaintenancePanelState, IsPanelOpen && IsPanelVisible);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Contains all registered wires.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public readonly List<Wire> WiresList = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Status messages are displayed at the bottom of the UI.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
private readonly Dictionary<object, object> _statuses = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// <see cref="AssignAppearance"/> and <see cref="WiresBuilder.CreateWire"/>.
|
|
||||||
/// </summary>
|
|
||||||
private readonly List<WireColor> _availableColors =
|
|
||||||
new((WireColor[]) Enum.GetValues(typeof(WireColor)));
|
|
||||||
|
|
||||||
private readonly List<WireLetter> _availableLetters =
|
|
||||||
new((WireLetter[]) Enum.GetValues(typeof(WireLetter)));
|
|
||||||
|
|
||||||
[DataField("BoardName")]
|
|
||||||
private string _boardName = "Wires";
|
|
||||||
|
|
||||||
[DataField("SerialNumber")]
|
|
||||||
private string? _serialNumber;
|
|
||||||
|
|
||||||
// Used to generate wire appearance randomization client side.
|
|
||||||
// We honestly don't care what it is or such but do care that it doesn't change between UI re-opens.
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("WireSeed")]
|
|
||||||
public int WireSeed;
|
|
||||||
[ViewVariables]
|
|
||||||
[DataField("LayoutId")]
|
|
||||||
public string? LayoutId = default;
|
|
||||||
|
|
||||||
[DataField("pulseSound")] public SoundSpecifier PulseSound = new SoundPathSpecifier("/Audio/Effects/multitool_pulse.ogg");
|
|
||||||
|
|
||||||
[DataField("screwdriverOpenSound")]
|
|
||||||
public SoundSpecifier ScrewdriverOpenSound = new SoundPathSpecifier("/Audio/Machines/screwdriveropen.ogg");
|
|
||||||
|
|
||||||
[DataField("screwdriverCloseSound")]
|
|
||||||
public SoundSpecifier ScrewdriverCloseSound = new SoundPathSpecifier("/Audio/Machines/screwdriverclose.ogg");
|
|
||||||
|
|
||||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(WiresUiKey.Key);
|
|
||||||
|
|
||||||
protected override void Initialize()
|
|
||||||
{
|
|
||||||
base.Initialize();
|
|
||||||
|
|
||||||
if (_entities.TryGetComponent(Owner, out AppearanceComponent? appearance))
|
|
||||||
{
|
|
||||||
appearance.SetData(WiresVisuals.MaintenancePanelState, IsPanelOpen);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (UserInterface != null)
|
|
||||||
{
|
|
||||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Returns whether the wire associated with <see cref="identifier"/> is cut.
|
|
||||||
/// </summary>
|
|
||||||
/// <exception cref="ArgumentException"></exception>
|
|
||||||
public bool IsWireCut(object identifier)
|
|
||||||
{
|
|
||||||
var wire = WiresList.Find(x => x.Identifier.Equals(identifier));
|
|
||||||
if (wire == null) throw new ArgumentException();
|
|
||||||
return wire.IsCut;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class Wire
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The component that registered the wire.
|
|
||||||
/// </summary>
|
|
||||||
public IWires 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; }
|
|
||||||
|
|
||||||
/// <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; }
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Registered by components implementing IWires, used to identify which wire the client interacted with.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables]
|
|
||||||
public object Identifier { get; }
|
|
||||||
|
|
||||||
public Wire(IWires owner, bool isCut, WireColor color, WireLetter letter, object identifier)
|
|
||||||
{
|
|
||||||
Owner = owner;
|
|
||||||
IsCut = isCut;
|
|
||||||
Color = color;
|
|
||||||
Letter = letter;
|
|
||||||
Identifier = identifier;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used by <see cref="IWires.RegisterWires"/>.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class WiresBuilder
|
|
||||||
{
|
|
||||||
private readonly WiresComponent _wires;
|
|
||||||
private readonly IWires _owner;
|
|
||||||
private readonly WireLayout? _layout;
|
|
||||||
|
|
||||||
public WiresBuilder(WiresComponent wires, IWires owner, WireLayout? layout)
|
|
||||||
{
|
|
||||||
_wires = wires;
|
|
||||||
_owner = owner;
|
|
||||||
_layout = layout;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void CreateWire(object identifier, (WireColor, WireLetter)? appearance = null, bool isCut = false)
|
|
||||||
{
|
|
||||||
WireLetter letter;
|
|
||||||
WireColor color;
|
|
||||||
if (!appearance.HasValue)
|
|
||||||
{
|
|
||||||
if (_layout != null && _layout.Specifications.TryGetValue(identifier, out var specification))
|
|
||||||
{
|
|
||||||
color = specification.Color;
|
|
||||||
letter = specification.Letter;
|
|
||||||
_wires._availableColors.Remove(color);
|
|
||||||
_wires._availableLetters.Remove(letter);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(color, letter) = _wires.AssignAppearance();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
(color, letter) = appearance.Value;
|
|
||||||
_wires._availableColors.Remove(color);
|
|
||||||
_wires._availableLetters.Remove(letter);
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: ENSURE NO RANDOM OVERLAP.
|
|
||||||
_wires.WiresList.Add(new Wire(_owner, isCut, color, letter, identifier));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Picks a color from <see cref="_availableColors"/> and removes it from the list.
|
|
||||||
/// </summary>
|
|
||||||
/// <returns>The picked color.</returns>
|
|
||||||
private (WireColor, WireLetter) AssignAppearance()
|
|
||||||
{
|
|
||||||
var color = _availableColors.Count == 0 ? WireColor.Red : _random.PickAndTake(_availableColors);
|
|
||||||
var letter = _availableLetters.Count == 0 ? WireLetter.α : _random.PickAndTake(_availableLetters);
|
|
||||||
|
|
||||||
return (color, letter);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Call this from other components to open the wires UI.
|
|
||||||
/// </summary>
|
|
||||||
public void OpenInterface(IPlayerSession session)
|
|
||||||
{
|
|
||||||
UserInterface?.Open(session);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Closes all wire UIs.
|
|
||||||
/// </summary>
|
|
||||||
public void CloseAll()
|
|
||||||
{
|
|
||||||
UserInterface?.CloseAll();
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanWiresInteract(EntityUid user, [NotNullWhen(true)] out ToolComponent? tool)
|
|
||||||
{
|
|
||||||
tool = null;
|
|
||||||
|
|
||||||
if (!_entities.TryGetComponent(user, out HandsComponent? handsComponent))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("wires-component-ui-on-receive-message-no-hands"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!EntitySystem.Get<SharedInteractionSystem>().InRangeUnobstructed(user, Owner))
|
|
||||||
{
|
|
||||||
Owner.PopupMessage(user, Loc.GetString("wires-component-ui-on-receive-message-cannot-reach"));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (handsComponent.ActiveHand?.HeldEntity is not { Valid: true } activeHandEntity ||
|
|
||||||
!_entities.TryGetComponent(activeHandEntity, out tool))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
|
||||||
{
|
|
||||||
var message = serverMsg.Message;
|
|
||||||
switch (message)
|
|
||||||
{
|
|
||||||
case WiresActionMessage msg:
|
|
||||||
var wire = WiresList.Find(x => x.Id == msg.Id);
|
|
||||||
if (wire == null ||
|
|
||||||
serverMsg.Session.AttachedEntity is not {} player ||
|
|
||||||
PendingDoAfters.Contains(wire.Id))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!CanWiresInteract(player, out var tool))
|
|
||||||
return;
|
|
||||||
|
|
||||||
var doAfterSystem = EntitySystem.Get<DoAfterSystem>();
|
|
||||||
|
|
||||||
switch (msg.Action)
|
|
||||||
{
|
|
||||||
case WiresAction.Cut:
|
|
||||||
if (!tool.Qualities.Contains(CuttingQuality))
|
|
||||||
{
|
|
||||||
player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
doAfterSystem.DoAfter(
|
|
||||||
new DoAfterEventArgs(player, CuttingTime, target: Owner)
|
|
||||||
{
|
|
||||||
TargetFinishedEvent = new WiresCutEvent
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
Tool = tool,
|
|
||||||
User = player,
|
|
||||||
},
|
|
||||||
TargetCancelledEvent = new WiresCancelledEvent()
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
},
|
|
||||||
NeedHand = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
PendingDoAfters.Add(wire.Id);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case WiresAction.Mend:
|
|
||||||
if (!tool.Qualities.Contains(CuttingQuality))
|
|
||||||
{
|
|
||||||
player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
doAfterSystem.DoAfter(
|
|
||||||
new DoAfterEventArgs(player, MendTime, target: Owner)
|
|
||||||
{
|
|
||||||
TargetFinishedEvent = new WiresMendedEvent()
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
Tool = tool,
|
|
||||||
User = player,
|
|
||||||
},
|
|
||||||
TargetCancelledEvent = new WiresCancelledEvent()
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
},
|
|
||||||
NeedHand = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
PendingDoAfters.Add(wire.Id);
|
|
||||||
|
|
||||||
break;
|
|
||||||
case WiresAction.Pulse:
|
|
||||||
if (!tool.Qualities.Contains(PulsingQuality))
|
|
||||||
{
|
|
||||||
player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (wire.IsCut)
|
|
||||||
{
|
|
||||||
player.PopupMessageCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-pulse-cut-wire"));
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
doAfterSystem.DoAfter(
|
|
||||||
new DoAfterEventArgs(player, PulseTime, target: Owner)
|
|
||||||
{
|
|
||||||
TargetFinishedEvent = new WiresPulsedEvent
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
Tool = tool,
|
|
||||||
User = player,
|
|
||||||
},
|
|
||||||
TargetCancelledEvent = new WiresCancelledEvent()
|
|
||||||
{
|
|
||||||
Wire = wire,
|
|
||||||
},
|
|
||||||
NeedHand = true,
|
|
||||||
});
|
|
||||||
|
|
||||||
PendingDoAfters.Add(wire.Id);
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WiresCancelledEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public Wire Wire { get; init; } = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public abstract class WiresEvent : EntityEventArgs
|
|
||||||
{
|
|
||||||
public EntityUid User { get; init; } = default!;
|
|
||||||
public Wire Wire { get; init; } = default!;
|
|
||||||
public ToolComponent Tool { get; init; } = default!;
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WiresCutEvent : WiresEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WiresMendedEvent : WiresEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
public sealed class WiresPulsedEvent : WiresEvent
|
|
||||||
{
|
|
||||||
}
|
|
||||||
|
|
||||||
internal void UpdateUserInterface()
|
|
||||||
{
|
|
||||||
var clientList = new List<ClientWire>();
|
|
||||||
foreach (var entry in WiresList)
|
|
||||||
{
|
|
||||||
clientList.Add(new ClientWire(entry.Id, entry.IsCut, entry.Color,
|
|
||||||
entry.Letter));
|
|
||||||
}
|
|
||||||
|
|
||||||
UserInterface?.SetState(
|
|
||||||
new WiresBoundUserInterfaceState(
|
|
||||||
clientList.ToArray(),
|
|
||||||
_statuses.Select(p => new StatusEntry(p.Key, p.Value)).ToArray(),
|
|
||||||
BoardName,
|
|
||||||
SerialNumber,
|
|
||||||
WireSeed));
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetStatus(object statusIdentifier, object status)
|
|
||||||
{
|
|
||||||
if (_statuses.TryGetValue(statusIdentifier, out var storedMessage))
|
|
||||||
{
|
|
||||||
if (storedMessage == status)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_statuses[statusIdentifier] = status;
|
|
||||||
UpdateUserInterface();
|
|
||||||
}
|
|
||||||
/// Just opens/closes the panel state in the right namespace
|
|
||||||
public void InvertPanel()
|
|
||||||
{
|
|
||||||
IsPanelOpen = !IsPanelOpen;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
76
Content.Server/Wires/BaseToggleWireAction.cs
Normal file
76
Content.Server/Wires/BaseToggleWireAction.cs
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Utility class meant to be implemented. This is to
|
||||||
|
/// toggle a value whenever a wire is cut, mended,
|
||||||
|
/// or pulsed.
|
||||||
|
/// </summary>
|
||||||
|
public abstract class BaseToggleWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles the value on the given entity. An implementor
|
||||||
|
/// is expected to handle the value toggle appropriately.
|
||||||
|
/// </summary>
|
||||||
|
public abstract void ToggleValue(EntityUid owner, bool setting);
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the value on the given entity. An implementor
|
||||||
|
/// is expected to handle the value getter properly.
|
||||||
|
/// </summary>
|
||||||
|
public abstract bool GetValue(EntityUid owner);
|
||||||
|
/// <summary>
|
||||||
|
/// Timeout key for the wire, if it is pulsed.
|
||||||
|
/// If this is null, there will be no value revert
|
||||||
|
/// after a given delay, otherwise, the value will
|
||||||
|
/// be set to the opposite of what it currently is
|
||||||
|
/// (according to GetValue)
|
||||||
|
/// </summary>
|
||||||
|
public virtual object? TimeoutKey { get; } = null;
|
||||||
|
public virtual int Delay { get; } = 30;
|
||||||
|
|
||||||
|
public override bool Cut(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
ToggleValue(wire.Owner, false);
|
||||||
|
|
||||||
|
if (TimeoutKey != null)
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, TimeoutKey);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Mend(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
ToggleValue(wire.Owner, true);
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire)
|
||||||
|
{
|
||||||
|
ToggleValue(wire.Owner, !GetValue(wire.Owner));
|
||||||
|
|
||||||
|
if (TimeoutKey != null)
|
||||||
|
{
|
||||||
|
WiresSystem.StartWireAction(wire.Owner, Delay, TimeoutKey, new TimedWireEvent(AwaitPulseCancel, wire));
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Update(Wire wire)
|
||||||
|
{
|
||||||
|
if (TimeoutKey != null && !IsPowered(wire.Owner))
|
||||||
|
{
|
||||||
|
WiresSystem.TryCancelWireAction(wire.Owner, TimeoutKey);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AwaitPulseCancel(Wire wire)
|
||||||
|
{
|
||||||
|
if (!wire.IsCut)
|
||||||
|
{
|
||||||
|
ToggleValue(wire.Owner, !GetValue(wire.Owner));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
54
Content.Server/Wires/BaseWireAction.cs
Normal file
54
Content.Server/Wires/BaseWireAction.cs
Normal file
@@ -0,0 +1,54 @@
|
|||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
using Robust.Shared.IoC;
|
||||||
|
using Robust.Shared.Serialization.Manager.Attributes;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
/// <summary><see cref="IWireAction" /></summary>
|
||||||
|
public abstract class BaseWireAction : IWireAction
|
||||||
|
{
|
||||||
|
public IEntityManager EntityManager = default!;
|
||||||
|
public WiresSystem WiresSystem = default!;
|
||||||
|
|
||||||
|
// not virtual so implementors are aware that they need a nullable here
|
||||||
|
public abstract object? StatusKey { get; }
|
||||||
|
|
||||||
|
// ugly, but IoC doesn't work during deserialization
|
||||||
|
public virtual void Initialize()
|
||||||
|
{
|
||||||
|
EntityManager = IoCManager.Resolve<IEntityManager>();
|
||||||
|
|
||||||
|
WiresSystem = EntitySystem.Get<WiresSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public virtual bool AddWire(Wire wire, int count) => count == 1;
|
||||||
|
public abstract bool Cut(EntityUid user, Wire wire);
|
||||||
|
public abstract bool Mend(EntityUid user, Wire wire);
|
||||||
|
public abstract bool Pulse(EntityUid user, Wire wire);
|
||||||
|
public virtual void Update(Wire wire)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
public abstract StatusLightData? GetStatusLightData(Wire wire);
|
||||||
|
|
||||||
|
// most things that use wires are powered by *something*, so
|
||||||
|
//
|
||||||
|
// this isn't required by any wire system methods though, so whatever inherits it here
|
||||||
|
// can use it
|
||||||
|
/// <summary>
|
||||||
|
/// Utility function to check if this given entity is powered.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>true if powered, false otherwise</returns>
|
||||||
|
public bool IsPowered(EntityUid uid)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent<ApcPowerReceiverComponent>(uid, out var power)
|
||||||
|
|| power.PowerDisabled) // there's some kind of race condition here?
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return power.Powered;
|
||||||
|
}
|
||||||
|
}
|
||||||
29
Content.Server/Wires/DummyWireAction.cs
Normal file
29
Content.Server/Wires/DummyWireAction.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Content.Shared.Wires;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
// Exists so that dummy wires can be added.
|
||||||
|
//
|
||||||
|
// You *shouldn't* be adding these as raw
|
||||||
|
// wire actions, but it's here anyways as
|
||||||
|
// a serializable class for consistency.
|
||||||
|
// C'est la vie.
|
||||||
|
[DataDefinition]
|
||||||
|
public sealed class DummyWireAction : BaseWireAction
|
||||||
|
{
|
||||||
|
public override object? StatusKey { get; } = null;
|
||||||
|
|
||||||
|
public override StatusLightData? GetStatusLightData(Wire wire) => null;
|
||||||
|
public override bool AddWire(Wire wire, int count) => true;
|
||||||
|
public override bool Cut(EntityUid user, Wire wire) => true;
|
||||||
|
public override bool Mend(EntityUid user, Wire wire) => true;
|
||||||
|
public override bool Pulse(EntityUid user, Wire wire) => true;
|
||||||
|
|
||||||
|
// doesn't matter if you get any information off of this,
|
||||||
|
// if you really want to mess with dummy wires, you should
|
||||||
|
// probably code your own implementation?
|
||||||
|
private enum DummyWireActionIdentifier
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
}
|
||||||
|
}
|
||||||
80
Content.Server/Wires/IWireAction.cs
Normal file
80
Content.Server/Wires/IWireAction.cs
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
using Content.Shared.Wires;
|
||||||
|
using Robust.Shared.GameObjects;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// An interface used by WiresSystem to allow compositional wiresets.
|
||||||
|
/// This is expected to be flyweighted, do not store per-entity state
|
||||||
|
/// within an object/class that implements IWireAction.
|
||||||
|
/// </summary>
|
||||||
|
public interface IWireAction
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// This is to link the wire's status with
|
||||||
|
/// its corresponding UI key. If this is null,
|
||||||
|
/// GetStatusLightData MUST also return null,
|
||||||
|
/// otherwise nothing happens.
|
||||||
|
/// </summary>
|
||||||
|
public object? StatusKey { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when the wire in the layout
|
||||||
|
/// is created for the first time. Ensures
|
||||||
|
/// that the referenced action has all
|
||||||
|
/// the correct system references (plus
|
||||||
|
/// other information if needed,
|
||||||
|
/// but wire actions should NOT be stateful!)
|
||||||
|
/// </summary>
|
||||||
|
public void Initialize();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Called when a wire is finally processed
|
||||||
|
/// by WiresSystem upon wire layout
|
||||||
|
/// creation. Use this to set specific details
|
||||||
|
/// about the state of the entity in question.
|
||||||
|
///
|
||||||
|
/// If this returns false, this will convert
|
||||||
|
/// the given wire into a 'dummy' wire instead.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="wire">The wire in the entity's WiresComponent.</param>
|
||||||
|
/// <param name="count">The current count of this instance of the wire type.</param>
|
||||||
|
public bool AddWire(Wire wire, int count);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What happens when this wire is cut.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user attempting to interact with the wire.</param>
|
||||||
|
/// <param name="wire">The wire being interacted with.</param>
|
||||||
|
/// <returns>true if successful, false otherwise.</summary>
|
||||||
|
public bool Cut(EntityUid user, Wire wire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What happens when this wire is mended.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user attempting to interact with the wire.</param>
|
||||||
|
/// <param name="wire">The wire being interacted with.</param>
|
||||||
|
/// <returns>true if successful, false otherwise.</summary>
|
||||||
|
public bool Mend(EntityUid user, Wire wire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What happens when this wire is pulsed.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="user">The user attempting to interact with the wire.</param>
|
||||||
|
/// <param name="wire">The wire being interacted with.</param>
|
||||||
|
/// <returns>true if successful, false otherwise.</summary>
|
||||||
|
public bool Pulse(EntityUid user, Wire wire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used when a wire's state on an entity needs to be updated.
|
||||||
|
/// Mostly for things related to entity events, e.g., power.
|
||||||
|
/// </summary>
|
||||||
|
public void Update(Wire wire);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used for when WiresSystem requires the status light data
|
||||||
|
/// for display on the client.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns>StatusLightData to display light data, null to have no status light.</returns>
|
||||||
|
public StatusLightData? GetStatusLightData(Wire wire);
|
||||||
|
}
|
||||||
38
Content.Server/Wires/WireLayout.cs
Normal file
38
Content.Server/Wires/WireLayout.cs
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// WireLayout prototype.
|
||||||
|
///
|
||||||
|
/// This is meant for ease of organizing wire sets on entities that use
|
||||||
|
/// wires. Once one of these is initialized, it should be stored in the
|
||||||
|
/// WiresSystem as a functional wire set.
|
||||||
|
/// </summary>
|
||||||
|
[Prototype("wireLayout")]
|
||||||
|
public sealed class WireLayoutPrototype : IPrototype, IInheritingPrototype
|
||||||
|
{
|
||||||
|
[IdDataFieldAttribute]
|
||||||
|
public string ID { get; } = default!;
|
||||||
|
|
||||||
|
[ParentDataField(typeof(AbstractPrototypeIdSerializer<WireLayoutPrototype>))]
|
||||||
|
public string? Parent { get; } = default!;
|
||||||
|
|
||||||
|
[AbstractDataField]
|
||||||
|
public bool Abstract { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How many wires in this layout will do
|
||||||
|
/// nothing (these are added upon layout
|
||||||
|
/// initialization)
|
||||||
|
/// </summary>
|
||||||
|
[DataField("dummyWires")]
|
||||||
|
public int DummyWires { get; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// All the valid IWireActions currently in this layout.
|
||||||
|
/// </summary>
|
||||||
|
[DataField("wires")]
|
||||||
|
public List<IWireAction>? Wires { get; }
|
||||||
|
}
|
||||||
90
Content.Server/Wires/WiresComponent.cs
Normal file
90
Content.Server/Wires/WiresComponent.cs
Normal file
@@ -0,0 +1,90 @@
|
|||||||
|
using Content.Shared.Sound;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed class WiresComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Is the panel open for this entity's wires?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool IsPanelOpen { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Should this entity's wires panel be visible at all?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public bool IsPanelVisible { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The name of this entity's internal board.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("BoardName")]
|
||||||
|
public string BoardName { get; set; } = "Wires";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The layout ID of this entity's wires.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("LayoutId", required: true)]
|
||||||
|
public string LayoutId { get; set; } = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The serial number of this board. Randomly generated upon start,
|
||||||
|
/// does not need to be set.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public string? SerialNumber { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The seed that dictates the wires appearance, as well as
|
||||||
|
/// the status ordering on the UI client side.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public int WireSeed { get; set; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The list of wires currently active on this entity.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public List<Wire> WiresList { get; set; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Queue of wires saved while the wire's DoAfter event occurs, to prevent too much spam.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public List<int> WiresQueue { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If this should follow the layout saved the first time the layout dictated by the
|
||||||
|
/// layout ID is generated, or if a new wire order should be generated every time.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
[DataField("alwaysRandomize")]
|
||||||
|
public bool AlwaysRandomize { get; }
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Per wire status, keyed by an object.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<object, object> Statuses { get; } = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The state data for the set of wires inside of this entity.
|
||||||
|
/// This is so that wire objects can be flyweighted between
|
||||||
|
/// entities without any issues.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables]
|
||||||
|
public Dictionary<object, object> StateData { get; } = new();
|
||||||
|
|
||||||
|
[DataField("pulseSound")]
|
||||||
|
public SoundSpecifier PulseSound = new SoundPathSpecifier("/Audio/Effects/multitool_pulse.ogg");
|
||||||
|
|
||||||
|
[DataField("screwdriverOpenSound")]
|
||||||
|
public SoundSpecifier ScrewdriverOpenSound = new SoundPathSpecifier("/Audio/Machines/screwdriveropen.ogg");
|
||||||
|
|
||||||
|
[DataField("screwdriverCloseSound")]
|
||||||
|
public SoundSpecifier ScrewdriverCloseSound = new SoundPathSpecifier("/Audio/Machines/screwdriverclose.ogg");
|
||||||
|
}
|
||||||
954
Content.Server/Wires/WiresSystem.cs
Normal file
954
Content.Server/Wires/WiresSystem.cs
Normal file
@@ -0,0 +1,954 @@
|
|||||||
|
using System.Diagnostics.CodeAnalysis;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Threading;
|
||||||
|
using Content.Server.DoAfter;
|
||||||
|
using Content.Server.Hands.Systems;
|
||||||
|
using Content.Server.Hands.Components;
|
||||||
|
using Content.Server.Power.Components;
|
||||||
|
using Content.Server.Tools;
|
||||||
|
using Content.Shared.Examine;
|
||||||
|
using Content.Shared.GameTicking;
|
||||||
|
using Content.Shared.Interaction;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Tools.Components;
|
||||||
|
using Content.Shared.Wires;
|
||||||
|
using Robust.Server.GameObjects;
|
||||||
|
using Robust.Server.Player;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
|
||||||
|
namespace Content.Server.Wires;
|
||||||
|
|
||||||
|
public sealed class WiresSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
[Dependency] private readonly IPrototypeManager _protoMan = default!;
|
||||||
|
[Dependency] private readonly AudioSystem _audioSystem = default!;
|
||||||
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
[Dependency] private readonly ToolSystem _toolSystem = default!;
|
||||||
|
[Dependency] private readonly SharedPopupSystem _popupSystem = default!;
|
||||||
|
[Dependency] private readonly SharedInteractionSystem _interactionSystem = default!;
|
||||||
|
[Dependency] private readonly HandsSystem _handsSystem = default!;
|
||||||
|
[Dependency] private readonly DoAfterSystem _doAfter = default!;
|
||||||
|
|
||||||
|
// This is where all the wire layouts are stored.
|
||||||
|
[ViewVariables] private readonly Dictionary<string, WireLayout> _layouts = new();
|
||||||
|
|
||||||
|
private const float ScrewTime = 2.5f;
|
||||||
|
private const float ToolTime = 1f;
|
||||||
|
|
||||||
|
private static DummyWireAction _dummyWire = new DummyWireAction();
|
||||||
|
|
||||||
|
#region Initialization
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
_dummyWire.Initialize();
|
||||||
|
|
||||||
|
SubscribeLocalEvent<RoundRestartCleanupEvent>(Reset);
|
||||||
|
|
||||||
|
// this is a broadcast event
|
||||||
|
SubscribeLocalEvent<WireToolFinishedEvent>(OnToolFinished);
|
||||||
|
SubscribeLocalEvent<WiresComponent, ComponentStartup>(OnWiresStartup);
|
||||||
|
SubscribeLocalEvent<WiresComponent, WiresActionMessage>(OnWiresActionMessage);
|
||||||
|
SubscribeLocalEvent<WiresComponent, InteractUsingEvent>(OnInteractUsing);
|
||||||
|
SubscribeLocalEvent<WiresComponent, ExaminedEvent>(OnExamine);
|
||||||
|
SubscribeLocalEvent<WiresComponent, MapInitEvent>(OnMapInit);
|
||||||
|
SubscribeLocalEvent<WiresComponent, TimedWireEvent>(OnTimedWire);
|
||||||
|
SubscribeLocalEvent<WiresComponent, PowerChangedEvent>(OnWiresPowered);
|
||||||
|
SubscribeLocalEvent<WiresComponent, OnWireDoAfterEvent>(OnWireDoAfter);
|
||||||
|
SubscribeLocalEvent<WiresComponent, OnWireDoAfterCancelEvent>(OnWireDoAfterCancel);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetOrCreateWireLayout(EntityUid uid, WiresComponent? wires = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref wires))
|
||||||
|
return;
|
||||||
|
|
||||||
|
WireLayout? layout = null;
|
||||||
|
List<Wire>? wireSet = null;
|
||||||
|
if (wires.LayoutId != null)
|
||||||
|
{
|
||||||
|
if (!wires.AlwaysRandomize)
|
||||||
|
{
|
||||||
|
TryGetLayout(wires.LayoutId, out layout);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_protoMan.TryIndex(wires.LayoutId, out WireLayoutPrototype? layoutPrototype))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// does the prototype have a parent (and are the wires empty?) if so, we just create
|
||||||
|
// a new layout based on that
|
||||||
|
//
|
||||||
|
// TODO: Merge wire layouts...
|
||||||
|
if (!string.IsNullOrEmpty(layoutPrototype.Parent) && layoutPrototype.Wires == null)
|
||||||
|
{
|
||||||
|
var parent = layoutPrototype.Parent;
|
||||||
|
|
||||||
|
if (!_protoMan.TryIndex(parent, out WireLayoutPrototype? parentPrototype))
|
||||||
|
return;
|
||||||
|
|
||||||
|
layoutPrototype = parentPrototype;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (layoutPrototype.Wires != null)
|
||||||
|
{
|
||||||
|
foreach (var wire in layoutPrototype.Wires)
|
||||||
|
{
|
||||||
|
wire.Initialize();
|
||||||
|
}
|
||||||
|
|
||||||
|
wireSet = CreateWireSet(uid, layout, layoutPrototype.Wires, layoutPrototype.DummyWires);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wireSet == null || wireSet.Count == 0)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wires.WiresList.AddRange(wireSet);
|
||||||
|
|
||||||
|
Dictionary<object, int> types = new Dictionary<object, int>();
|
||||||
|
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
var wireType = wire.Action.GetType();
|
||||||
|
if (types.ContainsKey(wireType))
|
||||||
|
{
|
||||||
|
types[wireType] += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
types.Add(wireType, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
wire.Id = id;
|
||||||
|
id++;
|
||||||
|
|
||||||
|
// 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];
|
||||||
|
|
||||||
|
var wireType = d.Action.GetType();
|
||||||
|
if (types.ContainsKey(wireType))
|
||||||
|
{
|
||||||
|
types[wireType] += 1;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
types.Add(wireType, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
d.Id = i;
|
||||||
|
|
||||||
|
if (!d.Action.AddWire(d, types[wireType]))
|
||||||
|
{
|
||||||
|
d.Action = _dummyWire;
|
||||||
|
}
|
||||||
|
|
||||||
|
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++)
|
||||||
|
{
|
||||||
|
wireSet.Add(CreateWire(uid, _dummyWire, wires.Count + i, layout, colors, letters));
|
||||||
|
}
|
||||||
|
|
||||||
|
return wireSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private Wire CreateWire(EntityUid uid, IWireAction action, int position, WireLayout? layout, List<WireColor> colors, List<WireLetter> letters)
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
action);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWiresStartup(EntityUid uid, WiresComponent component, ComponentStartup args)
|
||||||
|
{
|
||||||
|
if (!String.IsNullOrEmpty(component.LayoutId))
|
||||||
|
SetOrCreateWireLayout(uid, component);
|
||||||
|
|
||||||
|
UpdateUserInterface(uid);
|
||||||
|
}
|
||||||
|
#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>
|
||||||
|
/// <param id="key">The key used to cancel the action.</param>
|
||||||
|
public bool TryCancelWireAction(EntityUid owner, object key)
|
||||||
|
{
|
||||||
|
if (TryGetData(owner, key, out CancellationTokenSource? token))
|
||||||
|
{
|
||||||
|
token.Cancel();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Starts a timed action for this entity.
|
||||||
|
/// </summary>
|
||||||
|
/// <param id="delay">How long this takes to finish</param>
|
||||||
|
/// <param id="key">The key used to cancel the action</param>
|
||||||
|
/// <param id="onFinish">The event that is sent out when the wire is finished <see cref="TimedWireEvent" /></param>
|
||||||
|
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)
|
||||||
|
{
|
||||||
|
foreach (var wire in activeWires)
|
||||||
|
{
|
||||||
|
if (wire.CancelToken.IsCancellationRequested)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(owner, wire.OnFinish);
|
||||||
|
_finishedWires.Add((owner, wire));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
wire.TimeLeft -= frameTime;
|
||||||
|
if (wire.TimeLeft <= 0)
|
||||||
|
{
|
||||||
|
RaiseLocalEvent(owner, wire.OnFinish);
|
||||||
|
_finishedWires.Add((owner, wire));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_finishedWires.Count != 0)
|
||||||
|
{
|
||||||
|
foreach (var (owner, wireAction) in _finishedWires)
|
||||||
|
{
|
||||||
|
// sure
|
||||||
|
_activeWires[owner].RemoveAll(action => action.CancelToken == wireAction.CancelToken);
|
||||||
|
|
||||||
|
if (_activeWires[owner].Count == 0)
|
||||||
|
{
|
||||||
|
_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
|
||||||
|
private void OnWiresPowered(EntityUid uid, WiresComponent component, PowerChangedEvent args)
|
||||||
|
{
|
||||||
|
UpdateUserInterface(uid);
|
||||||
|
foreach (var wire in component.WiresList)
|
||||||
|
{
|
||||||
|
wire.Action.Update(wire);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
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))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("wires-component-ui-on-receive-message-no-hands"), uid, Filter.Entities(player));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!_interactionSystem.InRangeUnobstructed(player, uid))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupEntity(Loc.GetString("wires-component-ui-on-receive-message-cannot-reach"), uid, Filter.Entities(player));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWireDoAfter(EntityUid uid, WiresComponent component, OnWireDoAfterEvent args)
|
||||||
|
{
|
||||||
|
UpdateWires(args.Target, args.User, args.Tool, args.Id, args.Action, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnWireDoAfterCancel(EntityUid uid, WiresComponent component, OnWireDoAfterCancelEvent args)
|
||||||
|
{
|
||||||
|
component.WiresQueue.Remove(args.Id);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnInteractUsing(EntityUid uid, WiresComponent component, InteractUsingEvent args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(args.Used, out ToolComponent? tool))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (component.IsPanelOpen &&
|
||||||
|
_toolSystem.HasQuality(args.Used, "Cutting", tool) ||
|
||||||
|
_toolSystem.HasQuality(args.Used, "Pulsing", tool))
|
||||||
|
{
|
||||||
|
if (EntityManager.TryGetComponent(args.User, out ActorComponent? actor))
|
||||||
|
{
|
||||||
|
_uiSystem.GetUiOrNull(uid, WiresUiKey.Key)?.Open(actor.PlayerSession);
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (_toolSystem.UseTool(args.Used, args.User, uid, 0f, ScrewTime, new string[]{ "Screwing" }, doAfterCompleteEvent:new WireToolFinishedEvent(uid), toolComponent:tool))
|
||||||
|
{
|
||||||
|
args.Handled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnToolFinished(WireToolFinishedEvent args)
|
||||||
|
{
|
||||||
|
if (!EntityManager.TryGetComponent(args.Target, out WiresComponent? component))
|
||||||
|
return;
|
||||||
|
|
||||||
|
component.IsPanelOpen = !component.IsPanelOpen;
|
||||||
|
UpdateAppearance(args.Target);
|
||||||
|
|
||||||
|
if (component.IsPanelOpen)
|
||||||
|
{
|
||||||
|
SoundSystem.Play(Filter.Pvs(args.Target), component.ScrewdriverOpenSound.GetSound(), args.Target);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
SoundSystem.Play(Filter.Pvs(args.Target), component.ScrewdriverCloseSound.GetSound(), args.Target);
|
||||||
|
_uiSystem.GetUiOrNull(args.Target, WiresUiKey.Key)?.CloseAll();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExamine(EntityUid uid, WiresComponent component, ExaminedEvent args)
|
||||||
|
{
|
||||||
|
args.PushMarkup(Loc.GetString(component.IsPanelOpen
|
||||||
|
? "wires-component-on-examine-panel-open"
|
||||||
|
: "wires-component-on-examine-panel-closed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapInit(EntityUid uid, WiresComponent component, MapInitEvent args)
|
||||||
|
{
|
||||||
|
if (component.SerialNumber == null)
|
||||||
|
{
|
||||||
|
GenerateSerialNumber(uid, component);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (component.WireSeed == 0)
|
||||||
|
{
|
||||||
|
component.WireSeed = _random.Next(1, int.MaxValue);
|
||||||
|
UpdateUserInterface(uid);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#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);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateAppearance(EntityUid uid, AppearanceComponent? appearance = null, WiresComponent? wires = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(uid, ref appearance, ref wires))
|
||||||
|
return;
|
||||||
|
|
||||||
|
appearance.SetData(WiresVisuals.MaintenancePanelState, wires.IsPanelOpen && wires.IsPanelVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateUserInterface(EntityUid uid, WiresComponent? wires = null, ServerUserInterfaceComponent? ui = null)
|
||||||
|
{
|
||||||
|
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));
|
||||||
|
|
||||||
|
var statusData = entry.Action.GetStatusLightData(entry);
|
||||||
|
if (statusData != null && entry.Action.StatusKey != null)
|
||||||
|
{
|
||||||
|
wires.Statuses[entry.Action.StatusKey] = statusData;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_uiSystem.GetUiOrNull(uid, WiresUiKey.Key)?.SetState(
|
||||||
|
new WiresBoundUserInterfaceState(
|
||||||
|
clientList.ToArray(),
|
||||||
|
wires.Statuses.Select(p => new StatusEntry(p.Key, p.Value)).ToArray(),
|
||||||
|
wires.BoardName,
|
||||||
|
wires.SerialNumber,
|
||||||
|
wires.WireSeed));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void OpenUserInterface(EntityUid uid, IPlayerSession player)
|
||||||
|
{
|
||||||
|
_uiSystem.GetUiOrNull(uid, WiresUiKey.Key)?.Open(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void TryDoWireAction(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(used, ref wires)
|
||||||
|
|| !Resolve(toolEntity, ref tool))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (wires.WiresQueue.Contains(id))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var wire = TryGetWire(used, id, wires);
|
||||||
|
|
||||||
|
if (wire == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case WiresAction.Cut:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Cutting", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case WiresAction.Mend:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Cutting", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
case WiresAction.Pulse:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Pulsing", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-multitool"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
var args = new DoAfterEventArgs(user, ToolTime, default, used)
|
||||||
|
{
|
||||||
|
NeedHand = true,
|
||||||
|
BreakOnStun = true,
|
||||||
|
BreakOnDamage = true,
|
||||||
|
BreakOnUserMove = true,
|
||||||
|
TargetFinishedEvent = new OnWireDoAfterEvent
|
||||||
|
{
|
||||||
|
Target = used,
|
||||||
|
User = user,
|
||||||
|
Tool = toolEntity,
|
||||||
|
Action = action,
|
||||||
|
Id = id
|
||||||
|
},
|
||||||
|
TargetCancelledEvent = new OnWireDoAfterCancelEvent
|
||||||
|
{
|
||||||
|
Id = id
|
||||||
|
}
|
||||||
|
};
|
||||||
|
_doAfter.DoAfter(args);
|
||||||
|
|
||||||
|
wires.WiresQueue.Add(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
private void UpdateWires(EntityUid used, EntityUid user, EntityUid toolEntity, int id, WiresAction action, WiresComponent? wires = null, ToolComponent? tool = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(used, ref wires)
|
||||||
|
|| !Resolve(toolEntity, ref tool))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var wire = TryGetWire(used, id, wires);
|
||||||
|
|
||||||
|
if (wire == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
switch (action)
|
||||||
|
{
|
||||||
|
case WiresAction.Cut:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Cutting", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_toolSystem.PlayToolSound(toolEntity, tool);
|
||||||
|
if (wire.Action.Cut(user, wire))
|
||||||
|
{
|
||||||
|
wire.IsCut = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateUserInterface(used);
|
||||||
|
break;
|
||||||
|
case WiresAction.Mend:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Cutting", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-wirecutters"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_toolSystem.PlayToolSound(toolEntity, tool);
|
||||||
|
if (wire.Action.Mend(user, wire))
|
||||||
|
{
|
||||||
|
wire.IsCut = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
UpdateUserInterface(used);
|
||||||
|
break;
|
||||||
|
case WiresAction.Pulse:
|
||||||
|
if (!_toolSystem.HasQuality(toolEntity, "Pulsing", tool))
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-need-multitool"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wire.IsCut)
|
||||||
|
{
|
||||||
|
_popupSystem.PopupCursor(Loc.GetString("wires-component-ui-on-receive-message-cannot-pulse-cut-wire"), Filter.Entities(user));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wire.Action.Pulse(user, wire);
|
||||||
|
|
||||||
|
UpdateUserInterface(used);
|
||||||
|
SoundSystem.Play(Filter.Pvs(used), wires.PulseSound.GetSound(), used);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
wires.WiresQueue.Remove(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to get the stateful data stored in this entity's WiresComponent.
|
||||||
|
/// </summary>
|
||||||
|
/// <param id="identifier">The key that stores the data in the WiresComponent.</param>
|
||||||
|
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>
|
||||||
|
/// <param id="identifier">The key that stores the data in the WiresComponent.</param>
|
||||||
|
/// <param id="data">The data to store using the given identifier.</param>
|
||||||
|
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>
|
||||||
|
/// <param id="identifier">The key that stores the data in the WiresComponent.</param>
|
||||||
|
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
|
||||||
|
|
||||||
|
#region Events
|
||||||
|
private sealed class WireToolFinishedEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public EntityUid Target { get; }
|
||||||
|
|
||||||
|
public WireToolFinishedEvent(EntityUid target)
|
||||||
|
{
|
||||||
|
Target = target;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class OnWireDoAfterEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public EntityUid User { get; set; }
|
||||||
|
public EntityUid Target { get; set; }
|
||||||
|
public EntityUid Tool { get; set; }
|
||||||
|
public WiresAction Action { get; set; }
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private sealed class OnWireDoAfterCancelEvent : EntityEventArgs
|
||||||
|
{
|
||||||
|
public int Id { get; set; }
|
||||||
|
}
|
||||||
|
#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; }
|
||||||
|
|
||||||
|
/// <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; }
|
||||||
|
|
||||||
|
// The action that this wire performs upon activation.
|
||||||
|
public IWireAction Action { get; set; }
|
||||||
|
|
||||||
|
public Wire(EntityUid owner, bool isCut, WireColor color, WireLetter letter, IWireAction action)
|
||||||
|
{
|
||||||
|
Owner = owner;
|
||||||
|
IsCut = isCut;
|
||||||
|
Color = color;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
12
Content.Shared/Access/SharedAccessWire.cs
Normal file
12
Content.Shared/Access/SharedAccessWire.cs
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Access;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum AccessWireActionKey : byte
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
Status,
|
||||||
|
Pulsed,
|
||||||
|
PulseCancel
|
||||||
|
}
|
||||||
@@ -0,0 +1,9 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Atmos.Monitor;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum AtmosMonitorAlarmWireActionKeys : byte
|
||||||
|
{
|
||||||
|
Network,
|
||||||
|
}
|
||||||
@@ -10,4 +10,15 @@ namespace Content.Shared.Power
|
|||||||
Charging,
|
Charging,
|
||||||
Discharging,
|
Discharging,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public enum PowerWireActionKey : byte
|
||||||
|
{
|
||||||
|
Key,
|
||||||
|
Status,
|
||||||
|
Pulsed,
|
||||||
|
Electrified,
|
||||||
|
PulseCancel,
|
||||||
|
ElectrifiedCancel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,27 +5,23 @@ using Robust.Shared.GameObjects;
|
|||||||
using Robust.Shared.Localization;
|
using Robust.Shared.Localization;
|
||||||
using Robust.Shared.Maths;
|
using Robust.Shared.Maths;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
|
|
||||||
namespace Content.Shared.Wires
|
namespace Content.Shared.Wires
|
||||||
{
|
|
||||||
[Virtual]
|
|
||||||
public class SharedWiresComponent : Component
|
|
||||||
{
|
{
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum WiresVisuals
|
public enum WiresVisuals : byte
|
||||||
{
|
{
|
||||||
MaintenancePanelState
|
MaintenancePanelState
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum WiresUiKey
|
public enum WiresUiKey : byte
|
||||||
{
|
{
|
||||||
Key,
|
Key,
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum WiresAction
|
public enum WiresAction : byte
|
||||||
{
|
{
|
||||||
Mend,
|
Mend,
|
||||||
Cut,
|
Cut,
|
||||||
@@ -33,7 +29,7 @@ namespace Content.Shared.Wires
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum StatusLightState
|
public enum StatusLightState : byte
|
||||||
{
|
{
|
||||||
Off,
|
Off,
|
||||||
On,
|
On,
|
||||||
@@ -41,6 +37,19 @@ namespace Content.Shared.Wires
|
|||||||
BlinkingSlow
|
BlinkingSlow
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public class WiresActionMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public readonly int Id;
|
||||||
|
public readonly WiresAction Action;
|
||||||
|
|
||||||
|
public WiresActionMessage(int id, WiresAction action)
|
||||||
|
{
|
||||||
|
Id = id;
|
||||||
|
Action = action;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
[SuppressMessage("ReSharper", "InconsistentNaming")]
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
@@ -111,7 +120,7 @@ namespace Content.Shared.Wires
|
|||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class WiresBoundUserInterfaceState : BoundUserInterfaceState
|
public class WiresBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
{
|
{
|
||||||
public string BoardName { get; }
|
public string BoardName { get; }
|
||||||
public string? SerialNumber { get; }
|
public string? SerialNumber { get; }
|
||||||
@@ -132,7 +141,16 @@ namespace Content.Shared.Wires
|
|||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public struct StatusEntry
|
public struct StatusEntry
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The key of this status, according to the status dictionary
|
||||||
|
/// server side.
|
||||||
|
/// </summary>
|
||||||
public readonly object Key;
|
public readonly object Key;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// The value of this status, according to the status dictionary
|
||||||
|
/// server side..
|
||||||
|
/// </summary>
|
||||||
public readonly object Value;
|
public readonly object Value;
|
||||||
|
|
||||||
public StatusEntry(object key, object value)
|
public StatusEntry(object key, object value)
|
||||||
@@ -148,12 +166,32 @@ namespace Content.Shared.Wires
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// ClientWire, sent by the server so that the client knows
|
||||||
|
/// what wires there are on an entity.
|
||||||
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class ClientWire
|
public class ClientWire
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// ID of this wire, which corresponds to
|
||||||
|
/// the ID server side.
|
||||||
|
/// </summary>
|
||||||
public int Id;
|
public int Id;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Whether this wire is cut or not.
|
||||||
|
/// </summary>
|
||||||
public bool IsCut;
|
public bool IsCut;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current color of the wire.
|
||||||
|
/// </summary>
|
||||||
public WireColor Color;
|
public WireColor Color;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Current letter of the wire.
|
||||||
|
/// </summary>
|
||||||
public WireLetter Letter;
|
public WireLetter Letter;
|
||||||
|
|
||||||
public ClientWire(int id, bool isCut, WireColor color, WireLetter letter)
|
public ClientWire(int id, bool isCut, WireColor color, WireLetter letter)
|
||||||
@@ -165,20 +203,6 @@ namespace Content.Shared.Wires
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class WiresActionMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public readonly int Id;
|
|
||||||
public readonly WiresAction Action;
|
|
||||||
|
|
||||||
public WiresActionMessage(int id, WiresAction action)
|
|
||||||
{
|
|
||||||
Id = id;
|
|
||||||
Action = action;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public static class HackingWiresExt
|
public static class HackingWiresExt
|
||||||
{
|
{
|
||||||
public static string Name(this WireColor color)
|
public static string Name(this WireColor color)
|
||||||
|
|||||||
@@ -3,7 +3,6 @@ using System.Collections.Generic;
|
|||||||
using Content.Shared.Wires;
|
using Content.Shared.Wires;
|
||||||
using NUnit.Framework;
|
using NUnit.Framework;
|
||||||
using Robust.UnitTesting;
|
using Robust.UnitTesting;
|
||||||
using static Content.Shared.Wires.SharedWiresComponent;
|
|
||||||
|
|
||||||
namespace Content.Tests.Shared
|
namespace Content.Tests.Shared
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
wires-component-ui-on-receive-message-no-hands = You have no hands.
|
wires-component-ui-on-receive-message-no-hands = You have no hands.
|
||||||
wires-component-ui-on-receive-message-cannot-reach = You can't reach there!
|
wires-component-ui-on-receive-message-cannot-reach = You can't reach there!
|
||||||
wires-component-ui-on-receive-message-need-wirecutters = You need to hold a wirecutter in your hand!
|
wires-component-ui-on-receive-message-need-wirecutters = You need to hold a wirecutter in your hand!
|
||||||
|
wires-component-ui-on-receive-message-need-multitool = You need to hold a multitool in your hand!
|
||||||
wires-component-ui-on-receive-message-cannot-pulse-cut-wire = You can't pulse a wire that's been cut!
|
wires-component-ui-on-receive-message-cannot-pulse-cut-wire = You can't pulse a wire that's been cut!
|
||||||
wires-component-on-examine-panel-open = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]open[/color].
|
wires-component-on-examine-panel-open = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]open[/color].
|
||||||
wires-component-on-examine-panel-closed = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]closed[/color].
|
wires-component-on-examine-panel-closed = The [color=lightgray]maintenance panel[/color] is [color=darkgreen]closed[/color].
|
||||||
|
|||||||
@@ -56,6 +56,9 @@
|
|||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
powerLoad: 20
|
powerLoad: 20
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
|
- type: Electrified
|
||||||
|
enabled: false
|
||||||
|
usesApcPower: true
|
||||||
- type: Wires
|
- type: Wires
|
||||||
BoardName: "Airlock Control"
|
BoardName: "Airlock Control"
|
||||||
LayoutId: Airlock
|
LayoutId: Airlock
|
||||||
|
|||||||
@@ -40,6 +40,7 @@
|
|||||||
components:
|
components:
|
||||||
- type: SpaceVillainArcade
|
- type: SpaceVillainArcade
|
||||||
- type: Wires
|
- type: Wires
|
||||||
|
LayoutId: Arcade
|
||||||
BoardName: "Arcade"
|
BoardName: "Arcade"
|
||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.SpaceVillainArcadeUiKey.Key
|
key: enum.SpaceVillainArcadeUiKey.Key
|
||||||
@@ -63,6 +64,9 @@
|
|||||||
- type: ActivatableUI
|
- type: ActivatableUI
|
||||||
key: enum.BlockGameUiKey.Key
|
key: enum.BlockGameUiKey.Key
|
||||||
- type: ActivatableUIRequiresPower
|
- type: ActivatableUIRequiresPower
|
||||||
|
- type: Wires
|
||||||
|
LayoutId: Arcade
|
||||||
|
BoardName: "Arcade"
|
||||||
- type: UserInterface
|
- type: UserInterface
|
||||||
interfaces:
|
interfaces:
|
||||||
- key: enum.BlockGameUiKey.Key
|
- key: enum.BlockGameUiKey.Key
|
||||||
|
|||||||
@@ -52,6 +52,10 @@
|
|||||||
BoardName: "Vending Machine"
|
BoardName: "Vending Machine"
|
||||||
LayoutId: Vending
|
LayoutId: Vending
|
||||||
- type: Anchorable
|
- type: Anchorable
|
||||||
|
- type: DoAfter
|
||||||
|
- type: Electrified
|
||||||
|
enabled: false
|
||||||
|
usesApcPower: true
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
enabled: false
|
enabled: false
|
||||||
castShadows: false
|
castShadows: false
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
components:
|
components:
|
||||||
- type: WallMount
|
- type: WallMount
|
||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
|
- type: Electrified
|
||||||
|
enabled: false
|
||||||
|
usesApcPower: true
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: Apc
|
||||||
|
|||||||
@@ -5,6 +5,9 @@
|
|||||||
components:
|
components:
|
||||||
- type: WallMount
|
- type: WallMount
|
||||||
- type: ApcPowerReceiver
|
- type: ApcPowerReceiver
|
||||||
|
- type: Electrified
|
||||||
|
enabled: false
|
||||||
|
usesApcPower: true
|
||||||
- type: ExtensionCableReceiver
|
- type: ExtensionCableReceiver
|
||||||
- type: DeviceNetwork
|
- type: DeviceNetwork
|
||||||
deviceNetId: Apc
|
deviceNetId: Apc
|
||||||
|
|||||||
52
Resources/Prototypes/Wires/layouts.yml
Normal file
52
Resources/Prototypes/Wires/layouts.yml
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
- type: wireLayout
|
||||||
|
id: Airlock
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:PowerWireAction
|
||||||
|
pulseTimeout: 15
|
||||||
|
- !type:DoorBoltWireAction
|
||||||
|
- !type:DoorBoltLightWireAction
|
||||||
|
- !type:DoorTimingWireAction
|
||||||
|
- !type:DoorSafetyWireAction
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
parent: Airlock
|
||||||
|
id: AirlockSecurity
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
parent: Airlock
|
||||||
|
id: AirlockCommand
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
parent: Airlock
|
||||||
|
id: AirlockArmory
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
id: Vending
|
||||||
|
dummyWires: 2
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:AccessWireAction
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
id: AirAlarm
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:AccessWireAction
|
||||||
|
- !type:AirAlarmPanicWire
|
||||||
|
- !type:AtmosMonitorDeviceNetWire
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
id: FireAlarm
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:AtmosMonitorDeviceNetWire
|
||||||
|
alarmOnPulse: true
|
||||||
|
|
||||||
|
- type: wireLayout
|
||||||
|
id: Arcade
|
||||||
|
wires:
|
||||||
|
- !type:PowerWireAction
|
||||||
|
- !type:ArcadeOverflowWireAction
|
||||||
|
- !type:ArcadePlayerInvincibleWireAction
|
||||||
|
- !type:ArcadeEnemyInvincibleWireAction
|
||||||
Reference in New Issue
Block a user