From 9513ea66e41dfa148257267274839a4ec489ae4c Mon Sep 17 00:00:00 2001 From: Leon Friedrich <60421075+ElectroJr@users.noreply.github.com> Date: Wed, 19 Apr 2023 22:10:08 +1200 Subject: [PATCH] ECS BatteryComponent (#15337) --- .../Power/Components/BatteryComponent.cs | 88 ++++++------------- .../Power/EntitySystems/ApcSystem.cs | 54 +++++------- .../Power/EntitySystems/BatterySystem.cs | 69 ++++++++++++++- Content.Server/PowerCell/PowerCellSystem.cs | 7 +- .../Ranged/Systems/GunSystem.Battery.cs | 19 ++-- .../Weapons/Ranged/Systems/GunSystem.cs | 3 + 6 files changed, 128 insertions(+), 112 deletions(-) diff --git a/Content.Server/Power/Components/BatteryComponent.cs b/Content.Server/Power/Components/BatteryComponent.cs index 55202e1e70..8071321745 100644 --- a/Content.Server/Power/Components/BatteryComponent.cs +++ b/Content.Server/Power/Components/BatteryComponent.cs @@ -1,3 +1,5 @@ +using Content.Server.Power.EntitySystems; + namespace Content.Server.Power.Components { /// @@ -12,17 +14,32 @@ namespace Content.Server.Power.Components /// /// Maximum charge of the battery in joules (ie. watt seconds) /// - [ViewVariables(VVAccess.ReadWrite)] public float MaxCharge { get => _maxCharge; set => SetMaxCharge(value); } + [ViewVariables(VVAccess.ReadWrite)] + public float MaxCharge + { + get => _maxCharge; + [Obsolete("Use system method")] + set => _entMan.System().SetMaxCharge(Owner, value, this); + } + [DataField("maxCharge")] - private float _maxCharge; + [Access(typeof(BatterySystem))] + public float _maxCharge; /// /// Current charge of the battery in joules (ie. watt seconds) /// [ViewVariables(VVAccess.ReadWrite)] - public float CurrentCharge { get => _currentCharge; set => SetCurrentCharge(value); } + public float CurrentCharge + { + get => Charge; + [Obsolete("Use system method")] + set => _entMan.System().SetCharge(Owner, value, this); + } + [DataField("startingCharge")] - private float _currentCharge; + [Access(typeof(BatterySystem))] + public float Charge; /// /// True if the battery is fully charged. @@ -36,61 +53,14 @@ namespace Content.Server.Power.Components [ViewVariables(VVAccess.ReadWrite)] public float PricePerJoule = 0.0001f; - /// - /// If sufficient charge is avaiable on the battery, use it. Otherwise, don't. - /// - 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() - { - _entMan.EventBus.RaiseLocalEvent(Owner, new ChargeChangedEvent(), false); - } - - 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(); - } + [Obsolete("Use system method")] + public bool TryUseCharge(float value) + => _entMan.System().TryUseCharge(Owner, value, this); } - public struct ChargeChangedEvent {} + /// + /// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage). + /// + [ByRefEvent] + public record struct ChargeChangedEvent(float Charge, float MaxCharge); } diff --git a/Content.Server/Power/EntitySystems/ApcSystem.cs b/Content.Server/Power/EntitySystems/ApcSystem.cs index a66677970a..496ed01500 100644 --- a/Content.Server/Power/EntitySystems/ApcSystem.cs +++ b/Content.Server/Power/EntitySystems/ApcSystem.cs @@ -1,6 +1,7 @@ using Content.Server.Emp; using Content.Server.Popups; using Content.Server.Power.Components; +using Content.Server.Power.Pow3r; using Content.Shared.Access.Components; using Content.Shared.Access.Systems; using Content.Shared.APC; @@ -54,7 +55,7 @@ namespace Content.Server.Power.EntitySystems } // 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) + private void OnBatteryChargeChanged(EntityUid uid, ApcComponent component, ref ChargeChangedEvent args) { UpdateApcState(uid, component); } @@ -116,7 +117,7 @@ namespace Content.Server.Power.EntitySystems public void UpdateApcState(EntityUid uid, ApcComponent? apc=null, - BatteryComponent? battery=null) + PowerNetworkBatteryComponent? battery = null) { if (!Resolve(uid, ref apc, ref battery)) return; @@ -126,7 +127,7 @@ namespace Content.Server.Power.EntitySystems UpdatePanelAppearance(uid, appearance, apc); } - var newState = CalcChargeState(uid, apc, battery); + var newState = CalcChargeState(uid, battery.NetworkBattery); if (newState != apc.LastChargeState && apc.LastChargeStateTime + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime) { apc.LastChargeState = newState; @@ -138,7 +139,7 @@ namespace Content.Server.Power.EntitySystems } } - var extPowerState = CalcExtPowerState(uid, apc, battery); + var extPowerState = CalcExtPowerState(uid, battery.NetworkBattery); if (extPowerState != apc.LastExternalState || apc.LastUiUpdate + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime) { @@ -150,58 +151,43 @@ namespace Content.Server.Power.EntitySystems public void UpdateUIState(EntityUid uid, ApcComponent? apc = null, - BatteryComponent? battery = null, + PowerNetworkBatteryComponent? netBat = null, ServerUserInterfaceComponent? ui = null) { - if (!Resolve(uid, ref apc, ref battery, ref ui)) + if (!Resolve(uid, ref apc, ref netBat, ref ui)) return; - var netBattery = Comp(uid); - float power = netBattery is not null ? netBattery.CurrentSupply : 0f; + var battery = netBat.NetworkBattery; - if (_userInterfaceSystem.GetUiOrNull(uid, ApcUiKey.Key, ui) is { } bui) - { - bui.SetState(new ApcBoundInterfaceState(apc.MainBreakerEnabled, apc.HasAccess, (int)MathF.Ceiling(power), apc.LastExternalState, battery.CurrentCharge / battery.MaxCharge)); - } + var state = new ApcBoundInterfaceState(apc.MainBreakerEnabled, apc.HasAccess, + (int) MathF.Ceiling(battery.CurrentSupply), apc.LastExternalState, + battery.AvailableSupply / battery.Capacity); + + _userInterfaceSystem.TrySetUiState(uid, ApcUiKey.Key, state, ui: ui); } - public ApcChargeState CalcChargeState(EntityUid uid, - ApcComponent? apc=null, - BatteryComponent? battery=null) + private ApcChargeState CalcChargeState(EntityUid uid, PowerState.Battery battery) { - if (apc != null && HasComp(uid)) + if (HasComp(uid)) return ApcChargeState.Emag; - if (!Resolve(uid, ref apc, ref battery)) - return ApcChargeState.Lack; - - var chargeFraction = battery.CurrentCharge / battery.MaxCharge; - - if (chargeFraction > ApcComponent.HighPowerThreshold) + if (battery.CurrentStorage / battery.Capacity > ApcComponent.HighPowerThreshold) { return ApcChargeState.Full; } - var netBattery = Comp(uid); - var delta = netBattery.CurrentSupply - netBattery.CurrentReceiving; - + var delta = battery.CurrentSupply - battery.CurrentReceiving; return delta < 0 ? ApcChargeState.Charging : ApcChargeState.Lack; } - public ApcExternalPowerState CalcExtPowerState(EntityUid uid, - ApcComponent? apc=null, - BatteryComponent? battery=null) + private ApcExternalPowerState CalcExtPowerState(EntityUid uid, PowerState.Battery battery) { - 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)) + if (battery.CurrentReceiving == 0 && !MathHelper.CloseTo(battery.CurrentStorage / battery.Capacity, 1)) { return ApcExternalPowerState.None; } - var delta = netBat.CurrentReceiving - netBat.CurrentSupply; + var delta = battery.CurrentSupply - battery.CurrentReceiving; if (!MathHelper.CloseToPercent(delta, 0, 0.1f) && delta < 0) { return ApcExternalPowerState.Low; diff --git a/Content.Server/Power/EntitySystems/BatterySystem.cs b/Content.Server/Power/EntitySystems/BatterySystem.cs index 0d2bfb3c25..479d07c26a 100644 --- a/Content.Server/Power/EntitySystems/BatterySystem.cs +++ b/Content.Server/Power/EntitySystems/BatterySystem.cs @@ -4,6 +4,7 @@ using Content.Server.Power.Components; using Content.Shared.Examine; using Content.Shared.Rejuvenate; using JetBrains.Annotations; +using Robust.Shared.Utility; namespace Content.Server.Power.EntitySystems { @@ -61,8 +62,9 @@ namespace Content.Server.Power.EntitySystems var enumerator = AllEntityQuery(); while (enumerator.MoveNext(out var netBat, out var bat)) { + DebugTools.Assert(bat.Charge <= bat.MaxCharge && bat.Charge >= 0); netBat.NetworkBattery.Capacity = bat.MaxCharge; - netBat.NetworkBattery.CurrentStorage = bat.CurrentCharge; + netBat.NetworkBattery.CurrentStorage = bat.Charge; } } @@ -73,10 +75,17 @@ namespace Content.Server.Power.EntitySystems while (enumerator.MoveNext(out var uid, out var netBat, out var bat)) { var netCharge = netBat.NetworkBattery.CurrentStorage; + + bat.Charge = netCharge; + DebugTools.Assert(bat.Charge <= bat.MaxCharge && bat.Charge >= 0); + + // TODO maybe decrease tolerance & track the charge at the time the event was most recently raised. + // Ensures that events aren't skipped when there are many tiny power changes. if (MathHelper.CloseTo(bat.CurrentCharge, netCharge)) continue; - bat.CurrentCharge = netCharge; + var changeEv = new ChargeChangedEvent(netCharge, bat.MaxCharge); + RaiseLocalEvent(uid, ref changeEv); } } @@ -101,7 +110,61 @@ namespace Content.Server.Power.EntitySystems private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args) { args.Affected = true; - component.UseCharge(args.EnergyConsumption); + UseCharge(uid, args.EnergyConsumption, component); + } + + public float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null) + { + if (value <= 0 || !Resolve(uid, ref battery) || battery.CurrentCharge == 0) + return 0; + + var newValue = Math.Clamp(0, battery.CurrentCharge - value, battery._maxCharge); + var delta = newValue - battery.Charge; + battery.Charge = newValue; + var ev = new ChargeChangedEvent(battery.CurrentCharge, battery._maxCharge); + RaiseLocalEvent(uid, ref ev); + return delta; + } + + public void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null) + { + if (!Resolve(uid, ref battery)) + return; + + var old = battery._maxCharge; + battery._maxCharge = Math.Max(value, 0); + battery.Charge = Math.Min(battery.Charge, battery._maxCharge); + if (MathHelper.CloseTo(battery._maxCharge, old)) + return; + + var ev = new ChargeChangedEvent(battery.CurrentCharge, battery._maxCharge); + RaiseLocalEvent(uid, ref ev); + } + + public void SetCharge(EntityUid uid, float value, BatteryComponent? battery = null) + { + if (!Resolve(uid, ref battery)) + return; + + var old = battery.Charge; + battery.Charge = MathHelper.Clamp(value, 0, battery._maxCharge); + if (MathHelper.CloseTo(battery.Charge, old)) + return; + + var ev = new ChargeChangedEvent(battery.CurrentCharge, battery._maxCharge); + RaiseLocalEvent(uid, ref ev); + } + + /// + /// If sufficient charge is available on the battery, use it. Otherwise, don't. + /// + public bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null) + { + if (!Resolve(uid, ref battery, false) || value > battery.Charge) + return false; + + UseCharge(uid, value, battery); + return true; } } } diff --git a/Content.Server/PowerCell/PowerCellSystem.cs b/Content.Server/PowerCell/PowerCellSystem.cs index c199c6beb8..f7882dd644 100644 --- a/Content.Server/PowerCell/PowerCellSystem.cs +++ b/Content.Server/PowerCell/PowerCellSystem.cs @@ -66,7 +66,7 @@ public sealed class PowerCellSystem : SharedPowerCellSystem Explode(uid, component, args.User); } - private void OnChargeChanged(EntityUid uid, PowerCellComponent component, ChargeChangedEvent args) + private void OnChargeChanged(EntityUid uid, PowerCellComponent component, ref ChargeChangedEvent args) { if (component.IsRigged) { @@ -74,13 +74,10 @@ public sealed class PowerCellSystem : SharedPowerCellSystem return; } - if (!TryComp(uid, out BatteryComponent? battery)) - return; - if (!TryComp(uid, out AppearanceComponent? appearance)) return; - var frac = battery.CurrentCharge / battery.MaxCharge; + var frac = args.Charge / args.MaxCharge; var level = (byte) ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels); _sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level, appearance); diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs index 5c4dd90dab..39482048c9 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.Battery.cs @@ -32,9 +32,9 @@ public sealed partial class GunSystem UpdateShots(uid, component); } - private void OnBatteryChargeChange(EntityUid uid, BatteryAmmoProviderComponent component, ChargeChangedEvent args) + private void OnBatteryChargeChange(EntityUid uid, BatteryAmmoProviderComponent component, ref ChargeChangedEvent args) { - UpdateShots(uid, component); + UpdateShots(uid, component, args.Charge, args.MaxCharge); } private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component) @@ -42,13 +42,13 @@ public sealed partial class GunSystem if (!TryComp(uid, out var battery)) return; - UpdateShots(uid, component, battery); + UpdateShots(uid, component, battery.Charge, battery.MaxCharge); } - private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component, BatteryComponent battery) + private void UpdateShots(EntityUid uid, BatteryAmmoProviderComponent component, float charge, float maxCharge) { - var shots = (int) (battery.CurrentCharge / component.FireCost); - var maxShots = (int) (battery.MaxCharge / component.FireCost); + var shots = (int) (charge / component.FireCost); + var maxShots = (int) (maxCharge / component.FireCost); if (component.Shots != shots || component.Capacity != maxShots) { @@ -128,10 +128,7 @@ public sealed partial class GunSystem protected override void TakeCharge(EntityUid uid, BatteryAmmoProviderComponent component) { - if (!TryComp(uid, out var battery)) - return; - - battery.CurrentCharge -= component.FireCost; - UpdateShots(uid, component, battery); + // Will raise ChargeChangedEvent + _battery.UseCharge(uid, component.FireCost); } } diff --git a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs index 5868a45086..3eaf9d8bf4 100644 --- a/Content.Server/Weapons/Ranged/Systems/GunSystem.cs +++ b/Content.Server/Weapons/Ranged/Systems/GunSystem.cs @@ -3,6 +3,7 @@ using Content.Server.Administration.Logs; using Content.Server.Cargo.Systems; using Content.Server.Examine; using Content.Server.Interaction; +using Content.Server.Power.EntitySystems; using Content.Server.Stunnable; using Content.Server.Weapons.Ranged.Components; using Content.Shared.Damage; @@ -37,6 +38,8 @@ public sealed partial class GunSystem : SharedGunSystem [Dependency] private readonly StaminaSystem _stamina = default!; [Dependency] private readonly StunSystem _stun = default!; [Dependency] private readonly SharedTransformSystem _transform = default!; + [Dependency] private readonly BatterySystem _battery = default!; + public const float DamagePitchVariation = SharedMeleeWeaponSystem.DamagePitchVariation; public const float GunClumsyChance = 0.5f; -- 2.51.2