BatterySystem.cs 8.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225
  1. using Content.Server.Cargo.Systems;
  2. using Content.Server.Emp;
  3. using Content.Server.Power.Components;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Rejuvenate;
  6. using Content.Shared.Timing;
  7. using JetBrains.Annotations;
  8. using Robust.Shared.Utility;
  9. using Robust.Shared.Timing;
  10. namespace Content.Server.Power.EntitySystems
  11. {
  12. [UsedImplicitly]
  13. public sealed class BatterySystem : EntitySystem
  14. {
  15. [Dependency] protected readonly IGameTiming Timing = default!;
  16. public override void Initialize()
  17. {
  18. base.Initialize();
  19. SubscribeLocalEvent<ExaminableBatteryComponent, ExaminedEvent>(OnExamine);
  20. SubscribeLocalEvent<PowerNetworkBatteryComponent, RejuvenateEvent>(OnNetBatteryRejuvenate);
  21. SubscribeLocalEvent<BatteryComponent, RejuvenateEvent>(OnBatteryRejuvenate);
  22. SubscribeLocalEvent<BatteryComponent, PriceCalculationEvent>(CalculateBatteryPrice);
  23. SubscribeLocalEvent<BatteryComponent, EmpPulseEvent>(OnEmpPulse);
  24. SubscribeLocalEvent<NetworkBatteryPreSync>(PreSync);
  25. SubscribeLocalEvent<NetworkBatteryPostSync>(PostSync);
  26. }
  27. private void OnNetBatteryRejuvenate(EntityUid uid, PowerNetworkBatteryComponent component, RejuvenateEvent args)
  28. {
  29. component.NetworkBattery.CurrentStorage = component.NetworkBattery.Capacity;
  30. }
  31. private void OnBatteryRejuvenate(EntityUid uid, BatteryComponent component, RejuvenateEvent args)
  32. {
  33. SetCharge(uid, component.MaxCharge, component);
  34. }
  35. private void OnExamine(EntityUid uid, ExaminableBatteryComponent component, ExaminedEvent args)
  36. {
  37. if (!TryComp<BatteryComponent>(uid, out var batteryComponent))
  38. return;
  39. if (args.IsInDetailsRange)
  40. {
  41. var effectiveMax = batteryComponent.MaxCharge;
  42. if (effectiveMax == 0)
  43. effectiveMax = 1;
  44. var chargeFraction = batteryComponent.CurrentCharge / effectiveMax;
  45. var chargePercentRounded = (int) (chargeFraction * 100);
  46. args.PushMarkup(
  47. Loc.GetString(
  48. "examinable-battery-component-examine-detail",
  49. ("percent", chargePercentRounded),
  50. ("markupPercentColor", "green")
  51. )
  52. );
  53. }
  54. }
  55. private void PreSync(NetworkBatteryPreSync ev)
  56. {
  57. // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
  58. var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
  59. while (enumerator.MoveNext(out var netBat, out var bat))
  60. {
  61. DebugTools.Assert(bat.CurrentCharge <= bat.MaxCharge && bat.CurrentCharge >= 0);
  62. netBat.NetworkBattery.Capacity = bat.MaxCharge;
  63. netBat.NetworkBattery.CurrentStorage = bat.CurrentCharge;
  64. }
  65. }
  66. private void PostSync(NetworkBatteryPostSync ev)
  67. {
  68. // Ignoring entity pausing. If the entity was paused, neither component's data should have been changed.
  69. var enumerator = AllEntityQuery<PowerNetworkBatteryComponent, BatteryComponent>();
  70. while (enumerator.MoveNext(out var uid, out var netBat, out var bat))
  71. {
  72. SetCharge(uid, netBat.NetworkBattery.CurrentStorage, bat);
  73. }
  74. }
  75. public override void Update(float frameTime)
  76. {
  77. var query = EntityQueryEnumerator<BatterySelfRechargerComponent, BatteryComponent>();
  78. while (query.MoveNext(out var uid, out var comp, out var batt))
  79. {
  80. if (!comp.AutoRecharge || IsFull(uid, batt))
  81. continue;
  82. if (comp.AutoRechargePause)
  83. {
  84. if (comp.NextAutoRecharge > Timing.CurTime)
  85. continue;
  86. }
  87. SetCharge(uid, batt.CurrentCharge + comp.AutoRechargeRate * frameTime, batt);
  88. }
  89. }
  90. /// <summary>
  91. /// Gets the price for the power contained in an entity's battery.
  92. /// </summary>
  93. private void CalculateBatteryPrice(EntityUid uid, BatteryComponent component, ref PriceCalculationEvent args)
  94. {
  95. args.Price += component.CurrentCharge * component.PricePerJoule;
  96. }
  97. private void OnEmpPulse(EntityUid uid, BatteryComponent component, ref EmpPulseEvent args)
  98. {
  99. args.Affected = true;
  100. UseCharge(uid, args.EnergyConsumption, component);
  101. // Apply a cooldown to the entity's self recharge if needed to avoid it immediately self recharging after an EMP.
  102. TrySetChargeCooldown(uid);
  103. }
  104. public float UseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
  105. {
  106. if (value <= 0 || !Resolve(uid, ref battery) || battery.CurrentCharge == 0)
  107. return 0;
  108. var newValue = Math.Clamp(0, battery.CurrentCharge - value, battery.MaxCharge);
  109. var delta = newValue - battery.CurrentCharge;
  110. battery.CurrentCharge = newValue;
  111. // Apply a cooldown to the entity's self recharge if needed.
  112. TrySetChargeCooldown(uid);
  113. var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
  114. RaiseLocalEvent(uid, ref ev);
  115. return delta;
  116. }
  117. public void SetMaxCharge(EntityUid uid, float value, BatteryComponent? battery = null)
  118. {
  119. if (!Resolve(uid, ref battery))
  120. return;
  121. var old = battery.MaxCharge;
  122. battery.MaxCharge = Math.Max(value, 0);
  123. battery.CurrentCharge = Math.Min(battery.CurrentCharge, battery.MaxCharge);
  124. if (MathHelper.CloseTo(battery.MaxCharge, old))
  125. return;
  126. var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
  127. RaiseLocalEvent(uid, ref ev);
  128. }
  129. public void SetCharge(EntityUid uid, float value, BatteryComponent? battery = null)
  130. {
  131. if (!Resolve(uid, ref battery))
  132. return;
  133. var old = battery.CurrentCharge;
  134. battery.CurrentCharge = MathHelper.Clamp(value, 0, battery.MaxCharge);
  135. if (MathHelper.CloseTo(battery.CurrentCharge, old) &&
  136. !(old != battery.CurrentCharge && battery.CurrentCharge == battery.MaxCharge))
  137. {
  138. return;
  139. }
  140. var ev = new ChargeChangedEvent(battery.CurrentCharge, battery.MaxCharge);
  141. RaiseLocalEvent(uid, ref ev);
  142. }
  143. /// <summary>
  144. /// Checks if the entity has a self recharge and puts it on cooldown if applicable.
  145. /// </summary>
  146. public void TrySetChargeCooldown(EntityUid uid, float value = -1)
  147. {
  148. if (!TryComp<BatterySelfRechargerComponent>(uid, out var batteryself))
  149. return;
  150. if (!batteryself.AutoRechargePause)
  151. return;
  152. // If no answer or a negative is given for value, use the default from AutoRechargePauseTime.
  153. if (value < 0)
  154. value = batteryself.AutoRechargePauseTime;
  155. if (Timing.CurTime + TimeSpan.FromSeconds(value) <= batteryself.NextAutoRecharge)
  156. return;
  157. SetChargeCooldown(uid, batteryself.AutoRechargePauseTime, batteryself);
  158. }
  159. /// <summary>
  160. /// Puts the entity's self recharge on cooldown for the specified time.
  161. /// </summary>
  162. public void SetChargeCooldown(EntityUid uid, float value, BatterySelfRechargerComponent? batteryself = null)
  163. {
  164. if (!Resolve(uid, ref batteryself))
  165. return;
  166. if (value >= 0)
  167. batteryself.NextAutoRecharge = Timing.CurTime + TimeSpan.FromSeconds(value);
  168. else
  169. batteryself.NextAutoRecharge = Timing.CurTime;
  170. }
  171. /// <summary>
  172. /// If sufficient charge is available on the battery, use it. Otherwise, don't.
  173. /// </summary>
  174. public bool TryUseCharge(EntityUid uid, float value, BatteryComponent? battery = null)
  175. {
  176. if (!Resolve(uid, ref battery, false) || value > battery.CurrentCharge)
  177. return false;
  178. UseCharge(uid, value, battery);
  179. return true;
  180. }
  181. /// <summary>
  182. /// Returns whether the battery is full.
  183. /// </summary>
  184. public bool IsFull(EntityUid uid, BatteryComponent? battery = null)
  185. {
  186. if (!Resolve(uid, ref battery))
  187. return false;
  188. return battery.CurrentCharge >= battery.MaxCharge;
  189. }
  190. }
  191. }