Apc device network and apc based light switch (#4908)

* Remove WireNet node group from cables
Implement extension cable components and system
Remove connection over distance logic from ApcPower... components

* Add extension cable components to prototypes

* Implement ApcNetwork
Implement ApcNetSwitch

* Fix ignoredComponents.cs

* Add friend attribute to new components

* Add construction graph for a light switch

* Address reviews

* Fix broken test

* Move ConnectionType enum to DeviceNetworkComponent
Change netId data definition to use the ConnectionType enum values

Co-authored-by: Julian Giebel <j.giebel@netrocks.info>
This commit is contained in:
Julian Giebel
2021-10-24 01:23:19 +02:00
committed by GitHub
parent e7f352d6c2
commit 45caf25ea9
43 changed files with 678 additions and 237 deletions

View File

@@ -283,9 +283,13 @@ namespace Content.Client.Entry
"LitOnPowered", "LitOnPowered",
"TriggerOnSignalReceived", "TriggerOnSignalReceived",
"ToggleDoorOnTrigger", "ToggleDoorOnTrigger",
"DeviceNetworkConnection", "DeviceNetworkComponent",
"WiredNetworkConnection", "WiredNetworkConnection",
"WirelessNetworkConnection", "WirelessNetworkConnection",
"ExtensionCableReceiver",
"ExtensionCableProvider",
"ApcNetworkConnection",
"ApcNetSwitch",
"HandLabeler", "HandLabeler",
"Label", "Label",
"GhostRadio", "GhostRadio",

View File

@@ -28,7 +28,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
id: DummyWiredNetworkDevice id: DummyWiredNetworkDevice
components: components:
- type: DeviceNetworkComponent - type: DeviceNetworkComponent
deviceNetId: 1 deviceNetId: Wired
- type: WiredNetworkConnection - type: WiredNetworkConnection
- type: ApcPowerReceiver - type: ApcPowerReceiver
@@ -38,7 +38,7 @@ namespace Content.IntegrationTests.Tests.DeviceNetwork
components: components:
- type: DeviceNetworkComponent - type: DeviceNetworkComponent
frequency: 100 frequency: 100
deviceNetId: 1 deviceNetId: Wireless
- type: WirelessNetworkConnection - type: WirelessNetworkConnection
range: 100 range: 100
"; ";

View File

@@ -3,6 +3,7 @@ using System.Threading.Tasks;
using Content.Server.NodeContainer; using Content.Server.NodeContainer;
using Content.Server.NodeContainer.Nodes; using Content.Server.NodeContainer.Nodes;
using Content.Server.Power.Components; using Content.Server.Power.Components;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.Nodes; using Content.Server.Power.Nodes;
using Content.Shared.Coordinates; using Content.Shared.Coordinates;
using NUnit.Framework; using NUnit.Framework;
@@ -152,6 +153,7 @@ namespace Content.IntegrationTests.Tests.Power
id: ApcPowerReceiverDummy id: ApcPowerReceiverDummy
components: components:
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Transform - type: Transform
anchored: true anchored: true
"; ";
@@ -160,6 +162,7 @@ namespace Content.IntegrationTests.Tests.Power
private IMapManager _mapManager = default!; private IMapManager _mapManager = default!;
private IEntityManager _entityManager = default!; private IEntityManager _entityManager = default!;
private IGameTiming _gameTiming = default!; private IGameTiming _gameTiming = default!;
private ExtensionCableSystem _extensionCableSystem = default!;
[OneTimeSetUp] [OneTimeSetUp]
public async Task Setup() public async Task Setup()
@@ -171,6 +174,7 @@ namespace Content.IntegrationTests.Tests.Power
_mapManager = _server.ResolveDependency<IMapManager>(); _mapManager = _server.ResolveDependency<IMapManager>();
_entityManager = _server.ResolveDependency<IEntityManager>(); _entityManager = _server.ResolveDependency<IEntityManager>();
_gameTiming = _server.ResolveDependency<IGameTiming>(); _gameTiming = _server.ResolveDependency<IGameTiming>();
_extensionCableSystem = _entityManager.EntitySysManager.GetEntitySystem<ExtensionCableSystem>();
} }
/// <summary> /// <summary>
@@ -968,13 +972,12 @@ namespace Content.IntegrationTests.Tests.Power
var apcExtensionEnt = _entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0)); var apcExtensionEnt = _entityManager.SpawnEntity("CableApcExtension", grid.ToCoordinates(0, 0));
var powerReceiverEnt = _entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, 2)); var powerReceiverEnt = _entityManager.SpawnEntity("ApcPowerReceiverDummy", grid.ToCoordinates(0, 2));
var provider = apcExtensionEnt.GetComponent<ApcPowerProviderComponent>();
receiver = powerReceiverEnt.GetComponent<ApcPowerReceiverComponent>(); receiver = powerReceiverEnt.GetComponent<ApcPowerReceiverComponent>();
var battery = apcEnt.GetComponent<BatteryComponent>(); var battery = apcEnt.GetComponent<BatteryComponent>();
apcNetBattery = apcEnt.GetComponent<PowerNetworkBatteryComponent>(); apcNetBattery = apcEnt.GetComponent<PowerNetworkBatteryComponent>();
provider.PowerTransferRange = 5; //arbitrary range to reach receiver _extensionCableSystem.SetProviderTransferRange(apcExtensionEnt.Uid, 5);
receiver.PowerReceptionRange = 5; //arbitrary range to reach provider _extensionCableSystem.SetReceiverReceptionRange(powerReceiverEnt.Uid, 5);
battery.MaxCharge = 10000; //arbitrary nonzero amount of charge battery.MaxCharge = 10000; //arbitrary nonzero amount of charge
battery.CurrentCharge = battery.MaxCharge; //fill battery battery.CurrentCharge = battery.MaxCharge; //fill battery

View File

@@ -0,0 +1,20 @@
using Content.Server.DeviceNetwork.Systems;
using Content.Server.NodeContainer.Nodes;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
namespace Content.Server.DeviceNetwork.Components
{
[RegisterComponent]
[Friend(typeof(ApcNetworkSystem))]
public class ApcNetworkComponent : Component
{
public override string Name => "ApcNetworkConnection";
/// <summary>
/// The node Group the ApcNetworkConnection is connected to
/// </summary>
[ViewVariables] public Node? ConnectedNode;
}
}

View File

@@ -1,3 +1,4 @@
using System;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes; using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
@@ -9,16 +10,22 @@ namespace Content.Server.DeviceNetwork.Components
{ {
public override string Name => "DeviceNetworkComponent"; public override string Name => "DeviceNetworkComponent";
/// <summary> /// <summary>
/// The device networks netID this DeviceNetworkComponent connects to. /// Valid device network NetIDs.
/// The netID is used to seperate device networks that shouldn't interact with each other e.g. wireless and wired. /// The netID is used to separate device networks that shouldn't interact with each other e.g. wireless and wired.
/// The default netID's are_
/// 0 -> Private
/// 1 -> Wired
/// 2 -> Wireless
/// </summary> /// </summary>
[Serializable]
public enum ConnectionType
{
Private,
Wired,
Wireless,
Apc
}
[DataField("deviceNetId")] [DataField("deviceNetId")]
public int DeviceNetId { get; set; } = (int)DeviceNetworkConstants.ConnectionType.Private; public ConnectionType DeviceNetId { get; set; } = ConnectionType.Private;
[DataField("frequency")] [DataField("frequency")]
public int Frequency { get; set; } = 0; public int Frequency { get; set; } = 0;

View File

@@ -0,0 +1,16 @@
using Content.Server.DeviceNetwork.Systems.Devices;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables;
namespace Content.Server.DeviceNetwork.Components.Devices
{
[RegisterComponent]
[Friend(typeof(ApcNetSwitchSystem))]
public class ApcNetSwitchComponent : Component
{
public override string Name => "ApcNetSwitch";
[ViewVariables] public bool State;
}
}

View File

@@ -1,21 +1,38 @@
namespace Content.Server.DeviceNetwork namespace Content.Server.DeviceNetwork
{ {
/// <summary> /// <summary>
/// A collection of utilities to help with using device networks /// A collection of constants to help with using device networks
/// </summary> /// </summary>
public static class DeviceNetworkConstants public static class DeviceNetworkConstants
{ {
public enum ConnectionType /// <summary>
{ /// Invalid address used for broadcasting
Private, /// </summary>
Wired, public const string NullAddress = "######";
Wireless
} #region Commands
/// <summary> /// <summary>
/// The key for command names /// The key for command names
/// E.g. [DeviceNetworkConstants.Command] = "ping" /// E.g. [DeviceNetworkConstants.Command] = "ping"
/// </summary> /// </summary>
public const string Command = "command"; public const string Command = "command";
/// <summary>
/// The command for setting a devices state
/// E.g. to turn a light on or off
/// </summary>
public const string CmdSetState = "set_state";
#endregion
#region SetState
/// <summary>
/// Used with the <see cref="CmdSetState"/> command to turn a device on or off
/// </summary>
public const string StateEnabled = "state_enabled";
#endregion
} }
} }

View File

@@ -0,0 +1,56 @@
using Content.Server.DeviceNetwork.Components;
using Content.Server.NodeContainer;
using JetBrains.Annotations;
using Robust.Shared.GameObjects;
using Content.Server.Power.EntitySystems;
using Content.Server.Power.Nodes;
namespace Content.Server.DeviceNetwork.Systems
{
[UsedImplicitly]
public class ApcNetworkSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ApcNetworkComponent, BeforePacketSentEvent>(OnBeforePacketSent);
SubscribeLocalEvent<ApcNetworkComponent, ExtensionCableSystem.ProviderConnectedEvent>(OnProviderConnected);
SubscribeLocalEvent<ApcNetworkComponent, ExtensionCableSystem.ProviderDisconnectedEvent>(OnProviderDisconnected);
}
/// <summary>
/// Checks if both devices are connected to the same apc
/// </summary>
private void OnBeforePacketSent(EntityUid uid, ApcNetworkComponent receiver, BeforePacketSentEvent args)
{
if (!EntityManager.TryGetComponent(args.Sender, out ApcNetworkComponent? sender)) return;
if (sender.ConnectedNode?.NodeGroup == null || !sender.ConnectedNode.NodeGroup.Equals(receiver.ConnectedNode?.NodeGroup))
{
args.Cancel();
}
}
private void OnProviderConnected(EntityUid uid, ApcNetworkComponent component, ExtensionCableSystem.ProviderConnectedEvent args)
{
if (!args.Provider.Owner.TryGetComponent(out NodeContainerComponent? nodeContainer)) return;
if (nodeContainer.TryGetNode("power", out CableNode? node))
{
component.ConnectedNode = node;
}
else if (nodeContainer.TryGetNode("output", out CableDeviceNode? deviceNode))
{
component.ConnectedNode = deviceNode;
}
}
private void OnProviderDisconnected(EntityUid uid, ApcNetworkComponent component, ExtensionCableSystem.ProviderDisconnectedEvent args)
{
component.ConnectedNode = null;
}
}
}

View File

@@ -17,7 +17,7 @@ namespace Content.Server.DeviceNetwork.Systems
{ {
[Dependency] private readonly IRobustRandom _random = default!; [Dependency] private readonly IRobustRandom _random = default!;
private readonly Dictionary<int, List<DeviceNetworkComponent>> _connections = new(); private readonly Dictionary<DeviceNetworkComponent.ConnectionType, List<DeviceNetworkComponent>> _connections = new();
private readonly Queue<NetworkPacket> _packets = new(); private readonly Queue<NetworkPacket> _packets = new();
public override void Initialize() public override void Initialize()
@@ -139,7 +139,7 @@ namespace Content.Server.DeviceNetwork.Systems
/// <summary> /// <summary>
/// Generates a valid address by randomly generating one and checking if it already exists on the device network with the given device netId. /// Generates a valid address by randomly generating one and checking if it already exists on the device network with the given device netId.
/// </summary> /// </summary>
private string GenerateValidAddress(int netId) private string GenerateValidAddress(DeviceNetworkComponent.ConnectionType netId)
{ {
var unique = false; var unique = false;
var connections = _connections[netId]; var connections = _connections[netId];
@@ -154,7 +154,7 @@ namespace Content.Server.DeviceNetwork.Systems
return address; return address;
} }
private List<DeviceNetworkComponent> ConnectionsForFrequency(int netId, int frequency) private List<DeviceNetworkComponent> ConnectionsForFrequency(DeviceNetworkComponent.ConnectionType netId, int frequency)
{ {
if (!_connections.ContainsKey(netId)) if (!_connections.ContainsKey(netId))
return new List<DeviceNetworkComponent>(); return new List<DeviceNetworkComponent>();
@@ -164,7 +164,7 @@ namespace Content.Server.DeviceNetwork.Systems
return result; return result;
} }
private bool TryGetConnectionWithAddress(int netId, int frequency, string address, [NotNullWhen(true)] out DeviceNetworkComponent connection) private bool TryGetConnectionWithAddress(DeviceNetworkComponent.ConnectionType netId, int frequency, string address, [NotNullWhen(true)] out DeviceNetworkComponent connection)
{ {
var connections = ConnectionsForFrequency(netId, frequency); var connections = ConnectionsForFrequency(netId, frequency);
@@ -180,7 +180,7 @@ namespace Content.Server.DeviceNetwork.Systems
return false; return false;
} }
private List<DeviceNetworkComponent> ConnectionsWithReceiveAll(int netId, int frequency) private List<DeviceNetworkComponent> ConnectionsWithReceiveAll(DeviceNetworkComponent.ConnectionType netId, int frequency)
{ {
if (!_connections.ContainsKey(netId)) if (!_connections.ContainsKey(netId))
return new List<DeviceNetworkComponent>(); return new List<DeviceNetworkComponent>();
@@ -195,7 +195,7 @@ namespace Content.Server.DeviceNetwork.Systems
if (!TryGetConnectionWithAddress(packet.NetId, packet.Frequency, packet.Address, out var connection)) if (!TryGetConnectionWithAddress(packet.NetId, packet.Frequency, packet.Address, out var connection))
return; return;
var receivers = ConnectionsWithReceiveAll(packet.Frequency, packet.NetId); var receivers = ConnectionsWithReceiveAll(packet.NetId, packet.Frequency);
receivers.Add(connection); receivers.Add(connection);
SendToConnections(receivers, packet); SendToConnections(receivers, packet);
@@ -203,7 +203,7 @@ namespace Content.Server.DeviceNetwork.Systems
private void BroadcastPacket(NetworkPacket packet) private void BroadcastPacket(NetworkPacket packet)
{ {
var receivers = ConnectionsForFrequency(packet.Frequency, packet.NetId); var receivers = ConnectionsForFrequency(packet.NetId, packet.Frequency);
SendToConnections(receivers, packet); SendToConnections(receivers, packet);
} }
@@ -223,7 +223,7 @@ namespace Content.Server.DeviceNetwork.Systems
internal struct NetworkPacket internal struct NetworkPacket
{ {
public int NetId; public DeviceNetworkComponent.ConnectionType NetId;
public int Frequency; public int Frequency;
public string Address; public string Address;
public bool Broadcast; public bool Broadcast;

View File

@@ -0,0 +1,54 @@
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Components.Devices;
using Content.Shared.Interaction;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
namespace Content.Server.DeviceNetwork.Systems.Devices
{
public sealed class ApcNetSwitchSystem : EntitySystem
{
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ApcNetSwitchComponent, InteractHandEvent>(OnInteracted);
SubscribeLocalEvent<ApcNetSwitchComponent, PacketSentEvent>(OnPackedReceived);
}
/// <summary>
/// Toggles the state of the switch and sents a <see cref="DeviceNetworkConstants.CmdSetState"/> command with the
/// <see cref="DeviceNetworkConstants.StateEnabled"/> value set to state.
/// </summary>
private void OnInteracted(EntityUid uid, ApcNetSwitchComponent component, InteractHandEvent args)
{
if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? networkComponent)) return;
component.State = !component.State;
var payload = new NetworkPayload
{
[DeviceNetworkConstants.Command] = DeviceNetworkConstants.CmdSetState,
[DeviceNetworkConstants.StateEnabled] = component.State,
};
_deviceNetworkSystem.QueuePacket(uid, DeviceNetworkConstants.NullAddress, networkComponent.Frequency, payload, true);
args.Handled = true;
}
/// <summary>
/// Listens to the <see cref="DeviceNetworkConstants.CmdSetState"/> command of other switches to sync state
/// </summary>
private void OnPackedReceived(EntityUid uid, ApcNetSwitchComponent component, PacketSentEvent args)
{
if (!EntityManager.TryGetComponent(uid, out DeviceNetworkComponent? networkComponent) || args.SenderAddress == networkComponent.Address) return;
if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return;
if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return;
component.State = enabled;
}
}
}

View File

@@ -317,6 +317,10 @@ namespace Content.Server.Light.Components
UpdateLight(); UpdateLight();
} }
public void SetState(bool state)
{
_on = state;
UpdateLight();
}
} }
} }

View File

@@ -1,4 +1,6 @@
using System; using System;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.Ghost; using Content.Server.Ghost;
using Content.Server.Light.Components; using Content.Server.Light.Components;
using Content.Server.MachineLinking.Events; using Content.Server.MachineLinking.Events;
@@ -24,9 +26,11 @@ namespace Content.Server.Light.EntitySystems
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo); SubscribeLocalEvent<PoweredLightComponent, GhostBooEvent>(OnGhostBoo);
SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged); SubscribeLocalEvent<PoweredLightComponent, DamageChangedEvent>(HandleLightDamaged);
SubscribeLocalEvent<PoweredLightComponent, SignalReceivedEvent>(OnSignalReceived);
SubscribeLocalEvent<PoweredLightComponent, PacketSentEvent>(OnPacketReceived);
SubscribeLocalEvent<LitOnPoweredComponent, PowerChangedEvent>(OnPowerChanged); SubscribeLocalEvent<LitOnPoweredComponent, PowerChangedEvent>(OnPowerChanged);
SubscribeLocalEvent<LitOnPoweredComponent, PowerNetBatterySupplyEvent>(OnPowerSupply); SubscribeLocalEvent<LitOnPoweredComponent, PowerNetBatterySupplyEvent>(OnPowerSupply);
} }
@@ -90,6 +94,18 @@ namespace Content.Server.Light.EntitySystems
} }
} }
/// <summary>
/// Turns the light on or of when receiving a <see cref="DeviceNetworkConstants.CmdSetState"/> command.
/// The light is turned on or of according to the <see cref="DeviceNetworkConstants.StateEnabled"/> value
/// </summary>
private void OnPacketReceived(EntityUid uid, PoweredLightComponent component, PacketSentEvent args)
{
if (!args.Data.TryGetValue(DeviceNetworkConstants.Command, out string? command) || command != DeviceNetworkConstants.CmdSetState) return;
if (!args.Data.TryGetValue(DeviceNetworkConstants.StateEnabled, out bool enabled)) return;
component.SetState(enabled);
}
private void OnPowerChanged(EntityUid uid, LitOnPoweredComponent component, PowerChangedEvent args) private void OnPowerChanged(EntityUid uid, LitOnPoweredComponent component, PowerChangedEvent args)
{ {
if (EntityManager.TryGetComponent<PointLightComponent>(uid, out var light)) if (EntityManager.TryGetComponent<PointLightComponent>(uid, out var light))

View File

@@ -1,9 +1,6 @@
using System;
using System.Collections.Generic; using System.Collections.Generic;
using Content.Server.Power.NodeGroups; using Content.Server.Power.NodeGroups;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
namespace Content.Server.Power.Components namespace Content.Server.Power.Components
@@ -13,24 +10,8 @@ namespace Content.Server.Power.Components
{ {
public override string Name => "PowerProvider"; public override string Name => "PowerProvider";
public IEntity ProviderOwner => Owner;
/// <summary>
/// The max distance this can transmit power to <see cref="ApcPowerReceiverComponent"/>s from.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int PowerTransferRange { get => _powerTransferRange; set => SetPowerTransferRange(value); }
[DataField("powerTransferRange")]
private int _powerTransferRange = 3;
[ViewVariables] public List<ApcPowerReceiverComponent> LinkedReceivers { get; } = new(); [ViewVariables] public List<ApcPowerReceiverComponent> LinkedReceivers { get; } = new();
/// <summary>
/// If <see cref="ApcPowerReceiverComponent"/>s should consider connecting to this.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool Connectable { get; private set; } = true;
public void AddReceiver(ApcPowerReceiverComponent receiver) public void AddReceiver(ApcPowerReceiverComponent receiver)
{ {
LinkedReceivers.Add(receiver); LinkedReceivers.Add(receiver);
@@ -47,49 +28,6 @@ namespace Content.Server.Power.Components
Net?.QueueNetworkReconnect(); Net?.QueueNetworkReconnect();
} }
protected override void Startup()
{
base.Startup();
foreach (var receiver in FindAvailableReceivers())
{
receiver.Provider = this;
}
}
protected override void OnRemove()
{
Connectable = false;
var receivers = LinkedReceivers.ToArray();
foreach (var receiver in receivers)
{
receiver.Provider = null;
}
foreach (var receiver in receivers)
{
receiver.TryFindAndSetProvider();
}
base.OnRemove();
}
private IEnumerable<ApcPowerReceiverComponent> FindAvailableReceivers()
{
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
.GetEntitiesInRange(Owner, PowerTransferRange);
foreach (var entity in nearbyEntities)
{
if (entity.TryGetComponent<ApcPowerReceiverComponent>(out var receiver) &&
receiver.Connectable &&
receiver.NeedsProvider &&
receiver.Owner.Transform.Coordinates.TryDistance(Owner.EntityManager, Owner.Transform.Coordinates, out var distance) &&
distance < Math.Min(PowerTransferRange, receiver.PowerReceptionRange))
{
yield return receiver;
}
}
}
protected override void AddSelfToNet(IApcNet apcNet) protected override void AddSelfToNet(IApcNet apcNet)
{ {
apcNet.AddPowerProvider(this); apcNet.AddPowerProvider(this);
@@ -99,22 +37,5 @@ namespace Content.Server.Power.Components
{ {
apcNet.RemovePowerProvider(this); apcNet.RemovePowerProvider(this);
} }
private void SetPowerTransferRange(int newPowerTransferRange)
{
var receivers = LinkedReceivers.ToArray();
foreach (var receiver in receivers)
{
receiver.Provider = null;
}
_powerTransferRange = newPowerTransferRange;
foreach (var receiver in receivers)
{
receiver.TryFindAndSetProvider();
}
}
} }
} }

View File

@@ -23,47 +23,11 @@ namespace Content.Server.Power.Components
[RegisterComponent] [RegisterComponent]
public class ApcPowerReceiverComponent : Component, IExamine public class ApcPowerReceiverComponent : Component, IExamine
{ {
[ViewVariables] [ComponentDependency] private readonly IPhysBody? _physicsComponent = null;
public override string Name => "ApcPowerReceiver"; public override string Name => "ApcPowerReceiver";
[ViewVariables] [ViewVariables]
public bool Powered => (MathHelper.CloseToPercent(NetworkLoad.ReceivingPower, Load) || !NeedsPower) && !PowerDisabled; public bool Powered => (MathHelper.CloseToPercent(NetworkLoad.ReceivingPower, Load) || !NeedsPower) && !PowerDisabled;
/// <summary>
/// The max distance from a <see cref="ApcPowerProviderComponent"/> that this can receive power from.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public int PowerReceptionRange { get => _powerReceptionRange; set => SetPowerReceptionRange(value); }
[DataField("powerReceptionRange")]
private int _powerReceptionRange = 3;
[ViewVariables]
public ApcPowerProviderComponent? Provider
{
get => _provider;
set
{
// Will get updated before power networks process.
NetworkLoad.LinkedNetwork = default;
_provider?.RemoveReceiver(this);
_provider = value;
value?.AddReceiver(this);
ApcPowerChanged();
}
}
private ApcPowerProviderComponent? _provider;
/// <summary>
/// If this should be considered for connection by <see cref="ApcPowerProviderComponent"/>s.
/// </summary>
public bool Connectable => Anchored;
private bool Anchored => _physicsComponent == null || _physicsComponent.BodyType == BodyType.Static;
[ViewVariables] public bool NeedsProvider => Provider == null;
/// <summary> /// <summary>
/// Amount of charge this needs from an APC per second to function. /// Amount of charge this needs from an APC per second to function.
/// </summary> /// </summary>
@@ -104,73 +68,11 @@ namespace Content.Server.Power.Components
DesiredPower = 5 DesiredPower = 5
}; };
protected override void Startup()
{
base.Startup();
if (NeedsProvider)
{
TryFindAndSetProvider();
}
if (_physicsComponent != null)
{
AnchorUpdate();
}
}
protected override void OnRemove()
{
_provider?.RemoveReceiver(this);
base.OnRemove();
}
public void TryFindAndSetProvider()
{
if (TryFindAvailableProvider(out var provider))
{
Provider = provider;
}
}
public void ApcPowerChanged() public void ApcPowerChanged()
{ {
OnNewPowerState(); OnNewPowerState();
} }
private bool TryFindAvailableProvider([NotNullWhen(true)] out ApcPowerProviderComponent? foundProvider)
{
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
.GetEntitiesInRange(Owner, PowerReceptionRange);
foreach (var entity in nearbyEntities)
{
if (entity.TryGetComponent<ApcPowerProviderComponent>(out var provider))
{
if (provider.Connectable)
{
if (provider.Owner.Transform.Coordinates.TryDistance(Owner.EntityManager, Owner.Transform.Coordinates, out var distance))
{
if (distance < Math.Min(PowerReceptionRange, provider.PowerTransferRange))
{
foundProvider = provider;
return true;
}
}
}
}
}
foundProvider = default;
return false;
}
private void SetPowerReceptionRange(int newPowerReceptionRange)
{
Provider = null;
_powerReceptionRange = newPowerReceptionRange;
TryFindAndSetProvider();
}
private void OnNewPowerState() private void OnNewPowerState()
{ {
SendMessage(new PowerChangedMessage(Powered)); SendMessage(new PowerChangedMessage(Powered));
@@ -182,21 +84,6 @@ namespace Content.Server.Power.Components
} }
} }
public void AnchorUpdate()
{
if (Anchored)
{
if (NeedsProvider)
{
TryFindAndSetProvider();
}
}
else
{
Provider = null;
}
}
///<summary> ///<summary>
///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you. ///Adds some markup to the examine text of whatever object is using this component to tell you if it's powered or not, even if it doesn't have an icon state to do this for you.
///</summary> ///</summary>

View File

@@ -0,0 +1,33 @@
using System.Collections.Generic;
using Content.Server.Power.EntitySystems;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Power.Components
{
[RegisterComponent]
[Friend(typeof(ExtensionCableSystem))]
public class ExtensionCableProviderComponent : Component
{
public override string Name => "ExtensionCableProvider";
/// <summary>
/// The max distance this can connect to <see cref="ExtensionCableReceiverComponent"/>s from.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("transferRange")]
public int TransferRange { get; set; } = 3;
[ViewVariables] public List<ExtensionCableReceiverComponent> LinkedReceivers { get; } = new();
/// <summary>
/// If <see cref="ExtensionCableReceiverComponent"/>s should consider connecting to this.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
public bool Connectable { get; set; } = true;
}
}

View File

@@ -0,0 +1,28 @@
using Content.Server.Power.EntitySystems;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Serialization.Manager.Attributes;
using Robust.Shared.ViewVariables;
namespace Content.Server.Power.Components
{
[RegisterComponent]
[Friend(typeof(ExtensionCableSystem))]
public class ExtensionCableReceiverComponent : Component
{
public override string Name => "ExtensionCableReceiver";
[ViewVariables]
public ExtensionCableProviderComponent? Provider { get; set; }
[ViewVariables]
public bool Connectable = false;
/// <summary>
/// The max distance from a <see cref="ExtensionCableProviderComponent"/> that this can receive power from.
/// </summary>
[ViewVariables(VVAccess.ReadWrite)]
[DataField("receptionRange")]
public int ReceptionRange { get; set; } = 3;
}
}

View File

@@ -0,0 +1,256 @@
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using Content.Server.Power.Components;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Physics;
namespace Content.Server.Power.EntitySystems
{
public sealed class ExtensionCableSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
//Lifecycle events
SubscribeLocalEvent<ExtensionCableProviderComponent, ComponentStartup>(OnProviderStarted);
SubscribeLocalEvent<ExtensionCableProviderComponent, ComponentShutdown>(OnProviderShutdown);
SubscribeLocalEvent<ExtensionCableReceiverComponent, ComponentStartup>(OnReceiverStarted);
SubscribeLocalEvent<ExtensionCableReceiverComponent, ComponentShutdown>(OnReceiverShutdown);
//Anchoring
SubscribeLocalEvent<ExtensionCableReceiverComponent, AnchorStateChangedEvent>(AnchorStateChanged);
}
#region Provider
public void SetProviderTransferRange(EntityUid uid, int range, ExtensionCableProviderComponent? provider = null)
{
if (!Resolve(uid, ref provider))
return;
provider.TransferRange = range;
ResetReceivers(provider);
}
private void OnProviderStarted(EntityUid uid, ExtensionCableProviderComponent provider, ComponentStartup args)
{
foreach (var receiver in FindAvailableReceivers(uid, provider.TransferRange))
{
receiver.Provider?.LinkedReceivers.Remove(receiver);
receiver.Provider = provider;
provider.LinkedReceivers.Add(receiver);
RaiseLocalEvent(receiver.Owner.Uid, new ProviderConnectedEvent(provider), broadcast: false);
RaiseLocalEvent(uid, new ReceiverConnectedEvent(receiver), broadcast: false);
}
}
private void OnProviderShutdown(EntityUid uid, ExtensionCableProviderComponent provider, ComponentShutdown args)
{
provider.Connectable = false;
ResetReceivers(provider);
}
private void ResetReceivers(ExtensionCableProviderComponent provider)
{
var receivers = provider.LinkedReceivers.ToArray();
foreach (var receiver in receivers)
{
receiver.Provider = null;
RaiseLocalEvent(receiver.Owner.Uid, new ProviderDisconnectedEvent(provider), broadcast: false);
RaiseLocalEvent(provider.Owner.Uid, new ReceiverDisconnectedEvent(receiver), broadcast: false);
}
foreach (var receiver in receivers)
{
TryFindAndSetProvider(receiver);
}
}
private IEnumerable<ExtensionCableReceiverComponent> FindAvailableReceivers(EntityUid uid, float range)
{
var owner = EntityManager.GetEntity(uid);
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
.GetEntitiesInRange(owner, range);
foreach (var entity in nearbyEntities)
{
if (entity.TryGetComponent<ExtensionCableReceiverComponent>(out var receiver) &&
receiver.Connectable &&
receiver.Provider == null &&
entity.Transform.Coordinates.TryDistance(owner.EntityManager, owner.Transform.Coordinates, out var distance) &&
distance < Math.Min(range, receiver.ReceptionRange))
{
yield return receiver;
}
}
}
#endregion
#region Receiver
public void SetReceiverReceptionRange(EntityUid uid, int range, ExtensionCableReceiverComponent? receiver = null)
{
if (!Resolve(uid, ref receiver))
return;
var provider = receiver.Provider;
receiver.Provider = null;
RaiseLocalEvent(uid, new ProviderDisconnectedEvent(provider), broadcast: false);
if (provider != null)
RaiseLocalEvent(provider.Owner.Uid, new ReceiverDisconnectedEvent(receiver), broadcast: false);
receiver.ReceptionRange = range;
TryFindAndSetProvider(receiver);
}
private void OnReceiverStarted(EntityUid uid, ExtensionCableReceiverComponent receiver, ComponentStartup args)
{
if (receiver.Owner.TryGetComponent(out PhysicsComponent? physicsComponent))
{
receiver.Connectable = physicsComponent.BodyType == BodyType.Static;
}
if (receiver.Provider == null)
{
TryFindAndSetProvider(receiver);
}
}
private void OnReceiverShutdown(EntityUid uid, ExtensionCableReceiverComponent receiver, ComponentShutdown args)
{
if (receiver.Provider == null) return;
receiver.Provider.LinkedReceivers.Remove(receiver);
RaiseLocalEvent(uid, new ProviderDisconnectedEvent(receiver.Provider), broadcast: false);
RaiseLocalEvent(receiver.Provider.Owner.Uid, new ReceiverDisconnectedEvent(receiver), broadcast: false);
}
private void AnchorStateChanged(EntityUid uid, ExtensionCableReceiverComponent receiver, ref AnchorStateChangedEvent args)
{
if (args.Anchored)
{
receiver.Connectable = true;
if (receiver.Provider == null)
{
TryFindAndSetProvider(receiver);
}
}
else
{
receiver.Connectable = false;
RaiseLocalEvent(uid, new ProviderDisconnectedEvent(receiver.Provider), broadcast: false);
if (receiver.Provider != null)
RaiseLocalEvent(receiver.Provider.Owner.Uid, new ReceiverDisconnectedEvent(receiver), broadcast: false);
receiver.Provider = null;
}
}
private void TryFindAndSetProvider(ExtensionCableReceiverComponent receiver)
{
if (!TryFindAvailableProvider(receiver.Owner, receiver.ReceptionRange, out var provider)) return;
receiver.Provider = provider;
RaiseLocalEvent(receiver.Owner.Uid, new ProviderConnectedEvent(provider), broadcast: false);
RaiseLocalEvent(provider.Owner.Uid, new ReceiverConnectedEvent(receiver), broadcast: false);
}
private static bool TryFindAvailableProvider(IEntity owner, float range, [NotNullWhen(true)] out ExtensionCableProviderComponent? foundProvider)
{
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
.GetEntitiesInRange(owner, range);
foreach (var entity in nearbyEntities)
{
if (!entity.TryGetComponent<ExtensionCableProviderComponent>(out var provider)) continue;
if (!provider.Connectable) continue;
if (!entity.Transform.Coordinates.TryDistance(owner.EntityManager, owner.Transform.Coordinates, out var distance)) continue;
if (!(distance < Math.Min(range, provider.TransferRange))) continue;
foundProvider = provider;
return true;
}
foundProvider = default;
return false;
}
#endregion
#region Events
/// <summary>
/// Sent when a <see cref="ExtensionCableProviderComponent"/> connects to a <see cref="ExtensionCableReceiverComponent"/>
/// </summary>
public class ProviderConnectedEvent : EntityEventArgs
{
/// <summary>
/// The <see cref="ExtensionCableProviderComponent"/> that connected.
/// </summary>
public ExtensionCableProviderComponent Provider;
public ProviderConnectedEvent(ExtensionCableProviderComponent provider)
{
Provider = provider;
}
}
/// <summary>
/// Sent when a <see cref="ExtensionCableProviderComponent"/> disconnects from a <see cref="ExtensionCableReceiverComponent"/>
/// </summary>
public class ProviderDisconnectedEvent : EntityEventArgs
{
/// <summary>
/// The <see cref="ExtensionCableProviderComponent"/> that disconnected.
/// </summary>
public ExtensionCableProviderComponent? Provider;
public ProviderDisconnectedEvent(ExtensionCableProviderComponent? provider)
{
Provider = provider;
}
}
/// <summary>
/// Sent when a <see cref="ExtensionCableReceiverComponent"/> connects to a <see cref="ExtensionCableProviderComponent"/>
/// </summary>
public class ReceiverConnectedEvent : EntityEventArgs
{
/// <summary>
/// The <see cref="ExtensionCableReceiverComponent"/> that connected.
/// </summary>
public ExtensionCableReceiverComponent Receiver;
public ReceiverConnectedEvent(ExtensionCableReceiverComponent receiver)
{
Receiver = receiver;
}
}
/// <summary>
/// Sent when a <see cref="ExtensionCableReceiverComponent"/> disconnects from a <see cref="ExtensionCableProviderComponent"/>
/// </summary>
public class ReceiverDisconnectedEvent : EntityEventArgs
{
/// <summary>
/// The <see cref="ExtensionCableReceiverComponent"/> that disconnected.
/// </summary>
public ExtensionCableReceiverComponent Receiver;
public ReceiverDisconnectedEvent(ExtensionCableReceiverComponent receiver)
{
Receiver = receiver;
}
}
#endregion
}
}

View File

@@ -9,15 +9,43 @@ namespace Content.Server.Power.EntitySystems
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<ApcPowerReceiverComponent, PhysicsBodyTypeChangedEvent>(BodyTypeChanged); SubscribeLocalEvent<ApcPowerReceiverComponent, ExtensionCableSystem.ProviderConnectedEvent>(OnProviderConnected);
SubscribeLocalEvent<ApcPowerReceiverComponent, ExtensionCableSystem.ProviderDisconnectedEvent>(OnProviderDisconnected);
SubscribeLocalEvent<ApcPowerProviderComponent, ExtensionCableSystem.ReceiverConnectedEvent>(OnReceiverConnected);
SubscribeLocalEvent<ApcPowerProviderComponent, ExtensionCableSystem.ReceiverDisconnectedEvent>(OnReceiverDisconnected);
} }
private static void BodyTypeChanged( private void OnProviderConnected(EntityUid uid, ApcPowerReceiverComponent receiver, ExtensionCableSystem.ProviderConnectedEvent args)
EntityUid uid,
ApcPowerReceiverComponent component,
PhysicsBodyTypeChangedEvent args)
{ {
component.AnchorUpdate(); ProviderChanged(receiver);
}
private void OnProviderDisconnected(EntityUid uid, ApcPowerReceiverComponent receiver, ExtensionCableSystem.ProviderDisconnectedEvent args)
{
ProviderChanged(receiver);
}
private void OnReceiverConnected(EntityUid uid, ApcPowerProviderComponent provider, ExtensionCableSystem.ReceiverConnectedEvent args)
{
if (EntityManager.TryGetComponent(args.Receiver.Owner.Uid, out ApcPowerReceiverComponent receiver))
{
provider.AddReceiver(receiver);
}
}
private void OnReceiverDisconnected(EntityUid uid, ApcPowerProviderComponent provider, ExtensionCableSystem.ReceiverDisconnectedEvent args)
{
if (EntityManager.TryGetComponent(args.Receiver.Owner.Uid, out ApcPowerReceiverComponent receiver))
{
provider.RemoveReceiver(receiver);
}
}
private static void ProviderChanged(ApcPowerReceiverComponent receiver)
{
receiver.NetworkLoad.LinkedNetwork = default;
receiver.ApcPowerChanged();
} }
} }
} }

View File

@@ -24,6 +24,7 @@
- MobImpassable - MobImpassable
- SmallImpassable - SmallImpassable
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.ReagentDispenserUiKey.Key - key: enum.ReagentDispenserUiKey.Key

View File

@@ -10,3 +10,4 @@
- type: ReagentDispenser - type: ReagentDispenser
pack: ChemDispenserStandardInventory pack: ChemDispenserStandardInventory
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver

View File

@@ -53,6 +53,7 @@
- type: AirlockVisualizer - type: AirlockVisualizer
- type: WiresVisualizer - type: WiresVisualizer
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Wires - type: Wires
BoardName: "Airlock Control" BoardName: "Airlock Control"
LayoutId: Airlock LayoutId: Airlock

View File

@@ -37,6 +37,7 @@
- state: panel_open - state: panel_open
map: ["enum.WiresVisualLayers.MaintenancePanel"] map: ["enum.WiresVisualLayers.MaintenancePanel"]
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Damageable - type: Damageable
damageContainer: Inorganic damageContainer: Inorganic
damageModifierSet: Glass damageModifierSet: Glass

View File

@@ -6,6 +6,7 @@
parent: ComputerBase parent: ComputerBase
components: components:
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: PointLight - type: PointLight
radius: 1.5 radius: 1.5
energy: 1.6 energy: 1.6

View File

@@ -11,6 +11,7 @@
node: computer node: computer
- type: Computer - type: Computer
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Sprite - type: Sprite
sprite: Structures/Machines/computers.rsi sprite: Structures/Machines/computers.rsi
layers: layers:

View File

@@ -20,6 +20,7 @@
- type: ShuttleConsole - type: ShuttleConsole
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerLoad: 200 powerLoad: 200
- type: ExtensionCableReceiver
- type: PointLight - type: PointLight
radius: 1.5 radius: 1.5
energy: 1.6 energy: 1.6
@@ -108,6 +109,7 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerLoad: 200 powerLoad: 200
priority: Low priority: Low
- type: ExtensionCableReceiver
- type: Computer - type: Computer
board: ResearchComputerCircuitboard board: ResearchComputerCircuitboard
- type: PointLight - type: PointLight

View File

@@ -38,3 +38,4 @@
id: BaseMachinePowered id: BaseMachinePowered
components: components:
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver

View File

@@ -17,6 +17,7 @@
state: mixer_loaded state: mixer_loaded
- type: ChemMaster - type: ChemMaster
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: InteractionOutline - type: InteractionOutline
- type: Anchorable - type: Anchorable
- type: Physics - type: Physics

View File

@@ -24,6 +24,7 @@
anchored: true anchored: true
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerLoad: 500 powerLoad: 500
- type: ExtensionCableReceiver
- type: Physics - type: Physics
bodyType: Static bodyType: Static
fixtures: fixtures:

View File

@@ -12,6 +12,7 @@
- type: ApcPowerReceiver - type: ApcPowerReceiver
powerLoad: 200 powerLoad: 200
priority: Low priority: Low
- type: ExtensionCableReceiver
- type: Destructible - type: Destructible
thresholds: thresholds:
- trigger: - trigger:

View File

@@ -18,6 +18,7 @@
- type: ParticleAcceleratorPartVisualizer - type: ParticleAcceleratorPartVisualizer
baseState: unlit baseState: unlit
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: ParticleAcceleratorControlBox - type: ParticleAcceleratorControlBox
- type: Construction - type: Construction
graph: particleAcceleratorControlBox graph: particleAcceleratorControlBox

View File

@@ -62,6 +62,7 @@
!type:CableDeviceNode !type:CableDeviceNode
nodeGroupID: HVPower nodeGroupID: HVPower
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: PowerSupplier - type: PowerSupplier
supplyRate: 0 supplyRate: 0

View File

@@ -56,6 +56,7 @@
voltage: Apc voltage: Apc
- type: Apc - type: Apc
voltage: Apc voltage: Apc
- type: ExtensionCableProvider
- type: UserInterface - type: UserInterface
interfaces: interfaces:
- key: enum.ApcUiKey.Key - key: enum.ApcUiKey.Key

View File

@@ -50,9 +50,6 @@
power: power:
!type:CableNode !type:CableNode
nodeGroupID: HVPower nodeGroupID: HVPower
wire:
!type:AdjacentNode
nodeGroupID: WireNet
- type: Cable - type: Cable
cableDroppedOnCutPrototype: CableHVStack1 cableDroppedOnCutPrototype: CableHVStack1
cableType: HighVoltage cableType: HighVoltage
@@ -93,9 +90,6 @@
power: power:
!type:CableNode !type:CableNode
nodeGroupID: MVPower nodeGroupID: MVPower
wire:
!type:AdjacentNode
nodeGroupID: WireNet
- type: Cable - type: Cable
cableDroppedOnCutPrototype: CableMVStack1 cableDroppedOnCutPrototype: CableMVStack1
cableType: MediumVoltage cableType: MediumVoltage
@@ -136,11 +130,9 @@
power: power:
!type:CableNode !type:CableNode
nodeGroupID: Apc nodeGroupID: Apc
wire:
!type:AdjacentNode
nodeGroupID: WireNet
- type: PowerProvider - type: PowerProvider
voltage: Apc voltage: Apc
- type: ExtensionCableProvider
- type: Cable - type: Cable
cableDroppedOnCutPrototype: CableApcStack1 cableDroppedOnCutPrototype: CableApcStack1
cableType: Apc cableType: Apc

View File

@@ -12,6 +12,7 @@
- type: PowerCellCharger - type: PowerCellCharger
transfer_efficiency: 0.85 transfer_efficiency: 0.85
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Appearance - type: Appearance
visuals: visuals:
- type: PowerChargerVisualizer - type: PowerChargerVisualizer
@@ -33,6 +34,7 @@
- type: WeaponCapacitorCharger - type: WeaponCapacitorCharger
transfer_efficiency: 0.85 transfer_efficiency: 0.85
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Appearance - type: Appearance
visuals: visuals:
- type: PowerChargerVisualizer - type: PowerChargerVisualizer
@@ -54,6 +56,7 @@
- type: WeaponCapacitorCharger - type: WeaponCapacitorCharger
transfer_efficiency: 0.95 transfer_efficiency: 0.95
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Appearance - type: Appearance
visuals: visuals:
- type: PowerChargerVisualizer - type: PowerChargerVisualizer

View File

@@ -164,4 +164,5 @@
sprite: Structures/Power/power.rsi sprite: Structures/Power/power.rsi
state: wirelessmachine state: wirelessmachine
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Anchorable - type: Anchorable

View File

@@ -15,6 +15,7 @@
sprite: Structures/Wallmounts/barsign.rsi sprite: Structures/Wallmounts/barsign.rsi
state: empty state: empty
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: BarSign - type: BarSign
- type: Destructible - type: Destructible
thresholds: thresholds:
@@ -38,6 +39,7 @@
sprite: Structures/Wallmounts/sylphs.rsi sprite: Structures/Wallmounts/sylphs.rsi
state: sylph state: sylph
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: BarSign - type: BarSign
- type: entity - type: entity

View File

@@ -12,6 +12,7 @@
color: "#FF4020" color: "#FF4020"
mask: /Textures/Effects/LightMasks/cone.png mask: /Textures/Effects/LightMasks/cone.png
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Battery - type: Battery
maxCharge: 30000 maxCharge: 30000
startingCharge: 0 startingCharge: 0

View File

@@ -74,6 +74,10 @@
types: types:
Heat: 20 Heat: 20
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: DeviceNetworkComponent
deviceNetId: Apc
- type: ApcNetworkConnection
- type: Appearance - type: Appearance
visuals: visuals:
- type: PoweredLightVisualizer - type: PoweredLightVisualizer
@@ -159,6 +163,10 @@
types: types:
Heat: 20 Heat: 20
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: DeviceNetworkComponent
deviceNetId: Apc
- type: ApcNetworkConnection
- type: Appearance - type: Appearance
visuals: visuals:
- type: PoweredLightVisualizer - type: PoweredLightVisualizer

View File

@@ -43,3 +43,27 @@
placement: placement:
snap: snap:
- Wallmount - Wallmount
- type: entity
id: ApcNetSwitch
name: apc net switch
description: Its a switch for toggling lights that are connected to the same apc.
components:
- type: Clickable
- type: InteractionOutline
- type: Physics
- type: Sprite
sprite: Structures/Wallmounts/switch.rsi
state: on
- type: Rotatable
- type: Construction
graph: lightSwitch
node: lightSwitch
- type: ExtensionCableReceiver
- type: DeviceNetworkComponent
deviceNetId: Apc
- type: ApcNetworkConnection
- type: ApcNetSwitch
placement:
snap:
- Wallmount

View File

@@ -35,4 +35,5 @@
- !type:DoActsBehavior - !type:DoActsBehavior
acts: ["Destruction"] acts: ["Destruction"]
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: CargoTelepad - type: CargoTelepad

View File

@@ -31,6 +31,7 @@
type: Content.Shared.MachineLinking.TwoWayLeverSignal type: Content.Shared.MachineLinking.TwoWayLeverSignal
maxConnections: 1 maxConnections: 1
- type: ApcPowerReceiver - type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Conveyor - type: Conveyor
- type: Appearance - type: Appearance
visuals: visuals:

View File

@@ -44,3 +44,27 @@
prototype: SheetSteel1 prototype: SheetSteel1
amount: 1 amount: 1
- !type:DeleteEntity {} - !type:DeleteEntity {}
- type: constructionGraph
id: lightSwitch
start: start
graph:
- node: start
edges:
- to: lightSwitch
steps:
- material: Steel
amount: 1
doAfter: 2.0
- node: lightSwitch
entity: ApcNetSwitch
edges:
- to: start
steps:
- tool: Screwing
doAfter: 2.0
completed:
- !type:SpawnPrototype
prototype: SheetSteel1
amount: 1
- !type:DeleteEntity {}

View File

@@ -34,3 +34,22 @@
conditions: conditions:
- !type:WallmountCondition {} - !type:WallmountCondition {}
- type: construction
name: light switch
id: LightSwitchRecipe
graph: lightSwitch
startNode: start
targetNode: lightSwitch
category: Structures
description: A switch for toggling lights that are connected to the same apc.
icon:
sprite: Structures/Wallmounts/switch.rsi
state: on
objectType: Structure
placementMode: SnapgridCenter
canRotate: true
canBuildInImpassable: true
conditions:
- !type:WallmountCondition {}