1
0

SurgerySystem.cs 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172
  1. // SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
  2. // SPDX-FileCopyrightText: 2024 deltanedas <39013340+deltanedas@users.noreply.github.com>
  3. // SPDX-FileCopyrightText: 2024 deltanedas <@deltanedas:kde.org>
  4. // SPDX-FileCopyrightText: 2024 gluesniffler <159397573+gluesniffler@users.noreply.github.com>
  5. // SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
  6. // SPDX-FileCopyrightText: 2025 Misandry <mary@thughunt.ing>
  7. // SPDX-FileCopyrightText: 2025 gus <august.eymann@gmail.com>
  8. //
  9. // SPDX-License-Identifier: AGPL-3.0-or-later
  10. using Content.Server.Atmos.Rotting;
  11. using Content.Server.Body.Systems;
  12. using Content.Server.Chat.Systems;
  13. using Content.Shared.Body.Part;
  14. using Content.Server.Popups;
  15. using Content.Shared.Bed.Sleep;
  16. using Content.Shared.Damage;
  17. using Content.Shared.Eye.Blinding.Systems;
  18. using Content.Shared._Shitmed.Medical.Surgery;
  19. using Content.Shared._Shitmed.Medical.Surgery.Conditions;
  20. using Content.Shared._Shitmed.Medical.Surgery.Effects.Step;
  21. using Content.Shared._Shitmed.Medical.Surgery.Tools;
  22. using Robust.Server.GameObjects;
  23. using Robust.Shared.Configuration;
  24. using Robust.Shared.Prototypes;
  25. using Robust.Shared.Utility;
  26. using Content.Shared.Verbs;
  27. namespace Content.Server._Shitmed.Medical.Surgery;
  28. public sealed class SurgerySystem : SharedSurgerySystem
  29. {
  30. [Dependency] private readonly BodySystem _body = default!;
  31. [Dependency] private readonly ChatSystem _chat = default!;
  32. [Dependency] private readonly IConfigurationManager _config = default!;
  33. [Dependency] private readonly DamageableSystem _damageable = default!;
  34. [Dependency] private readonly IPrototypeManager _prototypes = default!;
  35. [Dependency] private readonly PopupSystem _popup = default!;
  36. [Dependency] private readonly UserInterfaceSystem _ui = default!;
  37. [Dependency] private readonly RottingSystem _rot = default!;
  38. [Dependency] private readonly BlindableSystem _blindableSystem = default!;
  39. public override void Initialize()
  40. {
  41. base.Initialize();
  42. SubscribeLocalEvent<SurgeryToolComponent, GetVerbsEvent<UtilityVerb>>(OnUtilityVerb);
  43. SubscribeLocalEvent<SurgeryTargetComponent, SurgeryStepDamageEvent>(OnSurgeryStepDamage);
  44. // You might be wondering "why aren't we using StepEvent for these two?" reason being that StepEvent fires off regardless of success on the previous functions
  45. // so this would heal entities even if you had a used or incorrect organ.
  46. SubscribeLocalEvent<SurgerySpecialDamageChangeEffectComponent, SurgeryStepDamageChangeEvent>(OnSurgerySpecialDamageChange);
  47. SubscribeLocalEvent<SurgeryDamageChangeEffectComponent, SurgeryStepDamageChangeEvent>(OnSurgeryDamageChange);
  48. SubscribeLocalEvent<SurgeryStepEmoteEffectComponent, SurgeryStepEvent>(OnStepScreamComplete);
  49. SubscribeLocalEvent<SurgeryStepSpawnEffectComponent, SurgeryStepEvent>(OnStepSpawnComplete);
  50. }
  51. protected override void RefreshUI(EntityUid body)
  52. {
  53. var surgeries = new Dictionary<NetEntity, List<EntProtoId>>();
  54. foreach (var surgery in AllSurgeries)
  55. {
  56. if (GetSingleton(surgery) is not { } surgeryEnt)
  57. continue;
  58. foreach (var part in _body.GetBodyChildren(body))
  59. {
  60. var ev = new SurgeryValidEvent(body, part.Id);
  61. RaiseLocalEvent(surgeryEnt, ref ev);
  62. if (ev.Cancelled)
  63. continue;
  64. surgeries.GetOrNew(GetNetEntity(part.Id)).Add(surgery);
  65. }
  66. }
  67. _ui.SetUiState(body, SurgeryUIKey.Key, new SurgeryBuiState(surgeries));
  68. /*
  69. Reason we do this is because when applying a BUI State, it rolls back the state on the entity temporarily,
  70. which just so happens to occur right as we're checking for step completion, so we end up with the UI
  71. not updating at all until you change tools or reopen the window. I love shitcode.
  72. */
  73. _ui.ServerSendUiMessage(body, SurgeryUIKey.Key, new SurgeryBuiRefreshMessage());
  74. }
  75. private void SetDamage(EntityUid body,
  76. DamageSpecifier damage,
  77. float partMultiplier,
  78. EntityUid user,
  79. EntityUid part)
  80. {
  81. if (!TryComp<BodyPartComponent>(part, out var partComp))
  82. return;
  83. _damageable.TryChangeDamage(body,
  84. damage,
  85. true,
  86. origin: user,
  87. canSever: false,
  88. partMultiplier: partMultiplier,
  89. targetPart: _body.GetTargetBodyPart(partComp));
  90. }
  91. private void AttemptStartSurgery(Entity<SurgeryToolComponent> ent, EntityUid user, EntityUid target)
  92. {
  93. if (!IsLyingDown(target, user))
  94. return;
  95. if (user == target)
  96. {
  97. _popup.PopupEntity(Loc.GetString("surgery-error-self-surgery"), user, user);
  98. return;
  99. }
  100. _ui.OpenUi(target, SurgeryUIKey.Key, user);
  101. RefreshUI(target);
  102. }
  103. private void OnUtilityVerb(Entity<SurgeryToolComponent> ent, ref GetVerbsEvent<UtilityVerb> args)
  104. {
  105. if (!args.CanInteract
  106. || !args.CanAccess
  107. || !HasComp<SurgeryTargetComponent>(args.Target))
  108. return;
  109. var user = args.User;
  110. var target = args.Target;
  111. var verb = new UtilityVerb()
  112. {
  113. Act = () => AttemptStartSurgery(ent, user, target),
  114. Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Specific/Medical/Surgery/scalpel.rsi/"), "scalpel"),
  115. Text = Loc.GetString("surgery-verb-text"),
  116. Message = Loc.GetString("surgery-verb-message"),
  117. DoContactInteraction = true
  118. };
  119. args.Verbs.Add(verb);
  120. }
  121. private void OnSurgeryStepDamage(Entity<SurgeryTargetComponent> ent, ref SurgeryStepDamageEvent args) =>
  122. SetDamage(args.Body, args.Damage, args.PartMultiplier, args.User, args.Part);
  123. private void OnSurgeryDamageChange(Entity<SurgeryDamageChangeEffectComponent> ent, ref SurgeryStepDamageChangeEvent args)
  124. {
  125. var damageChange = ent.Comp.Damage;
  126. if (HasComp<ForcedSleepingComponent>(args.Body))
  127. damageChange = damageChange * ent.Comp.SleepModifier;
  128. SetDamage(args.Body, damageChange, 0.5f, args.User, args.Part);
  129. }
  130. private void OnSurgerySpecialDamageChange(Entity<SurgerySpecialDamageChangeEffectComponent> ent, ref SurgeryStepDamageChangeEvent args)
  131. {
  132. // Im killing this shit soon too, inshallah.
  133. if (ent.Comp.DamageType == "Rot")
  134. _rot.ReduceAccumulator(args.Body, TimeSpan.FromSeconds(2147483648)); // BEHOLD, SHITCODE THAT I JUST COPY PASTED. I'll redo it at some point, pinky swear :)
  135. /*else if (ent.Comp.DamageType == "Eye"
  136. && TryComp(ent, out BlindableComponent? blindComp)
  137. && blindComp.EyeDamage > 0)
  138. _blindableSystem.AdjustEyeDamage((args.Body, blindComp), -blindComp!.EyeDamage);*/
  139. }
  140. private void OnStepScreamComplete(Entity<SurgeryStepEmoteEffectComponent> ent, ref SurgeryStepEvent args)
  141. {
  142. if (HasComp<ForcedSleepingComponent>(args.Body))
  143. return;
  144. _chat.TryEmoteWithChat(args.Body, ent.Comp.Emote);
  145. }
  146. private void OnStepSpawnComplete(Entity<SurgeryStepSpawnEffectComponent> ent, ref SurgeryStepEvent args) =>
  147. SpawnAtPosition(ent.Comp.Entity, Transform(args.Body).Coordinates);
  148. }