+using Content.Server.Power.EntitySystems;
+
namespace Content.Server.Power.Components
{
/// <summary>
/// <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.
[ViewVariables(VVAccess.ReadWrite)]
public float PricePerJoule = 0.0001f;
- /// <summary>
- /// If sufficient charge is avaiable on the battery, use it. Otherwise, don't.
- /// </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();
- }
+ [Obsolete("Use system method")]
+ public bool TryUseCharge(float value)
+ => _entMan.System<BatterySystem>().TryUseCharge(Owner, value, this);
}
- public struct ChargeChangedEvent {}
+ /// <summary>
+ /// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage).
+ /// </summary>
+ [ByRefEvent]
+ public record struct ChargeChangedEvent(float Charge, float MaxCharge);
}
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;
}
// 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);
}
public void UpdateApcState(EntityUid uid,
ApcComponent? apc=null,
- BatteryComponent? battery=null)
+ PowerNetworkBatteryComponent? battery = null)
{
if (!Resolve(uid, ref apc, ref battery))
return;
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;
}
}
- var extPowerState = CalcExtPowerState(uid, apc, battery);
+ var extPowerState = CalcExtPowerState(uid, battery.NetworkBattery);
if (extPowerState != apc.LastExternalState
|| apc.LastUiUpdate + ApcComponent.VisualsChangeDelay < _gameTiming.CurTime)
{
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;
using Content.Shared.Examine;
using Content.Shared.Rejuvenate;
using JetBrains.Annotations;
+using Robust.Shared.Utility;
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;
}
}
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);
}
}
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;
}
}
}
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)
{
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);
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)
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)
{
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);
}
}
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;
[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;