ChemistryGuideDataSystem.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233
  1. using System.Linq;
  2. using Content.Shared.Chemistry.EntitySystems;
  3. using Content.Shared.Atmos.Prototypes;
  4. using Content.Shared.Body.Part;
  5. using Content.Shared.Chemistry;
  6. using Content.Shared.Chemistry.Components;
  7. using Content.Shared.Chemistry.Components.SolutionManager;
  8. using Content.Shared.Chemistry.Reaction;
  9. using Content.Shared.Chemistry.Reagent;
  10. using Content.Shared.Kitchen.Components;
  11. using Content.Shared.Prototypes;
  12. using Robust.Shared.Prototypes;
  13. namespace Content.Client.Chemistry.EntitySystems;
  14. /// <inheritdoc/>
  15. public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
  16. {
  17. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainer = default!;
  18. [ValidatePrototypeId<MixingCategoryPrototype>]
  19. private const string DefaultMixingCategory = "DummyMix";
  20. [ValidatePrototypeId<MixingCategoryPrototype>]
  21. private const string DefaultGrindCategory = "DummyGrind";
  22. [ValidatePrototypeId<MixingCategoryPrototype>]
  23. private const string DefaultJuiceCategory = "DummyJuice";
  24. [ValidatePrototypeId<MixingCategoryPrototype>]
  25. private const string DefaultCondenseCategory = "DummyCondense";
  26. private readonly Dictionary<string, List<ReagentSourceData>> _reagentSources = new();
  27. /// <inheritdoc/>
  28. public override void Initialize()
  29. {
  30. base.Initialize();
  31. SubscribeNetworkEvent<ReagentGuideRegistryChangedEvent>(OnReceiveRegistryUpdate);
  32. SubscribeLocalEvent<PrototypesReloadedEventArgs>(OnPrototypesReloaded);
  33. OnPrototypesReloaded(null);
  34. }
  35. private void OnReceiveRegistryUpdate(ReagentGuideRegistryChangedEvent message)
  36. {
  37. var data = message.Changeset;
  38. foreach (var remove in data.Removed)
  39. {
  40. Registry.Remove(remove);
  41. }
  42. foreach (var (key, val) in data.GuideEntries)
  43. {
  44. Registry[key] = val;
  45. }
  46. }
  47. private void OnPrototypesReloaded(PrototypesReloadedEventArgs? ev)
  48. {
  49. // this doesn't check what prototypes are being reloaded because, to be frank, we use a lot of them.
  50. _reagentSources.Clear();
  51. foreach (var reagent in PrototypeManager.EnumeratePrototypes<ReagentPrototype>())
  52. {
  53. _reagentSources.Add(reagent.ID, new());
  54. }
  55. foreach (var reaction in PrototypeManager.EnumeratePrototypes<ReactionPrototype>())
  56. {
  57. if (!reaction.Source)
  58. continue;
  59. var data = new ReagentReactionSourceData(
  60. reaction.MixingCategories ?? new () { DefaultMixingCategory },
  61. reaction);
  62. foreach (var product in reaction.Products.Keys)
  63. {
  64. _reagentSources[product].Add(data);
  65. }
  66. }
  67. foreach (var gas in PrototypeManager.EnumeratePrototypes<GasPrototype>())
  68. {
  69. if (gas.Reagent == null)
  70. continue;
  71. var data = new ReagentGasSourceData(
  72. new () { DefaultCondenseCategory },
  73. gas);
  74. _reagentSources[gas.Reagent].Add(data);
  75. }
  76. // store the names of the entities used so we don't get repeats in the guide.
  77. var usedNames = new List<string>();
  78. foreach (var entProto in PrototypeManager.EnumeratePrototypes<EntityPrototype>())
  79. {
  80. if (entProto.Abstract || usedNames.Contains(entProto.Name))
  81. continue;
  82. if (!entProto.TryGetComponent<ExtractableComponent>(out var extractableComponent, EntityManager.ComponentFactory))
  83. continue;
  84. //these bloat the hell out of blood/fat
  85. if (entProto.HasComponent<BodyPartComponent>())
  86. continue;
  87. //these feel obvious...
  88. if (entProto.HasComponent<PillComponent>())
  89. continue;
  90. if (extractableComponent.JuiceSolution is { } juiceSolution)
  91. {
  92. var data = new ReagentEntitySourceData(
  93. new() { DefaultJuiceCategory },
  94. entProto,
  95. juiceSolution);
  96. foreach (var (id, _) in juiceSolution.Contents)
  97. {
  98. _reagentSources[id.Prototype].Add(data);
  99. }
  100. usedNames.Add(entProto.Name);
  101. }
  102. if (extractableComponent.GrindableSolution is { } grindableSolutionId &&
  103. entProto.TryGetComponent<SolutionContainerManagerComponent>(out var manager, EntityManager.ComponentFactory) &&
  104. _solutionContainer.TryGetSolution(manager, grindableSolutionId, out var grindableSolution))
  105. {
  106. var data = new ReagentEntitySourceData(
  107. new() { DefaultGrindCategory },
  108. entProto,
  109. grindableSolution);
  110. foreach (var (id, _) in grindableSolution.Contents)
  111. {
  112. _reagentSources[id.Prototype].Add(data);
  113. }
  114. usedNames.Add(entProto.Name);
  115. }
  116. }
  117. }
  118. public List<ReagentSourceData> GetReagentSources(string id)
  119. {
  120. return _reagentSources.GetValueOrDefault(id) ?? new List<ReagentSourceData>();
  121. }
  122. // Is handled on server and updated on client via ReagentGuideRegistryChangedEvent
  123. public override void ReloadAllReagentPrototypes()
  124. {
  125. }
  126. }
  127. /// <summary>
  128. /// A generic class meant to hold information about a reagent source.
  129. /// </summary>
  130. public abstract class ReagentSourceData
  131. {
  132. /// <summary>
  133. /// The mixing type that applies to this source.
  134. /// </summary>
  135. public readonly IReadOnlyList<ProtoId<MixingCategoryPrototype>> MixingType;
  136. /// <summary>
  137. /// The number of distinct outputs. Used for primary ordering.
  138. /// </summary>
  139. public abstract int OutputCount { get; }
  140. /// <summary>
  141. /// A text string corresponding to this source. Typically a name. Used for secondary ordering.
  142. /// </summary>
  143. public abstract string IdentifierString { get; }
  144. protected ReagentSourceData(List<ProtoId<MixingCategoryPrototype>> mixingType)
  145. {
  146. MixingType = mixingType;
  147. }
  148. }
  149. /// <summary>
  150. /// Used to store a reagent source that's an entity with a corresponding solution.
  151. /// </summary>
  152. public sealed class ReagentEntitySourceData : ReagentSourceData
  153. {
  154. public readonly EntityPrototype SourceEntProto;
  155. public readonly Solution Solution;
  156. public override int OutputCount => Solution.Contents.Count;
  157. public override string IdentifierString => SourceEntProto.Name;
  158. public ReagentEntitySourceData(List<ProtoId<MixingCategoryPrototype>> mixingType, EntityPrototype sourceEntProto, Solution solution)
  159. : base(mixingType)
  160. {
  161. SourceEntProto = sourceEntProto;
  162. Solution = solution;
  163. }
  164. }
  165. /// <summary>
  166. /// Used to store a reagent source that comes from a reaction between multiple reagents.
  167. /// </summary>
  168. public sealed class ReagentReactionSourceData : ReagentSourceData
  169. {
  170. public readonly ReactionPrototype ReactionPrototype;
  171. public override int OutputCount => ReactionPrototype.Products.Count + ReactionPrototype.Reactants.Count(r => r.Value.Catalyst);
  172. public override string IdentifierString => ReactionPrototype.ID;
  173. public ReagentReactionSourceData(List<ProtoId<MixingCategoryPrototype>> mixingType, ReactionPrototype reactionPrototype)
  174. : base(mixingType)
  175. {
  176. ReactionPrototype = reactionPrototype;
  177. }
  178. }
  179. /// <summary>
  180. /// Used to store a reagent source that comes from gas condensation.
  181. /// </summary>
  182. public sealed class ReagentGasSourceData : ReagentSourceData
  183. {
  184. public readonly GasPrototype GasPrototype;
  185. public override int OutputCount => 1;
  186. public override string IdentifierString => Loc.GetString(GasPrototype.Name);
  187. public ReagentGasSourceData(List<ProtoId<MixingCategoryPrototype>> mixingType, GasPrototype gasPrototype)
  188. : base(mixingType)
  189. {
  190. GasPrototype = gasPrototype;
  191. }
  192. }