using Content.Server.Cargo.Systems; using Content.Server.Emp; 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 { [UsedImplicitly] public sealed class BatterySystem : EntitySystem { public override void Initialize() { base.Initialize(); SubscribeLocalEvent(OnExamine); SubscribeLocalEvent(OnNetBatteryRejuvenate); SubscribeLocalEvent(OnBatteryRejuvenate); SubscribeLocalEvent(CalculateBatteryPrice); SubscribeLocalEvent(OnEmpPulse); SubscribeLocalEvent(PreSync); SubscribeLocalEvent(PostSync); } private void OnNetBatteryRejuvenate(EntityUid uid, PowerNetworkBatteryComponent component, RejuvenateEvent args) { component.NetworkBattery.CurrentStorage = component.NetworkBattery.Capacity; } 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(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(); 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.Charge; } } private void PostSync(NetworkBatteryPostSync ev) { // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed. var enumerator = AllEntityQuery(); 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(); while (query.MoveNext(out var uid, out var comp, out var batt)) { if (!comp.AutoRecharge) continue; if (batt.IsFullyCharged) continue; SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt); } } /// /// Gets the price for the power contained in an entity's battery. /// private void CalculateBatteryPrice(EntityUid uid, BatteryComponent component, ref PriceCalculationEvent args) { args.Price += component.CurrentCharge * component.PricePerJoule; } private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args) { args.Affected = true; 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; } } }