MaterialReclaimerSystem.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286
  1. using Content.Server.Administration.Logs;
  2. using Content.Server.Fluids.EntitySystems;
  3. using Content.Server.Ghost;
  4. using Content.Server.Popups;
  5. using Content.Server.Repairable;
  6. using Content.Server.Stack;
  7. using Content.Server.Wires;
  8. using Content.Shared.Body.Systems;
  9. using Content.Shared.Chemistry.Components;
  10. using Content.Shared.Chemistry.Components.SolutionManager;
  11. using Content.Shared.Chemistry.EntitySystems;
  12. using Content.Shared.Database;
  13. using Content.Shared.Destructible;
  14. using Content.Shared.Emag.Components;
  15. using Content.Shared.IdentityManagement;
  16. using Content.Shared.Interaction;
  17. using Content.Shared.Interaction.Events;
  18. using Content.Shared.Materials;
  19. using Content.Shared.Mind;
  20. using Content.Shared.Nutrition.EntitySystems;
  21. using Content.Shared.Power;
  22. using Robust.Server.GameObjects;
  23. using Robust.Shared.Player;
  24. using Robust.Shared.Prototypes;
  25. using Robust.Shared.Utility;
  26. using System.Linq;
  27. namespace Content.Server.Materials;
  28. /// <inheritdoc/>
  29. public sealed class MaterialReclaimerSystem : SharedMaterialReclaimerSystem
  30. {
  31. [Dependency] private readonly IPrototypeManager _prototype = default!;
  32. [Dependency] private readonly AppearanceSystem _appearance = default!;
  33. [Dependency] private readonly GhostSystem _ghostSystem = default!;
  34. [Dependency] private readonly MaterialStorageSystem _materialStorage = default!;
  35. [Dependency] private readonly OpenableSystem _openable = default!;
  36. [Dependency] private readonly PopupSystem _popup = default!;
  37. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
  38. [Dependency] private readonly SharedBodySystem _body = default!; //bobby
  39. [Dependency] private readonly PuddleSystem _puddle = default!;
  40. [Dependency] private readonly StackSystem _stack = default!;
  41. [Dependency] private readonly SharedMindSystem _mind = default!;
  42. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  43. /// <inheritdoc/>
  44. public override void Initialize()
  45. {
  46. base.Initialize();
  47. SubscribeLocalEvent<MaterialReclaimerComponent, PowerChangedEvent>(OnPowerChanged);
  48. SubscribeLocalEvent<MaterialReclaimerComponent, InteractUsingEvent>(OnInteractUsing,
  49. before: [typeof(WiresSystem), typeof(SolutionTransferSystem)]);
  50. SubscribeLocalEvent<MaterialReclaimerComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
  51. SubscribeLocalEvent<ActiveMaterialReclaimerComponent, PowerChangedEvent>(OnActivePowerChanged);
  52. SubscribeLocalEvent<MaterialReclaimerComponent, BreakageEventArgs>(OnBreakage);
  53. SubscribeLocalEvent<MaterialReclaimerComponent, RepairedEvent>(OnRepaired);
  54. }
  55. private void OnPowerChanged(Entity<MaterialReclaimerComponent> entity, ref PowerChangedEvent args)
  56. {
  57. AmbientSound.SetAmbience(entity.Owner, entity.Comp.Enabled && args.Powered);
  58. entity.Comp.Powered = args.Powered;
  59. Dirty(entity);
  60. }
  61. private void OnInteractUsing(Entity<MaterialReclaimerComponent> entity, ref InteractUsingEvent args)
  62. {
  63. if (args.Handled)
  64. return;
  65. // if we're trying to get a solution out of the reclaimer, don't destroy it
  66. if (_solutionContainer.TryGetSolution(entity.Owner, entity.Comp.SolutionContainerId, out _, out var outputSolution) && outputSolution.Contents.Any())
  67. {
  68. if (TryComp<SolutionContainerManagerComponent>(args.Used, out var managerComponent) &&
  69. _solutionContainer.EnumerateSolutions((args.Used, managerComponent)).Any(s => s.Solution.Comp.Solution.AvailableVolume > 0))
  70. {
  71. if (_openable.IsClosed(args.Used))
  72. return;
  73. if (TryComp<SolutionTransferComponent>(args.Used, out var transfer) &&
  74. transfer.CanReceive)
  75. return;
  76. }
  77. }
  78. args.Handled = TryStartProcessItem(entity.Owner, args.Used, entity.Comp, args.User);
  79. }
  80. private void OnSuicideByEnvironment(Entity<MaterialReclaimerComponent> entity, ref SuicideByEnvironmentEvent args)
  81. {
  82. if (args.Handled)
  83. return;
  84. var victim = args.Victim;
  85. if (TryComp(victim, out ActorComponent? actor) &&
  86. _mind.TryGetMind(actor.PlayerSession, out var mindId, out var mind))
  87. {
  88. _ghostSystem.OnGhostAttempt(mindId, false, mind: mind);
  89. if (mind.OwnedEntity is { Valid: true } suicider)
  90. {
  91. _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message"), suicider);
  92. }
  93. }
  94. _popup.PopupEntity(Loc.GetString("recycler-component-suicide-message-others",
  95. ("victim", Identity.Entity(victim, EntityManager))),
  96. victim,
  97. Filter.PvsExcept(victim, entityManager: EntityManager),
  98. true);
  99. _body.GibBody(victim, true);
  100. _appearance.SetData(entity.Owner, RecyclerVisuals.Bloody, true);
  101. args.Handled = true;
  102. }
  103. private void OnActivePowerChanged(Entity<ActiveMaterialReclaimerComponent> entity, ref PowerChangedEvent args)
  104. {
  105. if (!args.Powered)
  106. TryFinishProcessItem(entity, null, entity.Comp);
  107. }
  108. private void OnBreakage(Entity<MaterialReclaimerComponent> ent, ref BreakageEventArgs args)
  109. {
  110. //un-emags itself when it breaks
  111. RemComp<EmaggedComponent>(ent);
  112. SetBroken(ent, true);
  113. }
  114. private void OnRepaired(Entity<MaterialReclaimerComponent> ent, ref RepairedEvent args)
  115. {
  116. SetBroken(ent, false);
  117. }
  118. public void SetBroken(Entity<MaterialReclaimerComponent> ent, bool val)
  119. {
  120. if (ent.Comp.Broken == val)
  121. return;
  122. _appearance.SetData(ent, RecyclerVisuals.Broken, val);
  123. SetReclaimerEnabled(ent, false);
  124. ent.Comp.Broken = val;
  125. Dirty(ent);
  126. }
  127. /// <inheritdoc/>
  128. public override bool TryFinishProcessItem(EntityUid uid, MaterialReclaimerComponent? component = null, ActiveMaterialReclaimerComponent? active = null)
  129. {
  130. if (!Resolve(uid, ref component, ref active, false))
  131. return false;
  132. if (!base.TryFinishProcessItem(uid, component, active))
  133. return false;
  134. if (active.ReclaimingContainer.ContainedEntities.FirstOrNull() is not { } item)
  135. return false;
  136. Container.Remove(item, active.ReclaimingContainer);
  137. Dirty(uid, component);
  138. // scales the output if the process was interrupted.
  139. var completion = 1f - Math.Clamp((float) Math.Round((active.EndTime - Timing.CurTime) / active.Duration),
  140. 0f,
  141. 1f);
  142. Reclaim(uid, item, completion, component);
  143. return true;
  144. }
  145. /// <inheritdoc/>
  146. public override void Reclaim(EntityUid uid,
  147. EntityUid item,
  148. float completion = 1f,
  149. MaterialReclaimerComponent? component = null)
  150. {
  151. if (!Resolve(uid, ref component))
  152. return;
  153. base.Reclaim(uid, item, completion, component);
  154. var xform = Transform(uid);
  155. SpawnMaterialsFromComposition(uid, item, completion * component.Efficiency, xform: xform);
  156. if (CanGib(uid, item, component))
  157. {
  158. _adminLogger.Add(LogType.Gib, LogImpact.Extreme, $"{ToPrettyString(item):victim} was gibbed by {ToPrettyString(uid):entity} ");
  159. SpawnChemicalsFromComposition(uid, item, completion, false, component, xform);
  160. _body.GibBody(item, true);
  161. _appearance.SetData(uid, RecyclerVisuals.Bloody, true);
  162. }
  163. else
  164. {
  165. SpawnChemicalsFromComposition(uid, item, completion, true, component, xform);
  166. }
  167. QueueDel(item);
  168. }
  169. private void SpawnMaterialsFromComposition(EntityUid reclaimer,
  170. EntityUid item,
  171. float efficiency,
  172. MaterialStorageComponent? storage = null,
  173. TransformComponent? xform = null,
  174. PhysicalCompositionComponent? composition = null)
  175. {
  176. if (!Resolve(reclaimer, ref storage, ref xform, false))
  177. return;
  178. if (!Resolve(item, ref composition, false))
  179. return;
  180. foreach (var (material, amount) in composition.MaterialComposition)
  181. {
  182. var outputAmount = (int) (amount * efficiency);
  183. _materialStorage.TryChangeMaterialAmount(reclaimer, material, outputAmount, storage);
  184. }
  185. foreach (var (storedMaterial, storedAmount) in storage.Storage)
  186. {
  187. var stacks = _materialStorage.SpawnMultipleFromMaterial(storedAmount,
  188. storedMaterial,
  189. xform.Coordinates,
  190. out var materialOverflow);
  191. var amountConsumed = storedAmount - materialOverflow;
  192. _materialStorage.TryChangeMaterialAmount(reclaimer, storedMaterial, -amountConsumed, storage);
  193. foreach (var stack in stacks)
  194. {
  195. _stack.TryMergeToContacts(stack);
  196. }
  197. }
  198. }
  199. private void SpawnChemicalsFromComposition(EntityUid reclaimer,
  200. EntityUid item,
  201. float efficiency,
  202. bool sound = true,
  203. MaterialReclaimerComponent? reclaimerComponent = null,
  204. TransformComponent? xform = null,
  205. PhysicalCompositionComponent? composition = null)
  206. {
  207. if (!Resolve(reclaimer, ref reclaimerComponent, ref xform))
  208. return;
  209. efficiency *= reclaimerComponent.Efficiency;
  210. var totalChemicals = new Solution();
  211. if (Resolve(item, ref composition, false))
  212. {
  213. foreach (var (key, value) in composition.ChemicalComposition)
  214. {
  215. // TODO use ReagentQuantity
  216. totalChemicals.AddReagent(key, value * efficiency, false);
  217. }
  218. }
  219. // if the item we inserted has reagents, add it in.
  220. if (reclaimerComponent.OnlyReclaimDrainable)
  221. {
  222. // Are we a recycler? Only use drainable solution.
  223. if (_solutionContainer.TryGetDrainableSolution(item, out _, out var drainableSolution))
  224. {
  225. totalChemicals.AddSolution(drainableSolution, _prototype);
  226. }
  227. }
  228. else
  229. {
  230. // Are we an industrial reagent grinder? Use extractable solution.
  231. if (_solutionContainer.TryGetExtractableSolution(item, out _, out var extractableSolution))
  232. {
  233. totalChemicals.AddSolution(extractableSolution, _prototype);
  234. }
  235. }
  236. if (!_solutionContainer.TryGetSolution(reclaimer, reclaimerComponent.SolutionContainerId, out var outputSolution) ||
  237. !_solutionContainer.TryTransferSolution(outputSolution.Value, totalChemicals, totalChemicals.Volume) ||
  238. totalChemicals.Volume > 0)
  239. {
  240. _puddle.TrySpillAt(reclaimer, totalChemicals, out _, sound, transformComponent: xform);
  241. }
  242. }
  243. }