1
0

BarotraumaSystem.cs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291
  1. using System.Diagnostics.CodeAnalysis;
  2. using Content.Server.Administration.Logs;
  3. using Content.Server.Atmos.Components;
  4. using Content.Shared.Alert;
  5. using Content.Shared.Atmos;
  6. using Content.Shared.Damage;
  7. using Content.Shared.Database;
  8. using Content.Shared.FixedPoint;
  9. using Content.Shared.Inventory;
  10. using Content.Shared.Inventory.Events;
  11. using Robust.Shared.Containers;
  12. namespace Content.Server.Atmos.EntitySystems
  13. {
  14. public sealed class BarotraumaSystem : EntitySystem
  15. {
  16. [Dependency] private readonly AtmosphereSystem _atmosphereSystem = default!;
  17. [Dependency] private readonly DamageableSystem _damageableSystem = default!;
  18. [Dependency] private readonly AlertsSystem _alertsSystem = default!;
  19. [Dependency] private readonly IAdminLogManager _adminLogger = default!;
  20. [Dependency] private readonly InventorySystem _inventorySystem = default!;
  21. private const float UpdateTimer = 1f;
  22. private float _timer;
  23. public override void Initialize()
  24. {
  25. SubscribeLocalEvent<PressureProtectionComponent, GotEquippedEvent>(OnPressureProtectionEquipped);
  26. SubscribeLocalEvent<PressureProtectionComponent, GotUnequippedEvent>(OnPressureProtectionUnequipped);
  27. SubscribeLocalEvent<PressureProtectionComponent, ComponentInit>(OnUpdateResistance);
  28. SubscribeLocalEvent<PressureProtectionComponent, ComponentRemove>(OnUpdateResistance);
  29. SubscribeLocalEvent<PressureImmunityComponent, ComponentInit>(OnPressureImmuneInit);
  30. SubscribeLocalEvent<PressureImmunityComponent, ComponentRemove>(OnPressureImmuneRemove);
  31. }
  32. private void OnPressureImmuneInit(EntityUid uid, PressureImmunityComponent pressureImmunity, ComponentInit args)
  33. {
  34. if (TryComp<BarotraumaComponent>(uid, out var barotrauma))
  35. {
  36. barotrauma.HasImmunity = true;
  37. }
  38. }
  39. private void OnPressureImmuneRemove(EntityUid uid, PressureImmunityComponent pressureImmunity, ComponentRemove args)
  40. {
  41. if (TryComp<BarotraumaComponent>(uid, out var barotrauma))
  42. {
  43. barotrauma.HasImmunity = false;
  44. }
  45. }
  46. /// <summary>
  47. /// Generic method for updating resistance on component Lifestage events
  48. /// </summary>
  49. private void OnUpdateResistance(EntityUid uid, PressureProtectionComponent pressureProtection, EntityEventArgs args)
  50. {
  51. if (TryComp<BarotraumaComponent>(uid, out var barotrauma))
  52. {
  53. UpdateCachedResistances(uid, barotrauma);
  54. }
  55. }
  56. private void OnPressureProtectionEquipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotEquippedEvent args)
  57. {
  58. if (TryComp<BarotraumaComponent>(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot))
  59. {
  60. UpdateCachedResistances(args.Equipee, barotrauma);
  61. }
  62. }
  63. private void OnPressureProtectionUnequipped(EntityUid uid, PressureProtectionComponent pressureProtection, GotUnequippedEvent args)
  64. {
  65. if (TryComp<BarotraumaComponent>(args.Equipee, out var barotrauma) && barotrauma.ProtectionSlots.Contains(args.Slot))
  66. {
  67. UpdateCachedResistances(args.Equipee, barotrauma);
  68. }
  69. }
  70. /// <summary>
  71. /// Computes the pressure resistance for the entity coming from the equipment and any innate resistance.
  72. /// The ProtectionSlots field of the Barotrauma component specifies which parts must be protected for the protection to have any effet.
  73. /// </summary>
  74. private void UpdateCachedResistances(EntityUid uid, BarotraumaComponent barotrauma)
  75. {
  76. if (barotrauma.ProtectionSlots.Count != 0)
  77. {
  78. if (!TryComp(uid, out InventoryComponent? inv) || !TryComp(uid, out ContainerManagerComponent? contMan))
  79. {
  80. return;
  81. }
  82. var hPModifier = float.MinValue;
  83. var hPMultiplier = float.MinValue;
  84. var lPModifier = float.MaxValue;
  85. var lPMultiplier = float.MaxValue;
  86. foreach (var slot in barotrauma.ProtectionSlots)
  87. {
  88. if (!_inventorySystem.TryGetSlotEntity(uid, slot, out var equipment, inv, contMan)
  89. || !TryGetPressureProtectionValues(equipment.Value,
  90. out var itemHighMultiplier,
  91. out var itemHighModifier,
  92. out var itemLowMultiplier,
  93. out var itemLowModifier))
  94. {
  95. // Missing protection, skin is exposed.
  96. hPModifier = 0f;
  97. hPMultiplier = 1f;
  98. lPModifier = 0f;
  99. lPMultiplier = 1f;
  100. break;
  101. }
  102. // The entity is as protected as its weakest part protection
  103. hPModifier = Math.Max(hPModifier, itemHighModifier.Value);
  104. hPMultiplier = Math.Max(hPMultiplier, itemHighMultiplier.Value);
  105. lPModifier = Math.Min(lPModifier, itemLowModifier.Value);
  106. lPMultiplier = Math.Min(lPMultiplier, itemLowMultiplier.Value);
  107. }
  108. barotrauma.HighPressureModifier = hPModifier;
  109. barotrauma.HighPressureMultiplier = hPMultiplier;
  110. barotrauma.LowPressureModifier = lPModifier;
  111. barotrauma.LowPressureMultiplier = lPMultiplier;
  112. }
  113. // any innate pressure resistance ?
  114. if (TryGetPressureProtectionValues(uid,
  115. out var highMultiplier,
  116. out var highModifier,
  117. out var lowMultiplier,
  118. out var lowModifier))
  119. {
  120. barotrauma.HighPressureModifier += highModifier.Value;
  121. barotrauma.HighPressureMultiplier *= highMultiplier.Value;
  122. barotrauma.LowPressureModifier += lowModifier.Value;
  123. barotrauma.LowPressureMultiplier *= lowMultiplier.Value;
  124. }
  125. }
  126. /// <summary>
  127. /// Returns adjusted pressure after having applied resistances from equipment and innate (if any), to check against a low pressure hazard threshold
  128. /// </summary>
  129. public float GetFeltLowPressure(EntityUid uid, BarotraumaComponent barotrauma, float environmentPressure)
  130. {
  131. if (barotrauma.HasImmunity)
  132. {
  133. return Atmospherics.OneAtmosphere;
  134. }
  135. var modified = (environmentPressure + barotrauma.LowPressureModifier) * (barotrauma.LowPressureMultiplier);
  136. return Math.Min(modified, Atmospherics.OneAtmosphere);
  137. }
  138. /// <summary>
  139. /// Returns adjusted pressure after having applied resistances from equipment and innate (if any), to check against a high pressure hazard threshold
  140. /// </summary>
  141. public float GetFeltHighPressure(EntityUid uid, BarotraumaComponent barotrauma, float environmentPressure)
  142. {
  143. if (barotrauma.HasImmunity)
  144. {
  145. return Atmospherics.OneAtmosphere;
  146. }
  147. var modified = (environmentPressure + barotrauma.HighPressureModifier) * (barotrauma.HighPressureMultiplier);
  148. return Math.Max(modified, Atmospherics.OneAtmosphere);
  149. }
  150. public bool TryGetPressureProtectionValues(
  151. Entity<PressureProtectionComponent?> ent,
  152. [NotNullWhen(true)] out float? highMultiplier,
  153. [NotNullWhen(true)] out float? highModifier,
  154. [NotNullWhen(true)] out float? lowMultiplier,
  155. [NotNullWhen(true)] out float? lowModifier)
  156. {
  157. highMultiplier = null;
  158. highModifier = null;
  159. lowMultiplier = null;
  160. lowModifier = null;
  161. if (!Resolve(ent, ref ent.Comp, false))
  162. return false;
  163. var comp = ent.Comp;
  164. var ev = new GetPressureProtectionValuesEvent
  165. {
  166. HighPressureMultiplier = comp.HighPressureMultiplier,
  167. HighPressureModifier = comp.HighPressureModifier,
  168. LowPressureMultiplier = comp.LowPressureMultiplier,
  169. LowPressureModifier = comp.LowPressureModifier
  170. };
  171. RaiseLocalEvent(ent, ref ev);
  172. highMultiplier = ev.HighPressureMultiplier;
  173. highModifier = ev.HighPressureModifier;
  174. lowMultiplier = ev.LowPressureMultiplier;
  175. lowModifier = ev.LowPressureModifier;
  176. return true;
  177. }
  178. public override void Update(float frameTime)
  179. {
  180. _timer += frameTime;
  181. if (_timer < UpdateTimer)
  182. return;
  183. _timer -= UpdateTimer;
  184. var enumerator = EntityQueryEnumerator<BarotraumaComponent, DamageableComponent>();
  185. while (enumerator.MoveNext(out var uid, out var barotrauma, out var damageable))
  186. {
  187. var totalDamage = FixedPoint2.Zero;
  188. foreach (var (barotraumaDamageType, _) in barotrauma.Damage.DamageDict)
  189. {
  190. if (!damageable.Damage.DamageDict.TryGetValue(barotraumaDamageType, out var damage))
  191. continue;
  192. totalDamage += damage;
  193. }
  194. if (totalDamage >= barotrauma.MaxDamage)
  195. continue;
  196. var pressure = 1f;
  197. if (_atmosphereSystem.GetContainingMixture(uid) is { } mixture)
  198. {
  199. pressure = MathF.Max(mixture.Pressure, 1f);
  200. }
  201. pressure = pressure switch
  202. {
  203. // Adjust pressure based on equipment. Works differently depending on if it's "high" or "low".
  204. <= Atmospherics.WarningLowPressure => GetFeltLowPressure(uid, barotrauma, pressure),
  205. >= Atmospherics.WarningHighPressure => GetFeltHighPressure(uid, barotrauma, pressure),
  206. _ => pressure
  207. };
  208. if (pressure <= Atmospherics.HazardLowPressure)
  209. {
  210. // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
  211. _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * Atmospherics.LowPressureDamage, true, false, canSever: false, partMultiplier: 0.2f); // Shitmed Change
  212. if (!barotrauma.TakingDamage)
  213. {
  214. barotrauma.TakingDamage = true;
  215. _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking low pressure damage");
  216. }
  217. _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 2);
  218. }
  219. else if (pressure >= Atmospherics.HazardHighPressure)
  220. {
  221. var damageScale = MathF.Min(((pressure / Atmospherics.HazardHighPressure) - 1) * Atmospherics.PressureDamageCoefficient, Atmospherics.MaxHighPressureDamage);
  222. // Deal damage and ignore resistances. Resistance to pressure damage should be done via pressure protection gear.
  223. _damageableSystem.TryChangeDamage(uid, barotrauma.Damage * damageScale, true, false, canSever: false); // Shitmed Change
  224. if (!barotrauma.TakingDamage)
  225. {
  226. barotrauma.TakingDamage = true;
  227. _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} started taking high pressure damage");
  228. }
  229. _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 2);
  230. }
  231. else
  232. {
  233. // Within safe pressure limits
  234. if (barotrauma.TakingDamage)
  235. {
  236. barotrauma.TakingDamage = false;
  237. _adminLogger.Add(LogType.Barotrauma, $"{ToPrettyString(uid):entity} stopped taking pressure damage");
  238. }
  239. // Set correct alert.
  240. switch (pressure)
  241. {
  242. case <= Atmospherics.WarningLowPressure:
  243. _alertsSystem.ShowAlert(uid, barotrauma.LowPressureAlert, 1);
  244. break;
  245. case >= Atmospherics.WarningHighPressure:
  246. _alertsSystem.ShowAlert(uid, barotrauma.HighPressureAlert, 1);
  247. break;
  248. default:
  249. _alertsSystem.ClearAlertCategory(uid, barotrauma.PressureAlertCategory);
  250. break;
  251. }
  252. }
  253. }
  254. }
  255. }
  256. }