battery = entityManager.GetComponent<BatteryComponent>(generatorEnt);
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
- batterySys.SetMaxCharge(generatorEnt, startingCharge, battery);
- batterySys.SetCharge(generatorEnt, startingCharge, battery);
+ batterySys.SetMaxCharge((generatorEnt, battery), startingCharge);
+ batterySys.SetCharge((generatorEnt, battery), startingCharge);
netBattery.MaxSupply = 400;
netBattery.SupplyRampRate = 400;
netBattery.SupplyRampTolerance = 100;
supplier.SupplyRampRate = rampRate;
supplier.SupplyRampTolerance = rampTol;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
+ batterySys.SetMaxCharge((batteryEnt, battery), 100_000);
+ batterySys.SetCharge((batteryEnt, battery), 100_000);
netBattery.MaxSupply = draw / 2;
netBattery.SupplyRampRate = rampRate;
netBattery.SupplyRampTolerance = rampTol;
supplier.MaxSupply = 500;
supplier.SupplyRampTolerance = 500;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
+ batterySys.SetMaxCharge((batteryEnt, battery), 100_000);
netBattery.MaxChargeRate = 1_000;
netBattery.Efficiency = 0.5f;
});
netBattery.MaxSupply = 400;
netBattery.SupplyRampTolerance = 400;
netBattery.SupplyRampRate = 100_000;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
+ batterySys.SetMaxCharge((batteryEnt, battery), 100_000);
+ batterySys.SetCharge((batteryEnt, battery), 100_000);
});
// Run some ticks so everything is stable.
netBattery.SupplyRampTolerance = 400;
netBattery.SupplyRampRate = 100_000;
netBattery.Efficiency = 0.5f;
- batterySys.SetMaxCharge(batteryEnt, 1_000_000, battery);
- batterySys.SetCharge(batteryEnt, 1_000_000, battery);
+ batterySys.SetMaxCharge((batteryEnt, battery), 1_000_000);
+ batterySys.SetCharge((batteryEnt, battery), 1_000_000);
});
// Run some ticks so everything is stable.
supplier.MaxSupply = 1000;
supplier.SupplyRampTolerance = 1000;
- batterySys.SetMaxCharge(batteryEnt1, 1_000_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 1_000_000, battery2);
+ batterySys.SetMaxCharge((batteryEnt1, battery1), 1_000_000);
+ batterySys.SetMaxCharge((batteryEnt2, battery2), 1_000_000);
netBattery1.MaxChargeRate = 1_000;
netBattery2.MaxChargeRate = 1_000;
netBattery2.SupplyRampTolerance = 1000;
netBattery1.SupplyRampRate = 100_000;
netBattery2.SupplyRampRate = 100_000;
- batterySys.SetMaxCharge(batteryEnt1, 100_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 100_000, battery2);
- batterySys.SetCharge(batteryEnt1, 100_000, battery1);
- batterySys.SetCharge(batteryEnt2, 100_000, battery2);
+ batterySys.SetMaxCharge((batteryEnt1, battery1), 100_000);
+ batterySys.SetMaxCharge((batteryEnt2, battery2), 100_000);
+ batterySys.SetCharge((batteryEnt1, battery1), 100_000);
+ batterySys.SetCharge((batteryEnt2, battery2), 100_000);
});
// Run some ticks so everything is stable.
supplier.MaxSupply = 1000;
supplier.SupplyRampTolerance = 1000;
- batterySys.SetMaxCharge(batteryEnt1, 1_000_000, battery1);
- batterySys.SetMaxCharge(batteryEnt2, 1_000_000, battery2);
+ batterySys.SetMaxCharge((batteryEnt1, battery1), 1_000_000);
+ batterySys.SetMaxCharge((batteryEnt2, battery2), 1_000_000);
netBattery1.MaxChargeRate = 20;
netBattery2.MaxChargeRate = 20;
netBattery.MaxSupply = 1000;
netBattery.SupplyRampTolerance = 200;
netBattery.SupplyRampRate = 10;
- batterySys.SetMaxCharge(batteryEnt, 100_000, battery);
- batterySys.SetCharge(batteryEnt, 100_000, battery);
+ batterySys.SetMaxCharge((batteryEnt, battery), 100_000);
+ batterySys.SetCharge((batteryEnt, battery), 100_000);
});
// Run some ticks so everything is stable.
generatorSupplier.MaxSupply = 1000;
generatorSupplier.SupplyRampTolerance = 1000;
- batterySys.SetCharge(apcEnt, 0, apcBattery);
+ batterySys.SetCharge((apcEnt, apcBattery), 0);
});
server.RunTicks(5); //let run a few ticks for PowerNets to reevaluate and start charging apc
extensionCableSystem.SetProviderTransferRange(apcExtensionEnt, range);
extensionCableSystem.SetReceiverReceptionRange(powerReceiverEnt, range);
- batterySys.SetMaxCharge(apcEnt, 10000, battery); //arbitrary nonzero amount of charge
- batterySys.SetCharge(apcEnt, battery.MaxCharge, battery); //fill battery
+ batterySys.SetMaxCharge((apcEnt, battery), 10000); //arbitrary nonzero amount of charge
+ batterySys.SetCharge((apcEnt, battery), battery.MaxCharge); //fill battery
receiver.Load = 1; //arbitrary small amount of power
});
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
Act = () =>
{
- _batterySystem.SetCharge(args.Target, battery.MaxCharge, battery);
+ _batterySystem.SetCharge((args.Target, battery), battery.MaxCharge);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-refill-battery-description"),
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
Act = () =>
{
- _batterySystem.SetCharge(args.Target, 0, battery);
+ _batterySystem.SetCharge((args.Target, battery), 0);
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-drain-battery-description"),
Act = () =>
{
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
- recharger.AutoRecharge = true;
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
- recharger.AutoRechargePause = false; // No delay.
+ recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
continue;
var battery = EnsureComp<BatteryComponent>(ent);
- _batterySystem.SetCharge(ent, battery.MaxCharge, battery);
+ _batterySystem.SetCharge((ent, battery), battery.MaxCharge);
}
},
Impact = LogImpact.Extreme,
if (!HasComp<StationInfiniteBatteryTargetComponent>(ent))
continue;
var battery = EnsureComp<BatteryComponent>(ent);
- _batterySystem.SetCharge(ent, 0, battery);
+ _batterySystem.SetCharge((ent, battery), 0);
}
},
Impact = LogImpact.Extreme,
var recharger = EnsureComp<BatterySelfRechargerComponent>(ent);
var battery = EnsureComp<BatteryComponent>(ent);
- recharger.AutoRecharge = true;
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
- recharger.AutoRechargePause = false; // No delay.
+ recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
}
},
Impact = LogImpact.Extreme,
{
if (entity.Comp.State == EmergencyLightState.On)
{
- if (!_battery.TryUseCharge(entity.Owner, entity.Comp.Wattage * frameTime, battery))
+ if (!_battery.TryUseCharge((entity.Owner, battery), entity.Comp.Wattage * frameTime))
{
SetState(entity.Owner, entity.Comp, EmergencyLightState.Empty);
TurnOff(entity);
}
else
{
- _battery.SetCharge(entity.Owner, battery.CurrentCharge + entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency, battery);
- if (_battery.IsFull(entity, battery))
+ _battery.SetCharge((entity.Owner, battery), battery.CurrentCharge + entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency);
+ if (_battery.IsFull((entity.Owner, battery)))
{
if (TryComp<ApcPowerReceiverComponent>(entity.Owner, out var receiver))
{
_appearance.SetData(uid, HandheldLightVisuals.Power, HandheldLightPowerStates.Dying, appearanceComponent);
}
- if (component.Activated && !_battery.TryUseCharge(batteryUid.Value, component.Wattage * frameTime, battery))
+ if (component.Activated && !_battery.TryUseCharge((batteryUid.Value, battery), component.Wattage * frameTime))
TurnOff(uid, false);
UpdateLevel(uid);
if (!TryComp<BatteryComponent>(battery, out var batteryComp))
return false;
- _battery.SetCharge(battery!.Value, batteryComp.CurrentCharge + delta.Float(), batteryComp);
+ _battery.SetCharge((battery.Value, batteryComp), batteryComp.CurrentCharge + delta.Float());
if (batteryComp.CurrentCharge != component.Energy) //if there's a discrepency, we have to resync them
{
Log.Debug($"Battery charge was not equal to mech charge. Battery {batteryComp.CurrentCharge}. Mech {component.Energy}");
// higher tier storages can charge more
var maxDrained = pnb.MaxSupply * comp.DrainTime;
var input = Math.Min(Math.Min(available, required / comp.DrainEfficiency), maxDrained);
- if (!_battery.TryUseCharge(target, input, targetBattery))
+ if (!_battery.TryUseCharge((target, targetBattery), input))
return false;
var output = input * comp.DrainEfficiency;
- _battery.SetCharge(comp.BatteryUid.Value, battery.CurrentCharge + output, battery);
+ _battery.SetCharge((comp.BatteryUid.Value, battery), battery.CurrentCharge + output);
// TODO: create effect message or something
Spawn("EffectSparks", Transform(target).Coordinates);
_audio.PlayPvs(comp.SparkSound, target);
_popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid);
// repeat the doafter until battery is full
- return !_battery.IsFull(comp.BatteryUid.Value, battery);
+ return !_battery.IsFull((comp.BatteryUid.Value, battery));
}
}
/// <inheritdoc/>
public override bool TryUseCharge(EntityUid user, float charge)
{
- return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge(uid.Value, charge, battery);
+ return GetNinjaBattery(user, out var uid, out var battery) && _battery.TryUseCharge((uid.Value, battery), charge);
}
/// <summary>
+++ /dev/null
-using System;
-
-namespace Content.Server.Power.Components
-{
- /// <summary>
- /// Self-recharging battery.
- /// </summary>
- [RegisterComponent]
- public sealed partial class BatterySelfRechargerComponent : Component
- {
- /// <summary>
- /// Does the entity auto recharge?
- /// </summary>
- [DataField] public bool AutoRecharge;
-
- /// <summary>
- /// At what rate does the entity automatically recharge?
- /// </summary>
- [DataField] public float AutoRechargeRate;
-
- /// <summary>
- /// Should this entity stop automatically recharging if a charge is used?
- /// </summary>
- [DataField] public bool AutoRechargePause = false;
-
- /// <summary>
- /// How long should the entity stop automatically recharging if a charge is used?
- /// </summary>
- [DataField] public float AutoRechargePauseTime = 0f;
-
- /// <summary>
- /// Do not auto recharge if this timestamp has yet to happen, set for the auto recharge pause system.
- /// </summary>
- [DataField] public TimeSpan NextAutoRecharge = TimeSpan.FromSeconds(0f);
- }
-}
--- /dev/null
+using Content.Shared.Power;
+using Content.Shared.Power.Components;
+
+namespace Content.Server.Power.EntitySystems;
+
+public sealed partial class BatterySystem
+{
+ public override float ChangeCharge(Entity<BatteryComponent?> ent, float amount)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return 0;
+
+ var newValue = Math.Clamp(ent.Comp.CurrentCharge + amount, 0, ent.Comp.MaxCharge);
+ var delta = newValue - ent.Comp.CurrentCharge;
+ ent.Comp.CurrentCharge = newValue;
+
+ TrySetChargeCooldown(ent.Owner);
+
+ var ev = new ChargeChangedEvent(ent.Comp.CurrentCharge, ent.Comp.MaxCharge);
+ RaiseLocalEvent(ent, ref ev);
+ return delta;
+ }
+
+ public override float UseCharge(Entity<BatteryComponent?> ent, float amount)
+ {
+ if (amount <= 0 || !Resolve(ent, ref ent.Comp) || ent.Comp.CurrentCharge == 0)
+ return 0;
+
+ return ChangeCharge(ent, -amount);
+ }
+
+ public override bool TryUseCharge(Entity<BatteryComponent?> ent, float amount)
+ {
+ if (!Resolve(ent, ref ent.Comp, false) || amount > ent.Comp.CurrentCharge)
+ return false;
+
+ UseCharge(ent, amount);
+ return true;
+ }
+
+ public override void SetCharge(Entity<BatteryComponent?> ent, float value)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return;
+
+ var oldCharge = ent.Comp.CurrentCharge;
+ ent.Comp.CurrentCharge = MathHelper.Clamp(value, 0, ent.Comp.MaxCharge);
+ if (MathHelper.CloseTo(ent.Comp.CurrentCharge, oldCharge) &&
+ !(oldCharge != ent.Comp.CurrentCharge && ent.Comp.CurrentCharge == ent.Comp.MaxCharge))
+ {
+ return;
+ }
+
+ var ev = new ChargeChangedEvent(ent.Comp.CurrentCharge, ent.Comp.MaxCharge);
+ RaiseLocalEvent(ent, ref ev);
+ }
+ public override void SetMaxCharge(Entity<BatteryComponent?> ent, float value)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return;
+
+ var old = ent.Comp.MaxCharge;
+ ent.Comp.MaxCharge = Math.Max(value, 0);
+ ent.Comp.CurrentCharge = Math.Min(ent.Comp.CurrentCharge, ent.Comp.MaxCharge);
+ if (MathHelper.CloseTo(ent.Comp.MaxCharge, old))
+ return;
+
+ var ev = new ChargeChangedEvent(ent.Comp.CurrentCharge, ent.Comp.MaxCharge);
+ RaiseLocalEvent(ent, ref ev);
+ }
+
+ public override void TrySetChargeCooldown(Entity<BatterySelfRechargerComponent?> 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);
+ }
+
+ public override void SetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent, TimeSpan cooldown)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return;
+
+ ent.Comp.NextAutoRecharge = _timing.CurTime + cooldown;
+ }
+
+ /// <summary>
+ /// Returns whether the battery is full.
+ /// </summary>
+ public bool IsFull(Entity<BatteryComponent?> ent)
+ {
+ if (!Resolve(ent, ref ent.Comp))
+ return false;
+
+ return ent.Comp.CurrentCharge >= ent.Comp.MaxCharge;
+ }
+}
using Robust.Shared.Utility;
using Robust.Shared.Timing;
-namespace Content.Server.Power.EntitySystems
-{
- [UsedImplicitly]
- public sealed class BatterySystem : SharedBatterySystem
- {
- [Dependency] private readonly IGameTiming _timing = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<ExaminableBatteryComponent, ExaminedEvent>(OnExamine);
- SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
- SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
- SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
- SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
- SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
-
- SubscribeLocalEvent<NetworkBatteryPreSync>(PreSync);
- SubscribeLocalEvent<NetworkBatteryPostSync>(PostSync);
- }
-
- private void OnNetBatteryRejuvenate(EntityUid uid, PowerNetworkBatteryComponent component, RejuvenateEvent args)
- {
- component.NetworkBattery.CurrentStorage = component.NetworkBattery.Capacity;
- }
+namespace Content.Server.Power.EntitySystems;
- private void OnBatteryRejuvenate(EntityUid uid, BatteryComponent component, RejuvenateEvent args)
- {
- SetCharge(uid, component.MaxCharge, component);
- }
-
- private void OnExamine(EntityUid uid, ExaminableBatteryComponent component, ExaminedEvent args)
- {
- if (!TryComp<BatteryComponent>(uid, out var batteryComponent))
- return;
- if (args.IsInDetailsRange)
- {
- var effectiveMax = batteryComponent.MaxCharge;
- if (effectiveMax == 0)
- effectiveMax = 1;
- var chargeFraction = batteryComponent.CurrentCharge / effectiveMax;
- var chargePercentRounded = (int)(chargeFraction * 100);
- args.PushMarkup(
- Loc.GetString(
- "examinable-battery-component-examine-detail",
- ("percent", chargePercentRounded),
- ("markupPercentColor", "green")
- )
- );
- }
- }
-
- private void PreSync(NetworkBatteryPreSync ev)
- {
- // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
- var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
- while (enumerator.MoveNext(out var netBat, out var bat))
- {
- DebugTools.Assert(bat.CurrentCharge <= bat.MaxCharge && bat.CurrentCharge >= 0);
- netBat.NetworkBattery.Capacity = bat.MaxCharge;
- netBat.NetworkBattery.CurrentStorage = bat.CurrentCharge;
- }
- }
-
- private void PostSync(NetworkBatteryPostSync ev)
- {
- // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
- var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
- while (enumerator.MoveNext(out var uid, out var netBat, out var bat))
- {
- SetCharge(uid, netBat.NetworkBattery.CurrentStorage, bat);
- }
- }
-
- public override void Update(float frameTime)
- {
- var query = EntityQueryEnumerator<BatterySelfRechargerComponent, BatteryComponent>();
- while (query.MoveNext(out var uid, out var comp, out var batt))
- {
- if (!comp.AutoRecharge || IsFull(uid, batt))
- continue;
-
- if (comp.AutoRechargePause)
- {
- if (comp.NextAutoRecharge > _timing.CurTime)
- continue;
- }
-
- SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt);
- }
- }
-
- /// <summary>
- /// Gets the price for the power contained in an entity's battery.
- /// </summary>
- private void CalculateBatteryPrice(EntityUid uid, BatteryComponent component, ref PriceCalculationEvent args)
- {
- args.Price += component.CurrentCharge * component.PricePerJoule;
- }
- private void OnChangeCharge(Entity<BatteryComponent> entity, ref ChangeChargeEvent args)
- {
- if (args.ResidualValue == 0)
- return;
-
- args.ResidualValue -= ChangeCharge(entity, args.ResidualValue);
- }
+[UsedImplicitly]
+public sealed partial class BatterySystem : SharedBatterySystem
+{
+ [Dependency] private readonly IGameTiming _timing = default!;
- private void OnGetCharge(Entity<BatteryComponent> entity, ref GetChargeEvent args)
- {
- args.CurrentCharge += entity.Comp.CurrentCharge;
- args.MaxCharge += entity.Comp.MaxCharge;
- }
+ public override void Initialize()
+ {
+ base.Initialize();
- public override float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
- {
- if (value <= 0 || !Resolve(uid, ref battery) || battery.CurrentCharge == 0)
- return 0;
+ SubscribeLocalEvent<ExaminableBatteryComponent, ExaminedEvent>(OnExamine);
+ SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
+ SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
+ SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
+ SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
+ SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
- return ChangeCharge(uid, -value, battery);
- }
+ SubscribeLocalEvent<NetworkBatteryPreSync>(PreSync);
+ SubscribeLocalEvent<NetworkBatteryPostSync>(PostSync);
+ }
- public override void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null)
- {
- if (!Resolve(uid, ref battery))
- return;
+ private void OnNetBatteryRejuvenate(Entity<PowerNetworkBatteryComponent> ent, ref RejuvenateEvent args)
+ {
+ ent.Comp.NetworkBattery.CurrentStorage = ent.Comp.NetworkBattery.Capacity;
+ }
- var old = battery.MaxCharge;
- battery.MaxCharge = Math.Max(value, 0);
- battery.CurrentCharge = Math.Min(battery.CurrentCharge, battery.MaxCharge);
- if (MathHelper.CloseTo(battery.MaxCharge, old))
- return;
+ private void OnBatteryRejuvenate(Entity<BatteryComponent> ent, ref RejuvenateEvent args)
+ {
+ SetCharge(ent.AsNullable(), ent.Comp.MaxCharge);
+ }
- var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
- RaiseLocalEvent(uid, ref ev);
- }
+ private void OnExamine(Entity<ExaminableBatteryComponent> ent, ref ExaminedEvent args)
+ {
+ if (!args.IsInDetailsRange)
+ return;
+
+ if (!TryComp<BatteryComponent>(ent, out var battery))
+ return;
+
+ var chargePercentRounded = 0;
+ if (battery.MaxCharge != 0)
+ chargePercentRounded = (int)(100 * battery.CurrentCharge / battery.MaxCharge);
+ args.PushMarkup(
+ Loc.GetString(
+ "examinable-battery-component-examine-detail",
+ ("percent", chargePercentRounded),
+ ("markupPercentColor", "green")
+ )
+ );
+ }
- public void SetCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ private void PreSync(NetworkBatteryPreSync ev)
+ {
+ // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
+ var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
+ while (enumerator.MoveNext(out var netBat, out var bat))
{
- if (!Resolve(uid, ref battery))
- return;
-
- var old = battery.CurrentCharge;
- battery.CurrentCharge = MathHelper.Clamp(value, 0, battery.MaxCharge);
- if (MathHelper.CloseTo(battery.CurrentCharge, old) &&
- !(old != battery.CurrentCharge && battery.CurrentCharge == battery.MaxCharge))
- {
- return;
- }
-
- var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
- RaiseLocalEvent(uid, ref ev);
+ DebugTools.Assert(bat.CurrentCharge <= bat.MaxCharge && bat.CurrentCharge >= 0);
+ netBat.NetworkBattery.Capacity = bat.MaxCharge;
+ netBat.NetworkBattery.CurrentStorage = bat.CurrentCharge;
}
+ }
- /// <summary>
- /// Changes the current battery charge by some value
- /// </summary>
- public override float ChangeCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ private void PostSync(NetworkBatteryPostSync ev)
+ {
+ // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
+ var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
+ while (enumerator.MoveNext(out var uid, out var netBat, out var bat))
{
- if (!Resolve(uid, ref battery))
- return 0;
-
- var newValue = Math.Clamp(battery.CurrentCharge + value, 0, battery.MaxCharge);
- var delta = newValue - battery.CurrentCharge;
- battery.CurrentCharge = newValue;
-
- TrySetChargeCooldown(uid);
-
- var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
- RaiseLocalEvent(uid, ref ev);
- return delta;
+ SetCharge((uid, bat), netBat.NetworkBattery.CurrentStorage);
}
+ }
- public override void TrySetChargeCooldown(EntityUid uid, float value = -1)
- {
- if (!TryComp<BatterySelfRechargerComponent>(uid, out var batteryself))
- return;
-
- if (!batteryself.AutoRechargePause)
- return;
-
- // If no answer or a negative is given for value, use the default from AutoRechargePauseTime.
- if (value < 0)
- value = batteryself.AutoRechargePauseTime;
-
- if (_timing.CurTime + TimeSpan.FromSeconds(value) <= batteryself.NextAutoRecharge)
- return;
+ /// <summary>
+ /// Gets the price for the power contained in an entity's battery.
+ /// </summary>
+ private void CalculateBatteryPrice(Entity<BatteryComponent> ent, ref PriceCalculationEvent args)
+ {
+ args.Price += ent.Comp.CurrentCharge * ent.Comp.PricePerJoule;
+ }
- SetChargeCooldown(uid, batteryself.AutoRechargePauseTime, batteryself);
- }
+ private void OnChangeCharge(Entity<BatteryComponent> ent, ref ChangeChargeEvent args)
+ {
+ if (args.ResidualValue == 0)
+ return;
- /// <summary>
- /// Puts the entity's self recharge on cooldown for the specified time.
- /// </summary>
- public void SetChargeCooldown(EntityUid uid, float value, BatterySelfRechargerComponent? batteryself = null)
- {
- if (!Resolve(uid, ref batteryself))
- return;
+ args.ResidualValue -= ChangeCharge(ent.AsNullable(), args.ResidualValue);
+ }
- if (value >= 0)
- batteryself.NextAutoRecharge = _timing.CurTime + TimeSpan.FromSeconds(value);
- else
- batteryself.NextAutoRecharge = _timing.CurTime;
- }
+ private void OnGetCharge(Entity<BatteryComponent> entity, ref GetChargeEvent args)
+ {
+ args.CurrentCharge += entity.Comp.CurrentCharge;
+ args.MaxCharge += entity.Comp.MaxCharge;
+ }
- /// <summary>
- /// If sufficient charge is available on the battery, use it. Otherwise, don't.
- /// </summary>
- public override bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ public override void Update(float frameTime)
+ {
+ var query = EntityQueryEnumerator<BatterySelfRechargerComponent, BatteryComponent>();
+ var curTime = _timing.CurTime;
+ while (query.MoveNext(out var uid, out var comp, out var bat))
{
- if (!Resolve(uid, ref battery, false) || value > battery.CurrentCharge)
- return false;
-
- UseCharge(uid, value, battery);
- return true;
- }
+ if (!comp.AutoRecharge || IsFull((uid, bat)))
+ continue;
- /// <summary>
- /// Returns whether the battery is full.
- /// </summary>
- public bool IsFull(EntityUid uid, BatteryComponent? battery = null)
- {
- if (!Resolve(uid, ref battery))
- return false;
+ if (comp.NextAutoRecharge > curTime)
+ continue;
- return battery.CurrentCharge >= battery.MaxCharge;
+ SetCharge((uid, bat), bat.CurrentCharge + comp.AutoRechargeRate * frameTime);
}
}
}
if (!SearchForBattery(container.ContainedEntities[0], out var heldEnt, out var heldBattery))
return CellChargerStatus.Off;
- if (_battery.IsFull(heldEnt.Value, heldBattery))
+ if (_battery.IsFull((heldEnt.Value, heldBattery)))
return CellChargerStatus.Charged;
return CellChargerStatus.Charging;
if (!SearchForBattery(targetEntity, out var batteryUid, out var heldBattery))
return;
- _battery.SetCharge(batteryUid.Value, heldBattery.CurrentCharge + component.ChargeRate * frameTime, heldBattery);
+ _battery.SetCharge((batteryUid.Value, heldBattery), heldBattery.CurrentCharge + component.ChargeRate * frameTime);
UpdateStatus(uid, component);
}
if (requireBattery)
{
- _battery.SetCharge(uid, battery.CurrentCharge - apcBattery.IdleLoad * frameTime, battery);
+ _battery.SetCharge((uid, battery), battery.CurrentCharge - apcBattery.IdleLoad * frameTime);
}
// Otherwise try to charge the battery
- else if (powered && !_battery.IsFull(uid, battery))
+ else if (powered && !_battery.IsFull((uid, battery)))
{
apcReceiver.Load += apcBattery.BatteryRechargeRate * apcBattery.BatteryRechargeEfficiency;
- _battery.SetCharge(uid, battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime, battery);
+ _battery.SetCharge((uid, battery), battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime);
}
// Enable / disable the battery if the state changed
shell.WriteLine(Loc.GetString($"cmd-setbatterypercent-battery-not-found", ("id", id)));
return;
}
- _batterySystem.SetCharge(id.Value, battery.MaxCharge * percent / 100, battery);
+ _batterySystem.SetCharge((id.Value, battery), battery.MaxCharge * percent / 100);
// Don't acknowledge b/c people WILL forall this
}
}
if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, slot))
continue;
- if (_battery.TryUseCharge(batteryEnt.Value, comp.DrawRate * (float)comp.Delay.TotalSeconds, battery))
+ if (_battery.TryUseCharge((batteryEnt.Value, battery), comp.DrawRate * (float)comp.Delay.TotalSeconds))
continue;
var ev = new PowerCellSlotEmptyEvent();
return false;
}
- if (!_battery.TryUseCharge(batteryEnt.Value, charge, battery))
+ if (!_battery.TryUseCharge((batteryEnt.Value, battery), charge))
{
if (user != null)
_popup.PopupEntity(Loc.GetString("power-cell-insufficient"), uid, user.Value);
if (!transform.Anchored)
continue;
- _battery.ChangeCharge(entity, networkLoad.NetworkLoad.ReceivingPower * frameTime, battery);
+ _battery.ChangeCharge((entity, battery), networkLoad.NetworkLoad.ReceivingPower * frameTime);
var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
if (_powerCell.TryGetBatteryFromSlot(uid, out var batteryUid, out var battery))
{
- if (!_battery.TryUseCharge(batteryUid.Value, GetCurrentWattage((uid, jam)) * frameTime, battery))
+ if (!_battery.TryUseCharge((batteryUid.Value, battery), GetCurrentWattage((uid, jam)) * frameTime))
{
ChangeLEDState(uid, false);
RemComp<ActiveRadioJammerComponent>(uid);
// into an AI core that has a full battery and full integrity.
if (TryComp<BatteryComponent>(ent, out var battery))
{
- _battery.SetCharge(ent, battery.MaxCharge);
+ _battery.SetCharge((ent, battery), battery.MaxCharge);
}
_damageable.ClearAllDamage(ent.Owner);
private void OnStaminaHitAttempt(Entity<StunbatonComponent> entity, ref StaminaDamageOnHitAttemptEvent args)
{
if (!_itemToggle.IsActivated(entity.Owner) ||
- !TryComp<BatteryComponent>(entity.Owner, out var battery) || !_battery.TryUseCharge(entity.Owner, entity.Comp.EnergyPerUse, battery))
+ !TryComp<BatteryComponent>(entity.Owner, out var battery) || !_battery.TryUseCharge((entity.Owner, battery), entity.Comp.EnergyPerUse))
{
args.Cancelled = true;
}
{
if (TryComp<BatteryComponent>(coil, out var batteryComponent))
{
- _battery.SetCharge(coil, batteryComponent.CurrentCharge + coil.Comp.ChargeFromLightning);
+ _battery.SetCharge((coil, batteryComponent), batteryComponent.CurrentCharge + coil.Comp.ChargeFromLightning);
}
}
}
protected override void TakeCharge(Entity<BatteryAmmoProviderComponent> entity)
{
+ // Take charge from either the BatteryComponent or PowerCellSlotComponent.
var ev = new ChangeChargeEvent(-entity.Comp.FireCost);
RaiseLocalEvent(entity, ref ev);
}
_lookup.GetEntitiesInRange(args.Coordinates, chargeBatteryComponent.Radius, _batteryEntities);
foreach (var battery in _batteryEntities)
{
- _battery.SetCharge(battery, battery.Comp.MaxCharge, battery);
+ _battery.SetCharge(battery.AsNullable(), battery.Comp.MaxCharge);
}
}
}
+using Content.Shared.Power.Components;
+using Content.Shared.PowerCell.Components;
+
namespace Content.Shared.Power;
/// <summary>
public readonly record struct ChargeChangedEvent(float Charge, float MaxCharge);
/// <summary>
+/// Event that supports multiple battery types.
/// Raised when it is necessary to get information about battery charges.
+/// Works with either <see cref="BatteryComponent"/> or <see cref="PowerCellSlotComponent"/>.
+/// If there are multiple batteries then the results will be summed up.
/// </summary>
[ByRefEvent]
-public sealed class GetChargeEvent : EntityEventArgs
+public record struct GetChargeEvent
{
public float CurrentCharge;
public float MaxCharge;
}
/// <summary>
-/// Raised when it is necessary to change the current battery charge to a some value.
+/// Method event that supports multiple battery types.
+/// Raised when it is necessary to change the current battery charge by some value.
+/// Works with either <see cref="BatteryComponent"/> or <see cref="PowerCellSlotComponent"/>.
+/// If there are multiple batteries then they will be changed in order of subscription until the total value was reached.
/// </summary>
[ByRefEvent]
-public sealed class ChangeChargeEvent : EntityEventArgs
+public record struct ChangeChargeEvent(float Amount)
{
- public float OriginalValue;
- public float ResidualValue;
+ /// <summary>
+ /// The total amount of charge to change the battery's storage by (in joule).
+ /// A positive value adds charge, a negative value removes charge.
+ /// </summary>
+ public readonly float Amount = Amount;
- public ChangeChargeEvent(float value)
- {
- OriginalValue = value;
- ResidualValue = value;
- }
+ /// <summary>
+ /// The amount of charge that still has to be removed.
+ /// For cases where there are multiple batteries.
+ /// </summary>
+ public float ResidualValue = Amount;
}
[Access(typeof(SharedBatterySystem))]
public partial class BatteryComponent : Component
{
- public string SolutionName = "battery";
-
/// <summary>
- /// Maximum charge of the battery in joules (ie. watt seconds)
+ /// Maximum charge of the battery in joules (i.e. watt seconds)
/// </summary>
[DataField]
[GuidebookData]
/// <summary>
/// Current charge of the battery in joules (ie. watt seconds)
/// </summary>
- [DataField("startingCharge")]
+ [DataField("startingCharge")] // TODO: rename this datafield to currentCharge
public float CurrentCharge;
/// <summary>
- /// The price per one joule. Default is 1 credit for 10kJ.
+ /// The price per one joule. Default is 1 speso for 10kJ.
/// </summary>
[DataField]
public float PricePerJoule = 0.0001f;
--- /dev/null
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
+
+namespace Content.Shared.Power.Components;
+
+/// <summary>
+/// Self-recharging battery.
+/// To be used in combination with <see cref="BatteryComponent"/>.
+/// </summary>
+[RegisterComponent, AutoGenerateComponentPause]
+public sealed partial class BatterySelfRechargerComponent : Component
+{
+ /// <summary>
+ /// Is the component currently enabled?
+ /// </summary>
+ [DataField]
+ public bool AutoRecharge = true;
+
+ /// <summary>
+ /// At what rate does the entity automatically recharge?
+ /// </summary>
+ [DataField]
+ public float AutoRechargeRate;
+
+ /// <summary>
+ /// How long should the entity stop automatically recharging if charge is used?
+ /// </summary>
+ [DataField]
+ public TimeSpan AutoRechargePauseTime = TimeSpan.FromSeconds(0);
+
+ /// <summary>
+ /// Do not auto recharge if this timestamp has yet to happen, set for the auto recharge pause system.
+ /// </summary>
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [AutoPausedField]
+ public TimeSpan NextAutoRecharge = TimeSpan.FromSeconds(0);
+}
SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
}
- private void OnEmpPulse(Entity<BatteryComponent> entity, ref EmpPulseEvent args)
+ private void OnEmpPulse(Entity<BatteryComponent> ent, ref EmpPulseEvent args)
{
args.Affected = true;
- UseCharge(entity, args.EnergyConsumption, entity.Comp);
+ UseCharge(ent.AsNullable(), args.EnergyConsumption);
// Apply a cooldown to the entity's self recharge if needed to avoid it immediately self recharging after an EMP.
- TrySetChargeCooldown(entity);
+ TrySetChargeCooldown(ent.Owner);
}
- public virtual float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ /// <summary>
+ /// Changes the battery's charge by the given amount.
+ /// A positive value will add charge, a negative value will remove charge.
+ /// </summary>
+ /// <returns>The actually changed amount.</returns>
+ public virtual float ChangeCharge(Entity<BatteryComponent?> ent, float amount)
{
return 0f;
}
- public virtual void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null) { }
-
- public virtual float ChangeCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ /// <summary>
+ /// Removes the given amount of charge from the battery.
+ /// </summary>
+ /// <returns>The actually changed amount.</returns>
+ public virtual float UseCharge(Entity<BatteryComponent?> ent, float amount)
{
return 0f;
}
/// <summary>
- /// Checks if the entity has a self recharge and puts it on cooldown if applicable.
+ /// If sufficient charge is available on the battery, use it. Otherwise, don't.
+ /// Always returns false on the client.
/// </summary>
- public virtual void TrySetChargeCooldown(EntityUid uid, float value = -1) { }
-
- public virtual bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
+ /// <returns>If the full amount was able to be removed.</returns>
+ public virtual bool TryUseCharge(Entity<BatteryComponent?> ent, float amount)
{
return false;
}
+
+ /// <summary>
+ /// Sets the battery's charge.
+ /// </summary>
+ public virtual void SetCharge(Entity<BatteryComponent?> ent, float value) { }
+
+ /// <summary>
+ /// Sets the battery's maximum charge.
+ /// </summary>
+ public virtual void SetMaxCharge(Entity<BatteryComponent?> ent, float value) { }
+
+ /// <summary>
+ /// Checks if the entity has a self recharge and puts it on cooldown if applicable.
+ /// Uses the cooldown time given in the component.
+ /// </summary>
+ public virtual void TrySetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent) { }
+
+ /// <summary>
+ /// Puts the entity's self recharge on cooldown for the specified time.
+ /// </summary>
+ public virtual void SetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent, TimeSpan cooldown) { }
}
parent: 1
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- proto: BasaltOne
entities:
- uid: 353
parent: 1
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- proto: AtmosFixFreezerMarker
entities:
- uid: 348
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
- type: Godmode
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Fixtures
fixtures: {}
missingComponents:
parent: 1
- type: BatterySelfRecharger
autoRechargeRate: 200000
- autoRecharge: True
- proto: BarberScissors
entities:
- uid: 649
parent: 2
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- uid: 362
components:
- type: Transform
parent: 292
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: Godmode
missingComponents:
- Construction
maxCharge: 600 #lights drain 3/s but recharge of 2 makes this 1/s. Therefore 600 is 10 minutes of light.
startingCharge: 600
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 2 #recharge of 2 makes total drain 1w / s so max charge is 1:1 with time. Time to fully charge should be 5 minutes. Having recharge gives light an extended flicker period which gives you some warning to return to light area.
- type: entity
maxCharge: 600
startingCharge: 600
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 2
- type: Item
size: Normal
maxCharge: 1000
startingCharge: 1000
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 40
- type: Gun
fireRate: 0.75
proto: WatcherBolt
fireCost: 50
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 50
- type: Battery
maxCharge: 1000
maxCharge: 1000
startingCharge: 1000
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 50
- type: Gun
fireRate: 0.3
proto: RedLightLaser
fireCost: 50
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 50
- type: Battery
maxCharge: 1000
description: A self rechargeable power cell, designed for fast recharge rate at the expense of capacity.
components:
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 36 # 10 seconds to recharge
- autoRechargePause: true
autoRechargePauseTime: 30
- type: entity
maxCharge: 720
startingCharge: 720
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 12 # takes 1 minute to charge itself back to full
- type: entity
maxCharge: 1200
startingCharge: 1200
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 40
# Power cage (big heavy power cell for big devices)
proto: RedMediumLaser
fireCost: 100
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 40
- type: MagazineVisuals
magState: mag
proto: RedMediumLaser
fireCost: 100
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 30
- type: MagazineVisuals
magState: mag
proto: RedMediumLaser
fireCost: 100
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 40
- type: StaticPrice
price: 750
fireCost: 62.5
pacifismAllowedMode: true
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 48
- autoRechargePause: true
autoRechargePauseTime: 10
- type: entity
maxCharge: 10000
startingCharge: 10000
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 25
- type: AmmoCounter
maxCharge: 1000
startingCharge: 1000
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 25
- type: AmmoCounter
maxCharge: 3000
startingCharge: 3000
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 25
- type: AmmoCounter
maxCharge: 90
startingCharge: 90
- type: BatterySelfRecharger
- autoRecharge: true
autoRechargeRate: 1
- type: AmmoCounter
- type: Item
components:
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: entity
parent: BaseSubstation
components:
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: entity
parent: BaseSubstationWall
components:
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: entity
parent: BaseAPC
components:
- type: BatterySelfRecharger
autoRechargeRate: 50000
- autoRecharge: True
- type: entity
id: DebugPowerReceiver