MachineFrameSystem.cs 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. using Content.Server.Construction.Components;
  2. using Content.Server.Stack;
  3. using Content.Shared.Construction.Components;
  4. using Content.Shared.Examine;
  5. using Content.Shared.Interaction;
  6. using Content.Shared.Stacks;
  7. using Content.Shared.Tag;
  8. using Content.Shared.Popups;
  9. using Robust.Shared.Containers;
  10. using Robust.Shared.Prototypes;
  11. namespace Content.Server.Construction;
  12. public sealed class MachineFrameSystem : EntitySystem
  13. {
  14. [Dependency] private readonly IComponentFactory _factory = default!;
  15. [Dependency] private readonly SharedContainerSystem _container = default!;
  16. [Dependency] private readonly TagSystem _tag = default!;
  17. [Dependency] private readonly StackSystem _stack = default!;
  18. [Dependency] private readonly ConstructionSystem _construction = default!;
  19. [Dependency] private readonly SharedPopupSystem _popupSystem = default!;
  20. public override void Initialize()
  21. {
  22. base.Initialize();
  23. SubscribeLocalEvent<MachineFrameComponent, ComponentInit>(OnInit);
  24. SubscribeLocalEvent<MachineFrameComponent, ComponentStartup>(OnStartup);
  25. SubscribeLocalEvent<MachineFrameComponent, InteractUsingEvent>(OnInteractUsing);
  26. SubscribeLocalEvent<MachineFrameComponent, ExaminedEvent>(OnMachineFrameExamined);
  27. }
  28. private void OnInit(EntityUid uid, MachineFrameComponent component, ComponentInit args)
  29. {
  30. component.BoardContainer = _container.EnsureContainer<Container>(uid, MachineFrameComponent.BoardContainerName);
  31. component.PartContainer = _container.EnsureContainer<Container>(uid, MachineFrameComponent.PartContainerName);
  32. }
  33. private void OnStartup(EntityUid uid, MachineFrameComponent component, ComponentStartup args)
  34. {
  35. RegenerateProgress(component);
  36. if (TryComp<ConstructionComponent>(uid, out var construction) && construction.TargetNode == null)
  37. {
  38. // Attempt to set pathfinding to the machine node...
  39. _construction.SetPathfindingTarget(uid, "machine", construction);
  40. }
  41. }
  42. private void OnInteractUsing(EntityUid uid, MachineFrameComponent component, InteractUsingEvent args)
  43. {
  44. if (args.Handled)
  45. return;
  46. if (!component.HasBoard)
  47. {
  48. if (TryInsertBoard(uid, args.Used, component))
  49. args.Handled = true;
  50. return;
  51. }
  52. // If this changes in the future, then RegenerateProgress() also needs to be updated.
  53. // Note that one entity is ALLOWED to satisfy more than one kind of component or tag requirements. This is
  54. // necessary in order to avoid weird entity-ordering shenanigans in RegenerateProgress().
  55. if (TryComp<StackComponent>(args.Used, out var stack))
  56. {
  57. if (TryInsertStack(uid, args.Used, component, stack))
  58. args.Handled = true;
  59. return;
  60. }
  61. // Handle component requirements
  62. foreach (var (compName, info) in component.ComponentRequirements)
  63. {
  64. if (component.ComponentProgress[compName] >= info.Amount)
  65. continue;
  66. var registration = _factory.GetRegistration(compName);
  67. if (!HasComp(args.Used, registration.Type))
  68. continue;
  69. // Insert the entity, if it hasn't already been inserted
  70. if (!args.Handled)
  71. {
  72. if (!_container.TryRemoveFromContainer(args.Used))
  73. return;
  74. args.Handled = true;
  75. if (!_container.Insert(args.Used, component.PartContainer))
  76. return;
  77. }
  78. component.ComponentProgress[compName]++;
  79. if (IsComplete(component))
  80. {
  81. _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid);
  82. return;
  83. }
  84. }
  85. // Handle tag requirements
  86. if (!TryComp<TagComponent>(args.Used, out var tagComp))
  87. return;
  88. foreach (var (tagName, info) in component.TagRequirements)
  89. {
  90. if (component.TagProgress[tagName] >= info.Amount)
  91. continue;
  92. if (!_tag.HasTag(tagComp, tagName))
  93. continue;
  94. // Insert the entity, if it hasn't already been inserted
  95. if (!args.Handled)
  96. {
  97. if (!_container.TryRemoveFromContainer(args.Used))
  98. return;
  99. args.Handled = true;
  100. if (!_container.Insert(args.Used, component.PartContainer))
  101. return;
  102. }
  103. component.TagProgress[tagName]++;
  104. args.Handled = true;
  105. if (IsComplete(component))
  106. {
  107. _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid);
  108. return;
  109. }
  110. }
  111. }
  112. /// <returns>Whether or not the function had any effect. Does not indicate success.</returns>
  113. private bool TryInsertBoard(EntityUid uid, EntityUid used, MachineFrameComponent component)
  114. {
  115. if (!TryComp<MachineBoardComponent>(used, out var machineBoard))
  116. return false;
  117. if (!_container.TryRemoveFromContainer(used))
  118. return false;
  119. if (!_container.Insert(used, component.BoardContainer))
  120. return true;
  121. ResetProgressAndRequirements(component, machineBoard);
  122. // Reset edge so that prying the components off works correctly.
  123. if (TryComp(uid, out ConstructionComponent? construction))
  124. _construction.ResetEdge(uid, construction);
  125. return true;
  126. }
  127. /// <returns>Whether or not the function had any effect. Does not indicate success.</returns>
  128. private bool TryInsertStack(EntityUid uid, EntityUid used, MachineFrameComponent component, StackComponent stack)
  129. {
  130. var type = stack.StackTypeId;
  131. if (!component.MaterialRequirements.ContainsKey(type))
  132. return false;
  133. var progress = component.MaterialProgress[type];
  134. var requirement = component.MaterialRequirements[type];
  135. var needed = requirement - progress;
  136. if (needed <= 0)
  137. return false;
  138. var count = stack.Count;
  139. if (count < needed)
  140. {
  141. if (!_container.TryRemoveFromContainer(used))
  142. return false;
  143. if (!_container.Insert(used, component.PartContainer))
  144. return true;
  145. component.MaterialProgress[type] += count;
  146. return true;
  147. }
  148. var splitStack = _stack.Split(used, needed, Transform(uid).Coordinates, stack);
  149. if (splitStack == null)
  150. return false;
  151. if (!_container.Insert(splitStack.Value, component.PartContainer))
  152. return true;
  153. component.MaterialProgress[type] += needed;
  154. if (IsComplete(component))
  155. _popupSystem.PopupEntity(Loc.GetString("machine-frame-component-on-complete"), uid);
  156. return true;
  157. }
  158. public bool IsComplete(MachineFrameComponent component)
  159. {
  160. if (!component.HasBoard)
  161. return false;
  162. foreach (var (type, amount) in component.MaterialRequirements)
  163. {
  164. if (component.MaterialProgress[type] < amount)
  165. return false;
  166. }
  167. foreach (var (compName, info) in component.ComponentRequirements)
  168. {
  169. if (component.ComponentProgress[compName] < info.Amount)
  170. return false;
  171. }
  172. foreach (var (tagName, info) in component.TagRequirements)
  173. {
  174. if (component.TagProgress[tagName] < info.Amount)
  175. return false;
  176. }
  177. return true;
  178. }
  179. public void ResetProgressAndRequirements(MachineFrameComponent component, MachineBoardComponent machineBoard)
  180. {
  181. component.MaterialRequirements = new Dictionary<ProtoId<StackPrototype>, int>(machineBoard.StackRequirements);
  182. component.ComponentRequirements = new Dictionary<string, GenericPartInfo>(machineBoard.ComponentRequirements);
  183. component.TagRequirements = new Dictionary<ProtoId<TagPrototype>, GenericPartInfo>(machineBoard.TagRequirements);
  184. component.MaterialProgress.Clear();
  185. component.ComponentProgress.Clear();
  186. component.TagProgress.Clear();
  187. foreach (var (stackType, _) in component.MaterialRequirements)
  188. {
  189. component.MaterialProgress[stackType] = 0;
  190. }
  191. foreach (var (compName, _) in component.ComponentRequirements)
  192. {
  193. component.ComponentProgress[compName] = 0;
  194. }
  195. foreach (var (compName, _) in component.TagRequirements)
  196. {
  197. component.TagProgress[compName] = 0;
  198. }
  199. }
  200. public void RegenerateProgress(MachineFrameComponent component)
  201. {
  202. if (!component.HasBoard)
  203. {
  204. component.TagRequirements.Clear();
  205. component.MaterialRequirements.Clear();
  206. component.ComponentRequirements.Clear();
  207. component.TagRequirements.Clear();
  208. component.MaterialProgress.Clear();
  209. component.ComponentProgress.Clear();
  210. component.TagProgress.Clear();
  211. return;
  212. }
  213. var board = component.BoardContainer.ContainedEntities[0];
  214. if (!TryComp<MachineBoardComponent>(board, out var machineBoard))
  215. return;
  216. ResetProgressAndRequirements(component, machineBoard);
  217. // If the following code is updated, you need to make sure that it matches the logic in OnInteractUsing()
  218. foreach (var part in component.PartContainer.ContainedEntities)
  219. {
  220. if (TryComp<StackComponent>(part, out var stack))
  221. {
  222. var type = stack.StackTypeId;
  223. if (!component.MaterialRequirements.ContainsKey(type))
  224. continue;
  225. if (!component.MaterialProgress.ContainsKey(type))
  226. component.MaterialProgress[type] = stack.Count;
  227. else
  228. component.MaterialProgress[type] += stack.Count;
  229. continue;
  230. }
  231. // I have many regrets.
  232. foreach (var (compName, _) in component.ComponentRequirements)
  233. {
  234. var registration = _factory.GetRegistration(compName);
  235. if (!HasComp(part, registration.Type))
  236. continue;
  237. if (!component.ComponentProgress.TryAdd(compName, 1))
  238. component.ComponentProgress[compName]++;
  239. }
  240. if (!TryComp<TagComponent>(part, out var tagComp))
  241. continue;
  242. // I have MANY regrets.
  243. foreach (var tagName in component.TagRequirements.Keys)
  244. {
  245. if (!_tag.HasTag(tagComp, tagName))
  246. continue;
  247. if (!component.TagProgress.TryAdd(tagName, 1))
  248. component.TagProgress[tagName]++;
  249. }
  250. }
  251. }
  252. private void OnMachineFrameExamined(EntityUid uid, MachineFrameComponent component, ExaminedEvent args)
  253. {
  254. if (!args.IsInDetailsRange || !component.HasBoard)
  255. return;
  256. var board = component.BoardContainer.ContainedEntities[0];
  257. args.PushMarkup(Loc.GetString("machine-frame-component-on-examine-label", ("board", Name(board))));
  258. }
  259. }