using Content.Server.Popups; using Content.Server.Power.Components; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.APC; using Content.Shared.Emag.Systems; using Content.Shared.Popups; using JetBrains.Annotations; using Robust.Server.GameObjects; using Robust.Shared.Audio; using Robust.Shared.Player; using Robust.Shared.Timing; namespace Content.Server.Power.EntitySystems { [UsedImplicitly] internal sealed class ApcSystem : EntitySystem { [Dependency] private readonly AccessReaderSystem _accessReader = default!; [Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!; [Dependency] private readonly PopupSystem _popupSystem = default!; [Dependency] private readonly IGameTiming _gameTiming = default!; public override void Initialize() { base.Initialize(); UpdatesAfter.Add(typeof(PowerNetSystem)); SubscribeLocalEvent(OnApcInit); SubscribeLocalEvent(OnBatteryChargeChanged); SubscribeLocalEvent(OnToggleMainBreaker); SubscribeLocalEvent(OnEmagged); } // Change the APC's state only when the battery state changes, or when it's first created. private void OnBatteryChargeChanged(EntityUid uid, ApcComponent component, ChargeChangedEvent args) { UpdateApcState(uid, component); } private void OnApcInit(EntityUid uid, ApcComponent component, MapInitEvent args) { UpdateApcState(uid, component); } private void OnToggleMainBreaker(EntityUid uid, ApcComponent component, ApcToggleMainBreakerMessage args) { TryComp(uid, out var access); if (args.Session.AttachedEntity == null) return; if (access == null || _accessReader.IsAllowed(args.Session.AttachedEntity.Value, access)) { ApcToggleBreaker(uid, component); } else { _popupSystem.PopupCursor(Loc.GetString("apc-component-insufficient-access"), Filter.Entities(args.Session.AttachedEntity.Value), PopupType.Medium); } } public void ApcToggleBreaker(EntityUid uid, ApcComponent? apc = null, PowerNetworkBatteryComponent? battery = null) { if (!Resolve(uid, ref apc, ref battery)) return; apc.MainBreakerEnabled = !apc.MainBreakerEnabled; battery.CanDischarge = apc.MainBreakerEnabled; UpdateUIState(uid, apc); SoundSystem.Play(apc.OnReceiveMessageSound.GetSound(), Filter.Pvs(uid), uid, AudioParams.Default.WithVolume(-2f)); } private void OnEmagged(EntityUid uid, ApcComponent comp, GotEmaggedEvent args) { if(!comp.Emagged) { comp.Emagged = true; args.Handled = true; } } public void UpdateApcState(EntityUid uid, ApcComponent? apc=null, BatteryComponent? battery=null) { if (!Resolve(uid, ref apc, ref battery)) return; var newState = CalcChargeState(uid, apc, battery); if (newState != apc.LastChargeState && apc.LastChargeStateTime + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime) { apc.LastChargeState = newState; apc.LastChargeStateTime = _gameTiming.CurTime; if (TryComp(uid, out AppearanceComponent? appearance)) { appearance.SetData(ApcVisuals.ChargeState, newState); } } var extPowerState = CalcExtPowerState(uid, apc, battery); if (extPowerState != apc.LastExternalState || apc.LastUiUpdate + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime) { apc.LastExternalState = extPowerState; apc.LastUiUpdate = _gameTiming.CurTime; UpdateUIState(uid, apc, battery); } } public void UpdateUIState(EntityUid uid, ApcComponent? apc = null, BatteryComponent? battery = null, ServerUserInterfaceComponent? ui = null) { if (!Resolve(uid, ref apc, ref battery, ref ui)) return; if (_userInterfaceSystem.GetUiOrNull(uid, ApcUiKey.Key, ui) is { } bui) { bui.SetState(new ApcBoundInterfaceState(apc.MainBreakerEnabled, apc.LastExternalState, battery.CurrentCharge / battery.MaxCharge)); } } public ApcChargeState CalcChargeState(EntityUid uid, ApcComponent? apc=null, BatteryComponent? battery=null) { if (apc != null && apc.Emagged) return ApcChargeState.Emag; if (!Resolve(uid, ref apc, ref battery)) return ApcChargeState.Lack; var chargeFraction = battery.CurrentCharge / battery.MaxCharge; if (chargeFraction > ApcComponent.HighPowerThreshold) { return ApcChargeState.Full; } var netBattery = Comp(uid); var delta = netBattery.CurrentSupply - netBattery.CurrentReceiving; return delta < 0 ? ApcChargeState.Charging : ApcChargeState.Lack; } public ApcExternalPowerState CalcExtPowerState(EntityUid uid, ApcComponent? apc=null, BatteryComponent? battery=null) { if (!Resolve(uid, ref apc, ref battery)) return ApcExternalPowerState.None; var netBat = Comp(uid); if (netBat.CurrentReceiving == 0 && !MathHelper.CloseTo(battery.CurrentCharge / battery.MaxCharge, 1)) { return ApcExternalPowerState.None; } var delta = netBat.CurrentReceiving - netBat.LoadingNetworkDemand; if (!MathHelper.CloseToPercent(delta, 0, 0.1f) && delta < 0) { return ApcExternalPowerState.Low; } return ApcExternalPowerState.Good; } } }