| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218 |
- using Content.Server.Emp;
- using Content.Server.Popups;
- using Content.Server.Power.Components;
- using Content.Server.Power.Pow3r;
- using Content.Shared.Access.Systems;
- using Content.Shared.APC;
- using Content.Shared.Emag.Systems;
- using Content.Shared.Popups;
- using Content.Shared.Rounding;
- using Robust.Server.GameObjects;
- using Robust.Shared.Audio;
- using Robust.Shared.Audio.Systems;
- using Robust.Shared.Timing;
- namespace Content.Server.Power.EntitySystems;
- public sealed class ApcSystem : EntitySystem
- {
- [Dependency] private readonly AccessReaderSystem _accessReader = default!;
- [Dependency] private readonly IGameTiming _gameTiming = default!;
- [Dependency] private readonly EmagSystem _emag = default!;
- [Dependency] private readonly PopupSystem _popup = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly SharedAudioSystem _audio = default!;
- [Dependency] private readonly UserInterfaceSystem _ui = default!;
- public override void Initialize()
- {
- base.Initialize();
- UpdatesAfter.Add(typeof(PowerNetSystem));
- SubscribeLocalEvent<ApcComponent, BoundUIOpenedEvent>(OnBoundUiOpen);
- SubscribeLocalEvent<ApcComponent, ComponentStartup>(OnApcStartup);
- SubscribeLocalEvent<ApcComponent, ChargeChangedEvent>(OnBatteryChargeChanged);
- SubscribeLocalEvent<ApcComponent, ApcToggleMainBreakerMessage>(OnToggleMainBreaker);
- SubscribeLocalEvent<ApcComponent, GotEmaggedEvent>(OnEmagged);
- SubscribeLocalEvent<ApcComponent, EmpPulseEvent>(OnEmpPulse);
- }
- public override void Update(float deltaTime)
- {
- var query = EntityQueryEnumerator<ApcComponent, PowerNetworkBatteryComponent, UserInterfaceComponent>();
- while (query.MoveNext(out var uid, out var apc, out var battery, out var ui))
- {
- if (apc.LastUiUpdate + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime && _ui.IsUiOpen((uid, ui), ApcUiKey.Key))
- {
- apc.LastUiUpdate = _gameTiming.CurTime;
- UpdateUIState(uid, apc, battery);
- }
- if (apc.NeedStateUpdate)
- {
- UpdateApcState(uid, apc, battery);
- }
- }
- }
- // Change the APC's state only when the battery state changes, or when it's first created.
- private void OnBatteryChargeChanged(EntityUid uid, ApcComponent component, ref ChargeChangedEvent args)
- {
- UpdateApcState(uid, component);
- }
- private static void OnApcStartup(EntityUid uid, ApcComponent component, ComponentStartup args)
- {
- // We cannot update immediately, as various network/battery state is not valid yet.
- // Defer until the next tick.
- component.NeedStateUpdate = true;
- }
- private void OnBoundUiOpen(EntityUid uid, ApcComponent component, BoundUIOpenedEvent args)
- {
- UpdateApcState(uid, component);
- }
- private void OnToggleMainBreaker(EntityUid uid, ApcComponent component, ApcToggleMainBreakerMessage args)
- {
- var attemptEv = new ApcToggleMainBreakerAttemptEvent();
- RaiseLocalEvent(uid, ref attemptEv);
- if (attemptEv.Cancelled)
- {
- _popup.PopupCursor(Loc.GetString("apc-component-on-toggle-cancel"),
- args.Actor, PopupType.Medium);
- return;
- }
- if (_accessReader.IsAllowed(args.Actor, uid))
- {
- ApcToggleBreaker(uid, component);
- }
- else
- {
- _popup.PopupCursor(Loc.GetString("apc-component-insufficient-access"),
- args.Actor, 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);
- _audio.PlayPvs(apc.OnReceiveMessageSound, uid, AudioParams.Default.WithVolume(-2f));
- }
- private void OnEmagged(EntityUid uid, ApcComponent comp, ref GotEmaggedEvent args)
- {
- if (!_emag.CompareFlag(args.Type, EmagType.Interaction))
- return;
- if (_emag.CheckFlag(uid, EmagType.Interaction))
- return;
- args.Handled = true;
- }
- public void UpdateApcState(EntityUid uid,
- ApcComponent? apc=null,
- PowerNetworkBatteryComponent? battery = null)
- {
- if (!Resolve(uid, ref apc, ref battery, false))
- return;
- if (apc.LastChargeStateTime == null || apc.LastChargeStateTime + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime)
- {
- var newState = CalcChargeState(uid, battery.NetworkBattery);
- if (newState != apc.LastChargeState)
- {
- apc.LastChargeState = newState;
- apc.LastChargeStateTime = _gameTiming.CurTime;
- if (TryComp(uid, out AppearanceComponent? appearance))
- {
- _appearance.SetData(uid, ApcVisuals.ChargeState, newState, appearance);
- }
- }
- }
- var extPowerState = CalcExtPowerState(uid, battery.NetworkBattery);
- if (extPowerState != apc.LastExternalState)
- {
- apc.LastExternalState = extPowerState;
- UpdateUIState(uid, apc, battery);
- }
- apc.NeedStateUpdate = false;
- }
- public void UpdateUIState(EntityUid uid,
- ApcComponent? apc = null,
- PowerNetworkBatteryComponent? netBat = null,
- UserInterfaceComponent? ui = null)
- {
- if (!Resolve(uid, ref apc, ref netBat, ref ui))
- return;
- var battery = netBat.NetworkBattery;
- const int ChargeAccuracy = 5;
- // TODO: Fix ContentHelpers or make a new one coz this is cooked.
- var charge = ContentHelpers.RoundToNearestLevels(battery.CurrentStorage / battery.Capacity, 1.0, 100 / ChargeAccuracy) / 100f * ChargeAccuracy;
- var state = new ApcBoundInterfaceState(apc.MainBreakerEnabled,
- (int) MathF.Ceiling(battery.CurrentSupply), apc.LastExternalState,
- charge);
- _ui.SetUiState((uid, ui), ApcUiKey.Key, state);
- }
- private ApcChargeState CalcChargeState(EntityUid uid, PowerState.Battery battery)
- {
- if (_emag.CheckFlag(uid, EmagType.Interaction))
- return ApcChargeState.Emag;
- if (battery.CurrentStorage / battery.Capacity > ApcComponent.HighPowerThreshold)
- {
- return ApcChargeState.Full;
- }
- var delta = battery.CurrentSupply - battery.CurrentReceiving;
- return delta < 0 ? ApcChargeState.Charging : ApcChargeState.Lack;
- }
- private ApcExternalPowerState CalcExtPowerState(EntityUid uid, PowerState.Battery battery)
- {
- if (battery.CurrentReceiving == 0 && !MathHelper.CloseTo(battery.CurrentStorage / battery.Capacity, 1))
- {
- return ApcExternalPowerState.None;
- }
- var delta = battery.CurrentSupply - battery.CurrentReceiving;
- if (!MathHelper.CloseToPercent(delta, 0, 0.1f) && delta < 0)
- {
- return ApcExternalPowerState.Low;
- }
- return ApcExternalPowerState.Good;
- }
- private void OnEmpPulse(EntityUid uid, ApcComponent component, ref EmpPulseEvent args)
- {
- if (component.MainBreakerEnabled)
- {
- args.Affected = true;
- args.Disabled = true;
- ApcToggleBreaker(uid, component);
- }
- }
- }
- [ByRefEvent]
- public record struct ApcToggleMainBreakerAttemptEvent(bool Cancelled);
|