using Content.Shared.Power.Components;
using JetBrains.Annotations;
namespace Content.Shared.Power.EntitySystems;
///
/// Responsible for .
/// Predicted equivalent of .
/// If you make changes to this make sure to keep the two consistent.
///
public abstract partial class SharedBatterySystem
{
///
/// Changes the battery's charge by the given amount
/// and resets the self-recharge cooldown if it exists.
/// A positive value will add charge, a negative value will remove charge.
///
/// The actually changed amount.
[PublicAPI]
public float ChangeCharge(Entity ent, float amount)
{
if (!Resolve(ent, ref ent.Comp))
return 0;
var oldValue = GetCharge(ent);
var newValue = Math.Clamp(oldValue + amount, 0, ent.Comp.MaxCharge);
var delta = newValue - oldValue;
if (delta == 0f)
return 0f;
var curTime = _timing.CurTime;
ent.Comp.LastCharge = newValue;
ent.Comp.LastUpdate = curTime;
Dirty(ent);
TrySetChargeCooldown(ent.Owner);
var changedEv = new ChargeChangedEvent(newValue, delta, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref changedEv);
// Raise events if the battery status changed between full, empty, or neither.
UpdateState(ent);
return delta;
}
///
/// Removes the given amount of charge from the battery
/// and resets the self-recharge cooldown if it exists.
///
/// The actually changed amount.
[PublicAPI]
public float UseCharge(Entity ent, float amount)
{
if (amount <= 0f)
return 0f;
return ChangeCharge(ent, -amount);
}
///
/// If sufficient charge is available on the battery, use it. Otherwise, don't.
/// Resets the self-recharge cooldown if it exists.
/// Always returns false on the client.
///
/// If the full amount was able to be removed.
[PublicAPI]
public bool TryUseCharge(Entity ent, float amount)
{
if (!Resolve(ent, ref ent.Comp, false) || amount > GetCharge(ent))
return false;
UseCharge(ent, amount);
return true;
}
///
/// Sets the battery's charge.
///
[PublicAPI]
public void SetCharge(Entity ent, float value)
{
if (!Resolve(ent, ref ent.Comp))
return;
var oldValue = GetCharge(ent);
var newValue = Math.Clamp(value, 0, ent.Comp.MaxCharge);
var delta = newValue - oldValue;
if (delta == 0f)
return;
var curTime = _timing.CurTime;
ent.Comp.LastCharge = newValue;
ent.Comp.LastUpdate = curTime;
Dirty(ent);
var ev = new ChargeChangedEvent(newValue, delta, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref ev);
// Raise events if the battery status changed between full, empty, or neither.
UpdateState(ent);
}
///
/// Sets the battery's maximum charge.
///
[PublicAPI]
public void SetMaxCharge(Entity ent, float value)
{
if (!Resolve(ent, ref ent.Comp))
return;
// ReSharper disable once CompareOfFloatsByEqualityOperator
if (value == ent.Comp.MaxCharge)
return;
var oldCharge = GetCharge(ent);
ent.Comp.MaxCharge = Math.Max(value, 0);
ent.Comp.LastCharge = GetCharge(ent); // This clamps it using the new max.
var curTime = _timing.CurTime;
ent.Comp.LastUpdate = curTime;
Dirty(ent);
var ev = new ChargeChangedEvent(ent.Comp.LastCharge, ent.Comp.LastCharge - oldCharge, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref ev);
// Raise events if the battery status changed between full, empty, or neither.
UpdateState(ent);
}
///
/// Updates the battery's charge state and sends an event if it changed.
///
[PublicAPI]
public void UpdateState(Entity ent)
{
if (!Resolve(ent, ref ent.Comp))
return;
var oldState = ent.Comp.State;
var newState = BatteryState.Neither;
var charge = GetCharge(ent);
if (charge >= ent.Comp.MaxCharge)
newState = BatteryState.Full;
else if (charge == 0f)
newState = BatteryState.Empty;
if (oldState == newState)
return;
ent.Comp.State = newState;
Dirty(ent);
var changedEv = new BatteryStateChangedEvent(oldState, newState);
RaiseLocalEvent(ent, ref changedEv);
}
///
/// Gets the battery's current charge.
///
[PublicAPI]
public float GetCharge(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
var curTime = _timing.CurTime;
// We have a constant charge rate, so the charge changes linearly over time.
var dt = (curTime - ent.Comp.LastUpdate).TotalSeconds;
var charge = Math.Clamp(ent.Comp.LastCharge + (float)(dt * ent.Comp.ChargeRate), 0f, ent.Comp.MaxCharge);
return charge;
}
///
/// Gets the fraction of charge remaining (0–1).
///
[PublicAPI]
public float GetChargeLevel(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
if (ent.Comp.MaxCharge <= 0f)
return 0f;
return GetCharge(ent) / ent.Comp.MaxCharge;
}
///
/// Gets number of remaining uses for the given charge cost.
///
[PublicAPI]
public int GetRemainingUses(Entity ent, float cost)
{
if (cost <= 0)
return 0;
if (!Resolve(ent, ref ent.Comp))
return 0;
return (int)(GetCharge(ent) / cost);
}
///
/// Gets number of maximum uses at full charge for the given charge cost.
///
[PublicAPI]
public int GetMaxUses(Entity ent, float cost)
{
if (cost <= 0)
return 0;
if (!Resolve(ent, ref ent.Comp))
return 0;
return (int)(ent.Comp.MaxCharge / cost);
}
///
/// Refreshes the battery's current charge rate by raising a .
/// Subscribe to that event to add to or subtract from the total charge rate.
///
/// The new charge rate.
[PublicAPI]
public float RefreshChargeRate(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
ent.Comp.LastCharge = GetCharge(ent); // Prevent the new rate from modifying the current charge.
var curTime = _timing.CurTime;
ent.Comp.LastUpdate = curTime;
var refreshEv = new RefreshChargeRateEvent(ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref refreshEv);
ent.Comp.ChargeRate = refreshEv.NewChargeRate;
Dirty(ent);
// Inform other systems about the new rate;
var changedEv = new ChargeChangedEvent(ent.Comp.LastCharge, 0f, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref changedEv);
return refreshEv.NewChargeRate;
}
///
/// Checks if the entity has a self recharge and puts it on cooldown if applicable.
/// Uses the cooldown time given in the component.
///
[PublicAPI]
public void TrySetChargeCooldown(Entity ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
if (ent.Comp.AutoRechargePauseTime == TimeSpan.Zero)
return; // no recharge pause
if (_timing.CurTime + ent.Comp.AutoRechargePauseTime <= ent.Comp.NextAutoRecharge)
return; // the current pause is already longer
SetChargeCooldown(ent, ent.Comp.AutoRechargePauseTime);
}
///
/// Puts the entity's self recharge on cooldown for the specified time.
///
[PublicAPI]
public void SetChargeCooldown(Entity ent, TimeSpan cooldown)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.NextAutoRecharge = _timing.CurTime + cooldown;
Dirty(ent);
RefreshChargeRate(ent.Owner); // Apply the new charge rate.
}
///
/// Returns whether the battery is full.
///
[PublicAPI]
public bool IsFull(Entity ent)
{
if (!Resolve(ent, ref ent.Comp))
return false;
return GetCharge(ent) >= ent.Comp.MaxCharge;
}
}