SmokingSystem.cs 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. using Content.Server.Atmos.EntitySystems;
  2. using Content.Server.Body.Components;
  3. using Content.Server.Body.Systems;
  4. using Content.Shared.Chemistry.EntitySystems;
  5. using Content.Server.Forensics;
  6. using Content.Shared.Chemistry;
  7. using Content.Shared.Chemistry.Reagent;
  8. using Content.Shared.Clothing.Components;
  9. using Content.Shared.Clothing.EntitySystems;
  10. using Content.Shared.FixedPoint;
  11. using Content.Shared.Inventory;
  12. using Content.Shared.Inventory.Events;
  13. using Content.Shared.Item;
  14. using Content.Shared.Nutrition.Components;
  15. using Content.Shared.Smoking;
  16. using Content.Shared.Temperature;
  17. using Robust.Server.GameObjects;
  18. using Robust.Shared.Containers;
  19. using System.Linq;
  20. namespace Content.Server.Nutrition.EntitySystems
  21. {
  22. public sealed partial class SmokingSystem : EntitySystem
  23. {
  24. [Dependency] private readonly ReactiveSystem _reactiveSystem = default!;
  25. [Dependency] private readonly SharedSolutionContainerSystem _solutionContainerSystem = default!;
  26. [Dependency] private readonly BloodstreamSystem _bloodstreamSystem = default!;
  27. [Dependency] private readonly AtmosphereSystem _atmos = default!;
  28. [Dependency] private readonly TransformSystem _transformSystem = default!;
  29. [Dependency] private readonly InventorySystem _inventorySystem = default!;
  30. [Dependency] private readonly ClothingSystem _clothing = default!;
  31. [Dependency] private readonly SharedItemSystem _items = default!;
  32. [Dependency] private readonly SharedContainerSystem _container = default!;
  33. [Dependency] private readonly SharedAppearanceSystem _appearance = default!;
  34. [Dependency] private readonly ForensicsSystem _forensics = default!;
  35. private const float UpdateTimer = 3f;
  36. private float _timer;
  37. /// <summary>
  38. /// We keep a list of active smokables, because iterating all existing smokables would be dumb.
  39. /// </summary>
  40. private readonly HashSet<EntityUid> _active = new();
  41. public override void Initialize()
  42. {
  43. SubscribeLocalEvent<SmokableComponent, IsHotEvent>(OnSmokableIsHotEvent);
  44. SubscribeLocalEvent<SmokableComponent, ComponentShutdown>(OnSmokableShutdownEvent);
  45. SubscribeLocalEvent<SmokableComponent, GotEquippedEvent>(OnSmokeableEquipEvent);
  46. InitializeCigars();
  47. InitializePipes();
  48. InitializeVapes();
  49. }
  50. public void SetSmokableState(EntityUid uid, SmokableState state, SmokableComponent? smokable = null,
  51. AppearanceComponent? appearance = null, ClothingComponent? clothing = null)
  52. {
  53. if (!Resolve(uid, ref smokable, ref appearance, ref clothing))
  54. return;
  55. smokable.State = state;
  56. _appearance.SetData(uid, SmokingVisuals.Smoking, state, appearance);
  57. var newState = state switch
  58. {
  59. SmokableState.Lit => smokable.LitPrefix,
  60. SmokableState.Burnt => smokable.BurntPrefix,
  61. _ => smokable.UnlitPrefix
  62. };
  63. _clothing.SetEquippedPrefix(uid, newState, clothing);
  64. _items.SetHeldPrefix(uid, newState);
  65. if (state == SmokableState.Lit)
  66. _active.Add(uid);
  67. else
  68. _active.Remove(uid);
  69. }
  70. private void OnSmokableIsHotEvent(Entity<SmokableComponent> entity, ref IsHotEvent args)
  71. {
  72. args.IsHot = entity.Comp.State == SmokableState.Lit;
  73. }
  74. private void OnSmokableShutdownEvent(Entity<SmokableComponent> entity, ref ComponentShutdown args)
  75. {
  76. _active.Remove(entity);
  77. }
  78. private void OnSmokeableEquipEvent(Entity<SmokableComponent> entity, ref GotEquippedEvent args)
  79. {
  80. if (args.Slot == "mask")
  81. {
  82. _forensics.TransferDna(entity.Owner, args.Equipee, false);
  83. }
  84. }
  85. public override void Update(float frameTime)
  86. {
  87. _timer += frameTime;
  88. if (_timer < UpdateTimer)
  89. return;
  90. // TODO Use an "active smoke" component instead, EntityQuery over that.
  91. foreach (var uid in _active.ToArray())
  92. {
  93. if (!TryComp(uid, out SmokableComponent? smokable))
  94. {
  95. _active.Remove(uid);
  96. continue;
  97. }
  98. if (!_solutionContainerSystem.TryGetSolution(uid, smokable.Solution, out var soln, out var solution))
  99. {
  100. _active.Remove(uid);
  101. continue;
  102. }
  103. if (smokable.ExposeTemperature > 0 && smokable.ExposeVolume > 0)
  104. {
  105. var transform = Transform(uid);
  106. if (transform.GridUid is { } gridUid)
  107. {
  108. var position = _transformSystem.GetGridOrMapTilePosition(uid, transform);
  109. _atmos.HotspotExpose(gridUid, position, smokable.ExposeTemperature, smokable.ExposeVolume, uid, true);
  110. }
  111. }
  112. var inhaledSolution = _solutionContainerSystem.SplitSolution(soln.Value, smokable.InhaleAmount * _timer);
  113. if (solution.Volume == FixedPoint2.Zero)
  114. {
  115. RaiseLocalEvent(uid, new SmokableSolutionEmptyEvent(), true);
  116. }
  117. if (inhaledSolution.Volume == FixedPoint2.Zero)
  118. continue;
  119. // This is awful. I hate this so much.
  120. // TODO: Please, someone refactor containers and free me from this bullshit.
  121. if (!_container.TryGetContainingContainer((uid, null, null), out var containerManager) ||
  122. !(_inventorySystem.TryGetSlotEntity(containerManager.Owner, "mask", out var inMaskSlotUid) && inMaskSlotUid == uid) ||
  123. !TryComp(containerManager.Owner, out BloodstreamComponent? bloodstream))
  124. {
  125. continue;
  126. }
  127. _reactiveSystem.DoEntityReaction(containerManager.Owner, inhaledSolution, ReactionMethod.Ingestion);
  128. _bloodstreamSystem.TryAddToChemicals(containerManager.Owner, inhaledSolution, bloodstream);
  129. }
  130. _timer -= UpdateTimer;
  131. }
  132. }
  133. /// <summary>
  134. /// Directed event raised when the smokable solution is empty.
  135. /// </summary>
  136. public sealed class SmokableSolutionEmptyEvent : EntityEventArgs
  137. {
  138. }
  139. }