PowerCellSystem.cs 9.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247
  1. using Content.Server.Emp;
  2. using Content.Server.Power.Components;
  3. using Content.Shared.Examine;
  4. using Content.Shared.PowerCell;
  5. using Content.Shared.PowerCell.Components;
  6. using Content.Shared.Rounding;
  7. using Robust.Shared.Containers;
  8. using System.Diagnostics.CodeAnalysis;
  9. using Content.Server.Kitchen.Components;
  10. using Content.Server.Power.EntitySystems;
  11. using Content.Server.UserInterface;
  12. using Content.Shared.Containers.ItemSlots;
  13. using Content.Shared.Popups;
  14. using ActivatableUISystem = Content.Shared.UserInterface.ActivatableUISystem;
  15. namespace Content.Server.PowerCell;
  16. /// <summary>
  17. /// Handles Power cells
  18. /// </summary>
  19. public sealed partial class PowerCellSystem : SharedPowerCellSystem
  20. {
  21. [Dependency] private readonly ActivatableUISystem _activatable = default!;
  22. [Dependency] private readonly BatterySystem _battery = default!;
  23. [Dependency] private readonly SharedContainerSystem _containerSystem = default!;
  24. [Dependency] private readonly ItemSlotsSystem _itemSlotsSystem = default!;
  25. [Dependency] private readonly SharedAppearanceSystem _sharedAppearanceSystem = default!;
  26. [Dependency] private readonly SharedPopupSystem _popup = default!;
  27. [Dependency] private readonly RiggableSystem _riggableSystem = default!;
  28. public override void Initialize()
  29. {
  30. base.Initialize();
  31. SubscribeLocalEvent<PowerCellComponent, ChargeChangedEvent>(OnChargeChanged);
  32. SubscribeLocalEvent<PowerCellComponent, ExaminedEvent>(OnCellExamined);
  33. SubscribeLocalEvent<PowerCellComponent, EmpAttemptEvent>(OnCellEmpAttempt);
  34. SubscribeLocalEvent<PowerCellDrawComponent, ChargeChangedEvent>(OnDrawChargeChanged);
  35. SubscribeLocalEvent<PowerCellDrawComponent, PowerCellChangedEvent>(OnDrawCellChanged);
  36. SubscribeLocalEvent<PowerCellSlotComponent, ExaminedEvent>(OnCellSlotExamined);
  37. // funny
  38. SubscribeLocalEvent<PowerCellSlotComponent, BeingMicrowavedEvent>(OnSlotMicrowaved);
  39. }
  40. private void OnSlotMicrowaved(EntityUid uid, PowerCellSlotComponent component, BeingMicrowavedEvent args)
  41. {
  42. if (!_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out var slot))
  43. return;
  44. if (slot.Item == null)
  45. return;
  46. RaiseLocalEvent(slot.Item.Value, args);
  47. }
  48. private void OnChargeChanged(EntityUid uid, PowerCellComponent component, ref ChargeChangedEvent args)
  49. {
  50. if (TryComp<RiggableComponent>(uid, out var rig) && rig.IsRigged)
  51. {
  52. _riggableSystem.Explode(uid, cause: null);
  53. return;
  54. }
  55. var frac = args.Charge / args.MaxCharge;
  56. var level = (byte)ContentHelpers.RoundToNearestLevels(frac, 1, PowerCellComponent.PowerCellVisualsLevels);
  57. _sharedAppearanceSystem.SetData(uid, PowerCellVisuals.ChargeLevel, level);
  58. // If this power cell is inside a cell-slot, inform that entity that the power has changed (for updating visuals n such).
  59. if (_containerSystem.TryGetContainingContainer((uid, null, null), out var container)
  60. && TryComp(container.Owner, out PowerCellSlotComponent? slot)
  61. && _itemSlotsSystem.TryGetSlot(container.Owner, slot.CellSlotId, out var itemSlot))
  62. {
  63. if (itemSlot.Item == uid)
  64. RaiseLocalEvent(container.Owner, new PowerCellChangedEvent(false));
  65. }
  66. }
  67. protected override void OnCellRemoved(EntityUid uid, PowerCellSlotComponent component, EntRemovedFromContainerMessage args)
  68. {
  69. base.OnCellRemoved(uid, component, args);
  70. if (args.Container.ID != component.CellSlotId)
  71. return;
  72. var ev = new PowerCellSlotEmptyEvent();
  73. RaiseLocalEvent(uid, ref ev);
  74. }
  75. #region Activatable
  76. /// <inheritdoc/>
  77. public override bool HasActivatableCharge(EntityUid uid, PowerCellDrawComponent? battery = null, PowerCellSlotComponent? cell = null, EntityUid? user = null)
  78. {
  79. // Default to true if we don't have the components.
  80. if (!Resolve(uid, ref battery, ref cell, false))
  81. return true;
  82. return HasCharge(uid, battery.UseRate, cell, user);
  83. }
  84. /// <summary>
  85. /// Tries to use the <see cref="PowerCellDrawComponent.UseRate"/> for this entity.
  86. /// </summary>
  87. /// <param name="user">Popup to this user with the relevant detail if specified.</param>
  88. public bool TryUseActivatableCharge(EntityUid uid, PowerCellDrawComponent? battery = null, PowerCellSlotComponent? cell = null, EntityUid? user = null)
  89. {
  90. // Default to true if we don't have the components.
  91. if (!Resolve(uid, ref battery, ref cell, false))
  92. return true;
  93. if (TryUseCharge(uid, battery.UseRate, cell, user))
  94. {
  95. _sharedAppearanceSystem.SetData(uid, PowerCellSlotVisuals.Enabled, HasActivatableCharge(uid, battery, cell, user));
  96. _activatable.CheckUsage(uid);
  97. return true;
  98. }
  99. return false;
  100. }
  101. /// <inheritdoc/>
  102. public override bool HasDrawCharge(
  103. EntityUid uid,
  104. PowerCellDrawComponent? battery = null,
  105. PowerCellSlotComponent? cell = null,
  106. EntityUid? user = null)
  107. {
  108. if (!Resolve(uid, ref battery, ref cell, false))
  109. return true;
  110. return HasCharge(uid, battery.DrawRate, cell, user);
  111. }
  112. #endregion
  113. /// <summary>
  114. /// Returns whether the entity has a slotted battery and charge for the requested action.
  115. /// </summary>
  116. /// <param name="user">Popup to this user with the relevant detail if specified.</param>
  117. public bool HasCharge(EntityUid uid, float charge, PowerCellSlotComponent? component = null, EntityUid? user = null)
  118. {
  119. if (!TryGetBatteryFromSlot(uid, out var battery, component))
  120. {
  121. if (user != null)
  122. _popup.PopupEntity(Loc.GetString("power-cell-no-battery"), uid, user.Value);
  123. return false;
  124. }
  125. if (battery.CurrentCharge < charge)
  126. {
  127. if (user != null)
  128. _popup.PopupEntity(Loc.GetString("power-cell-insufficient"), uid, user.Value);
  129. return false;
  130. }
  131. return true;
  132. }
  133. /// <summary>
  134. /// Tries to use charge from a slotted battery.
  135. /// </summary>
  136. public bool TryUseCharge(EntityUid uid, float charge, PowerCellSlotComponent? component = null, EntityUid? user = null)
  137. {
  138. if (!TryGetBatteryFromSlot(uid, out var batteryEnt, out var battery, component))
  139. {
  140. if (user != null)
  141. _popup.PopupEntity(Loc.GetString("power-cell-no-battery"), uid, user.Value);
  142. return false;
  143. }
  144. if (!_battery.TryUseCharge(batteryEnt.Value, charge, battery))
  145. {
  146. if (user != null)
  147. _popup.PopupEntity(Loc.GetString("power-cell-insufficient"), uid, user.Value);
  148. return false;
  149. }
  150. _sharedAppearanceSystem.SetData(uid, PowerCellSlotVisuals.Enabled, battery.CurrentCharge > 0);
  151. return true;
  152. }
  153. public bool TryGetBatteryFromSlot(EntityUid uid, [NotNullWhen(true)] out BatteryComponent? battery, PowerCellSlotComponent? component = null)
  154. {
  155. return TryGetBatteryFromSlot(uid, out _, out battery, component);
  156. }
  157. public bool TryGetBatteryFromSlot(EntityUid uid,
  158. [NotNullWhen(true)] out EntityUid? batteryEnt,
  159. [NotNullWhen(true)] out BatteryComponent? battery,
  160. PowerCellSlotComponent? component = null)
  161. {
  162. if (!Resolve(uid, ref component, false))
  163. {
  164. batteryEnt = null;
  165. battery = null;
  166. return false;
  167. }
  168. if (_itemSlotsSystem.TryGetSlot(uid, component.CellSlotId, out ItemSlot? slot))
  169. {
  170. batteryEnt = slot.Item;
  171. return TryComp(slot.Item, out battery);
  172. }
  173. batteryEnt = null;
  174. battery = null;
  175. return false;
  176. }
  177. private void OnCellExamined(EntityUid uid, PowerCellComponent component, ExaminedEvent args)
  178. {
  179. TryComp<BatteryComponent>(uid, out var battery);
  180. OnBatteryExamined(uid, battery, args);
  181. }
  182. private void OnCellEmpAttempt(EntityUid uid, PowerCellComponent component, EmpAttemptEvent args)
  183. {
  184. var parent = Transform(uid).ParentUid;
  185. // relay the attempt event to the slot so it can cancel it
  186. if (HasComp<PowerCellSlotComponent>(parent))
  187. RaiseLocalEvent(parent, args);
  188. }
  189. private void OnCellSlotExamined(EntityUid uid, PowerCellSlotComponent component, ExaminedEvent args)
  190. {
  191. TryGetBatteryFromSlot(uid, out var battery);
  192. OnBatteryExamined(uid, battery, args);
  193. }
  194. private void OnBatteryExamined(EntityUid uid, BatteryComponent? component, ExaminedEvent args)
  195. {
  196. if (component != null)
  197. {
  198. var charge = component.CurrentCharge / component.MaxCharge * 100;
  199. args.PushMarkup(Loc.GetString("power-cell-component-examine-details", ("currentCharge", $"{charge:F0}")));
  200. }
  201. else
  202. {
  203. args.PushMarkup(Loc.GetString("power-cell-component-examine-details-no-battery"));
  204. }
  205. }
  206. }