ECS BatteryComponent (#15337)

This commit is contained in:
Leon Friedrich
2023-04-19 22:10:08 +12:00
committed by GitHub
parent e95cd6e412
commit 9513ea66e4
6 changed files with 128 additions and 112 deletions

View File

@@ -1,3 +1,5 @@
using Content.Server.Power.EntitySystems;
namespace Content.Server.Power.Components
{
/// <summary>
@@ -12,17 +14,32 @@ namespace Content.Server.Power.Components
/// <summary>
/// Maximum charge of the battery in joules (ie. watt seconds)
/// </summary>
[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<BatterySystem>().SetMaxCharge(Owner, value, this);
}
[DataField("maxCharge")]
private float _maxCharge;
[Access(typeof(BatterySystem))]
public 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); }
public float CurrentCharge
{
get => Charge;
[Obsolete("Use system method")]
set => _entMan.System<BatterySystem>().SetCharge(Owner, value, this);
}
[DataField("startingCharge")]
private float _currentCharge;
[Access(typeof(BatterySystem))]
public float Charge;
/// <summary>
/// True if the battery is fully charged.
@@ -36,61 +53,14 @@ namespace Content.Server.Power.Components
[ViewVariables(VVAccess.ReadWrite)]
public float PricePerJoule = 0.0001f;
[Obsolete("Use system method")]
public bool TryUseCharge(float value)
=> _entMan.System<BatterySystem>().TryUseCharge(Owner, value, this);
}
/// <summary>
/// If sufficient charge is avaiable on the battery, use it. Otherwise, don't.
/// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage).
/// </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()
{
_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();
}
}
public struct ChargeChangedEvent {}
[ByRefEvent]
public record struct ChargeChangedEvent(float Charge, float MaxCharge);
}

View File

@@ -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<PowerNetworkBatteryComponent>(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<EmaggedComponent>(uid))
if (HasComp<EmaggedComponent>(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<PowerNetworkBatteryComponent>(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<PowerNetworkBatteryComponent>(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;

View File

@@ -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<PowerNetworkBatteryComponent, BatteryComponent>();
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);
}
/// <summary>
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
/// </summary>
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;
}
}
}

View File

@@ -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);

View File

@@ -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<BatteryComponent>(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<BatteryComponent>(uid, out var battery))
return;
battery.CurrentCharge -= component.FireCost;
UpdateShots(uid, component, battery);
// Will raise ChargeChangedEvent
_battery.UseCharge(uid, component.FireCost);
}
}

View File

@@ -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;