1
0

AutodocSteps.cs 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244
  1. // SPDX-FileCopyrightText: 2024 deltanedas <39013340+deltanedas@users.noreply.github.com>
  2. // SPDX-FileCopyrightText: 2024 deltanedas <@deltanedas:kde.org>
  3. // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
  4. //
  5. // SPDX-License-Identifier: AGPL-3.0-or-later
  6. using Content.Shared._Shitmed.Autodoc.Components;
  7. using Content.Shared._Shitmed.Autodoc.Systems;
  8. using Content.Shared._Shitmed.Medical.Surgery;
  9. using Content.Shared.Body.Part;
  10. using Content.Shared.Hands.Components;
  11. using Content.Shared.Whitelist;
  12. using Robust.Shared.Prototypes;
  13. using Robust.Shared.Serialization;
  14. namespace Content.Shared._Shitmed.Autodoc;
  15. [Serializable, NetSerializable, DataRecord]
  16. public sealed partial class AutodocProgram
  17. {
  18. public List<IAutodocStep> Steps = new();
  19. public bool SkipFailed;
  20. public string Title = string.Empty;
  21. }
  22. /// <summary>
  23. /// Something the autodoc can do during a program.
  24. /// </summary>
  25. [ImplicitDataDefinitionForInheritors]
  26. public partial interface IAutodocStep
  27. {
  28. /// <summary>
  29. /// Title of this step to display in the UI
  30. /// </summary>
  31. string Title { get; }
  32. /// <summary>
  33. /// Run the step, returning true if it is instantly complete and ready to go to the next step, or false if it needs to wait for something else.
  34. /// Should throw AutodocError for player-facing errors.
  35. /// </summary>
  36. bool Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc);
  37. /// <summary>
  38. /// Check that this step is valid, returning false if it isn't.
  39. /// </summary>
  40. bool Validate(Entity<AutodocComponent> ent, SharedAutodocSystem autodoc)
  41. {
  42. return true;
  43. }
  44. }
  45. /// <summary>
  46. /// Perform a surgery including any prerequesites like opening an incision.
  47. /// </summary>
  48. [Serializable, NetSerializable]
  49. public sealed partial class SurgeryAutodocStep : IAutodocStep
  50. {
  51. /// <summary>
  52. /// The type of part to operate on.
  53. /// </summary>
  54. [DataField(required: true)]
  55. public BodyPartType Part;
  56. /// <summary>
  57. /// The symmetry required. If this is null then symmetry is not checked (operate on an arbitrary leg for example).
  58. /// </summary>
  59. [DataField]
  60. public BodyPartSymmetry? Symmetry;
  61. /// <summary>
  62. /// The ID of the surgery to perform.
  63. /// </summary>
  64. [DataField(required: true)]
  65. public EntProtoId<SurgeryComponent> Surgery;
  66. public string Title {
  67. get {
  68. var protoMan = IoCManager.Resolve<IPrototypeManager>();
  69. var proto = protoMan.Index(Surgery);
  70. var part = Loc.GetString("autodoc-body-part-" + Part.ToString());
  71. return Loc.GetString("autodoc-program-step-surgery", ("part", part), ("name", proto.Name));
  72. }
  73. }
  74. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  75. {
  76. var patient = autodoc.GetPatientOrThrow((ent.Owner, ent.Comp1));
  77. if (autodoc.FindPart(patient, Part, Symmetry) is not {} part)
  78. throw new AutodocError("body-part");
  79. if (!autodoc.StartSurgery((ent.Owner, ent.Comp1), patient, part, Surgery))
  80. throw new AutodocError("surgery-impossible");
  81. return false; // wait for the surgery to be completed before going onto the next program step
  82. }
  83. bool IAutodocStep.Validate(Entity<AutodocComponent> ent, SharedAutodocSystem autodoc)
  84. {
  85. return autodoc.IsSurgery(Surgery);
  86. }
  87. }
  88. /// <summary>
  89. /// Grab a specific item from storage, failing if it isn't found.
  90. /// </summary>
  91. [Serializable, NetSerializable]
  92. public sealed partial class GrabItemAutodocStep : IAutodocStep
  93. {
  94. /// <summary>
  95. /// The name that an item in storage must match to get grabbed.
  96. /// </summary>
  97. [DataField(required: true)]
  98. public string Name = string.Empty;
  99. public string Title => Loc.GetString("autodoc-program-step-grab-item", ("name", Name));
  100. bool IAutodocStep.Validate(Entity<AutodocComponent> ent, SharedAutodocSystem autodoc)
  101. {
  102. // client will never send a blank string for name
  103. return !string.IsNullOrEmpty(Name) && Name.Length <= 100;
  104. }
  105. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  106. {
  107. if (autodoc.FindItem(ent, Name) is not {} item)
  108. throw new AutodocError("item-unavailable");
  109. autodoc.GrabItemOrThrow(ent, item);
  110. return true;
  111. }
  112. }
  113. /// <summary>
  114. /// Grab the first item that matches a whitelist, failing if none are found.
  115. /// </summary>
  116. [Serializable, NetSerializable]
  117. public abstract partial class GrabAnyItemAutodocStep : IAutodocStep
  118. {
  119. /// <summary>
  120. /// A whitelist that must be matched.
  121. /// </summary>
  122. public virtual EntityWhitelist Whitelist { get; }
  123. private EntityWhitelist? _whitelist;
  124. /// <summary>
  125. /// Name that represents the whitelist.
  126. /// </summary>
  127. public virtual LocId Name { get; }
  128. string IAutodocStep.Title => Loc.GetString("autodoc-program-step-grab-any", ("name", Loc.GetString(Name)));
  129. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  130. {
  131. if (autodoc.FindItem(ent, _whitelist ??= Whitelist) is not {} item)
  132. throw new AutodocError("item-unavailable");
  133. autodoc.GrabItemOrThrow(ent, item);
  134. return true;
  135. }
  136. }
  137. [Serializable, NetSerializable]
  138. public sealed partial class GrabAnyOrganAutodocStep : GrabAnyItemAutodocStep
  139. {
  140. public override EntityWhitelist Whitelist => new EntityWhitelist()
  141. {
  142. Components = ["Organ"]
  143. };
  144. public override LocId Name => "autodoc-item-organ";
  145. }
  146. [Serializable, NetSerializable]
  147. public sealed partial class GrabAnyBodyPartAutodocStep : GrabAnyItemAutodocStep
  148. {
  149. public override EntityWhitelist Whitelist => new EntityWhitelist()
  150. {
  151. Components = ["BodyPart"]
  152. };
  153. public override LocId Name => "autodoc-item-part";
  154. }
  155. /// <summary>
  156. /// Store the held item in storage, failing if it can't be picked up.
  157. /// </summary>
  158. [Serializable, NetSerializable]
  159. public sealed partial class StoreItemAutodocStep : IAutodocStep
  160. {
  161. string IAutodocStep.Title => Loc.GetString("autodoc-program-step-store-item");
  162. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  163. {
  164. autodoc.StoreItemOrThrow(ent);
  165. return true;
  166. }
  167. }
  168. /// <summary>
  169. /// Gives the held item a label, failing if there is no held item.
  170. /// </summary>
  171. [Serializable, NetSerializable]
  172. public sealed partial class SetLabelAutodocStep : IAutodocStep
  173. {
  174. [DataField(required: true)]
  175. public string Label = string.Empty;
  176. string IAutodocStep.Title => Loc.GetString("autodoc-program-step-set-label", ("label", Label));
  177. bool IAutodocStep.Validate(Entity<AutodocComponent> ent, SharedAutodocSystem autodoc)
  178. {
  179. // client will never send a blank string for label
  180. return !string.IsNullOrEmpty(Label) && Label.Length <= 20;
  181. }
  182. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  183. {
  184. var item = autodoc.GetHeldOrThrow(ent);
  185. autodoc.LabelItem(item, Label);
  186. return true;
  187. }
  188. }
  189. /// <summary>
  190. /// Waits a number of seconds before going onto the next step.
  191. /// </summary>
  192. [Serializable, NetSerializable]
  193. public sealed partial class WaitAutodocStep : IAutodocStep
  194. {
  195. [DataField(required: true)]
  196. public int Length;
  197. string IAutodocStep.Title => Loc.GetString("autodoc-program-step-wait", ("length", Length));
  198. bool IAutodocStep.Validate(Entity<AutodocComponent> ent, SharedAutodocSystem autodoc)
  199. {
  200. return Length > 0 && Length < 30;
  201. }
  202. bool IAutodocStep.Run(Entity<AutodocComponent, HandsComponent> ent, SharedAutodocSystem autodoc)
  203. {
  204. autodoc.Say(ent, Loc.GetString("autodoc-waiting"));
  205. autodoc.DelayUpdate(ent, TimeSpan.FromSeconds(Length));
  206. return true; // Waiting is for surgery
  207. }
  208. }