1
0

KitchenSpikeSystem.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. using Content.Server.Administration.Logs;
  2. using Content.Server.Body.Systems;
  3. using Content.Server.Kitchen.Components;
  4. using Content.Server.Popups;
  5. using Content.Shared.Chat;
  6. using Content.Shared.Damage;
  7. using Content.Shared.Database;
  8. using Content.Shared.DoAfter;
  9. using Content.Shared.DragDrop;
  10. using Content.Shared.IdentityManagement;
  11. using Content.Shared.Interaction;
  12. using Content.Shared.Interaction.Events;
  13. using Content.Shared.Kitchen;
  14. using Content.Shared.Kitchen.Components;
  15. using Content.Shared.Mobs.Components;
  16. using Content.Shared.Mobs.Systems;
  17. using Content.Shared.Nutrition.Components;
  18. using Content.Shared.Popups;
  19. using Content.Shared.Storage;
  20. using Robust.Server.GameObjects;
  21. using Robust.Shared.Audio.Systems;
  22. using Robust.Shared.Player;
  23. using Robust.Shared.Random;
  24. using static Content.Shared.Kitchen.Components.KitchenSpikeComponent;
  25. namespace Content.Server.Kitchen.EntitySystems
  26. {
  27. public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
  28. {
  29. [Dependency] private readonly PopupSystem _popupSystem = default!;
  30. [Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
  31. [Dependency] private readonly IAdminLogManager _logger = default!;
  32. [Dependency] private readonly MobStateSystem _mobStateSystem = default!;
  33. [Dependency] private readonly IRobustRandom _random = default!;
  34. [Dependency] private readonly TransformSystem _transform = default!;
  35. [Dependency] private readonly BodySystem _bodySystem = default!;
  36. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  37. [Dependency] private readonly SharedAudioSystem _audio = default!;
  38. [Dependency] private readonly MetaDataSystem _metaData = default!;
  39. [Dependency] private readonly SharedSuicideSystem _suicide = default!;
  40. public override void Initialize()
  41. {
  42. base.Initialize();
  43. SubscribeLocalEvent<KitchenSpikeComponent, InteractUsingEvent>(OnInteractUsing);
  44. SubscribeLocalEvent<KitchenSpikeComponent, InteractHandEvent>(OnInteractHand);
  45. SubscribeLocalEvent<KitchenSpikeComponent, DragDropTargetEvent>(OnDragDrop);
  46. //DoAfter
  47. SubscribeLocalEvent<KitchenSpikeComponent, SpikeDoAfterEvent>(OnDoAfter);
  48. SubscribeLocalEvent<KitchenSpikeComponent, SuicideByEnvironmentEvent>(OnSuicideByEnvironment);
  49. SubscribeLocalEvent<ButcherableComponent, CanDropDraggedEvent>(OnButcherableCanDrop);
  50. }
  51. private void OnButcherableCanDrop(Entity<ButcherableComponent> entity, ref CanDropDraggedEvent args)
  52. {
  53. args.Handled = true;
  54. args.CanDrop |= entity.Comp.Type != ButcheringType.Knife;
  55. }
  56. /// <summary>
  57. /// TODO: Update this so it actually meatspikes the user instead of applying lethal damage to them.
  58. /// </summary>
  59. private void OnSuicideByEnvironment(Entity<KitchenSpikeComponent> entity, ref SuicideByEnvironmentEvent args)
  60. {
  61. if (args.Handled)
  62. return;
  63. if (!TryComp<DamageableComponent>(args.Victim, out var damageableComponent))
  64. return;
  65. _suicide.ApplyLethalDamage((args.Victim, damageableComponent), "Piercing");
  66. var othersMessage = Loc.GetString("comp-kitchen-spike-suicide-other", ("victim", args.Victim));
  67. _popupSystem.PopupEntity(othersMessage, args.Victim, Filter.PvsExcept(args.Victim), true);
  68. var selfMessage = Loc.GetString("comp-kitchen-spike-suicide-self");
  69. _popupSystem.PopupEntity(selfMessage, args.Victim, args.Victim);
  70. args.Handled = true;
  71. }
  72. private void OnDoAfter(Entity<KitchenSpikeComponent> entity, ref SpikeDoAfterEvent args)
  73. {
  74. if (args.Args.Target == null)
  75. return;
  76. if (TryComp<ButcherableComponent>(args.Args.Target.Value, out var butcherable))
  77. butcherable.BeingButchered = false;
  78. if (args.Cancelled)
  79. {
  80. entity.Comp.InUse = false;
  81. return;
  82. }
  83. if (args.Handled)
  84. return;
  85. if (Spikeable(entity, args.Args.User, args.Args.Target.Value, entity.Comp, butcherable))
  86. Spike(entity, args.Args.User, args.Args.Target.Value, entity.Comp);
  87. entity.Comp.InUse = false;
  88. args.Handled = true;
  89. }
  90. private void OnDragDrop(Entity<KitchenSpikeComponent> entity, ref DragDropTargetEvent args)
  91. {
  92. if (args.Handled)
  93. return;
  94. args.Handled = true;
  95. if (Spikeable(entity, args.User, args.Dragged, entity.Comp))
  96. TrySpike(entity, args.User, args.Dragged, entity.Comp);
  97. }
  98. private void OnInteractHand(Entity<KitchenSpikeComponent> entity, ref InteractHandEvent args)
  99. {
  100. if (args.Handled)
  101. return;
  102. if (entity.Comp.PrototypesToSpawn?.Count > 0)
  103. {
  104. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-knife-needed"), entity, args.User);
  105. args.Handled = true;
  106. }
  107. }
  108. private void OnInteractUsing(Entity<KitchenSpikeComponent> entity, ref InteractUsingEvent args)
  109. {
  110. if (args.Handled)
  111. return;
  112. if (TryGetPiece(entity, args.User, args.Used))
  113. args.Handled = true;
  114. }
  115. private void Spike(EntityUid uid, EntityUid userUid, EntityUid victimUid,
  116. KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null)
  117. {
  118. if (!Resolve(uid, ref component) || !Resolve(victimUid, ref butcherable))
  119. return;
  120. _logger.Add(LogType.Gib, LogImpact.Extreme, $"{ToPrettyString(userUid):user} kitchen spiked {ToPrettyString(victimUid):target}");
  121. // TODO VERY SUS
  122. component.PrototypesToSpawn = EntitySpawnCollection.GetSpawns(butcherable.SpawnedEntities, _random);
  123. // This feels not okay, but entity is getting deleted on "Spike", for now...
  124. component.MeatSource1p = Loc.GetString("comp-kitchen-spike-remove-meat", ("victim", victimUid));
  125. component.MeatSource0 = Loc.GetString("comp-kitchen-spike-remove-meat-last", ("victim", victimUid));
  126. component.Victim = Name(victimUid);
  127. UpdateAppearance(uid, null, component);
  128. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-kill", ("user", Identity.Entity(userUid, EntityManager)), ("victim", victimUid)), uid, PopupType.LargeCaution);
  129. _transform.SetCoordinates(victimUid, Transform(uid).Coordinates);
  130. // THE WHAT?
  131. // TODO: Need to be able to leave them on the spike to do DoT, see ss13.
  132. var gibs = _bodySystem.GibBody(victimUid);
  133. foreach (var gib in gibs) {
  134. QueueDel(gib);
  135. }
  136. _audio.PlayPvs(component.SpikeSound, uid);
  137. }
  138. private bool TryGetPiece(EntityUid uid, EntityUid user, EntityUid used,
  139. KitchenSpikeComponent? component = null, SharpComponent? sharp = null)
  140. {
  141. if (!Resolve(uid, ref component) || component.PrototypesToSpawn == null || component.PrototypesToSpawn.Count == 0)
  142. return false;
  143. // Is using knife
  144. if (!Resolve(used, ref sharp, false) )
  145. {
  146. return false;
  147. }
  148. var item = _random.PickAndTake(component.PrototypesToSpawn);
  149. var ent = Spawn(item, Transform(uid).Coordinates);
  150. _metaData.SetEntityName(ent,
  151. Loc.GetString("comp-kitchen-spike-meat-name", ("name", Name(ent)), ("victim", component.Victim)));
  152. if (component.PrototypesToSpawn.Count != 0)
  153. _popupSystem.PopupEntity(component.MeatSource1p, uid, user, PopupType.MediumCaution);
  154. else
  155. {
  156. UpdateAppearance(uid, null, component);
  157. _popupSystem.PopupEntity(component.MeatSource0, uid, user, PopupType.MediumCaution);
  158. }
  159. return true;
  160. }
  161. private void UpdateAppearance(EntityUid uid, AppearanceComponent? appearance = null, KitchenSpikeComponent? component = null)
  162. {
  163. if (!Resolve(uid, ref component, ref appearance, false))
  164. return;
  165. _appearance.SetData(uid, KitchenSpikeVisuals.Status, component.PrototypesToSpawn?.Count > 0 ? KitchenSpikeStatus.Bloody : KitchenSpikeStatus.Empty, appearance);
  166. }
  167. private bool Spikeable(EntityUid uid, EntityUid userUid, EntityUid victimUid,
  168. KitchenSpikeComponent? component = null, ButcherableComponent? butcherable = null)
  169. {
  170. if (!Resolve(uid, ref component))
  171. return false;
  172. if (component.PrototypesToSpawn?.Count > 0)
  173. {
  174. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-collect", ("this", uid)), uid, userUid);
  175. return false;
  176. }
  177. if (!Resolve(victimUid, ref butcherable, false))
  178. {
  179. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid);
  180. return false;
  181. }
  182. switch (butcherable.Type)
  183. {
  184. case ButcheringType.Spike:
  185. return true;
  186. case ButcheringType.Knife:
  187. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher-knife", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid);
  188. return false;
  189. default:
  190. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-butcher", ("victim", Identity.Entity(victimUid, EntityManager)), ("this", uid)), victimUid, userUid);
  191. return false;
  192. }
  193. }
  194. public bool TrySpike(EntityUid uid, EntityUid userUid, EntityUid victimUid, KitchenSpikeComponent? component = null,
  195. ButcherableComponent? butcherable = null, MobStateComponent? mobState = null)
  196. {
  197. if (!Resolve(uid, ref component) || component.InUse ||
  198. !Resolve(victimUid, ref butcherable) || butcherable.BeingButchered)
  199. return false;
  200. // THE WHAT? (again)
  201. // Prevent dead from being spiked TODO: Maybe remove when rounds can be played and DOT is implemented
  202. if (Resolve(victimUid, ref mobState, false) &&
  203. _mobStateSystem.IsAlive(victimUid, mobState))
  204. {
  205. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-deny-not-dead", ("victim", Identity.Entity(victimUid, EntityManager))),
  206. victimUid, userUid);
  207. return true;
  208. }
  209. if (userUid != victimUid)
  210. {
  211. _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-begin-hook-victim", ("user", Identity.Entity(userUid, EntityManager)), ("this", uid)), victimUid, victimUid, PopupType.LargeCaution);
  212. }
  213. // TODO: make it work when SuicideEvent is implemented
  214. // else
  215. // _popupSystem.PopupEntity(Loc.GetString("comp-kitchen-spike-begin-hook-self", ("this", uid)), victimUid, Filter.Pvs(uid)); // This is actually unreachable and should be in SuicideEvent
  216. butcherable.BeingButchered = true;
  217. component.InUse = true;
  218. var doAfterArgs = new DoAfterArgs(EntityManager, userUid, component.SpikeDelay + butcherable.ButcherDelay, new SpikeDoAfterEvent(), uid, target: victimUid, used: uid)
  219. {
  220. BreakOnDamage = true,
  221. BreakOnMove = true,
  222. NeedHand = true,
  223. BreakOnDropItem = false,
  224. };
  225. _doAfter.TryStartDoAfter(doAfterArgs);
  226. return true;
  227. }
  228. }
  229. }