committed by
GitHub
parent
ea60a81fdf
commit
103bc19508
193
Content.Server/Power/Components/ApcComponent.cs
Normal file
193
Content.Server/Power/Components/ApcComponent.cs
Normal file
@@ -0,0 +1,193 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.Access.Components;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.UserInterface;
|
||||
using Content.Shared.APC;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Notification.Managers;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Timing;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
[ComponentReference(typeof(IActivate))]
|
||||
public class ApcComponent : BaseApcNetComponent, IActivate
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _gameTiming = default!;
|
||||
|
||||
public override string Name => "Apc";
|
||||
|
||||
public bool MainBreakerEnabled { get; private set; } = true;
|
||||
|
||||
private ApcChargeState _lastChargeState;
|
||||
|
||||
private TimeSpan _lastChargeStateChange;
|
||||
|
||||
private ApcExternalPowerState _lastExternalPowerState;
|
||||
|
||||
private TimeSpan _lastExternalPowerStateChange;
|
||||
|
||||
private float _lastCharge;
|
||||
|
||||
private TimeSpan _lastChargeChange;
|
||||
|
||||
private bool _uiDirty = true;
|
||||
|
||||
private const float HighPowerThreshold = 0.9f;
|
||||
|
||||
private const int VisualsChangeDelay = 1;
|
||||
|
||||
[ViewVariables] private BoundUserInterface? UserInterface => Owner.GetUIOrNull(ApcUiKey.Key);
|
||||
|
||||
public BatteryComponent? Battery => Owner.TryGetComponent(out BatteryComponent? batteryComponent) ? batteryComponent : null;
|
||||
|
||||
[ComponentDependency] private AccessReader? _accessReader = null;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponentWarn<ServerUserInterfaceComponent>();
|
||||
Owner.EnsureComponentWarn<AccessReader>();
|
||||
|
||||
if (UserInterface != null)
|
||||
{
|
||||
UserInterface.OnReceiveMessage += UserInterfaceOnReceiveMessage;
|
||||
}
|
||||
|
||||
Update();
|
||||
}
|
||||
|
||||
protected override void AddSelfToNet(IApcNet apcNet)
|
||||
{
|
||||
apcNet.AddApc(this);
|
||||
}
|
||||
|
||||
protected override void RemoveSelfFromNet(IApcNet apcNet)
|
||||
{
|
||||
apcNet.RemoveApc(this);
|
||||
}
|
||||
|
||||
private void UserInterfaceOnReceiveMessage(ServerBoundUserInterfaceMessage serverMsg)
|
||||
{
|
||||
if (serverMsg.Message is ApcToggleMainBreakerMessage)
|
||||
{
|
||||
var user = serverMsg.Session.AttachedEntity;
|
||||
if(user == null) return;
|
||||
|
||||
if (_accessReader == null || _accessReader.IsAllowed(user))
|
||||
{
|
||||
MainBreakerEnabled = !MainBreakerEnabled;
|
||||
Owner.GetComponent<PowerNetworkBatteryComponent>().CanDischarge = MainBreakerEnabled;
|
||||
|
||||
_uiDirty = true;
|
||||
SoundSystem.Play(Filter.Pvs(Owner), "/Audio/Machines/machine_switch.ogg", Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
else
|
||||
{
|
||||
user.PopupMessageCursor(Loc.GetString("apc-component-insufficient-access"));
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
var newState = CalcChargeState();
|
||||
if (newState != _lastChargeState && _lastChargeStateChange + TimeSpan.FromSeconds(VisualsChangeDelay) < _gameTiming.CurTime)
|
||||
{
|
||||
_lastChargeState = newState;
|
||||
_lastChargeStateChange = _gameTiming.CurTime;
|
||||
|
||||
if (Owner.TryGetComponent(out AppearanceComponent? appearance))
|
||||
{
|
||||
appearance.SetData(ApcVisuals.ChargeState, newState);
|
||||
}
|
||||
}
|
||||
|
||||
Owner.TryGetComponent(out BatteryComponent? battery);
|
||||
|
||||
var newCharge = battery?.CurrentCharge;
|
||||
if (newCharge != null && newCharge != _lastCharge && _lastChargeChange + TimeSpan.FromSeconds(VisualsChangeDelay) < _gameTiming.CurTime)
|
||||
{
|
||||
_lastCharge = newCharge.Value;
|
||||
_lastChargeChange = _gameTiming.CurTime;
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
var extPowerState = CalcExtPowerState();
|
||||
if (extPowerState != _lastExternalPowerState && _lastExternalPowerStateChange + TimeSpan.FromSeconds(VisualsChangeDelay) < _gameTiming.CurTime)
|
||||
{
|
||||
_lastExternalPowerState = extPowerState;
|
||||
_lastExternalPowerStateChange = _gameTiming.CurTime;
|
||||
_uiDirty = true;
|
||||
}
|
||||
|
||||
if (_uiDirty && battery != null && newCharge != null)
|
||||
{
|
||||
UserInterface?.SetState(new ApcBoundInterfaceState(MainBreakerEnabled, extPowerState, newCharge.Value / battery.MaxCharge));
|
||||
_uiDirty = false;
|
||||
}
|
||||
}
|
||||
|
||||
private ApcChargeState CalcChargeState()
|
||||
{
|
||||
if (!Owner.TryGetComponent(out BatteryComponent? battery))
|
||||
{
|
||||
return ApcChargeState.Lack;
|
||||
}
|
||||
|
||||
var chargeFraction = battery.CurrentCharge / battery.MaxCharge;
|
||||
|
||||
if (chargeFraction > HighPowerThreshold)
|
||||
{
|
||||
return ApcChargeState.Full;
|
||||
}
|
||||
|
||||
var netBattery = Owner.GetComponent<PowerNetworkBatteryComponent>();
|
||||
var delta = netBattery.CurrentSupply - netBattery.CurrentReceiving;
|
||||
|
||||
return delta < 0 ? ApcChargeState.Charging : ApcChargeState.Lack;
|
||||
}
|
||||
|
||||
private ApcExternalPowerState CalcExtPowerState()
|
||||
{
|
||||
var bat = Battery;
|
||||
if (bat == null)
|
||||
return ApcExternalPowerState.None;
|
||||
|
||||
var netBat = Owner.GetComponent<PowerNetworkBatteryComponent>();
|
||||
if (netBat.CurrentReceiving == 0 && netBat.LoadingNetworkDemand != 0)
|
||||
{
|
||||
return ApcExternalPowerState.None;
|
||||
}
|
||||
|
||||
var delta = netBat.CurrentReceiving - netBat.LoadingNetworkDemand;
|
||||
if (!MathHelper.CloseTo(delta, 0, 0.1f) && delta < 0)
|
||||
{
|
||||
return ApcExternalPowerState.Low;
|
||||
}
|
||||
|
||||
return ApcExternalPowerState.Good;
|
||||
}
|
||||
|
||||
void IActivate.Activate(ActivateEventArgs eventArgs)
|
||||
{
|
||||
if (!eventArgs.User.TryGetComponent(out ActorComponent? actor))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
UserInterface?.Open(actor.PlayerSession);
|
||||
}
|
||||
}
|
||||
}
|
||||
121
Content.Server/Power/Components/ApcPowerProviderComponent.cs
Normal file
121
Content.Server/Power/Components/ApcPowerProviderComponent.cs
Normal file
@@ -0,0 +1,121 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ApcPowerProviderComponent : BaseApcNetComponent
|
||||
{
|
||||
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();
|
||||
|
||||
/// <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)
|
||||
{
|
||||
LinkedReceivers.Add(receiver);
|
||||
receiver.NetworkLoad.LinkedNetwork = default;
|
||||
|
||||
Net?.QueueNetworkReconnect();
|
||||
}
|
||||
|
||||
public void RemoveReceiver(ApcPowerReceiverComponent receiver)
|
||||
{
|
||||
LinkedReceivers.Remove(receiver);
|
||||
receiver.NetworkLoad.LinkedNetwork = default;
|
||||
|
||||
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)
|
||||
{
|
||||
apcNet.AddPowerProvider(this);
|
||||
}
|
||||
|
||||
protected override void RemoveSelfFromNet(IApcNet apcNet)
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,12 +1,15 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Content.Server.APC;
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Content.Shared.Examine;
|
||||
using Content.Shared.Power;
|
||||
using Robust.Server.GameObjects;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Physics;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.Utility;
|
||||
@@ -15,26 +18,21 @@ using Robust.Shared.ViewVariables;
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Attempts to link with a nearby <see cref="IPowerProvider"/>s so that it can receive power from a <see cref="IApcNet"/>.
|
||||
/// Attempts to link with a nearby <see cref="ApcPowerProviderComponent"/>s
|
||||
/// so that it can receive power from a <see cref="IApcNet"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class PowerReceiverComponent : Component, IExamine
|
||||
public class ApcPowerReceiverComponent : Component, IExamine
|
||||
{
|
||||
[ViewVariables] [ComponentDependency] private readonly IPhysBody? _physicsComponent = null;
|
||||
|
||||
public override string Name => "PowerReceiver";
|
||||
public override string Name => "ApcPowerReceiver";
|
||||
|
||||
[ViewVariables]
|
||||
public bool Powered => (HasApcPower || !NeedsPower) && !PowerDisabled;
|
||||
public bool Powered => (MathHelper.CloseTo(NetworkLoad.ReceivingPower, Load) || !NeedsPower) && !PowerDisabled;
|
||||
|
||||
/// <summary>
|
||||
/// If this is being powered by an Apc.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public bool HasApcPower { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// The max distance from a <see cref="PowerProviderComponent"/> that this can receive power from.
|
||||
/// 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); }
|
||||
@@ -42,32 +40,53 @@ namespace Content.Server.Power.Components
|
||||
private int _powerReceptionRange = 3;
|
||||
|
||||
[ViewVariables]
|
||||
public IPowerProvider Provider { get => _provider; set => SetProvider(value); }
|
||||
private IPowerProvider _provider = PowerProviderComponent.NullProvider;
|
||||
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="PowerProviderComponent"/>s.
|
||||
/// 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 { get; private set; } = true;
|
||||
[ViewVariables] public bool NeedsProvider => Provider == null;
|
||||
|
||||
/// <summary>
|
||||
/// Amount of charge this needs from an APC per second to function.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int Load { get => _load; set => SetLoad(value); }
|
||||
[DataField("powerLoad")]
|
||||
private int _load = 5;
|
||||
public float Load { get => NetworkLoad.DesiredPower; set => NetworkLoad.DesiredPower = value; }
|
||||
|
||||
/// <summary>
|
||||
/// When false, causes this to appear powered even if not receiving power from an Apc.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool NeedsPower { get => _needsPower; set => SetNeedsPower(value); }
|
||||
public bool NeedsPower
|
||||
{
|
||||
get => _needsPower;
|
||||
set
|
||||
{
|
||||
_needsPower = value;
|
||||
// Reset this so next tick will do a power update.
|
||||
LastPowerReceived = float.NaN;
|
||||
}
|
||||
}
|
||||
|
||||
[DataField("needsPower")]
|
||||
private bool _needsPower = true;
|
||||
|
||||
@@ -75,9 +94,16 @@ namespace Content.Server.Power.Components
|
||||
/// When true, causes this to never appear powered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool PowerDisabled { get => _powerDisabled; set => SetPowerDisabled(value); }
|
||||
[DataField("powerDisabled")]
|
||||
private bool _powerDisabled;
|
||||
public bool PowerDisabled { get => !NetworkLoad.Enabled; set => NetworkLoad.Enabled = !value; }
|
||||
|
||||
public float LastPowerReceived = float.NaN;
|
||||
|
||||
[ViewVariables]
|
||||
public PowerState.Load NetworkLoad { get; } = new PowerState.Load
|
||||
{
|
||||
DesiredPower = 5
|
||||
};
|
||||
|
||||
protected override void Startup()
|
||||
{
|
||||
@@ -94,7 +120,8 @@ namespace Content.Server.Power.Components
|
||||
|
||||
protected override void OnRemove()
|
||||
{
|
||||
_provider.RemoveReceiver(this);
|
||||
_provider?.RemoveReceiver(this);
|
||||
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
@@ -108,20 +135,17 @@ namespace Content.Server.Power.Components
|
||||
|
||||
public void ApcPowerChanged()
|
||||
{
|
||||
var oldPowered = Powered;
|
||||
HasApcPower = Provider.HasApcPower;
|
||||
if (Powered != oldPowered)
|
||||
OnNewPowerState();
|
||||
OnNewPowerState();
|
||||
}
|
||||
|
||||
private bool TryFindAvailableProvider(out IPowerProvider foundProvider)
|
||||
private bool TryFindAvailableProvider([NotNullWhen(true)] out ApcPowerProviderComponent? foundProvider)
|
||||
{
|
||||
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
|
||||
.GetEntitiesInRange(Owner, PowerReceptionRange);
|
||||
|
||||
foreach (var entity in nearbyEntities)
|
||||
{
|
||||
if (entity.TryGetComponent<PowerProviderComponent>(out var provider))
|
||||
if (entity.TryGetComponent<ApcPowerProviderComponent>(out var provider))
|
||||
{
|
||||
if (provider.Connectable)
|
||||
{
|
||||
@@ -136,60 +160,18 @@ namespace Content.Server.Power.Components
|
||||
}
|
||||
}
|
||||
}
|
||||
foundProvider = default!;
|
||||
|
||||
foundProvider = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
public void ClearProvider()
|
||||
{
|
||||
_provider.RemoveReceiver(this);
|
||||
_provider = PowerProviderComponent.NullProvider;
|
||||
NeedsProvider = true;
|
||||
ApcPowerChanged();
|
||||
}
|
||||
|
||||
private void SetProvider(IPowerProvider newProvider)
|
||||
{
|
||||
_provider.RemoveReceiver(this);
|
||||
_provider = newProvider;
|
||||
newProvider.AddReceiver(this);
|
||||
NeedsProvider = false;
|
||||
ApcPowerChanged();
|
||||
}
|
||||
|
||||
private void SetPowerReceptionRange(int newPowerReceptionRange)
|
||||
{
|
||||
ClearProvider();
|
||||
Provider = null;
|
||||
_powerReceptionRange = newPowerReceptionRange;
|
||||
TryFindAndSetProvider();
|
||||
}
|
||||
|
||||
private void SetLoad(int newLoad)
|
||||
{
|
||||
Provider.UpdateReceiverLoad(Load, newLoad);
|
||||
_load = newLoad;
|
||||
}
|
||||
|
||||
private void SetNeedsPower(bool newNeedsPower)
|
||||
{
|
||||
var oldPowered = Powered;
|
||||
_needsPower = newNeedsPower;
|
||||
if (oldPowered != Powered)
|
||||
{
|
||||
OnNewPowerState();
|
||||
}
|
||||
}
|
||||
|
||||
private void SetPowerDisabled(bool newPowerDisabled)
|
||||
{
|
||||
var oldPowered = Powered;
|
||||
_powerDisabled = newPowerDisabled;
|
||||
if (oldPowered != Powered)
|
||||
{
|
||||
OnNewPowerState();
|
||||
}
|
||||
}
|
||||
|
||||
private void OnNewPowerState()
|
||||
{
|
||||
SendMessage(new PowerChangedMessage(Powered));
|
||||
@@ -211,7 +193,7 @@ namespace Content.Server.Power.Components
|
||||
}
|
||||
else
|
||||
{
|
||||
ClearProvider();
|
||||
Provider = null;
|
||||
}
|
||||
}
|
||||
|
||||
9
Content.Server/Power/Components/BaseApcNetComponent.cs
Normal file
9
Content.Server/Power/Components/BaseApcNetComponent.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
#nullable enable
|
||||
using Content.Server.Power.NodeGroups;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
public abstract class BaseApcNetComponent : BaseNetConnectorComponent<IApcNet>
|
||||
{
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Battery.Components;
|
||||
using Content.Server.Hands.Components;
|
||||
using Content.Server.Items;
|
||||
using Content.Server.Weapon.Ranged.Barrels.Components;
|
||||
@@ -45,7 +44,7 @@ namespace Content.Server.Power.Components
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Owner.EnsureComponent<PowerReceiverComponent>();
|
||||
Owner.EnsureComponent<ApcPowerReceiverComponent>();
|
||||
_container = ContainerHelpers.EnsureContainer<ContainerSlot>(Owner, $"{Name}-powerCellContainer");
|
||||
// Default state in the visualizer is OFF, so when this gets powered on during initialization it will generally show empty
|
||||
}
|
||||
@@ -191,7 +190,7 @@ namespace Content.Server.Power.Components
|
||||
|
||||
private CellChargerStatus GetStatus()
|
||||
{
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) &&
|
||||
if (Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) &&
|
||||
!receiver.Powered)
|
||||
{
|
||||
return CellChargerStatus.Off;
|
||||
@@ -234,7 +233,7 @@ namespace Content.Server.Power.Components
|
||||
// Not called UpdateAppearance just because it messes with the load
|
||||
var status = GetStatus();
|
||||
if (_status == status ||
|
||||
!Owner.TryGetComponent(out PowerReceiverComponent? receiver))
|
||||
!Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver))
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -279,7 +278,7 @@ namespace Content.Server.Power.Components
|
||||
|
||||
private void TransferPower(float frameTime)
|
||||
{
|
||||
if (Owner.TryGetComponent(out PowerReceiverComponent? receiver) &&
|
||||
if (Owner.TryGetComponent(out ApcPowerReceiverComponent? receiver) &&
|
||||
!receiver.Powered)
|
||||
{
|
||||
return;
|
||||
|
||||
@@ -17,23 +17,18 @@ namespace Content.Server.Power.Components
|
||||
private Voltage _voltage = Voltage.High;
|
||||
|
||||
[ViewVariables]
|
||||
public TNetType Net { get => _net; set => SetNet(value); }
|
||||
private TNetType _net = default!; //set in OnAdd()
|
||||
|
||||
protected abstract TNetType NullNet { get; }
|
||||
public TNetType? Net { get => _net; set => SetNet(value); }
|
||||
private TNetType? _net;
|
||||
|
||||
[ViewVariables]
|
||||
private bool _needsNet = true;
|
||||
private bool _needsNet => _net != null;
|
||||
|
||||
protected override void OnAdd()
|
||||
{
|
||||
base.OnAdd();
|
||||
_net = NullNet;
|
||||
}
|
||||
[DataField("node")] [ViewVariables] public string? NodeId;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
if (_needsNet)
|
||||
{
|
||||
TryFindAndSetNet();
|
||||
@@ -56,9 +51,8 @@ namespace Content.Server.Power.Components
|
||||
|
||||
public void ClearNet()
|
||||
{
|
||||
RemoveSelfFromNet(_net);
|
||||
_net = NullNet;
|
||||
_needsNet = true;
|
||||
if (_net != null)
|
||||
RemoveSelfFromNet(_net);
|
||||
}
|
||||
|
||||
protected abstract void AddSelfToNet(TNetType net);
|
||||
@@ -70,7 +64,7 @@ namespace Content.Server.Power.Components
|
||||
if (Owner.TryGetComponent<NodeContainerComponent>(out var container))
|
||||
{
|
||||
var compatibleNet = container.Nodes.Values
|
||||
.Where(node => node.NodeGroupID == (NodeGroupID) Voltage)
|
||||
.Where(node => (NodeId == null || NodeId == node.Name) && node.NodeGroupID == (NodeGroupID) Voltage)
|
||||
.Select(node => node.NodeGroup)
|
||||
.OfType<TNetType>()
|
||||
.FirstOrDefault();
|
||||
@@ -85,12 +79,15 @@ namespace Content.Server.Power.Components
|
||||
return false;
|
||||
}
|
||||
|
||||
private void SetNet(TNetType newNet)
|
||||
private void SetNet(TNetType? newNet)
|
||||
{
|
||||
RemoveSelfFromNet(_net);
|
||||
AddSelfToNet(newNet);
|
||||
if (_net != null)
|
||||
RemoveSelfFromNet(_net);
|
||||
|
||||
if (newNet != null)
|
||||
AddSelfToNet(newNet);
|
||||
|
||||
_net = newNet;
|
||||
_needsNet = false;
|
||||
}
|
||||
|
||||
private void SetVoltage(Voltage newVoltage)
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
#nullable enable
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
public abstract class BasePowerNetComponent : BaseNetConnectorComponent<IPowerNet>
|
||||
{
|
||||
protected override IPowerNet NullNet => PowerNetNodeGroup.NullNet;
|
||||
}
|
||||
}
|
||||
|
||||
24
Content.Server/Power/Components/BatteryChargerComponent.cs
Normal file
24
Content.Server/Power/Components/BatteryChargerComponent.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Connects the loading side of a <see cref="BatteryComponent"/> to a non-APC power network.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class BatteryChargerComponent : BasePowerNetComponent
|
||||
{
|
||||
public override string Name => "BatteryCharger";
|
||||
|
||||
protected override void AddSelfToNet(IPowerNet net)
|
||||
{
|
||||
net.AddCharger(this);
|
||||
}
|
||||
|
||||
protected override void RemoveSelfFromNet(IPowerNet net)
|
||||
{
|
||||
net.RemoveCharger(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
101
Content.Server/Power/Components/BatteryComponent.cs
Normal file
101
Content.Server/Power/Components/BatteryComponent.cs
Normal file
@@ -0,0 +1,101 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Battery node on the pow3r network. Needs other components to connect to actual networks.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class BatteryComponent : Component
|
||||
{
|
||||
public override string Name => "Battery";
|
||||
|
||||
/// <summary>
|
||||
/// Maximum charge of the battery in joules (ie. watt seconds)
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)] public float MaxCharge { get => _maxCharge; set => SetMaxCharge(value); }
|
||||
[DataField("maxCharge")]
|
||||
private float _maxCharge;
|
||||
|
||||
/// <summary>
|
||||
/// Current charge of the battery in joules (ie. watt seconds)
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentCharge { get => _currentCharge; set => SetCurrentCharge(value); }
|
||||
[DataField("startingCharge")]
|
||||
private float _currentCharge;
|
||||
|
||||
/// <summary>
|
||||
/// True if the battery is fully charged.
|
||||
/// </summary>
|
||||
[ViewVariables] public bool IsFullyCharged => MathHelper.CloseTo(CurrentCharge, MaxCharge);
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("autoRecharge")] public bool AutoRecharge { get; set; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)] [DataField("autoRechargeRate")] public float AutoRechargeRate { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// If sufficient charge is avaiable on the battery, use it. Otherwise, don't.
|
||||
/// </summary>
|
||||
public virtual bool TryUseCharge(float chargeToUse)
|
||||
{
|
||||
if (chargeToUse >= CurrentCharge)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentCharge -= chargeToUse;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public virtual float UseCharge(float toDeduct)
|
||||
{
|
||||
var chargeChangedBy = Math.Min(CurrentCharge, toDeduct);
|
||||
CurrentCharge -= chargeChangedBy;
|
||||
return chargeChangedBy;
|
||||
}
|
||||
|
||||
public void FillFrom(BatteryComponent battery)
|
||||
{
|
||||
var powerDeficit = MaxCharge - CurrentCharge;
|
||||
if (battery.TryUseCharge(powerDeficit))
|
||||
{
|
||||
CurrentCharge += powerDeficit;
|
||||
}
|
||||
else
|
||||
{
|
||||
CurrentCharge += battery.CurrentCharge;
|
||||
battery.CurrentCharge = 0;
|
||||
}
|
||||
}
|
||||
|
||||
protected virtual void OnChargeChanged() { }
|
||||
|
||||
private void SetMaxCharge(float newMax)
|
||||
{
|
||||
_maxCharge = Math.Max(newMax, 0);
|
||||
_currentCharge = Math.Min(_currentCharge, MaxCharge);
|
||||
OnChargeChanged();
|
||||
}
|
||||
|
||||
private void SetCurrentCharge(float newChargeAmount)
|
||||
{
|
||||
_currentCharge = MathHelper.Clamp(newChargeAmount, 0, MaxCharge);
|
||||
OnChargeChanged();
|
||||
}
|
||||
|
||||
public void OnUpdate(float frameTime)
|
||||
{
|
||||
if (!AutoRecharge) return;
|
||||
if (IsFullyCharged) return;
|
||||
CurrentCharge += AutoRechargeRate * frameTime;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,79 +1,21 @@
|
||||
#nullable enable
|
||||
using Content.Server.Battery.Components;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Uses charge from a <see cref="BatteryComponent"/> to supply power via a <see cref="PowerSupplierComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class BatteryDischargerComponent : Component
|
||||
public class BatteryDischargerComponent : BasePowerNetComponent
|
||||
{
|
||||
public override string Name => "BatteryDischarger";
|
||||
|
||||
[ViewVariables]
|
||||
[ComponentDependency] private BatteryComponent? _battery = default!;
|
||||
|
||||
[ViewVariables]
|
||||
[ComponentDependency] private PowerSupplierComponent? _supplier = default!;
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int ActiveSupplyRate { get => _activeSupplyRate; set => SetActiveSupplyRate(value); }
|
||||
|
||||
[DataField("activeSupplyRate")]
|
||||
private int _activeSupplyRate = 50;
|
||||
|
||||
protected override void Initialize()
|
||||
protected override void AddSelfToNet(IPowerNet net)
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponentWarn<PowerSupplierComponent>();
|
||||
UpdateSupplyRate();
|
||||
net.AddDischarger(this);
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
protected override void RemoveSelfFromNet(IPowerNet net)
|
||||
{
|
||||
if (_battery == null)
|
||||
return;
|
||||
|
||||
//Simplified implementation - if the battery is empty, and charge is being added to the battery
|
||||
//at a lower rate that this is using it, the charge is used without creating power supply.
|
||||
_battery.CurrentCharge -= ActiveSupplyRate * frameTime;
|
||||
UpdateSupplyRate();
|
||||
}
|
||||
|
||||
private void UpdateSupplyRate()
|
||||
{
|
||||
if (_battery == null)
|
||||
return;
|
||||
|
||||
if (_battery.BatteryState == BatteryState.Empty)
|
||||
{
|
||||
SetSupplierSupplyRate(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetSupplierSupplyRate(ActiveSupplyRate);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetSupplierSupplyRate(int newSupplierSupplyRate)
|
||||
{
|
||||
if (_supplier == null)
|
||||
return;
|
||||
|
||||
if (_supplier.SupplyRate != newSupplierSupplyRate)
|
||||
{
|
||||
_supplier.SupplyRate = newSupplierSupplyRate;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetActiveSupplyRate(int newEnabledSupplyRate)
|
||||
{
|
||||
_activeSupplyRate = newEnabledSupplyRate;
|
||||
UpdateSupplyRate();
|
||||
net.RemoveDischarger(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
#nullable enable
|
||||
using Content.Server.Battery.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Takes power via a <see cref="PowerConsumerComponent"/> to charge a <see cref="BatteryComponent"/>.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class BatteryStorageComponent : Component
|
||||
{
|
||||
public override string Name => "BatteryStorage";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int ActiveDrawRate { get => _activeDrawRate; set => SetActiveDrawRate(value); }
|
||||
[DataField("activeDrawRate")]
|
||||
private int _activeDrawRate = 100;
|
||||
|
||||
[ViewVariables]
|
||||
[ComponentDependency] private BatteryComponent? _battery = default!;
|
||||
|
||||
[ViewVariables]
|
||||
public PowerConsumerComponent? Consumer => _consumer;
|
||||
|
||||
[ComponentDependency] private PowerConsumerComponent? _consumer = default!;
|
||||
|
||||
protected override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Owner.EnsureComponentWarn<PowerConsumerComponent>();
|
||||
UpdateDrawRate();
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
if (_consumer == null || _battery == null)
|
||||
return;
|
||||
|
||||
//Simplified implementation - If a frame adds more power to a partially full battery than it can hold, the power is lost.
|
||||
_battery.CurrentCharge += _consumer.ReceivedPower * frameTime;
|
||||
UpdateDrawRate();
|
||||
}
|
||||
|
||||
private void UpdateDrawRate()
|
||||
{
|
||||
if (_battery == null)
|
||||
return;
|
||||
|
||||
if (_battery.BatteryState == BatteryState.Full)
|
||||
{
|
||||
SetConsumerDraw(0);
|
||||
}
|
||||
else
|
||||
{
|
||||
SetConsumerDraw(ActiveDrawRate);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetConsumerDraw(int newConsumerDrawRate)
|
||||
{
|
||||
if (_consumer == null)
|
||||
return;
|
||||
|
||||
if (_consumer.DrawRate != newConsumerDrawRate)
|
||||
{
|
||||
_consumer.DrawRate = newConsumerDrawRate;
|
||||
}
|
||||
}
|
||||
|
||||
private void SetActiveDrawRate(int newEnabledDrawRate)
|
||||
{
|
||||
_activeDrawRate = newEnabledDrawRate;
|
||||
UpdateDrawRate();
|
||||
}
|
||||
}
|
||||
}
|
||||
59
Content.Server/Power/Components/CableComponent.cs
Normal file
59
Content.Server/Power/Components/CableComponent.cs
Normal file
@@ -0,0 +1,59 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Tools.Components;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Tool;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Allows the attached entity to be destroyed by a cutting tool, dropping a piece of cable.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class CableComponent : Component, IInteractUsing
|
||||
{
|
||||
public override string Name => "Cable";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("cableDroppedOnCutPrototype")]
|
||||
private string? _cableDroppedOnCutPrototype = "CableHVStack1";
|
||||
|
||||
/// <summary>
|
||||
/// Checked by <see cref="CablePlacerComponent"/> to determine if there is
|
||||
/// already a cable of a type on a tile.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public CableType CableType => _cableType;
|
||||
[DataField("cableType")]
|
||||
private CableType _cableType = CableType.HighVoltage;
|
||||
|
||||
async Task<bool> IInteractUsing.InteractUsing(InteractUsingEventArgs eventArgs)
|
||||
{
|
||||
if (_cableDroppedOnCutPrototype == null)
|
||||
return false;
|
||||
|
||||
if (!eventArgs.Using.TryGetComponent<ToolComponent>(out var tool)) return false;
|
||||
if (!await tool.UseTool(eventArgs.User, Owner, 0.25f, ToolQuality.Cutting)) return false;
|
||||
|
||||
Owner.Delete();
|
||||
var droppedEnt = Owner.EntityManager.SpawnEntity(_cableDroppedOnCutPrototype, eventArgs.ClickLocation);
|
||||
|
||||
// TODO: Literally just use a prototype that has a single thing in the stack, it's not that complicated...
|
||||
if (droppedEnt.TryGetComponent<StackComponent>(out var stack))
|
||||
EntitySystem.Get<StackSystem>().SetCount(droppedEnt.Uid, stack, 1);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public enum CableType
|
||||
{
|
||||
HighVoltage,
|
||||
MediumVoltage,
|
||||
Apc,
|
||||
}
|
||||
}
|
||||
58
Content.Server/Power/Components/CablePlacerComponent.cs
Normal file
58
Content.Server/Power/Components/CablePlacerComponent.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
#nullable enable
|
||||
using System.Threading.Tasks;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Interaction.Helpers;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
internal class CablePlacerComponent : Component, IAfterInteract
|
||||
{
|
||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||
|
||||
/// <inheritdoc />
|
||||
public override string Name => "CablePlacer";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("cablePrototypeID")]
|
||||
private string? _cablePrototypeID = "CableHV";
|
||||
|
||||
[ViewVariables]
|
||||
[DataField("blockingWireType")]
|
||||
private CableType _blockingCableType = CableType.HighVoltage;
|
||||
|
||||
/// <inheritdoc />
|
||||
async Task<bool> IAfterInteract.AfterInteract(AfterInteractEventArgs eventArgs)
|
||||
{
|
||||
if (_cablePrototypeID == null)
|
||||
return true;
|
||||
if (!eventArgs.InRangeUnobstructed(ignoreInsideBlocker: true, popup: true))
|
||||
return true;
|
||||
if(!_mapManager.TryGetGrid(eventArgs.ClickLocation.GetGridId(Owner.EntityManager), out var grid))
|
||||
return true;
|
||||
var snapPos = grid.TileIndicesFor(eventArgs.ClickLocation);
|
||||
if(grid.GetTileRef(snapPos).Tile.IsEmpty)
|
||||
return true;
|
||||
foreach (var anchored in grid.GetAnchoredEntities(snapPos))
|
||||
{
|
||||
if (Owner.EntityManager.ComponentManager.TryGetComponent<CableComponent>(anchored, out var wire) && wire.CableType == _blockingCableType)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (Owner.TryGetComponent<StackComponent>(out var stack)
|
||||
&& !EntitySystem.Get<StackSystem>().Use(Owner.Uid, stack, 1))
|
||||
return true;
|
||||
|
||||
Owner.EntityManager.SpawnEntity(_cablePrototypeID, grid.GridTileToLocal(snapPos));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
16
Content.Server/Power/Components/CableVisComponent.cs
Normal file
16
Content.Server/Power/Components/CableVisComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public sealed class CableVisComponent : Component
|
||||
{
|
||||
public override string Name => "CableVis";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("node")]
|
||||
public string? Node;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
#nullable enable
|
||||
using Content.Shared.Examine;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Utility;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
[RegisterComponent]
|
||||
public class ExaminableBatteryComponent : Component, IExamine
|
||||
{
|
||||
public override string Name => "ExaminableBattery";
|
||||
|
||||
[ViewVariables]
|
||||
[ComponentDependency] private BatteryComponent? _battery = default!;
|
||||
|
||||
void IExamine.Examine(FormattedMessage message, bool inDetailsRange)
|
||||
{
|
||||
if (_battery == null)
|
||||
return;
|
||||
if (inDetailsRange)
|
||||
{
|
||||
var effectiveMax = _battery.MaxCharge;
|
||||
if (effectiveMax == 0)
|
||||
effectiveMax = 1;
|
||||
var chargeFraction = _battery.CurrentCharge / effectiveMax;
|
||||
var chargePercentRounded = (int) (chargeFraction * 100);
|
||||
message.AddMarkup(
|
||||
Loc.GetString(
|
||||
"examinable-battery-component-examine-detail",
|
||||
("percent", chargePercentRounded),
|
||||
("markupPercentColor", "green")
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +0,0 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Maintains a set of <see cref="IPowerNet"/>s that need to be updated with <see cref="IPowerNet.UpdateConsumerReceivedPower"/>.
|
||||
/// Defers updating to reduce recalculations when a group is altered multiple times in a frame.
|
||||
/// </summary>
|
||||
public interface IPowerNetManager
|
||||
{
|
||||
/// <summary>
|
||||
/// Queue up an <see cref="IPowerNet"/> to be updated.
|
||||
/// </summary>
|
||||
void AddDirtyPowerNet(IPowerNet powerNet);
|
||||
|
||||
void Update(float frameTime);
|
||||
}
|
||||
|
||||
public class PowerNetManager : IPowerNetManager
|
||||
{
|
||||
private readonly HashSet<IPowerNet> _dirtyPowerNets = new();
|
||||
|
||||
public void AddDirtyPowerNet(IPowerNet powerNet)
|
||||
{
|
||||
_dirtyPowerNets.Add(powerNet);
|
||||
}
|
||||
|
||||
public void Update(float frameTime)
|
||||
{
|
||||
foreach (var powerNet in _dirtyPowerNets)
|
||||
{
|
||||
powerNet.UpdateConsumerReceivedPower();
|
||||
}
|
||||
_dirtyPowerNets.Clear();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,15 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Diagnostics;
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Draws power directly from an MV or HV wire it is on top of.
|
||||
/// </summary>
|
||||
[RegisterComponent]
|
||||
public class PowerConsumerComponent : BasePowerNetComponent
|
||||
{
|
||||
@@ -16,28 +18,19 @@ namespace Content.Server.Power.Components
|
||||
/// <summary>
|
||||
/// How much power this needs to be fully powered.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int DrawRate { get => _drawRate; set => SetDrawRate(value); }
|
||||
[DataField("drawRate")]
|
||||
private int _drawRate;
|
||||
|
||||
/// <summary>
|
||||
/// Determines which <see cref="PowerConsumerComponent"/>s receive power when there is not enough
|
||||
/// power for each.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public Priority Priority { get => _priority; set => SetPriority(value); }
|
||||
[DataField("priority")]
|
||||
private Priority _priority = Priority.First;
|
||||
public float DrawRate { get => NetworkLoad.DesiredPower; set => NetworkLoad.DesiredPower = value; }
|
||||
|
||||
/// <summary>
|
||||
/// How much power this is currently receiving from <see cref="PowerSupplierComponent"/>s.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public int ReceivedPower { get => _receivedPower; set => SetReceivedPower(value); }
|
||||
private int _receivedPower;
|
||||
public float ReceivedPower => NetworkLoad.ReceivingPower;
|
||||
|
||||
public event EventHandler<ReceivedPowerChangedEventArgs>? OnReceivedPowerChanged;
|
||||
public float LastReceived = float.NaN;
|
||||
|
||||
public PowerState.Load NetworkLoad { get; } = new();
|
||||
|
||||
protected override void AddSelfToNet(IPowerNet powerNet)
|
||||
{
|
||||
@@ -48,44 +41,5 @@ namespace Content.Server.Power.Components
|
||||
{
|
||||
powerNet.RemoveConsumer(this);
|
||||
}
|
||||
|
||||
private void SetDrawRate(int newDrawRate)
|
||||
{
|
||||
var oldDrawRate = DrawRate;
|
||||
_drawRate = newDrawRate; //must be set before updating powernet, as it checks the DrawRate of every consumer
|
||||
Net.UpdateConsumerDraw(this, oldDrawRate, newDrawRate);
|
||||
}
|
||||
|
||||
private void SetReceivedPower(int newReceivedPower)
|
||||
{
|
||||
Debug.Assert(newReceivedPower >= 0 && newReceivedPower <= DrawRate);
|
||||
if(_receivedPower == newReceivedPower) return;
|
||||
_receivedPower = newReceivedPower;
|
||||
OnReceivedPowerChanged?.Invoke(this, new ReceivedPowerChangedEventArgs(_drawRate, _receivedPower));
|
||||
}
|
||||
|
||||
private void SetPriority(Priority newPriority)
|
||||
{
|
||||
Net.UpdateConsumerPriority(this, Priority, newPriority);
|
||||
_priority = newPriority;
|
||||
}
|
||||
}
|
||||
|
||||
public enum Priority
|
||||
{
|
||||
First,
|
||||
Last,
|
||||
}
|
||||
|
||||
public class ReceivedPowerChangedEventArgs : EventArgs
|
||||
{
|
||||
public readonly int DrawRate;
|
||||
public readonly int ReceivedPower;
|
||||
|
||||
public ReceivedPowerChangedEventArgs(int drawRate, int receivedPower)
|
||||
{
|
||||
DrawRate = drawRate;
|
||||
ReceivedPower = receivedPower;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
119
Content.Server/Power/Components/PowerNetworkBatteryComponent.cs
Normal file
119
Content.Server/Power/Components/PowerNetworkBatteryComponent.cs
Normal file
@@ -0,0 +1,119 @@
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Glue component that manages the pow3r network node for batteries that are connected to the power network.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This needs components like <see cref="BatteryChargerComponent"/> to work correctly,
|
||||
/// and battery storage should be handed off to components like <see cref="BatteryComponent"/>.
|
||||
/// </remarks>
|
||||
[RegisterComponent]
|
||||
public sealed class PowerNetworkBatteryComponent : Component
|
||||
{
|
||||
public override string Name => "PowerNetworkBattery";
|
||||
|
||||
[DataField("maxChargeRate")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MaxChargeRate
|
||||
{
|
||||
get => NetworkBattery.MaxChargeRate;
|
||||
set => NetworkBattery.MaxChargeRate = value;
|
||||
}
|
||||
|
||||
[DataField("maxSupply")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float MaxSupply
|
||||
{
|
||||
get => NetworkBattery.MaxSupply;
|
||||
set => NetworkBattery.MaxSupply = value;
|
||||
}
|
||||
|
||||
[DataField("supplyRampTolerance")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SupplyRampTolerance
|
||||
{
|
||||
get => NetworkBattery.SupplyRampTolerance;
|
||||
set => NetworkBattery.SupplyRampTolerance = value;
|
||||
}
|
||||
|
||||
[DataField("supplyRampRate")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SupplyRampRate
|
||||
{
|
||||
get => NetworkBattery.SupplyRampRate;
|
||||
set => NetworkBattery.SupplyRampRate = value;
|
||||
}
|
||||
|
||||
[DataField("supplyRampPosition")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float SupplyRampPosition
|
||||
{
|
||||
get => NetworkBattery.SupplyRampPosition;
|
||||
set => NetworkBattery.SupplyRampPosition = value;
|
||||
}
|
||||
|
||||
[DataField("currentSupply")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentSupply
|
||||
{
|
||||
get => NetworkBattery.CurrentSupply;
|
||||
set => NetworkBattery.CurrentSupply = value;
|
||||
}
|
||||
|
||||
[DataField("currentReceiving")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float CurrentReceiving
|
||||
{
|
||||
get => NetworkBattery.CurrentReceiving;
|
||||
set => NetworkBattery.CurrentReceiving = value;
|
||||
}
|
||||
|
||||
[DataField("loadingNetworkDemand")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float LoadingNetworkDemand
|
||||
{
|
||||
get => NetworkBattery.LoadingNetworkDemand;
|
||||
set => NetworkBattery.LoadingNetworkDemand = value;
|
||||
}
|
||||
|
||||
[DataField("enabled")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Enabled
|
||||
{
|
||||
get => NetworkBattery.Enabled;
|
||||
set => NetworkBattery.Enabled = value;
|
||||
}
|
||||
|
||||
[DataField("canCharge")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanCharge
|
||||
{
|
||||
get => NetworkBattery.CanCharge;
|
||||
set => NetworkBattery.CanCharge = value;
|
||||
}
|
||||
|
||||
[DataField("canDisharge")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool CanDischarge
|
||||
{
|
||||
get => NetworkBattery.CanDischarge;
|
||||
set => NetworkBattery.CanDischarge = value;
|
||||
}
|
||||
|
||||
[DataField("efficiency")]
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public float Efficiency
|
||||
{
|
||||
get => NetworkBattery.Efficiency;
|
||||
set => NetworkBattery.Efficiency = value;
|
||||
}
|
||||
|
||||
[ViewVariables]
|
||||
public PowerState.Battery NetworkBattery { get; } = new();
|
||||
}
|
||||
}
|
||||
@@ -1,174 +0,0 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Content.Server.APC;
|
||||
using Content.Server.APC.Components;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
|
||||
namespace Content.Server.Power.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// Relays <see cref="PowerReceiverComponent"/>s in an area to a <see cref="IApcNet"/> so they can receive power.
|
||||
/// </summary>
|
||||
public interface IPowerProvider
|
||||
{
|
||||
void AddReceiver(PowerReceiverComponent receiver);
|
||||
|
||||
void RemoveReceiver(PowerReceiverComponent receiver);
|
||||
|
||||
void UpdateReceiverLoad(int oldLoad, int newLoad);
|
||||
|
||||
public IEntity? ProviderOwner { get; }
|
||||
|
||||
public bool HasApcPower { get; }
|
||||
}
|
||||
|
||||
[RegisterComponent]
|
||||
public class PowerProviderComponent : BaseApcNetComponent, IPowerProvider
|
||||
{
|
||||
public override string Name => "PowerProvider";
|
||||
|
||||
public IEntity ProviderOwner => Owner;
|
||||
|
||||
[ViewVariables]
|
||||
public bool HasApcPower => Net.Powered;
|
||||
|
||||
/// <summary>
|
||||
/// The max distance this can transmit power to <see cref="PowerReceiverComponent"/>s from.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int PowerTransferRange { get => _powerTransferRange; set => SetPowerTransferRange(value); }
|
||||
[DataField("powerTransferRange")]
|
||||
private int _powerTransferRange = 3;
|
||||
|
||||
[ViewVariables]
|
||||
public IReadOnlyList<PowerReceiverComponent> LinkedReceivers => _linkedReceivers;
|
||||
private List<PowerReceiverComponent> _linkedReceivers = new();
|
||||
|
||||
/// <summary>
|
||||
/// If <see cref="PowerReceiverComponent"/>s should consider connecting to this.
|
||||
/// </summary>
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public bool Connectable { get; private set; } = true;
|
||||
|
||||
public static readonly IPowerProvider NullProvider = new NullPowerProvider();
|
||||
|
||||
public void AddReceiver(PowerReceiverComponent receiver)
|
||||
{
|
||||
var oldLoad = GetTotalLoad();
|
||||
_linkedReceivers.Add(receiver);
|
||||
var newLoad = oldLoad + receiver.Load;
|
||||
Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad);
|
||||
}
|
||||
|
||||
public void RemoveReceiver(PowerReceiverComponent receiver)
|
||||
{
|
||||
var oldLoad = GetTotalLoad();
|
||||
_linkedReceivers.Remove(receiver);
|
||||
var newLoad = oldLoad - receiver.Load;
|
||||
Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad);
|
||||
}
|
||||
|
||||
public void UpdateReceiverLoad(int oldLoad, int newLoad)
|
||||
{
|
||||
Net.UpdatePowerProviderReceivers(this, oldLoad, newLoad);
|
||||
}
|
||||
|
||||
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.ClearProvider();
|
||||
}
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
receiver.TryFindAndSetProvider();
|
||||
}
|
||||
base.OnRemove();
|
||||
}
|
||||
|
||||
private List<PowerReceiverComponent> FindAvailableReceivers()
|
||||
{
|
||||
var nearbyEntities = IoCManager.Resolve<IEntityLookup>()
|
||||
.GetEntitiesInRange(Owner, PowerTransferRange);
|
||||
|
||||
var receivers = new List<PowerReceiverComponent>();
|
||||
|
||||
foreach (var entity in nearbyEntities)
|
||||
{
|
||||
if (entity.TryGetComponent<PowerReceiverComponent>(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))
|
||||
{
|
||||
receivers.Add(receiver);
|
||||
}
|
||||
}
|
||||
return receivers;
|
||||
}
|
||||
|
||||
protected override void AddSelfToNet(IApcNet apcNet)
|
||||
{
|
||||
apcNet.AddPowerProvider(this);
|
||||
}
|
||||
|
||||
protected override void RemoveSelfFromNet(IApcNet apcNet)
|
||||
{
|
||||
apcNet.RemovePowerProvider(this);
|
||||
}
|
||||
|
||||
private void SetPowerTransferRange(int newPowerTransferRange)
|
||||
{
|
||||
var receivers = _linkedReceivers.ToArray();
|
||||
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
receiver.ClearProvider();
|
||||
}
|
||||
_powerTransferRange = newPowerTransferRange;
|
||||
|
||||
foreach (var receiver in receivers)
|
||||
{
|
||||
receiver.TryFindAndSetProvider();
|
||||
}
|
||||
}
|
||||
|
||||
private int GetTotalLoad()
|
||||
{
|
||||
var load = 0;
|
||||
foreach (var receiver in _linkedReceivers)
|
||||
{
|
||||
load += receiver.Load;
|
||||
}
|
||||
return load;
|
||||
}
|
||||
|
||||
private class NullPowerProvider : IPowerProvider
|
||||
{
|
||||
/// <summary>
|
||||
/// It is important that this returns false, so <see cref="PowerReceiverComponent"/>s with a <see cref="NullPowerProvider"/> have no power.
|
||||
/// </summary>
|
||||
public bool HasApcPower => false;
|
||||
|
||||
public void AddReceiver(PowerReceiverComponent receiver) { }
|
||||
public void RemoveReceiver(PowerReceiverComponent receiver) { }
|
||||
public void UpdateReceiverLoad(int oldLoad, int newLoad) { }
|
||||
public IEntity? ProviderOwner => default;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#nullable enable
|
||||
using Content.Server.NodeContainer.NodeGroups;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Serialization.Manager.Attributes;
|
||||
using Robust.Shared.ViewVariables;
|
||||
@@ -12,9 +13,45 @@ namespace Content.Server.Power.Components
|
||||
public override string Name => "PowerSupplier";
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
public int SupplyRate { get => _supplyRate; set => SetSupplyRate(value); }
|
||||
[DataField("supplyRate")]
|
||||
private int _supplyRate;
|
||||
public float MaxSupply { get => NetworkSupply.MaxSupply; set => NetworkSupply.MaxSupply = value; }
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("supplyRampTolerance")]
|
||||
public float SupplyRampTolerance
|
||||
{
|
||||
get => NetworkSupply.SupplyRampTolerance;
|
||||
set => NetworkSupply.SupplyRampTolerance = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("supplyRampRate")]
|
||||
public float SupplyRampRate
|
||||
{
|
||||
get => NetworkSupply.SupplyRampRate;
|
||||
set => NetworkSupply.SupplyRampRate = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("supplyRampPosition")]
|
||||
public float SupplyRampPosition
|
||||
{
|
||||
get => NetworkSupply.SupplyRampPosition;
|
||||
set => NetworkSupply.SupplyRampPosition = value;
|
||||
}
|
||||
|
||||
[ViewVariables(VVAccess.ReadWrite)]
|
||||
[DataField("enabled")]
|
||||
public bool Enabled
|
||||
{
|
||||
get => NetworkSupply.Enabled;
|
||||
set => NetworkSupply.Enabled = value;
|
||||
}
|
||||
|
||||
[ViewVariables] public float CurrentSupply => NetworkSupply.CurrentSupply;
|
||||
|
||||
[ViewVariables]
|
||||
public PowerState.Supply NetworkSupply { get; } = new();
|
||||
|
||||
protected override void AddSelfToNet(IPowerNet powerNet)
|
||||
{
|
||||
@@ -25,11 +62,5 @@ namespace Content.Server.Power.Components
|
||||
{
|
||||
powerNet.RemoveSupplier(this);
|
||||
}
|
||||
|
||||
private void SetSupplyRate(int newSupplyRate)
|
||||
{
|
||||
Net.UpdateSupplierSupply(this, SupplyRate, newSupplyRate);
|
||||
_supplyRate = newSupplyRate;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user