[Dependency] private readonly IEntityManager _entity = default!;
private readonly NameModifierSystem _nameModifier;
private readonly PowerCellSystem _powerCell;
- private readonly PredictedBatterySystem _battery;
+ private readonly SharedBatterySystem _battery;
public Action? BrainButtonPressed;
public Action? EjectBatteryButtonPressed;
_nameModifier = _entity.System<NameModifierSystem>();
_powerCell = _entity.System<PowerCellSystem>();
- _battery = _entity.System<PredictedBatterySystem>();
+ _battery = _entity.System<SharedBatterySystem>();
_maxNameLength = _cfgManager.GetCVar(CCVars.MaxNameLength);
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IPlayerManager _player = default!;
nodeGroupID: HVPower
- type: PowerNetworkBattery
- type: Battery
+ netsync: false
- type: BatteryCharger
- type: entity
nodeGroupID: HVPower
- type: PowerNetworkBattery
- type: Battery
+ netsync: false
- type: BatteryDischarger
- type: entity
nodeGroupID: HVPower
- type: PowerNetworkBattery
- type: Battery
+ netsync: false
- type: BatteryDischarger
node: output
- type: BatteryCharger
maxSupply: 1000
supplyRampTolerance: 1000
- type: Battery
+ netsync: false
maxCharge: 1000
startingCharge: 1000
- type: Transform
id: ApcDummy
components:
- type: Battery
+ netsync: false
maxCharge: 10000
startingCharge: 10000
- type: PowerNetworkBattery
const float startingCharge = 100_000;
PowerNetworkBatteryComponent netBattery = default!;
+ EntityUid generatorEnt = default!;
+ EntityUid consumerEnt = default!;
BatteryComponent battery = default!;
PowerConsumerComponent consumer = default!;
entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates());
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
+ generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates());
+ consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(generatorEnt);
battery = entityManager.GetComponent<BatteryComponent>(generatorEnt);
// Trivial integral to calculate expected power spent.
const double spentExpected = (200 + 100) / 2.0 * 0.25;
- Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
+ var currentCharge = batterySys.GetCharge((generatorEnt, battery));
+ Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
});
});
// Trivial integral to calculate expected power spent.
const double spentExpected = (400 + 100) / 2.0 * 0.75 + 400 * 0.25;
- Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
+ var currentCharge = batterySys.GetCharge((generatorEnt, battery));
+ Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
});
});
var entityManager = server.ResolveDependency<IEntityManager>();
var batterySys = entityManager.System<BatterySystem>();
var mapSys = entityManager.System<SharedMapSystem>();
+ EntityUid generatorEnt = default!;
+ EntityUid batteryEnt = default!;
PowerSupplierComponent supplier = default!;
BatteryComponent battery = default!;
entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
}
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
- var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
+ generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
+ batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
var netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
{
// half a second @ 500 W = 250
// 50% efficiency, so 125 J stored total.
- Assert.That(battery.CurrentCharge, Is.EqualTo(125).Within(0.1));
+ var currentCharge = batterySys.GetCharge((batteryEnt, battery));
+ Assert.That(currentCharge, Is.EqualTo(125).Within(0.1));
Assert.That(supplier.CurrentSupply, Is.EqualTo(500).Within(0.1));
});
});
var gameTiming = server.ResolveDependency<IGameTiming>();
var batterySys = entityManager.System<BatterySystem>();
var mapSys = entityManager.System<SharedMapSystem>();
+ EntityUid batteryEnt = default!;
+ EntityUid supplyEnt = default!;
+ EntityUid consumerEnt = default!;
PowerConsumerComponent consumer = default!;
PowerSupplierComponent supplier = default!;
PowerNetworkBatteryComponent netBattery = default!;
var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
+ batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
+ supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
+ consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(200).Within(0.1));
const int expectedSpent = 200;
- Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
+ var currentCharge = batterySys.GetCharge((batteryEnt, battery));
+ Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
});
});
var gameTiming = server.ResolveDependency<IGameTiming>();
var batterySys = entityManager.System<BatterySystem>();
var mapSys = entityManager.System<SharedMapSystem>();
+ EntityUid batteryEnt = default!;
+ EntityUid supplyEnt = default!;
+ EntityUid consumerEnt = default!;
PowerConsumerComponent consumer = default!;
PowerSupplierComponent supplier = default!;
PowerNetworkBatteryComponent netBattery = default!;
var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
- var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
- var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
+ batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
+ supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
+ consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(400).Within(0.1));
const int expectedSpent = 400;
- Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
+ var currentCharge = batterySys.GetCharge((batteryEnt, battery));
+ Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
});
});
var entityManager = server.ResolveDependency<IEntityManager>();
var batterySys = entityManager.System<BatterySystem>();
var mapSys = entityManager.System<SharedMapSystem>();
+ EntityUid generatorEnt = default!;
+ EntityUid substationEnt = default!;
+ EntityUid apcEnt = default!;
PowerNetworkBatteryComponent substationNetBattery = default!;
BatteryComponent apcBattery = default!;
entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 1));
entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 2));
- var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
- var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1));
- var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2));
+ generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
+ substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1));
+ apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2));
var generatorSupplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
substationNetBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(substationEnt);
{
Assert.Multiple(() =>
{
+ var currentCharge = batterySys.GetCharge((apcEnt, apcBattery));
Assert.That(substationNetBattery.CurrentSupply, Is.GreaterThan(0)); //substation should be providing power
- Assert.That(apcBattery.CurrentCharge, Is.GreaterThan(0)); //apc battery should have gained charge
+ Assert.That(currentCharge, Is.GreaterThan(0)); //apc battery should have gained charge
});
});
using System.Linq;
using Content.Server.GameTicking;
using Content.Server.Power.Components;
+using Content.Server.Power.EntitySystems;
using Content.Server.Power.NodeGroups;
using Content.Server.Power.Pow3r;
using Content.Shared.Maps;
var entMan = server.EntMan;
var protoMan = server.ProtoMan;
var ticker = entMan.System<GameTicker>();
+ var batterySys = entMan.System<BatterySystem>();
// Load the map
await server.WaitAssertion(() =>
if (node.NodeGroup is not IBasePowerNet group)
continue;
networks.TryGetValue(group.NetworkNode, out var charge);
- networks[group.NetworkNode] = charge + battery.CurrentCharge;
+ var currentCharge = batterySys.GetCharge((uid, battery));
+ networks[group.NetworkNode] = charge + currentCharge;
}
var totalStartingCharge = networks.MaxBy(n => n.Value).Value;
using Content.Server.Cargo.Components;
using Content.Server.Doors.Systems;
using Content.Server.Hands.Systems;
-using Content.Server.Power.EntitySystems;
using Content.Server.Stack;
using Content.Server.Station.Systems;
using Content.Server.Weapons.Ranged.Systems;
[Dependency] private readonly AdminTestArenaSystem _adminTestArenaSystem = default!;
[Dependency] private readonly StationJobsSystem _stationJobsSystem = default!;
[Dependency] private readonly JointSystem _jointSystem = default!;
- [Dependency] private readonly BatterySystem _batterySystem = default!;
- [Dependency] private readonly PredictedBatterySystem _predictedBatterySystem = default!;
+ [Dependency] private readonly SharedBatterySystem _batterySystem = default!;
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
[Dependency] private readonly GunSystem _gun = default!;
args.Verbs.Add(makeVulnerable);
}
- if (TryComp<PredictedBatteryComponent>(args.Target, out var pBattery))
- {
- Verb refillBattery = new()
- {
- Text = Loc.GetString("admin-verbs-refill-battery"),
- Category = VerbCategory.Tricks,
- Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
- Act = () =>
- {
- _predictedBatterySystem.SetCharge((args.Target, pBattery), pBattery.MaxCharge);
- },
- Impact = LogImpact.Medium,
- Message = Loc.GetString("admin-trick-refill-battery-description"),
- Priority = (int)TricksVerbPriorities.RefillBattery,
- };
- args.Verbs.Add(refillBattery);
-
- Verb drainBattery = new()
- {
- Text = Loc.GetString("admin-verbs-drain-battery"),
- Category = VerbCategory.Tricks,
- Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
- Act = () =>
- {
- _predictedBatterySystem.SetCharge((args.Target, pBattery), 0);
- },
- Impact = LogImpact.Medium,
- Priority = (int)TricksVerbPriorities.DrainBattery,
- };
- args.Verbs.Add(drainBattery);
-
- Verb infiniteBattery = new()
- {
- Text = Loc.GetString("admin-verbs-infinite-battery"),
- Category = VerbCategory.Tricks,
- Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
- Act = () =>
- {
- var recharger = EnsureComp<PredictedBatterySelfRechargerComponent>(args.Target);
- recharger.AutoRechargeRate = pBattery.MaxCharge; // Instant refill.
- recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
- Dirty(args.Target, recharger);
- _predictedBatterySystem.RefreshChargeRate((args.Target, pBattery));
- },
- Impact = LogImpact.Medium,
- Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
- Priority = (int)TricksVerbPriorities.InfiniteBattery,
- };
- args.Verbs.Add(infiniteBattery);
- }
-
if (TryComp<BatteryComponent>(args.Target, out var battery))
{
Verb refillBattery = new()
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
+ Dirty(args.Target, recharger);
+ _batterySystem.RefreshChargeRate((args.Target, battery));
},
Impact = LogImpact.Medium,
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
var cell = container.ContainedEntities[0];
- if (!entityManager.TryGetComponent<PredictedBatteryComponent>(cell, out var batteryComponent))
+ if (!entityManager.TryGetComponent<BatteryComponent>(cell, out var batteryComponent))
{
Logger.Warning($"Mech construct entity {uid} had an invalid entity in container \"{Container}\"! Aborting build mech action.");
return;
}
else
{
- _battery.SetCharge((entity.Owner, battery), battery.CurrentCharge + entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency);
+ _battery.ChangeCharge((entity.Owner, battery), entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency);
if (_battery.IsFull((entity.Owner, battery)))
{
if (TryComp<ApcPowerReceiverComponent>(entity.Owner, out var receiver))
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedPointLightSystem _lights = default!;
}
// TODO: Very important: Make this charge rate based instead of instantly removing charge each update step.
- // See PredictedBatteryComponent
+ // See BatteryComponent
public override void Update(float frameTime)
{
var toRemove = new RemQueue<Entity<HandheldLightComponent>>();
{
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly ContainerSystem _container = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
if (TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open)
return;
- if (component.BatterySlot.ContainedEntity == null && TryComp<PredictedBatteryComponent>(args.Used, out var battery))
+ if (component.BatterySlot.ContainedEntity == null && TryComp<BatteryComponent>(args.Used, out var battery))
{
InsertBattery(uid, args.Used, component, battery);
_actionBlocker.UpdateCanMove(uid);
private void OnInsertBattery(EntityUid uid, MechComponent component, EntInsertedIntoContainerMessage args)
{
- if (args.Container != component.BatterySlot || !TryComp<PredictedBatteryComponent>(args.Entity, out var battery))
+ if (args.Container != component.BatterySlot || !TryComp<BatteryComponent>(args.Entity, out var battery))
return;
component.Energy = _battery.GetCharge((args.Entity, battery));
if (battery == null)
return false;
- if (!TryComp<PredictedBatteryComponent>(battery, out var batteryComp))
+ if (!TryComp<BatteryComponent>(battery, out var batteryComp))
return false;
_battery.SetCharge((battery.Value, batteryComp), _battery.GetCharge((battery.Value, batteryComp)) + delta.Float());
return true;
}
- public void InsertBattery(EntityUid uid, EntityUid toInsert, MechComponent? component = null, PredictedBatteryComponent? battery = null)
+ public void InsertBattery(EntityUid uid, EntityUid toInsert, MechComponent? component = null, BatteryComponent? battery = null)
{
if (!Resolve(uid, ref component, false))
return;
using Content.Server.Ninja.Events;
using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
using Content.Shared.DoAfter;
using Content.Shared.Interaction;
using Content.Shared.Ninja.Components;
/// </summary>
public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
{
- [Dependency] private readonly BatterySystem _battery = default!;
- [Dependency] private readonly PredictedBatterySystem _predictedBattery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
protected override bool TryDrainPower(Entity<BatteryDrainerComponent> ent, EntityUid target)
{
var (uid, comp) = ent;
- if (comp.BatteryUid == null || !TryComp<PredictedBatteryComponent>(comp.BatteryUid.Value, out var battery))
+ if (comp.BatteryUid == null || !TryComp<BatteryComponent>(comp.BatteryUid.Value, out var battery))
return false;
if (!TryComp<BatteryComponent>(target, out var targetBattery) || !TryComp<PowerNetworkBatteryComponent>(target, out var pnb))
return false;
- if (MathHelper.CloseToPercent(targetBattery.CurrentCharge, 0))
+ var available = _battery.GetCharge((target, targetBattery));
+ if (MathHelper.CloseToPercent(available, 0))
{
_popup.PopupEntity(Loc.GetString("battery-drainer-empty", ("battery", target)), uid, uid, PopupType.Medium);
return false;
}
- var available = targetBattery.CurrentCharge;
- var required = battery.MaxCharge - _predictedBattery.GetCharge((comp.BatteryUid.Value, battery));
+ var required = battery.MaxCharge - _battery.GetCharge((comp.BatteryUid.Value, battery));
// higher tier storages can charge more
var maxDrained = pnb.MaxSupply * comp.DrainTime;
var input = Math.Min(Math.Min(available, required / comp.DrainEfficiency), maxDrained);
return false;
var output = input * comp.DrainEfficiency;
- // PowerCells use PredictedBatteryComponent
- // SMES, substations and APCs use BatteryComponent
- _predictedBattery.ChangeCharge((comp.BatteryUid.Value, battery), output);
+ _battery.ChangeCharge((comp.BatteryUid.Value, battery), 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 !_predictedBattery.IsFull((comp.BatteryUid.Value, battery));
+ return !_battery.IsFull((comp.BatteryUid.Value, battery));
}
}
public sealed class ItemCreatorSystem : SharedItemCreatorSystem
{
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedHandsSystem _hands = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
if (!_powerCell.TryGetBatteryFromSlot(uid, out var battery))
return;
- if (!TryComp<PredictedBatteryComponent>(args.EntityUid, out var inserting))
+ if (!TryComp<BatteryComponent>(args.EntityUid, out var inserting))
{
args.Cancel();
return;
}
// this function assigns a score to a power cell depending on the capacity, to be used when comparing which cell is better.
- private float GetCellScore(EntityUid uid, PredictedBatteryComponent battcomp)
+ private float GetCellScore(EntityUid uid, BatteryComponent battcomp)
{
// if a cell is able to automatically recharge, boost the score drastically depending on the recharge rate,
// this is to ensure a ninja can still upgrade to a micro reactor cell even if they already have a medium or high.
- if (TryComp<PredictedBatterySelfRechargerComponent>(uid, out var selfcomp))
+ if (TryComp<BatterySelfRechargerComponent>(uid, out var selfcomp))
return battcomp.MaxCharge + selfcomp.AutoRechargeRate * AutoRechargeValue;
return battcomp.MaxCharge;
}
public sealed class SpaceNinjaSystem : SharedSpaceNinjaSystem
{
[Dependency] private readonly AlertsSystem _alerts = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly CodeConditionSystem _codeCondition = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly SharedMindSystem _mind = default!;
/// <summary>
/// Get the battery component in a ninja's suit, if it's worn.
/// </summary>
- public bool GetNinjaBattery(EntityUid user, [NotNullWhen(true)] out EntityUid? batteryUid, [NotNullWhen(true)] out PredictedBatteryComponent? batteryComp)
+ public bool GetNinjaBattery(EntityUid user, [NotNullWhen(true)] out EntityUid? batteryUid, [NotNullWhen(true)] out BatteryComponent? batteryComp)
{
if (TryComp<SpaceNinjaComponent>(user, out var ninja)
&& ninja.Suit != null
/// </summary>
public sealed class StunProviderSystem : SharedStunProviderSystem
{
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
using Content.Shared.Database;
using Content.Shared.Power;
using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
using Robust.Server.GameObjects;
namespace Content.Server.Power.EntitySystems;
{
[Dependency] private readonly IAdminLogManager _adminLog = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = null!;
+ [Dependency] private readonly SharedBatterySystem _battery = null!;
public override void Initialize()
{
var netBattery = Comp<PowerNetworkBatteryComponent>(ent);
netBattery.CanCharge = args.On;
- _adminLog.Add(LogType.Action,$"{ToPrettyString(args.Actor):actor} set input breaker to {args.On} on {ToPrettyString(ent):target}");
+ _adminLog.Add(LogType.Action, $"{ToPrettyString(args.Actor):actor} set input breaker to {args.On} on {ToPrettyString(ent):target}");
}
private void HandleSetOutputBreaker(Entity<BatteryInterfaceComponent> ent, ref BatterySetOutputBreakerMessage args)
var netBattery = Comp<PowerNetworkBatteryComponent>(ent);
netBattery.CanDischarge = args.On;
- _adminLog.Add(LogType.Action,$"{ToPrettyString(args.Actor):actor} set output breaker to {args.On} on {ToPrettyString(ent):target}");
+ _adminLog.Add(LogType.Action, $"{ToPrettyString(args.Actor):actor} set output breaker to {args.On} on {ToPrettyString(ent):target}");
}
private void HandleSetChargeRate(Entity<BatteryInterfaceComponent> ent, ref BatterySetChargeRateMessage args)
if (!_uiSystem.IsUiOpen(uid, BatteryUiKey.Key))
return;
+ var currentCharge = _battery.GetCharge((uid, battery));
_uiSystem.SetUiState(
uid,
BatteryUiKey.Key,
new BatteryBuiState
{
Capacity = battery.MaxCharge,
- Charge = battery.CurrentCharge,
+ Charge = currentCharge,
CanCharge = netBattery.CanCharge,
CanDischarge = netBattery.CanDischarge,
CurrentReceiving = netBattery.CurrentReceiving,
+++ /dev/null
-using Content.Shared.Power;
-using Content.Shared.Power.Components;
-using Content.Shared.Power.EntitySystems;
-
-namespace Content.Server.Power.EntitySystems;
-
-/// <summary>
-/// Responsible for <see cref="BatteryComponent"/>.
-/// Unpredicted equivalent of <see cref="PredictedBatterySystem"/>.
-/// If you make changes to this make sure to keep the two consistent.
-/// </summary>
-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;
-
- if (delta == 0f)
- return delta;
-
- ent.Comp.CurrentCharge = newValue;
-
- TrySetChargeCooldown(ent.Owner);
-
- var ev = new ChargeChangedEvent(ent.Comp.CurrentCharge, delta, ent.Comp.MaxCharge);
- RaiseLocalEvent(ent, ref ev);
- return delta;
- }
-
- public override float UseCharge(Entity<BatteryComponent?> ent, float amount)
- {
- if (amount <= 0f || !Resolve(ent, ref ent.Comp) || ent.Comp.CurrentCharge == 0)
- return 0f;
-
- 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.CurrentCharge - oldCharge, 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;
- var oldCharge = ent.Comp.CurrentCharge;
- 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.CurrentCharge - oldCharge, ent.Comp.MaxCharge);
- RaiseLocalEvent(ent, ref ev);
- }
-
- /// <summary>
- /// Gets the battery's current charge.
- /// </summary>
- public float GetCharge(Entity<BatteryComponent?> ent)
- {
- if (!Resolve(ent, ref ent.Comp, false))
- return 0f;
-
- return ent.Comp.CurrentCharge;
- }
-
- /// <summary>
- /// Gets number of remaining uses for the given charge cost.
- /// </summary>
- public int GetRemainingUses(Entity<BatteryComponent?> ent, float cost)
- {
- if (cost <= 0)
- return 0;
-
- if (!Resolve(ent, ref ent.Comp))
- return 0;
-
- return (int)(ent.Comp.CurrentCharge / cost);
- }
-
- /// <summary>
- /// Gets number of maximum uses at full charge for the given charge cost.
- /// </summary>
- public int GetMaxUses(Entity<BatteryComponent?> ent, float cost)
- {
- if (cost <= 0)
- return 0;
-
- if (!Resolve(ent, ref ent.Comp))
- return 0;
-
- return (int)(ent.Comp.MaxCharge / cost);
- }
-
- 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 Content.Server.Power.Components;
-using Content.Shared.Cargo;
-using Content.Shared.Examine;
-using Content.Shared.Power;
using Content.Shared.Power.Components;
using Content.Shared.Power.EntitySystems;
using Content.Shared.Rejuvenate;
-using JetBrains.Annotations;
using Robust.Shared.Utility;
-using Robust.Shared.Timing;
namespace Content.Server.Power.EntitySystems;
-/// <summary>
-/// Responsible for <see cref="BatteryComponent"/>.
-/// Unpredicted equivalent of <see cref="PredictedBatterySystem"/>.
-/// If you make changes to this make sure to keep the two consistent.
-/// </summary>
-[UsedImplicitly]
-public sealed partial class BatterySystem : SharedBatterySystem
+public sealed class BatterySystem : SharedBatterySystem
{
- [Dependency] private readonly IGameTiming _timing = default!;
-
public override void Initialize()
{
base.Initialize();
- SubscribeLocalEvent<BatteryComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamine);
- SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
- SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
- SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
- SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
-
SubscribeLocalEvent<NetworkBatteryPreSync>(PreSync);
SubscribeLocalEvent<NetworkBatteryPostSync>(PostSync);
}
- private void OnInit(Entity<BatteryComponent> ent, ref ComponentInit args)
- {
- DebugTools.Assert(!HasComp<PredictedBatteryComponent>(ent), $"{ent} has both BatteryComponent and PredictedBatteryComponent");
- }
- private void OnNetBatteryRejuvenate(Entity<PowerNetworkBatteryComponent> ent, ref RejuvenateEvent args)
- {
- ent.Comp.NetworkBattery.CurrentStorage = ent.Comp.NetworkBattery.Capacity;
- }
- private void OnBatteryRejuvenate(Entity<BatteryComponent> ent, ref RejuvenateEvent args)
- {
- SetCharge(ent.AsNullable(), ent.Comp.MaxCharge);
- }
-
- private void OnExamine(Entity<BatteryComponent> ent, ref ExaminedEvent args)
+ protected override void OnStartup(Entity<BatteryComponent> ent, ref ComponentStartup args)
{
- if (!args.IsInDetailsRange)
+ // Debug assert to prevent anyone from killing their networking performance by dirtying a battery's charge every single tick.
+ // This checks for components that interact with the power network, have a charge rate that ramps up over time and therefore
+ // have to set the charge in an update loop instead of using a <see cref="RefreshChargeRateEvent"/> subscription.
+ // This is usually the case for APCs, SMES, battery powered turrets or similar.
+ // For those entities you should disable net sync for the battery in your prototype, using
+ /// <code>
+ /// - type: Battery
+ /// netSync: false
+ /// </code>
+ /// This disables networking and prediction for this battery.
+ if (!ent.Comp.NetSyncEnabled)
return;
- if (!HasComp<ExaminableBatteryComponent>(ent))
- return;
+ DebugTools.Assert(!HasComp<ApcPowerReceiverBatteryComponent>(ent), $"{ToPrettyString(ent.Owner)} has a predicted battery connected to the power net. Disable net sync!");
+ DebugTools.Assert(!HasComp<PowerNetworkBatteryComponent>(ent), $"{ToPrettyString(ent.Owner)} has a predicted battery connected to the power net. Disable net sync!");
+ DebugTools.Assert(!HasComp<PowerConsumerComponent>(ent), $"{ToPrettyString(ent.Owner)} has a predicted battery connected to the power net. Disable net sync!");
+ }
- var chargePercentRounded = 0;
- if (ent.Comp.MaxCharge != 0)
- chargePercentRounded = (int)(100 * ent.Comp.CurrentCharge / ent.Comp.MaxCharge);
- args.PushMarkup(
- Loc.GetString(
- "examinable-battery-component-examine-detail",
- ("percent", chargePercentRounded),
- ("markupPercentColor", "green")
- )
- );
+ private void OnNetBatteryRejuvenate(Entity<PowerNetworkBatteryComponent> ent, ref RejuvenateEvent args)
+ {
+ ent.Comp.NetworkBattery.CurrentStorage = ent.Comp.NetworkBattery.Capacity;
}
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))
+ while (enumerator.MoveNext(out var uid, out var netBat, out var bat))
{
- DebugTools.Assert(bat.CurrentCharge <= bat.MaxCharge && bat.CurrentCharge >= 0);
+ var currentCharge = GetCharge((uid, bat));
+ DebugTools.Assert(currentCharge <= bat.MaxCharge && currentCharge >= 0);
netBat.NetworkBattery.Capacity = bat.MaxCharge;
- netBat.NetworkBattery.CurrentStorage = bat.CurrentCharge;
+ netBat.NetworkBattery.CurrentStorage = currentCharge;
}
}
SetCharge((uid, bat), netBat.NetworkBattery.CurrentStorage);
}
}
-
- /// <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;
- }
-
- private void OnChangeCharge(Entity<BatteryComponent> ent, ref ChangeChargeEvent args)
- {
- if (args.ResidualValue == 0)
- return;
-
- args.ResidualValue -= ChangeCharge(ent.AsNullable(), args.ResidualValue);
- }
-
- private void OnGetCharge(Entity<BatteryComponent> entity, ref GetChargeEvent args)
- {
- args.CurrentCharge += entity.Comp.CurrentCharge;
- args.MaxCharge += entity.Comp.MaxCharge;
- }
-
- 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 (!comp.AutoRecharge || IsFull((uid, bat)))
- continue;
-
- if (comp.NextAutoRecharge > curTime)
- continue;
-
- SetCharge((uid, bat), bat.CurrentCharge + comp.AutoRechargeRate * frameTime);
- }
- }
}
using Content.Shared.Station.Components;
using Content.Shared.Power;
using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
using JetBrains.Annotations;
using Robust.Server.GameObjects;
using Robust.Shared.Map.Components;
{
[Dependency] private readonly UserInterfaceSystem _userInterfaceSystem = default!;
[Dependency] private readonly SharedMapSystem _sharedMapSystem = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
// Note: this data does not need to be saved
private Dictionary<EntityUid, Dictionary<Vector2i, PowerCableChunk>> _gridPowerCableChunks = new();
if (effectiveMax == 0)
effectiveMax = 1;
- return battery.CurrentCharge / effectiveMax;
+ return _battery.GetCharge((uid, battery)) / effectiveMax;
}
private void GetSourcesForNode(EntityUid uid, Node node, out List<PowerMonitoringConsoleEntry> sources)
if (requireBattery)
{
- _battery.SetCharge((uid, battery), battery.CurrentCharge - apcBattery.IdleLoad * frameTime);
+ _battery.ChangeCharge((uid, battery), -apcBattery.IdleLoad * frameTime);
}
// Otherwise try to charge the battery
else if (powered && !_battery.IsFull((uid, battery)))
{
apcReceiver.Load += apcBattery.BatteryRechargeRate * apcBattery.BatteryRechargeEfficiency;
- _battery.SetCharge((uid, battery), battery.CurrentCharge + apcBattery.BatteryRechargeRate * frameTime);
+ _battery.ChangeCharge((uid, battery), apcBattery.BatteryRechargeRate * frameTime);
}
// Enable / disable the battery if the state changed
- var enableBattery = requireBattery && battery.CurrentCharge > 0;
+ var currentCharge = _battery.GetCharge((uid, battery));
+ var enableBattery = requireBattery && currentCharge > 0;
if (apcBattery.Enabled != enableBattery)
{
{
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
[Dependency] private readonly IAdminLogManager _adminLogger = default!;
- [Dependency] private readonly PredictedBatterySystem _predictedBattery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
public override void Initialize()
{
SubscribeLocalEvent<RiggableComponent, BeingMicrowavedEvent>(OnMicrowaved);
SubscribeLocalEvent<RiggableComponent, SolutionContainerChangedEvent>(OnSolutionChanged);
SubscribeLocalEvent<RiggableComponent, ChargeChangedEvent>(OnChargeChanged);
- SubscribeLocalEvent<RiggableComponent, PredictedBatteryChargeChangedEvent>(OnChargeChanged);
}
private void OnRejuvenate(Entity<RiggableComponent> entity, ref RejuvenateEvent args)
{
if (TryComp<BatteryComponent>(entity, out var batteryComponent))
{
- if (batteryComponent.CurrentCharge == 0f)
- return;
-
- Explode(entity, batteryComponent.CurrentCharge);
- args.Handled = true;
- }
-
- if (TryComp<PredictedBatteryComponent>(entity, out var predictedBatteryComponent))
- {
- var charge = _predictedBattery.GetCharge((entity, predictedBatteryComponent));
+ var charge = _battery.GetCharge((entity, batteryComponent));
if (charge == 0f)
return;
QueueDel(uid);
}
- // non-predicted batteries
private void OnChargeChanged(Entity<RiggableComponent> ent, ref ChargeChangedEvent args)
- {
- if (!ent.Comp.IsRigged)
- return;
-
- if (args.Charge == 0f)
- return; // No charge to cause an explosion.
-
- Explode(ent, args.Charge);
- }
-
- // predicted batteries
- private void OnChargeChanged(Entity<RiggableComponent> ent, ref PredictedBatteryChargeChangedEvent args)
{
if (!ent.Comp.IsRigged)
return;
using Content.Server.Power.EntitySystems;
using Content.Shared.Power;
using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
using Content.Shared.Rounding;
using Content.Shared.SMES;
using JetBrains.Annotations;
{
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
public override void Initialize()
{
if (!Resolve(uid, ref battery, false))
return 0;
- return ContentHelpers.RoundToLevels(battery.CurrentCharge, battery.MaxCharge, 6);
+ var currentCharge = _battery.GetCharge((uid, battery));
+ return ContentHelpers.RoundToLevels(currentCharge, battery.MaxCharge, 6);
}
private ChargeState CalcChargeState(EntityUid uid, PowerNetworkBatteryComponent? netBattery = null)
using Content.Server.Administration;
-using Content.Server.Power.EntitySystems;
using Content.Shared.Administration;
using Content.Shared.Power.Components;
using Content.Shared.Power.EntitySystems;
[AdminCommand(AdminFlags.Debug)]
public sealed class SetBatteryPercentCommand : LocalizedEntityCommands
{
- [Dependency] private readonly BatterySystem _batterySystem = default!;
- [Dependency] private readonly PredictedBatterySystem _predictedBatterySystem = default!;
+ [Dependency] private readonly SharedBatterySystem _batterySystem = default!;
public override string Command => "setbatterypercent";
if (EntityManager.TryGetComponent<BatteryComponent>(id, out var battery))
_batterySystem.SetCharge((id.Value, battery), battery.MaxCharge * percent / 100);
- else if (EntityManager.TryGetComponent<PredictedBatteryComponent>(id, out var pBattery))
- _predictedBatterySystem.SetCharge((id.Value, pBattery), pBattery.MaxCharge * percent / 100);
else
{
shell.WriteLine(Loc.GetString($"cmd-setbatterypercent-battery-not-found", ("id", id)));
_battery.ChangeCharge((entity, battery), networkLoad.NetworkLoad.ReceivingPower * frameTime);
- var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
+ var currentBatteryThreshold = _battery.GetChargeLevel((entity, battery));
// Check for warning message threshold
if (!component.SentImminentExplosionWarningMessage &&
}
// Check for explosion
- if (battery.CurrentCharge < battery.MaxCharge)
+ if (!_battery.IsFull((entity, battery)))
continue;
if (component.ExplosionTime == null)
public sealed class JammerSystem : SharedJammerSystem
{
[Dependency] private readonly PowerCellSystem _powerCell = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly SharedDeviceNetworkJammerSystem _jammer = default!;
}
// TODO: Very important: Make this charge rate based instead of updating every single tick
- // See PredictedBatteryComponent
+ // See BatteryComponent
public override void Update(float frameTime)
{
var query = EntityQueryEnumerator<ActiveRadioJammerComponent, RadioJammerComponent>();
using Content.Shared.DeviceNetwork;
using Content.Shared.DeviceNetwork.Events;
using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
namespace Content.Server.SensorMonitoring;
public const string DeviceNetworkCommandSyncData = "bat_sync_data";
[Dependency] private readonly DeviceNetworkSystem _deviceNetwork = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
public override void Initialize()
{
{
case DeviceNetworkCommandSyncData:
var battery = Comp<BatteryComponent>(uid);
+ var currentCharge = _battery.GetCharge((uid, battery));
var netBattery = Comp<PowerNetworkBatteryComponent>(uid);
var payload = new NetworkPayload
{
[DeviceNetworkConstants.Command] = DeviceNetworkCommandSyncData,
[DeviceNetworkCommandSyncData] = new BatterySensorData(
- battery.CurrentCharge,
+ currentCharge,
battery.MaxCharge,
netBattery.CurrentReceiving,
netBattery.MaxChargeRate,
[Dependency] private readonly TriggerSystem _trigger = default!;
[Dependency] private readonly MobStateSystem _mobState = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly EmagSystem _emag = default!;
[Dependency] private readonly MobThresholdSystem _mobThresholdSystem = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
using Content.Server.Ghost;
using Content.Server.Mind;
using Content.Server.Power.Components;
-using Content.Server.Power.EntitySystems;
using Content.Server.Roles;
using Content.Server.Spawners.Components;
using Content.Server.Spawners.EntitySystems;
using Content.Shared.Mobs.Systems;
using Content.Shared.Popups;
using Content.Shared.Power;
+using Content.Shared.Power.EntitySystems;
using Content.Shared.Power.Components;
using Content.Shared.Rejuvenate;
using Content.Shared.Roles;
[Dependency] private readonly GhostSystem _ghost = default!;
[Dependency] private readonly AlertsSystem _alerts = default!;
[Dependency] private readonly DestructibleSystem _destructible = default!;
- [Dependency] private readonly BatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly DamageableSystem _damageable = default!;
[Dependency] private readonly SharedPopupSystem _popups = default!;
[Dependency] private readonly StationSystem _station = default!;
UpdateDamagedAccent(entity);
}
+ // TODO: This should just read the current damage and charge when speaking instead of updating the component all the time even if we don't even use it.
private void UpdateDamagedAccent(Entity<StationAiCoreComponent> ent)
{
if (!TryGetHeld((ent.Owner, ent.Comp), out var held))
return;
if (TryComp<BatteryComponent>(ent, out var battery))
- accent.OverrideChargeLevel = battery.CurrentCharge / battery.MaxCharge;
+ accent.OverrideChargeLevel = _battery.GetChargeLevel((ent.Owner, battery));
if (TryComp<DamageableComponent>(ent, out var damageable))
accent.OverrideTotalDamage = damageable.TotalDamage;
if (!_proto.TryIndex(_batteryAlert, out var proto))
return;
- var chargePercent = battery.CurrentCharge / battery.MaxCharge;
+ var chargePercent = _battery.GetChargeLevel((ent.Owner, battery));
var chargeLevel = Math.Round(chargePercent * proto.MaxSeverity);
_alerts.ShowAlert(held.Value, _batteryAlert, (short)Math.Clamp(chargeLevel, 0, proto.MaxSeverity));
public sealed class DamagedSiliconAccentSystem : EntitySystem
{
[Dependency] private readonly IRobustRandom _random = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly PowerCellSystem _powerCell = default!;
[Dependency] private readonly DestructibleSystem _destructibleSystem = default!;
{
[Dependency] private readonly RiggableSystem _riggableSystem = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly ItemToggleSystem _itemToggle = default!;
public override void Initialize()
SubscribeLocalEvent<StunbatonComponent, ExaminedEvent>(OnExamined);
SubscribeLocalEvent<StunbatonComponent, SolutionContainerChangedEvent>(OnSolutionChange);
SubscribeLocalEvent<StunbatonComponent, StaminaDamageOnHitAttemptEvent>(OnStaminaHitAttempt);
- SubscribeLocalEvent<StunbatonComponent, PredictedBatteryChargeChangedEvent>(OnChargeChanged);
+ SubscribeLocalEvent<StunbatonComponent, ChargeChangedEvent>(OnChargeChanged);
}
private void OnStaminaHitAttempt(Entity<StunbatonComponent> entity, ref StaminaDamageOnHitAttemptEvent args)
{
if (!_itemToggle.IsActivated(entity.Owner) ||
- !TryComp<PredictedBatteryComponent>(entity.Owner, out var battery) || !_battery.TryUseCharge((entity.Owner, battery), entity.Comp.EnergyPerUse))
+ !TryComp<BatteryComponent>(entity.Owner, out var battery) || !_battery.TryUseCharge((entity.Owner, battery), entity.Comp.EnergyPerUse))
{
args.Cancelled = true;
}
: Loc.GetString("comp-stunbaton-examined-off");
args.PushMarkup(onMsg);
- if (TryComp<PredictedBatteryComponent>(entity.Owner, out var battery))
+ if (TryComp<BatteryComponent>(entity.Owner, out var battery))
{
var count = _battery.GetRemainingUses((entity.Owner, battery), entity.Comp.EnergyPerUse);
args.PushMarkup(Loc.GetString("melee-battery-examine", ("color", "yellow"), ("count", count)));
{
base.TryTurnOn(entity, ref args);
- if (!TryComp<PredictedBatteryComponent>(entity, out var battery) || _battery.GetCharge((entity, battery)) < entity.Comp.EnergyPerUse)
+ if (!TryComp<BatteryComponent>(entity, out var battery) || _battery.GetCharge((entity, battery)) < entity.Comp.EnergyPerUse)
{
args.Cancelled = true;
if (args.User != null)
{
// Explode if baton is activated and rigged.
if (!TryComp<RiggableComponent>(entity, out var riggable) ||
- !TryComp<PredictedBatteryComponent>(entity, out var battery))
+ !TryComp<BatteryComponent>(entity, out var battery))
return;
if (_itemToggle.IsActivated(entity.Owner) && riggable.IsRigged)
});
}
- private void OnChargeChanged(Entity<StunbatonComponent> entity, ref PredictedBatteryChargeChangedEvent args)
+ private void OnChargeChanged(Entity<StunbatonComponent> entity, ref ChargeChangedEvent args)
{
- if (TryComp<PredictedBatteryComponent>(entity.Owner, out var battery) &&
+ if (TryComp<BatteryComponent>(entity.Owner, out var battery) &&
_battery.GetCharge((entity.Owner, battery)) < entity.Comp.EnergyPerUse)
{
_itemToggle.TryDeactivate(entity.Owner, predicted: false);
{
if (TryComp<BatteryComponent>(coil, out var batteryComponent))
{
- _battery.SetCharge((coil, batteryComponent), batteryComponent.CurrentCharge + coil.Comp.ChargeFromLightning);
+ _battery.ChangeCharge((coil, batteryComponent), coil.Comp.ChargeFromLightning);
}
}
}
public sealed class XAEChargeBatterySystem : BaseXAESystem<XAEChargeBatteryComponent>
{
[Dependency] private readonly BatterySystem _battery = default!;
- [Dependency] private readonly PredictedBatterySystem _predictedBattery = default!;
[Dependency] private readonly EntityLookupSystem _lookup = default!;
/// <summary> Pre-allocated and re-used collection.</summary>
private readonly HashSet<Entity<BatteryComponent>> _batteryEntities = new();
- private readonly HashSet<Entity<PredictedBatteryComponent>> _pBatteryEntities = new();
/// <inheritdoc />
protected override void OnActivated(Entity<XAEChargeBatteryComponent> ent, ref XenoArtifactNodeActivatedEvent args)
{
_batteryEntities.Clear();
- _pBatteryEntities.Clear();
_lookup.GetEntitiesInRange(args.Coordinates, ent.Comp.Radius, _batteryEntities);
foreach (var battery in _batteryEntities)
{
_battery.SetCharge(battery.AsNullable(), battery.Comp.MaxCharge);
}
-
- _lookup.GetEntitiesInRange(args.Coordinates, ent.Comp.Radius, _pBatteryEntities);
- foreach (var pBattery in _pBatteryEntities)
- {
- _predictedBattery.SetCharge(pBattery.AsNullable(), pBattery.Comp.MaxCharge);
- }
}
}
using Content.Shared.Power.Components;
+using Content.Shared.Power.EntitySystems;
using Content.Shared.PowerCell.Components;
namespace Content.Shared.Power;
/// <summary>
-/// Raised when a battery's charge or capacity changes (capacity affects relative charge percentage).
-/// Only raised for entities with <see cref="BatteryComponent"/>.
+/// Raised when a battery's charge, charge rate or capacity was updated (capacity affects relative charge percentage).
+/// If a battery uses <see cref="BatteryComponent.ChargeRate"/> to (dis)charge this is NOT raised every single tick, but only when the charge rate is updated.
+/// For instantaneous charge changes using <see cref="SharedBatterySystem.SetCharge"/>, <see cref="SharedBatterySystem.ChangeCharge"/> or similar this DOES get raised, but
+/// you should avoid doing so in update loops if the component has net sync enabled.
/// </summary>
[ByRefEvent]
-public readonly record struct ChargeChangedEvent(float Charge, float Delta, float MaxCharge)
-{
- /// <summary>
- /// The new charge of the battery.
- /// </summary>
- public readonly float Charge = Charge;
-
- /// <summary>
- /// The amount the charge was changed by.
- /// </summary>
- public readonly float Delta = Delta;
-
- /// <summary>
- /// The maximum charge of the battery.
- /// </summary>
- public readonly float MaxCharge = MaxCharge;
-}
-
-/// <summary>
-/// Raised when a predicted battery's charge or capacity changes (capacity affects relative charge percentage).
-/// Unlike <see cref="ChargeChangedEvent"/> this is not raised repeatedly each time the charge changes, but only when the charge rate is changed
-/// or a charge amount was added or removed instantaneously. The current charge can be inferred from the time of the last update and the charge and
-/// charge rate at that time.
-/// Only raised for entities with <see cref="PredictedBatteryComponent"/>.
-/// </summary>
-[ByRefEvent]
-public readonly record struct PredictedBatteryChargeChangedEvent(float CurrentCharge, float Delta, float CurrentChargeRate, float MaxCharge)
+public readonly record struct ChargeChangedEvent(float CurrentCharge, float Delta, float CurrentChargeRate, float MaxCharge)
{
/// <summary>
/// The new charge of the battery.
/// <summary>
/// Raised when a battery changes its state between full, empty, or neither.
-/// Used only for <see cref="PredictedBatteryComponent"/>.
+/// Useful to detect when a battery is empty or fully charged (since ChargeChangedEvent does not get raised every tick for batteries with a constant charge rate).
/// </summary>
[ByRefEvent]
-public record struct PredictedBatteryStateChangedEvent(BatteryState OldState, BatteryState NewState);
+public record struct BatteryStateChangedEvent(BatteryState OldState, BatteryState NewState);
/// <summary>
/// Raised to calculate a predicted battery's recharge rate.
/// Subscribe to this to offset its current charge rate.
-/// Used only for <see cref="PredictedBatteryComponent"/>.
/// </summary>
[ByRefEvent]
public record struct RefreshChargeRateEvent(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"/>, <see cref="PredictedBatteryComponent"/>, or <see cref="PowerCellSlotComponent"/>.
+/// Works with either <see cref="BatteryComponent"/> or <see cref="PowerCellSlotComponent"/>.
/// If there are multiple batteries then the results will be summed up.
/// </summary>
[ByRefEvent]
/// <summary>
/// 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"/>, <see cref="PredictedBatteryComponent"/>, or <see cref="PowerCellSlotComponent"/>.
+/// 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]
using Content.Shared.Power.EntitySystems;
+using Content.Shared.PowerCell.Components;
using Content.Shared.Guidebook;
+using Robust.Shared.GameStates;
+using Robust.Shared.Serialization;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
namespace Content.Shared.Power.Components;
/// <summary>
-/// Battery node on the pow3r network. Needs other components to connect to actual networks.
-/// Use this for batteries that cannot be predicted.
-/// Use <see cref="PredictedBatteryComponent"/> otherwise.
+/// Used for any sort of battery that stores electical power.
+/// Can be used as a battery node on the pow3r network. Needs other components to connect to actual networks, see PowerNetworkBatteryComponent.
+/// Also used for power cells using <see cref="PowerCellComponent"/> or battery powered guns with intrinsic battery.
/// </summary>
-[RegisterComponent]
-[Virtual]
+/// <remarks>
+/// IMPORTANT: If your battery has an update loop setting the charge every single tick you should set <see cref="Component.NetSyncEnabled"> to false
+/// in your prototype to prevent it from getting networked every single tick. However, this will disable prediction.
+/// This is mostly needed for anything connected to the power network (APCs, SMES, turrets with battery), as their power supply ramps up over time.
+/// Everything else that only has a constant charge rate (e.g. charging/discharging a battery at a certain wattage) or instantaneous power draw (e.g. shooting a gun) is fine being networked.
+/// However, you should write your systems to avoid using update loops and instead change the battery's charge rate using <see cref="SharedBatterySystem.RefreshChargeRate"/> and
+/// the current charge will automatically be inferred if you use <see cref="SharedBatterySystem.GetCharge"/>.
+/// </remarks>
+[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
[Access(typeof(SharedBatterySystem))]
-public partial class BatteryComponent : Component
+public sealed partial class BatteryComponent : Component
{
/// <summary>
- /// Maximum charge of the battery in joules (i.e. watt seconds)
+ /// Maximum charge of the battery in joules (ie. watt seconds)
/// </summary>
- [DataField]
+ [DataField, AutoNetworkedField, ViewVariables]
[GuidebookData]
public float MaxCharge;
/// <summary>
- /// Current charge of the battery in joules (ie. watt seconds)
+ /// The price per one joule. Default is 1 speso for 10kJ.
+ /// </summary>
+ [DataField]
+ public float PricePerJoule = 0.0001f;
+
+ /// <summary>
+ /// Time stamp of the last networked update.
/// </summary>
- [DataField("startingCharge")] // TODO: rename this datafield to currentCharge
- public float CurrentCharge;
+ [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
+ [AutoNetworkedField, AutoPausedField, ViewVariables]
+ public TimeSpan LastUpdate = TimeSpan.Zero;
/// <summary>
- /// The price per one joule. Default is 1 speso for 10kJ.
+ /// The intial charge to be set on map init.
/// </summary>
[DataField]
- public float PricePerJoule = 0.0001f;
+ public float StartingCharge;
+
+ /// <summary>
+ /// The charge at the last update in joules (i.e. watt seconds).
+ /// </summary>
+ [DataField, AutoNetworkedField, ViewVariables]
+ public float LastCharge;
+
+ /// <summary>
+ /// The current charge rate in watt.
+ /// </summary>
+ /// <remarks>
+ /// Not a datafield as this is only cached and recalculated on component startup.
+ /// </remarks>
+ [ViewVariables, AutoNetworkedField]
+ public float ChargeRate;
+
+ /// <summary>
+ /// The current charge state of the battery.
+ /// Used to track state changes for raising <see cref="BatteryStateChangedEvent"/>.
+ /// </summary>
+ /// <remarks>
+ /// Not a datafield as this is only cached and recalculated in an update loop.
+ /// </remarks>
+ [ViewVariables, AutoNetworkedField]
+ public BatteryState State = BatteryState.Neither;
}
+
+/// <summary>
+/// Charge level status of the battery.
+/// </summary>
+[Serializable, NetSerializable]
+public enum BatteryState : byte
+{
+ /// <summary>
+ /// Full charge.
+ /// </summary>
+ Full,
+ /// <summary>
+ /// No charge.
+ /// </summary>
+ Empty,
+ /// <summary>
+ /// Neither full nor empty.
+ /// </summary>
+ Neither,
+}
+
+using Robust.Shared.GameStates;
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"/>.
-/// For <see cref="PredictedBatteryComponent"/> use <see cref="PredictedBatterySelfRechargerComponent"/> instead.
/// </summary>
-[RegisterComponent, AutoGenerateComponentPause]
+[RegisterComponent, NetworkedComponent]
+[AutoGenerateComponentState, 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? In watts.
/// </summary>
- [DataField]
+ [DataField, AutoNetworkedField, ViewVariables]
public float AutoRechargeRate;
/// <summary>
- /// How long should the entity stop automatically recharging if charge is used?
+ /// How long should the entity stop automatically recharging if a charge is used?
/// </summary>
- [DataField]
- public TimeSpan AutoRechargePauseTime = TimeSpan.FromSeconds(0);
+ [DataField, AutoNetworkedField]
+ public TimeSpan AutoRechargePauseTime = TimeSpan.Zero;
/// <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);
+ [AutoNetworkedField, AutoPausedField, ViewVariables]
+ public TimeSpan? NextAutoRecharge = TimeSpan.FromSeconds(0);
}
namespace Content.Shared.Power.Components;
/// <summary>
-/// Marker component that makes an entity with <see cref="PredictedBatteryComponent"/> update its appearance data for use with visualizers.
+/// Marker component that makes an entity with <see cref="BatteryComponent"/> update its appearance data for use with visualizers.
/// Also works with an entity with <see cref="PowerCellSlotComponent"/> and will relay the state of the inserted powercell.
/// </summary>
[RegisterComponent, NetworkedComponent]
-public sealed partial class PredictedBatteryVisualsComponent : Component;
+public sealed partial class BatteryVisualsComponent : Component;
/// <summary>
/// Keys for the appearance data.
public enum BatteryChargingState : byte
{
/// <summary>
- /// PredictedBatteryComponent.ChargeRate > 0
+ /// BatteryComponent.ChargeRate > 0
/// </summary>
Charging,
/// <summary>
- /// PredictedBatteryComponent.ChargeRate < 0
+ /// BatteryComponent.ChargeRate < 0
/// </summary>
Decharging,
/// <summary>
- /// PredictedBatteryComponent.ChargeRate == 0
+ /// BatteryComponent.ChargeRate == 0
/// </summary>
Constant,
}
/// <summary>
/// Allows the charge of a battery to be seen by examination.
-/// Works with either <see cref="BatteryComponent"/> or <see cref="PredictedBatteryComponent"/>.
+/// Requires <see cref="BatteryComponent"/>.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class ExaminableBatteryComponent : Component;
+++ /dev/null
-using Content.Shared.Power.EntitySystems;
-using Content.Shared.Guidebook;
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-
-namespace Content.Shared.Power.Components;
-
-/// <summary>
-/// Predicted equivalent to <see cref="BatteryComponent"/>.
-/// Use this for electrical power storages that only have a constant charge rate or instantaneous power draw.
-/// Devices being directly charged by the power network do not fulfill that requirement as their power supply ramps up over time.
-/// </summary>
-/// <remarks>
-/// We cannot simply network <see cref="BatteryComponent"/> since it would get dirtied every single tick when it updates.
-/// This component solves this by requiring a constant charge rate and having the client infer the current charge from the rate
-/// and the timestamp the charge was last networked at. This can possibly be expanded in the future by adding a second time derivative.
-/// </remarks>
-[RegisterComponent, NetworkedComponent, AutoGenerateComponentState, AutoGenerateComponentPause]
-[Access(typeof(PredictedBatterySystem))]
-public sealed partial class PredictedBatteryComponent : Component
-{
- /// <summary>
- /// Maximum charge of the battery in joules (ie. watt seconds)
- /// </summary>
- [DataField, AutoNetworkedField, ViewVariables]
- [GuidebookData]
- public float MaxCharge;
-
- /// <summary>
- /// The price per one joule. Default is 1 speso for 10kJ.
- /// </summary>
- [DataField]
- public float PricePerJoule = 0.0001f;
-
- /// <summary>
- /// Time stamp of the last networked update.
- /// </summary>
- [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
- [AutoNetworkedField, AutoPausedField, ViewVariables]
- public TimeSpan LastUpdate = TimeSpan.Zero;
-
- /// <summary>
- /// The intial charge to be set on map init.
- /// </summary>
- [DataField]
- public float StartingCharge;
-
- /// <summary>
- /// The charge at the last update in joules (i.e. watt seconds).
- /// </summary>
- [DataField, AutoNetworkedField, ViewVariables]
- public float LastCharge;
-
- /// <summary>
- /// The current charge rate in watt.
- /// </summary>
- /// <remarks>
- /// Not a datafield as this is only cached and recalculated on component startup.
- /// </remarks>
- [ViewVariables, AutoNetworkedField]
- public float ChargeRate;
-
- /// <summary>
- /// The current charge state of the battery.
- /// Used to track state changes for raising <see cref="PredictedBatteryStateChangedEvent"/>.
- /// </summary>
- /// <remarks>
- /// Not a datafield as this is only cached and recalculated in an update loop.
- /// </remarks>
- [ViewVariables, AutoNetworkedField]
- public BatteryState State = BatteryState.Neither;
-}
-
-/// <summary>
-/// Charge level status of the battery.
-/// </summary>
-[Serializable, NetSerializable]
-public enum BatteryState : byte
-{
- /// <summary>
- /// Full charge.
- /// </summary>
- Full,
- /// <summary>
- /// No charge.
- /// </summary>
- Empty,
- /// <summary>
- /// Neither full nor empty.
- /// </summary>
- Neither,
-}
-
+++ /dev/null
-using Robust.Shared.GameStates;
-using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom;
-
-namespace Content.Shared.Power.Components;
-
-/// <summary>
-/// Self-recharging battery.
-/// To be used in combination with <see cref="PredictedBatteryComponent"/>.
-/// For <see cref="BatteryComponent"/> use <see cref="BatterySelfRechargerComponent"/> instead.
-/// </summary>
-[RegisterComponent, NetworkedComponent]
-[AutoGenerateComponentState, AutoGenerateComponentPause]
-public sealed partial class PredictedBatterySelfRechargerComponent : Component
-{
- /// <summary>
- /// At what rate does the entity automatically recharge? In watts.
- /// </summary>
- [DataField, AutoNetworkedField, ViewVariables]
- public float AutoRechargeRate;
-
- /// <summary>
- /// How long should the entity stop automatically recharging if a charge is used?
- /// </summary>
- [DataField, AutoNetworkedField]
- public TimeSpan AutoRechargePauseTime = TimeSpan.Zero;
-
- /// <summary>
- /// Do not auto recharge if this timestamp has yet to happen, set for the auto recharge pause system.
- /// </summary>
- [DataField(customTypeSerializer: typeof(TimeOffsetSerializer))]
- [AutoNetworkedField, AutoPausedField, ViewVariables]
- public TimeSpan? NextAutoRecharge = TimeSpan.FromSeconds(0);
-}
public sealed class ChargerSystem : EntitySystem
{
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedPowerReceiverSystem _receiver = default!;
[Dependency] private readonly EntityWhitelistSystem _whitelist = default!;
[Dependency] private readonly SharedContainerSystem _container = default!;
SubscribeLocalEvent<ChargerComponent, EmpPulseEvent>(OnEmpPulse);
SubscribeLocalEvent<ChargerComponent, EmpDisabledRemovedEvent>(OnEmpRemoved);
SubscribeLocalEvent<InsideChargerComponent, RefreshChargeRateEvent>(OnRefreshChargeRate);
- SubscribeLocalEvent<InsideChargerComponent, PredictedBatteryStateChangedEvent>(OnStatusChanged);
+ SubscribeLocalEvent<InsideChargerComponent, BatteryStateChangedEvent>(OnStatusChanged);
}
private void OnStartup(Entity<ChargerComponent> ent, ref ComponentStartup args)
args.NewChargeRate += chargerComp.ChargeRate;
}
- private void OnStatusChanged(Entity<InsideChargerComponent> ent, ref PredictedBatteryStateChangedEvent args)
+ private void OnStatusChanged(Entity<InsideChargerComponent> ent, ref BatteryStateChangedEvent args)
{
// If the battery is full update the visuals and power draw of the charger.
+++ /dev/null
-using Content.Shared.Cargo;
-using Content.Shared.Emp;
-using Content.Shared.Examine;
-using Content.Shared.Power.Components;
-using Content.Shared.Rejuvenate;
-using Robust.Shared.Timing;
-using Robust.Shared.Utility;
-
-namespace Content.Shared.Power.EntitySystems;
-
-/// <summary>
-/// Responsible for <see cref="PredictedBatteryComponent"/>.
-/// Predicted equivalent of <see cref="Content.Server.Power.EntitySystems.BatterySystem"/>.
-/// If you make changes to this make sure to keep the two consistent.
-/// </summary>
-public sealed partial class PredictedBatterySystem : EntitySystem
-{
- [Dependency] private readonly IGameTiming _timing = default!;
- [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
-
- public override void Initialize()
- {
- base.Initialize();
-
- SubscribeLocalEvent<PredictedBatteryComponent, ComponentInit>(OnInit);
- SubscribeLocalEvent<PredictedBatteryComponent, ComponentStartup>(OnStartup);
- SubscribeLocalEvent<PredictedBatteryComponent, MapInitEvent>(OnMapInit);
- SubscribeLocalEvent<PredictedBatteryComponent, EmpPulseEvent>(OnEmpPulse);
- SubscribeLocalEvent<PredictedBatteryComponent, RejuvenateEvent>(OnRejuvenate);
- SubscribeLocalEvent<PredictedBatteryComponent, ExaminedEvent>(OnExamine);
- SubscribeLocalEvent<PredictedBatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
- SubscribeLocalEvent<PredictedBatteryComponent, ChangeChargeEvent>(OnChangeCharge);
- SubscribeLocalEvent<PredictedBatteryComponent, GetChargeEvent>(OnGetCharge);
- SubscribeLocalEvent<PredictedBatterySelfRechargerComponent, RefreshChargeRateEvent>(OnRefreshChargeRate);
- SubscribeLocalEvent<PredictedBatterySelfRechargerComponent, ComponentStartup>(OnRechargerStartup);
- SubscribeLocalEvent<PredictedBatterySelfRechargerComponent, ComponentRemove>(OnRechargerRemove);
- SubscribeLocalEvent<PredictedBatteryVisualsComponent, PredictedBatteryChargeChangedEvent>(OnVisualsChargeChanged);
- SubscribeLocalEvent<PredictedBatteryVisualsComponent, PredictedBatteryStateChangedEvent>(OnVisualsStateChanged);
- }
-
- private void OnInit(Entity<PredictedBatteryComponent> ent, ref ComponentInit args)
- {
- DebugTools.Assert(!HasComp<BatteryComponent>(ent), $"{ent} has both BatteryComponent and PredictedBatteryComponent");
- }
-
- private void OnStartup(Entity<PredictedBatteryComponent> ent, ref ComponentStartup args)
- {
- // In case a recharging component was added before the battery component itself.
- // Doing this only on map init is not enough because the charge rate is not a datafield, but cached, so it would get lost when reloading the game.
- // If we would make it a datafield then the integration tests would complain about modifying it before map init.
- RefreshChargeRate(ent.AsNullable());
- }
-
- private void OnMapInit(Entity<PredictedBatteryComponent> ent, ref MapInitEvent args)
- {
- SetCharge(ent.AsNullable(), ent.Comp.StartingCharge);
- RefreshChargeRate(ent.AsNullable());
- }
-
- private void OnRejuvenate(Entity<PredictedBatteryComponent> ent, ref RejuvenateEvent args)
- {
- SetCharge(ent.AsNullable(), ent.Comp.MaxCharge);
- }
-
- private void OnEmpPulse(Entity<PredictedBatteryComponent> ent, ref EmpPulseEvent args)
- {
- args.Affected = true;
- UseCharge(ent.AsNullable(), args.EnergyConsumption);
- }
-
- private void OnExamine(Entity<PredictedBatteryComponent> ent, ref ExaminedEvent args)
- {
- if (!args.IsInDetailsRange)
- return;
-
- if (!HasComp<ExaminableBatteryComponent>(ent))
- return;
-
- var chargePercentRounded = 0;
- var currentCharge = GetCharge(ent.AsNullable());
- if (ent.Comp.MaxCharge != 0)
- chargePercentRounded = (int)(100 * currentCharge / ent.Comp.MaxCharge);
- args.PushMarkup(
- Loc.GetString(
- "examinable-battery-component-examine-detail",
- ("percent", chargePercentRounded),
- ("markupPercentColor", "green")
- )
- );
- }
-
- /// <summary>
- /// Gets the price for the power contained in an entity's battery.
- /// </summary>
- private void CalculateBatteryPrice(Entity<PredictedBatteryComponent> ent, ref PriceCalculationEvent args)
- {
- args.Price += GetCharge(ent.AsNullable()) * ent.Comp.PricePerJoule;
- }
-
- private void OnChangeCharge(Entity<PredictedBatteryComponent> ent, ref ChangeChargeEvent args)
- {
- if (args.ResidualValue == 0)
- return;
-
- args.ResidualValue -= ChangeCharge(ent.AsNullable(), args.ResidualValue);
- }
-
- private void OnGetCharge(Entity<PredictedBatteryComponent> ent, ref GetChargeEvent args)
- {
- args.CurrentCharge += GetCharge(ent.AsNullable());
- args.MaxCharge += ent.Comp.MaxCharge;
- }
-
- private void OnRefreshChargeRate(Entity<PredictedBatterySelfRechargerComponent> ent, ref RefreshChargeRateEvent args)
- {
- if (_timing.CurTime < ent.Comp.NextAutoRecharge)
- return; // Still on cooldown
-
- args.NewChargeRate += ent.Comp.AutoRechargeRate;
- }
-
- public override void Update(float frameTime)
- {
- var curTime = _timing.CurTime;
-
- // Update self-recharging cooldowns.
- var rechargerQuery = EntityQueryEnumerator<PredictedBatterySelfRechargerComponent, PredictedBatteryComponent>();
- while (rechargerQuery.MoveNext(out var uid, out var recharger, out var battery))
- {
- if (recharger.NextAutoRecharge == null || curTime < recharger.NextAutoRecharge)
- continue;
-
- recharger.NextAutoRecharge = null; // Don't refresh every tick.
- Dirty(uid, recharger);
- RefreshChargeRate((uid, battery)); // Cooldown is over, apply the new recharge rate.
- }
-
- // Raise events when the battery is full or empty so that other systems can react and visuals can get updated.
- // This is not doing that many calculations, it only has to get the current charge and only raises events if something did change.
- // If this turns out to be too expensive and shows up on grafana consider updating it less often.
- var batteryQuery = EntityQueryEnumerator<PredictedBatteryComponent>();
- while (batteryQuery.MoveNext(out var uid, out var battery))
- {
- if (battery.ChargeRate == 0f)
- continue; // No need to check if it's constant.
-
- UpdateState((uid, battery));
- }
- }
-
- private void OnRechargerStartup(Entity<PredictedBatterySelfRechargerComponent> ent, ref ComponentStartup args)
- {
- // In case this component is added after the battery component.
- RefreshChargeRate(ent.Owner);
- }
-
- private void OnRechargerRemove(Entity<PredictedBatterySelfRechargerComponent> ent, ref ComponentRemove args)
- {
- // We use ComponentRemove to make sure this component no longer subscribes to the refresh event.
- RefreshChargeRate(ent.Owner);
- }
-
- private void OnVisualsChargeChanged(Entity<PredictedBatteryVisualsComponent> ent, ref PredictedBatteryChargeChangedEvent args)
- {
- // Update the appearance data for the charge rate.
- // We have a separate component for this to not duplicate the networking cost unless we actually use it.
- var state = BatteryChargingState.Constant;
- if (args.CurrentChargeRate > 0f)
- state = BatteryChargingState.Charging;
- else if (args.CurrentChargeRate < 0f)
- state = BatteryChargingState.Decharging;
-
- _appearance.SetData(ent.Owner, BatteryVisuals.Charging, state);
- }
-
- private void OnVisualsStateChanged(Entity<PredictedBatteryVisualsComponent> ent, ref PredictedBatteryStateChangedEvent args)
- {
- // Update the appearance data for the fill level (empty, full, in-between).
- // We have a separate component for this to not duplicate the networking cost unless we actually use it.
- _appearance.SetData(ent.Owner, BatteryVisuals.State, args.NewState);
- }
-}
namespace Content.Shared.Power.EntitySystems;
/// <summary>
-/// Responsible for <see cref="PredictedBatteryComponent"/>.
+/// Responsible for <see cref="BatteryComponent"/>.
/// Predicted equivalent of <see cref="Content.Server.Power.EntitySystems.BatterySystem"/>.
/// If you make changes to this make sure to keep the two consistent.
/// </summary>
-public sealed partial class PredictedBatterySystem
+public abstract partial class SharedBatterySystem
{
/// <summary>
/// Changes the battery's charge by the given amount
/// </summary>
/// <returns>The actually changed amount.</returns>
[PublicAPI]
- public float ChangeCharge(Entity<PredictedBatteryComponent?> ent, float amount)
+ public float ChangeCharge(Entity<BatteryComponent?> ent, float amount)
{
if (!Resolve(ent, ref ent.Comp))
return 0;
TrySetChargeCooldown(ent.Owner);
- var changedEv = new PredictedBatteryChargeChangedEvent(newValue, delta, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
+ 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.
/// </summary>
/// <returns>The actually changed amount.</returns>
[PublicAPI]
- public float UseCharge(Entity<PredictedBatteryComponent?> ent, float amount)
+ public float UseCharge(Entity<BatteryComponent?> ent, float amount)
{
if (amount <= 0f)
return 0f;
/// </summary>
/// <returns>If the full amount was able to be removed.</returns>
[PublicAPI]
- public bool TryUseCharge(Entity<PredictedBatteryComponent?> ent, float amount)
+ public bool TryUseCharge(Entity<BatteryComponent?> ent, float amount)
{
if (!Resolve(ent, ref ent.Comp, false) || amount > GetCharge(ent))
return false;
/// Sets the battery's charge.
/// </summary>
[PublicAPI]
- public void SetCharge(Entity<PredictedBatteryComponent?> ent, float value)
+ public void SetCharge(Entity<BatteryComponent?> ent, float value)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.LastUpdate = curTime;
Dirty(ent);
- var ev = new PredictedBatteryChargeChangedEvent(newValue, delta, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
+ 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.
/// Sets the battery's maximum charge.
/// </summary>
[PublicAPI]
- public void SetMaxCharge(Entity<PredictedBatteryComponent?> ent, float value)
+ public void SetMaxCharge(Entity<BatteryComponent?> ent, float value)
{
if (!Resolve(ent, ref ent.Comp))
return;
ent.Comp.LastUpdate = curTime;
Dirty(ent);
- var ev = new PredictedBatteryChargeChangedEvent(ent.Comp.LastCharge, ent.Comp.LastCharge - oldCharge, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
+ 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.
/// Updates the battery's charge state and sends an event if it changed.
/// </summary>
[PublicAPI]
- public void UpdateState(Entity<PredictedBatteryComponent?> ent)
+ public void UpdateState(Entity<BatteryComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp))
return;
var charge = GetCharge(ent);
- if (charge == ent.Comp.MaxCharge)
+ if (charge >= ent.Comp.MaxCharge)
newState = BatteryState.Full;
else if (charge == 0f)
newState = BatteryState.Empty;
ent.Comp.State = newState;
Dirty(ent);
- var changedEv = new PredictedBatteryStateChangedEvent(oldState, newState);
+ var changedEv = new BatteryStateChangedEvent(oldState, newState);
RaiseLocalEvent(ent, ref changedEv);
}
/// Gets the battery's current charge.
/// </summary>
[PublicAPI]
- public float GetCharge(Entity<PredictedBatteryComponent?> ent)
+ public float GetCharge(Entity<BatteryComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
/// Gets the fraction of charge remaining (0–1).
/// </summary>
[PublicAPI]
- public float GetChargeLevel(Entity<PredictedBatteryComponent?> ent)
+ public float GetChargeLevel(Entity<BatteryComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
/// Gets number of remaining uses for the given charge cost.
/// </summary>
[PublicAPI]
- public int GetRemainingUses(Entity<PredictedBatteryComponent?> ent, float cost)
+ public int GetRemainingUses(Entity<BatteryComponent?> ent, float cost)
{
if (cost <= 0)
return 0;
/// Gets number of maximum uses at full charge for the given charge cost.
/// </summary>
[PublicAPI]
- public int GetMaxUses(Entity<PredictedBatteryComponent?> ent, float cost)
+ public int GetMaxUses(Entity<BatteryComponent?> ent, float cost)
{
if (cost <= 0)
return 0;
return (int)(ent.Comp.MaxCharge / cost);
}
-
/// <summary>
/// Refreshes the battery's current charge rate by raising a <see cref="RefreshChargeRateEvent"/>.
+ /// Subscribe to that event to add to or subtract from the total charge rate.
/// </summary>
/// <returns>The new charge rate.</returns>
[PublicAPI]
- public float RefreshChargeRate(Entity<PredictedBatteryComponent?> ent)
+ public float RefreshChargeRate(Entity<BatteryComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return 0f;
Dirty(ent);
// Inform other systems about the new rate;
- var changedEv = new PredictedBatteryChargeChangedEvent(ent.Comp.LastCharge, 0f, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
+ var changedEv = new ChargeChangedEvent(ent.Comp.LastCharge, 0f, ent.Comp.ChargeRate, ent.Comp.MaxCharge);
RaiseLocalEvent(ent, ref changedEv);
return refreshEv.NewChargeRate;
/// Uses the cooldown time given in the component.
/// </summary>
[PublicAPI]
- public void TrySetChargeCooldown(Entity<PredictedBatterySelfRechargerComponent?> ent)
+ public void TrySetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp, false))
return;
/// Puts the entity's self recharge on cooldown for the specified time.
/// </summary>
[PublicAPI]
- public void SetChargeCooldown(Entity<PredictedBatterySelfRechargerComponent?> ent, TimeSpan cooldown)
+ public void SetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent, TimeSpan cooldown)
{
if (!Resolve(ent, ref ent.Comp))
return;
/// Returns whether the battery is full.
/// </summary>
[PublicAPI]
- public bool IsFull(Entity<PredictedBatteryComponent?> ent)
+ public bool IsFull(Entity<BatteryComponent?> ent)
{
if (!Resolve(ent, ref ent.Comp))
return false;
+using Content.Shared.Cargo;
using Content.Shared.Emp;
+using Content.Shared.Examine;
using Content.Shared.Power.Components;
-using JetBrains.Annotations;
+using Content.Shared.Rejuvenate;
+using Robust.Shared.Timing;
namespace Content.Shared.Power.EntitySystems;
-public abstract class SharedBatterySystem : EntitySystem
+/// <summary>
+/// Responsible for <see cref="BatteryComponent"/>.
+/// </summary>
+public abstract partial class SharedBatterySystem : EntitySystem
{
+ [Dependency] private readonly IGameTiming _timing = default!;
+ [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
+
public override void Initialize()
{
base.Initialize();
+ SubscribeLocalEvent<BatteryComponent, ComponentStartup>(OnStartup);
+ SubscribeLocalEvent<BatteryComponent, MapInitEvent>(OnMapInit);
SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
+ SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnRejuvenate);
+ SubscribeLocalEvent<BatteryComponent, ExaminedEvent>(OnExamine);
+ SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
+ SubscribeLocalEvent<BatteryComponent, ChangeChargeEvent>(OnChangeCharge);
+ SubscribeLocalEvent<BatteryComponent, GetChargeEvent>(OnGetCharge);
+ SubscribeLocalEvent<BatterySelfRechargerComponent, RefreshChargeRateEvent>(OnRefreshChargeRate);
+ SubscribeLocalEvent<BatterySelfRechargerComponent, ComponentStartup>(OnRechargerStartup);
+ SubscribeLocalEvent<BatterySelfRechargerComponent, ComponentRemove>(OnRechargerRemove);
+ SubscribeLocalEvent<BatteryVisualsComponent, ChargeChangedEvent>(OnVisualsChargeChanged);
+ SubscribeLocalEvent<BatteryVisualsComponent, BatteryStateChangedEvent>(OnVisualsStateChanged);
+ }
+
+ protected virtual void OnStartup(Entity<BatteryComponent> ent, ref ComponentStartup args)
+ {
+ // In case a recharging component was added before the battery component itself.
+ // Doing this only on map init is not enough because the charge rate is not a datafield, but cached, so it would get lost when reloading the game.
+ // If we would make it a datafield then the integration tests would complain about modifying it before map init.
+ // Don't do this in case the battery is not net synced, because then the client would raise events overwriting the networked server state on spawn.
+ if (ent.Comp.NetSyncEnabled)
+ RefreshChargeRate(ent.AsNullable());
+ }
+
+ private void OnMapInit(Entity<BatteryComponent> ent, ref MapInitEvent args)
+ {
+ SetCharge(ent.AsNullable(), ent.Comp.StartingCharge);
+ RefreshChargeRate(ent.AsNullable());
+ }
+
+ private void OnRejuvenate(Entity<BatteryComponent> ent, ref RejuvenateEvent args)
+ {
+ SetCharge(ent.AsNullable(), ent.Comp.MaxCharge);
}
private void OnEmpPulse(Entity<BatteryComponent> ent, ref EmpPulseEvent args)
{
args.Affected = true;
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(ent.Owner);
}
- /// <summary>
- /// 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.
- /// </summary>
- /// <returns>The actually changed amount.</returns>
- [PublicAPI]
- public virtual float ChangeCharge(Entity<BatteryComponent?> ent, float amount)
+ private void OnExamine(Entity<BatteryComponent> ent, ref ExaminedEvent args)
{
- return 0f;
+ if (!args.IsInDetailsRange)
+ return;
+
+ if (!HasComp<ExaminableBatteryComponent>(ent))
+ return;
+
+ var chargePercentRounded = 0;
+ var currentCharge = GetCharge(ent.AsNullable());
+ if (ent.Comp.MaxCharge != 0)
+ chargePercentRounded = (int)(100 * currentCharge / ent.Comp.MaxCharge);
+ args.PushMarkup(
+ Loc.GetString(
+ "examinable-battery-component-examine-detail",
+ ("percent", chargePercentRounded),
+ ("markupPercentColor", "green")
+ )
+ );
}
/// <summary>
- /// Removes the given amount of charge from the battery
- /// and resets the self-recharge cooldown if it exists.
+ /// Gets the price for the power contained in an entity's battery.
/// </summary>
- /// <returns>The actually changed amount.</returns>
- [PublicAPI]
- public virtual float UseCharge(Entity<BatteryComponent?> ent, float amount)
+ private void CalculateBatteryPrice(Entity<BatteryComponent> ent, ref PriceCalculationEvent args)
{
- return 0f;
+ args.Price += GetCharge(ent.AsNullable()) * ent.Comp.PricePerJoule;
}
- /// <summary>
- /// 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.
- /// </summary>
- /// <returns>If the full amount was able to be removed.</returns>
- [PublicAPI]
- public virtual bool TryUseCharge(Entity<BatteryComponent?> ent, float amount)
+ private void OnChangeCharge(Entity<BatteryComponent> ent, ref ChangeChargeEvent args)
{
- return false;
+ if (args.ResidualValue == 0)
+ return;
+
+ args.ResidualValue -= ChangeCharge(ent.AsNullable(), args.ResidualValue);
}
- /// <summary>
- /// Sets the battery's charge.
- /// </summary>
- [PublicAPI]
- public virtual void SetCharge(Entity<BatteryComponent?> ent, float value) { }
+ private void OnGetCharge(Entity<BatteryComponent> ent, ref GetChargeEvent args)
+ {
+ args.CurrentCharge += GetCharge(ent.AsNullable());
+ args.MaxCharge += ent.Comp.MaxCharge;
+ }
- /// <summary>
- /// Sets the battery's maximum charge.
- /// </summary>
- [PublicAPI]
- public virtual void SetMaxCharge(Entity<BatteryComponent?> ent, float value) { }
+ private void OnRefreshChargeRate(Entity<BatterySelfRechargerComponent> ent, ref RefreshChargeRateEvent args)
+ {
+ if (_timing.CurTime < ent.Comp.NextAutoRecharge)
+ return; // Still on cooldown
- /// <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>
- [PublicAPI]
- public virtual void TrySetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent) { }
+ args.NewChargeRate += ent.Comp.AutoRechargeRate;
+ }
- /// <summary>
- /// Puts the entity's self recharge on cooldown for the specified time.
- /// </summary>
- [PublicAPI]
- public virtual void SetChargeCooldown(Entity<BatterySelfRechargerComponent?> ent, TimeSpan cooldown) { }
+ public override void Update(float frameTime)
+ {
+ var curTime = _timing.CurTime;
+
+ // Update self-recharging cooldowns.
+ var rechargerQuery = EntityQueryEnumerator<BatterySelfRechargerComponent, BatteryComponent>();
+ while (rechargerQuery.MoveNext(out var uid, out var recharger, out var battery))
+ {
+ if (recharger.NextAutoRecharge == null || curTime < recharger.NextAutoRecharge)
+ continue;
+
+ recharger.NextAutoRecharge = null; // Don't refresh every tick.
+ Dirty(uid, recharger);
+ RefreshChargeRate((uid, battery)); // Cooldown is over, apply the new recharge rate.
+ }
+
+ // Raise events when the battery is full or empty so that other systems can react and visuals can get updated.
+ // This is not doing that many calculations, it only has to get the current charge and only raises events if something did change.
+ // If this turns out to be too expensive and shows up on grafana consider updating it less often.
+ var batteryQuery = EntityQueryEnumerator<BatteryComponent>();
+ while (batteryQuery.MoveNext(out var uid, out var battery))
+ {
+ if (battery.ChargeRate == 0f)
+ continue; // No need to check if it's constant.
+
+ UpdateState((uid, battery));
+ }
+ }
+
+ private void OnRechargerStartup(Entity<BatterySelfRechargerComponent> ent, ref ComponentStartup args)
+ {
+ // In case this component is added after the battery component.
+ // Don't do this in case the battery is not net synced, because then the client would raise events overwriting the networked server state on spawn.
+ if (ent.Comp.NetSyncEnabled)
+ RefreshChargeRate(ent.Owner);
+ }
+
+ private void OnRechargerRemove(Entity<BatterySelfRechargerComponent> ent, ref ComponentRemove args)
+ {
+ // We use ComponentRemove to make sure this component no longer subscribes to the refresh event.
+ RefreshChargeRate(ent.Owner);
+ }
+
+ private void OnVisualsChargeChanged(Entity<BatteryVisualsComponent> ent, ref ChargeChangedEvent args)
+ {
+ // Update the appearance data for the charge rate.
+ // We have a separate component for this to not duplicate the networking cost unless we actually use it.
+ var state = BatteryChargingState.Constant;
+ if (args.CurrentChargeRate > 0f)
+ state = BatteryChargingState.Charging;
+ else if (args.CurrentChargeRate < 0f)
+ state = BatteryChargingState.Decharging;
+
+ _appearance.SetData(ent.Owner, BatteryVisuals.Charging, state);
+ }
+
+ private void OnVisualsStateChanged(Entity<BatteryVisualsComponent> ent, ref BatteryStateChangedEvent args)
+ {
+ // Update the appearance data for the fill level (empty, full, in-between).
+ // We have a separate component for this to not duplicate the networking cost unless we actually use it.
+ _appearance.SetData(ent.Owner, BatteryVisuals.State, args.NewState);
+ }
}
/// <summary>
/// This component enables power-cell related interactions (e.g. EntityWhitelists, cell sizes, examine, rigging).
-/// The actual power functionality is provided by the <see cref="PredictedBatteryComponent"/>.
+/// The actual power functionality is provided by the <see cref="BatteryComponent"/>.
/// </summary>
[RegisterComponent, NetworkedComponent]
public sealed partial class PowerCellComponent : Component;
[PublicAPI]
public bool TryGetBatteryFromSlot(
Entity<PowerCellSlotComponent?> ent,
- [NotNullWhen(true)] out Entity<PredictedBatteryComponent>? battery)
+ [NotNullWhen(true)] out Entity<BatteryComponent>? battery)
{
if (!Resolve(ent, ref ent.Comp, false))
{
return false;
}
- if (!TryComp<PredictedBatteryComponent>(slot.Item, out var batteryComp))
+ if (!TryComp<BatteryComponent>(slot.Item, out var batteryComp))
{
battery = null;
return false;
/// <summary>
/// First tries to get a battery from the entity's power cell slot.
- /// If that fails check if the entity itself is a battery with <see cref="PredictedBatteryComponent"/>.
+ /// If that fails check if the entity itself is a battery with <see cref="BatteryComponent"/>.
/// </summary>
[PublicAPI]
- public bool TryGetBatteryFromSlotOrEntity(Entity<PowerCellSlotComponent?> ent, [NotNullWhen(true)] out Entity<PredictedBatteryComponent>? battery)
+ public bool TryGetBatteryFromSlotOrEntity(Entity<PowerCellSlotComponent?> ent, [NotNullWhen(true)] out Entity<BatteryComponent>? battery)
{
if (TryGetBatteryFromSlot(ent, out battery))
return true;
- if (TryComp<PredictedBatteryComponent>(ent, out var batteryComp))
+ if (TryComp<BatteryComponent>(ent, out var batteryComp))
{
battery = (ent.Owner, batteryComp);
return true;
}
/// <summary>
- /// First checks if the entity itself is a battery with <see cref="PredictedBatteryComponent"/>.
+ /// First checks if the entity itself is a battery with <see cref="BatteryComponent"/>.
/// If that fails it will try to get a battery from the entity's power cell slot instead.
/// </summary>
[PublicAPI]
- public bool TryGetBatteryFromEntityOrSlot(Entity<PowerCellSlotComponent?> ent, [NotNullWhen(true)] out Entity<PredictedBatteryComponent>? battery)
+ public bool TryGetBatteryFromEntityOrSlot(Entity<PowerCellSlotComponent?> ent, [NotNullWhen(true)] out Entity<BatteryComponent>? battery)
{
- if (TryComp<PredictedBatteryComponent>(ent, out var batteryComp))
+ if (TryComp<BatteryComponent>(ent, out var batteryComp))
{
battery = (ent.Owner, batteryComp);
return true;
SubscribeLocalEvent<PowerCellSlotComponent, ChangeChargeEvent>(RelayToCell);
SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(RelayToCellSlot); // Prevent the ninja from EMPing its own battery
- SubscribeLocalEvent<PowerCellComponent, PredictedBatteryChargeChangedEvent>(RelayToCellSlot);
- SubscribeLocalEvent<PowerCellComponent, PredictedBatteryStateChangedEvent>(RelayToCellSlot); // For shutting down devices if the battery is empty
+ SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(RelayToCellSlot);
+ SubscribeLocalEvent<PowerCellComponent, BatteryStateChangedEvent>(RelayToCellSlot); // For shutting down devices if the battery is empty
SubscribeLocalEvent<PowerCellComponent, RefreshChargeRateEvent>(RelayToCellSlot); // Allow devices to charge/drain inserted batteries
}
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ItemSlotsSystem _itemSlots = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
- [Dependency] private readonly PredictedBatterySystem _battery = default!;
+ [Dependency] private readonly SharedBatterySystem _battery = default!;
[Dependency] private readonly SharedPopupSystem _popup = default!;
public override void Initialize()
SubscribeLocalEvent<PowerCellSlotComponent, EntInsertedIntoContainerMessage>(OnCellSlotInserted);
SubscribeLocalEvent<PowerCellSlotComponent, EntRemovedFromContainerMessage>(OnCellSlotRemoved);
SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
- SubscribeLocalEvent<PowerCellSlotComponent, PredictedBatteryStateChangedEvent>(OnCellSlotStateChanged);
+ SubscribeLocalEvent<PowerCellSlotComponent, BatteryStateChangedEvent>(OnCellSlotStateChanged);
SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
_battery.RefreshChargeRate(args.Entity);
// Only update the visuals if we actually use them.
- if (!HasComp<PredictedBatteryVisualsComponent>(ent))
+ if (!HasComp<BatteryVisualsComponent>(ent))
return;
// Set the data to that of the power cell
_battery.RefreshChargeRate(args.Entity);
// Only update the visuals if we actually use them.
- if (!HasComp<PredictedBatteryVisualsComponent>(ent))
+ if (!HasComp<BatteryVisualsComponent>(ent))
return;
// Set the appearance to empty.
}
- private void OnCellSlotStateChanged(Entity<PowerCellSlotComponent> ent, ref PredictedBatteryStateChangedEvent args)
+ private void OnCellSlotStateChanged(Entity<PowerCellSlotComponent> ent, ref BatteryStateChangedEvent args)
{
if (args.NewState != BatteryState.Empty)
return;
private void OnCellExamined(Entity<PowerCellComponent> ent, ref ExaminedEvent args)
{
- if (TryComp<PredictedBatteryComponent>(ent, out var battery))
+ if (TryComp<BatteryComponent>(ent, out var battery))
OnBatteryExamined((ent.Owner, battery), ref args);
}
- private void OnBatteryExamined(Entity<PredictedBatteryComponent> ent, ref ExaminedEvent args)
+ private void OnBatteryExamined(Entity<BatteryComponent> ent, ref ExaminedEvent args)
{
var chargePercent = _battery.GetChargeLevel(ent.AsNullable()) * 100;
args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{chargePercent:F0}")));
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, ItemToggledEvent>(OnToggled);
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIOpenedEvent>(OnBatteryOpened);
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BoundUIClosedEvent>(OnBatteryClosed);
- SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, PredictedBatteryStateChangedEvent>(OnBatteryStateChanged);
+ SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, BatteryStateChangedEvent>(OnBatteryStateChanged);
SubscribeLocalEvent<ActivatableUIRequiresPowerCellComponent, ActivatableUIOpenAttemptEvent>(OnBatteryOpenAttempt);
}
_toggle.TryDeactivate(uid);
}
- private void OnBatteryStateChanged(Entity<ActivatableUIRequiresPowerCellComponent> ent, ref PredictedBatteryStateChangedEvent args)
+ private void OnBatteryStateChanged(Entity<ActivatableUIRequiresPowerCellComponent> ent, ref BatteryStateChangedEvent args)
{
// Deactivate when empty.
if (args.NewState != BatteryState.Empty)
/// <summary>
/// Ammo provider that uses electric charge from a battery to provide ammunition to a weapon.
-/// This works with both <see cref="BatteryComponent"/> and <see cref="PredictedBatteryComponent"/>
+/// Works in combination with <see cref="BatteryComponent"/>.
/// </summary>
[RegisterComponent, NetworkedComponent]
[AutoGenerateComponentState(raiseAfterAutoHandleState: true), AutoGenerateComponentPause]
SubscribeLocalEvent<BatteryAmmoProviderComponent, ExaminedEvent>(OnBatteryExamine);
SubscribeLocalEvent<BatteryAmmoProviderComponent, DamageExamineEvent>(OnBatteryDamageExamine);
SubscribeLocalEvent<BatteryAmmoProviderComponent, PowerCellChangedEvent>(OnPowerCellChanged);
- SubscribeLocalEvent<BatteryAmmoProviderComponent, PredictedBatteryChargeChangedEvent>(OnPredictedChargeChanged);
SubscribeLocalEvent<BatteryAmmoProviderComponent, ChargeChangedEvent>(OnChargeChanged);
}
/// </summary>
public void TakeCharge(Entity<BatteryAmmoProviderComponent> ent, int shots = 1)
{
- // Take charge from either the BatteryComponent, PredictedBatteryComponent or PowerCellSlotComponent.
+ // Take charge from either the BatteryComponent or PowerCellSlotComponent.
var ev = new ChangeChargeEvent(-ent.Comp.FireCost * shots);
RaiseLocalEvent(ent, ref ev);
- // UpdateShots is already called by the resulting PredictedBatteryChargeChangedEvent or ChargeChangedEvent
+ // UpdateShots is already called by the resulting ChargeChangedEvent
}
private (EntityUid? Entity, IShootable) GetShootable(BatteryAmmoProviderComponent component, EntityCoordinates coordinates)
UpdateShots(ent);
}
- // For predicted batteries.
// If the entity is has a PowerCellSlotComponent then this event is relayed from the power cell to the slot entity.
- private void OnPredictedChargeChanged(Entity<BatteryAmmoProviderComponent> ent, ref PredictedBatteryChargeChangedEvent args)
+ private void OnChargeChanged(Entity<BatteryAmmoProviderComponent> ent, ref ChargeChangedEvent args)
{
// Update the visuals and charge counter UI.
UpdateShots(ent);
UpdateNextUpdate(ent, args.CurrentCharge, args.MaxCharge, args.CurrentChargeRate);
}
- // For unpredicted batteries.
- private void OnChargeChanged(Entity<BatteryAmmoProviderComponent> ent, ref ChargeChangedEvent args)
- {
- // Update the visuals and charge counter UI.
- UpdateShots(ent);
- // No need to queue an update here since unpredicted batteries already update periodically as they charge/discharge.
- }
-
private void UpdateNextUpdate(Entity<BatteryAmmoProviderComponent> ent, float currentCharge, float maxCharge, float currentChargeRate)
{
// Don't queue any updates if charge is constant.
// Shots are only chached, not a DataField, so we need to refresh this when the game is loaded.
private void OnBatteryStartup(Entity<BatteryAmmoProviderComponent> ent, ref ComponentStartup args)
{
+ if (_netManager.IsClient && !IsClientSide(ent.Owner))
+ return; // Don't overwrite the server state in cases where the battery is not predicted.
+
UpdateShots(ent);
}
/// <summary>
/// Gets the current and maximum amount of shots from this entity's battery.
- /// This works for BatteryComponent, PredictedBatteryComponent and PowercellSlotComponent.
+ /// This works for BatteryComponent and PowercellSlotComponent.
/// </summary>
public (int, int) GetShots(Entity<BatteryAmmoProviderComponent> ent)
{
}
/// <summary>
- /// Update loop for refreshing the ammo counter for charging/draining predicted batteries.
- /// This is not needed for unpredicted batteries since those already raise ChargeChangedEvent periodically.
+ /// Update loop for refreshing the ammo counter for charging/draining batteries.
/// </summary>
private void UpdateBattery(float frameTime)
{
whitelist:
components:
- Battery
- - PredictedBattery
- type: cargoBounty
id: BountyLaserGun
damage: 35
sound: /Audio/Weapons/egloves.ogg
- type: LandAtCursor # it deals stamina damage when thrown
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: GuideHelp
startValue: 0.1
endValue: 2.0
isLooped: true
- - type: PredictedBattery
+ - type: Battery
maxCharge: 600 #lights drain 3/s but recharge of 2 makes this 1/s. Therefore 600 is 10 minutes of light.
startingCharge: 600
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
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
startValue: 0.1
endValue: 2.0
isLooped: true
- - type: PredictedBattery
+ - type: Battery
maxCharge: 600
startingCharge: 600
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 2
- type: Item
size: Normal
- type: BatteryAmmoProvider
proto: DebugLaser
fireCost: 1
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 1000
- type: entity
- type: BatteryAmmoProvider
proto: RedLaser
fireCost: 62.5
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 40
- type: Gun
fireRate: 0.75
- type: BatteryAmmoProvider
proto: WatcherBolt
fireCost: 50
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 50
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: Gun
- type: BatteryAmmoProvider
proto: RedLaser
fireCost: 140
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 50
- type: Gun
fireRate: 0.3
- type: BatteryAmmoProvider
proto: RedLightLaser
fireCost: 50
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 50
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: Gun
powerLoad: 500
- type: ExtensionCableReceiver
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 300000
startingCharge: 300000
- type: ApcPowerReceiverBattery
components:
- type: Item
storedRotation: -90
- - type: PredictedBattery
+ - type: Battery
pricePerJoule: 0.15
- type: PowerCell
- type: Explosive
- PowerCell
- type: Riggable
- type: Appearance
- - type: PredictedBatteryVisuals
+ - type: BatteryVisuals
- type: GenericVisualizer
visuals:
enum.BatteryVisuals.State:
- type: Sprite
layers:
- state: potato
- - type: PredictedBattery
+ - type: Battery
maxCharge: 70
startingCharge: 70
- type: Tag
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 360
startingCharge: 360
- type: Tag
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
maxCharge: 360
startingCharge: 0
name: small-capacity nuclear power cell
description: A self rechargeable power cell, designed for fast recharge rate at the expense of capacity.
components:
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 36 # 10 seconds to recharge
autoRechargePauseTime: 30
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 720
startingCharge: 720
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
maxCharge: 720
startingCharge: 0
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1080
startingCharge: 1080
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1080
startingCharge: 0
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1800
startingCharge: 1800
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1800
startingCharge: 0
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 720
startingCharge: 720
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 12 # takes 1 minute to charge itself back to full
- type: entity
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
startingCharge: 0
- type: entity
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1200
startingCharge: 1200
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 40
# Power cage (big heavy power cell for big devices)
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1400
startingCharge: 1400
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 2700
startingCharge: 2700
- map: [ "enum.PowerCellVisualLayers.Unshaded" ]
state: o2
shader: unshaded
- - type: PredictedBattery
+ - type: Battery
maxCharge: 6200
startingCharge: 6200
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1400
startingCharge: 0
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
startingCharge: 0
- type: entity
state: o2
shader: unshaded
visible: false
- - type: PredictedBattery
+ - type: Battery
startingCharge: 0
acts: [ "Destruction" ]
- type: PowerSink
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 250000000
pricePerJoule: 0.000009
- type: ExaminableBattery
parent: [ HandheldHealthAnalyzerUnpowered, PowerCellSlotSmallItem]
suffix: ""
components:
- - type: PredictedBatteryVisuals
+ - type: BatteryVisuals
- type: PowerCellDraw
drawRate: 1.2 #Calculated for 5 minutes on a small cell
- type: ToggleCellDraw
maxRange: 256
followEntity: true
- type: Appearance
- - type: PredictedBatteryVisuals
+ - type: BatteryVisuals
- type: GenericVisualizer
visuals:
enum.BatteryVisuals.State:
- SemiAuto
soundGunshot:
path: /Audio/Weapons/Guns/Gunshots/laser.ogg
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: StaticPrice
- type: BatteryAmmoProvider
proto: RedLaser
fireCost: 62.5
- - type: PredictedBattery
+ - type: Battery
maxCharge: 500
startingCharge: 500
- type: Tag
- type: BatteryAmmoProvider
proto: Pulse
fireCost: 200
- - type: PredictedBattery
+ - type: Battery
maxCharge: 2000
startingCharge: 2000
- type: Tag
- type: BatteryAmmoProvider
proto: Pulse
fireCost: 200
- - type: PredictedBattery
+ - type: Battery
maxCharge: 5000
startingCharge: 5000
- type: BatteryAmmoProvider
proto: Pulse
fireCost: 100
- - type: PredictedBattery
+ - type: Battery
maxCharge: 40000
startingCharge: 40000
- type: BatteryAmmoProvider
proto: AntiParticlesProjectile
fireCost: 500
- - type: PredictedBattery
+ - type: Battery
maxCharge: 10000
startingCharge: 10000
- type: BatteryAmmoProvider
proto: RedMediumLaser
fireCost: 100
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 40
- type: MagazineVisuals
magState: mag
- type: BatteryAmmoProvider
proto: RedMediumLaser
fireCost: 100
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 30
- type: MagazineVisuals
magState: mag
- type: BatteryAmmoProvider
proto: RedMediumLaser
fireCost: 100
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 40
- type: StaticPrice
price: 750
sprite: Objects/Weapons/Guns/Battery/inhands_64x.rsi
heldPrefix: energy
- type: GunRequiresWield #remove when inaccuracy on spreads is fixed
- - type: PredictedBattery
+ - type: Battery
maxCharge: 480
startingCharge: 480
- proto: BulletDisabler
fireCost: 62.5
pacifismAllowedMode: true
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 48
autoRechargePauseTime: 10
fireCost: 100
- proto: BoltTempgunHot
fireCost: 100
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: StaticPrice
proto: BulletEnergyTurretLaser
fireCost: 100
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 2000
startingCharge: 2000
- type: ApcPowerReceiverBattery
- type: BatteryAmmoProvider
proto: FoodPieBananaCream
fireCost: 30
- - type: PredictedBattery
+ - type: Battery
maxCharge: 90
startingCharge: 90
- - type: PredictedBatterySelfRecharger
+ - type: BatterySelfRecharger
autoRechargeRate: 1
- type: AmmoCounter
- type: Item
damage: 35
sound: /Audio/Weapons/egloves.ogg
- type: LandAtCursor # it deals stamina damage when thrown
- - type: PredictedBattery
+ - type: Battery
maxCharge: 360
startingCharge: 360
- type: UseDelay
damage: 35
sound: /Audio/Weapons/egloves.ogg
- type: LandAtCursor # it deals stamina damage when thrown
- - type: PredictedBattery
+ - type: Battery
maxCharge: 1000
startingCharge: 1000
- type: UseDelay
- type: ApcPowerReceiver
- type: ExtensionCableReceiver
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 30000
startingCharge: 0
- type: EmergencyLight
sprite: Structures/Power/Generation/Tesla/coil.rsi
state: coil
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 5000000
startingCharge: 0
- type: BatteryDischarger
- type: Appearance
- type: ApcVisuals
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
maxCharge: 50000
startingCharge: 0
- type: ExaminableBattery
sprite: Structures/Power/power.rsi
state: provider
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
- type: NodeContainer
nodes:
input:
sprite: Structures/Power/power.rsi
state: provider
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
- type: NodeContainer
nodes:
input:
- type: Smes
- type: Appearance
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
startingCharge: 0
maxCharge: 8000000
- type: ExaminableBattery
components:
# Core power behavior
- type: Battery
+ # Very important:
+ # Disable networking to prevent the battery getting continuously dirtied every tick as it interacts with the power network.
+ # This can not be done by changing the charge rate as the power supply ramps up over time.
+ # This disables prediction for this battery.
+ netsync: false
- type: ExaminableBattery
- type: NodeContainer
examinable: true