#nullable enable using System.Collections.Generic; using System.Linq; using Content.Server.GameObjects.Components.NodeContainer.Nodes; using Content.Server.GameObjects.Components.Power; using Content.Server.GameObjects.Components.Power.ApcNetComponents; using Content.Server.GameObjects.EntitySystems; using Robust.Shared.GameObjects; using Robust.Shared.Map; using Robust.Shared.Utility; using Robust.Shared.ViewVariables; namespace Content.Server.GameObjects.Components.NodeContainer.NodeGroups { public interface IApcNet { bool Powered { get; } void AddApc(ApcComponent apc); void RemoveApc(ApcComponent apc); void AddPowerProvider(PowerProviderComponent provider); void RemovePowerProvider(PowerProviderComponent provider); void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad); void Update(float frameTime); GridId? GridId { get; } } [NodeGroup(NodeGroupID.Apc)] public class ApcNetNodeGroup : BaseNetConnectorNodeGroup, IApcNet { [ViewVariables] private readonly Dictionary _apcBatteries = new(); [ViewVariables] private readonly List _providers = new(); [ViewVariables] public bool Powered { get => _powered; private set => SetPowered(value); } private bool _powered = false; //Debug property [ViewVariables] private int TotalReceivers => _providers.SelectMany(provider => provider.LinkedReceivers).Count(); [ViewVariables] private int TotalPowerReceiverLoad { get => _totalPowerReceiverLoad; set => SetTotalPowerReceiverLoad(value); } GridId? IApcNet.GridId => GridId; private int _totalPowerReceiverLoad = 0; public static readonly IApcNet NullNet = new NullApcNet(); public override void Initialize(Node sourceNode) { base.Initialize(sourceNode); EntitySystem.Get().AddApcNet(this); } protected override void AfterRemake(IEnumerable newGroups) { base.AfterRemake(newGroups); foreach (var group in newGroups) { if (group is not ApcNetNodeGroup apcNet) continue; apcNet.Powered = Powered; } StopUpdates(); } protected override void OnGivingNodesForCombine(INodeGroup newGroup) { base.OnGivingNodesForCombine(newGroup); if (newGroup is ApcNetNodeGroup apcNet) { apcNet.Powered = Powered; } StopUpdates(); } private void StopUpdates() { EntitySystem.Get().RemoveApcNet(this); } #region IApcNet Methods protected override void SetNetConnectorNet(BaseApcNetComponent netConnectorComponent) { netConnectorComponent.Net = this; } public void AddApc(ApcComponent apc) { if (!apc.Owner.TryGetComponent(out BatteryComponent? battery)) { return; } _apcBatteries.Add(apc, battery); } public void RemoveApc(ApcComponent apc) { _apcBatteries.Remove(apc); } public void AddPowerProvider(PowerProviderComponent provider) { _providers.Add(provider); foreach (var receiver in provider.LinkedReceivers) { TotalPowerReceiverLoad += receiver.Load; } } public void RemovePowerProvider(PowerProviderComponent provider) { _providers.Remove(provider); foreach (var receiver in provider.LinkedReceivers) { TotalPowerReceiverLoad -= receiver.Load; } } public void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad) { DebugTools.Assert(_providers.Contains(provider)); TotalPowerReceiverLoad -= oldLoad; TotalPowerReceiverLoad += newLoad; } public void Update(float frameTime) { var remainingPowerNeeded = TotalPowerReceiverLoad * frameTime; foreach (var apcBatteryPair in _apcBatteries) { var apc = apcBatteryPair.Key; if (!apc.MainBreakerEnabled) continue; var battery = apcBatteryPair.Value; if (battery.CurrentCharge < remainingPowerNeeded) { remainingPowerNeeded -= battery.CurrentCharge; battery.CurrentCharge = 0; } else { battery.UseCharge(remainingPowerNeeded); remainingPowerNeeded = 0; } if (remainingPowerNeeded == 0) break; } Powered = remainingPowerNeeded == 0; } private void SetPowered(bool powered) { if (powered != Powered) { _powered = powered; PoweredChanged(); } } private void PoweredChanged() { foreach (var provider in _providers) { foreach (var receiver in provider.LinkedReceivers) { receiver.ApcPowerChanged(); } } } private void SetTotalPowerReceiverLoad(int totalPowerReceiverLoad) { DebugTools.Assert(totalPowerReceiverLoad >= 0, $"Expected load equal to or greater than 0, was {totalPowerReceiverLoad}"); _totalPowerReceiverLoad = totalPowerReceiverLoad; } #endregion private class NullApcNet : IApcNet { /// /// It is important that this returns false, so s with a have no power. /// public bool Powered => false; public GridId? GridId => default; public void AddApc(ApcComponent apc) { } public void AddPowerProvider(PowerProviderComponent provider) { } public void RemoveApc(ApcComponent apc) { } public void RemovePowerProvider(PowerProviderComponent provider) { } public void UpdatePowerProviderReceivers(PowerProviderComponent provider, int oldLoad, int newLoad) { } public void Update(float frameTime) { } } } }