RottingSystem.cs 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149
  1. using Content.Server.Atmos.EntitySystems;
  2. using Content.Server.Body.Components;
  3. using Content.Server.Temperature.Components;
  4. using Content.Shared.Atmos;
  5. using Content.Shared.Atmos.Rotting;
  6. using Content.Shared.Damage;
  7. using Robust.Server.Containers;
  8. using Robust.Shared.Physics.Components;
  9. using Robust.Shared.Timing;
  10. using Content.Server.GameTicking;
  11. using Content.Server.GameTicking.Rules.Components;
  12. using Content.Shared.GameTicking.Components;
  13. namespace Content.Server.Atmos.Rotting;
  14. public sealed class RottingSystem : SharedRottingSystem
  15. {
  16. [Dependency] private readonly IGameTiming _timing = default!;
  17. [Dependency] private readonly AtmosphereSystem _atmosphere = default!;
  18. [Dependency] private readonly ContainerSystem _container = default!;
  19. [Dependency] private readonly DamageableSystem _damageable = default!;
  20. [Dependency] private readonly GameTicker _gameTicker = default!;
  21. public override void Initialize()
  22. {
  23. base.Initialize();
  24. SubscribeLocalEvent<RottingComponent, BeingGibbedEvent>(OnGibbed);
  25. SubscribeLocalEvent<TemperatureComponent, IsRottingEvent>(OnTempIsRotting);
  26. }
  27. private void OnGibbed(EntityUid uid, RottingComponent component, BeingGibbedEvent args)
  28. {
  29. if (!TryComp<PhysicsComponent>(uid, out var physics))
  30. return;
  31. if (!TryComp<PerishableComponent>(uid, out var perishable))
  32. return;
  33. var molsToDump = perishable.MolsPerSecondPerUnitMass * physics.FixturesMass * (float)component.TotalRotTime.TotalSeconds;
  34. var tileMix = _atmosphere.GetTileMixture(uid, excite: true);
  35. tileMix?.AdjustMoles(Gas.Ammonia, molsToDump);
  36. }
  37. private void OnTempIsRotting(EntityUid uid, TemperatureComponent component, ref IsRottingEvent args)
  38. {
  39. if (args.Handled)
  40. return;
  41. args.Handled = component.CurrentTemperature < Atmospherics.T0C + 0.85f;
  42. }
  43. /// <summary>
  44. /// Is anything speeding up the decay?
  45. /// e.g. buried in a grave
  46. /// TODO: hot temperatures increase rot?
  47. /// </summary>
  48. /// <returns></returns>
  49. private float GetRotRate(EntityUid uid)
  50. {
  51. if (_container.TryGetContainingContainer((uid, null, null), out var container) &&
  52. TryComp<ProRottingContainerComponent>(container.Owner, out var rotContainer))
  53. {
  54. return rotContainer.DecayModifier;
  55. }
  56. return 1f;
  57. }
  58. public override void Update(float frameTime)
  59. {
  60. base.Update(frameTime);
  61. var perishQuery = EntityQueryEnumerator<PerishableComponent>();
  62. while (perishQuery.MoveNext(out var uid, out var perishable))
  63. {
  64. if (_timing.CurTime < perishable.RotNextUpdate)
  65. continue;
  66. perishable.RotNextUpdate += perishable.PerishUpdateRate;
  67. var stage = PerishStage((uid, perishable), MaxStages);
  68. if (stage != perishable.Stage)
  69. {
  70. perishable.Stage = stage;
  71. Dirty(uid, perishable);
  72. }
  73. if (IsRotten(uid) || !IsRotProgressing(uid, perishable))
  74. continue;
  75. perishable.RotAccumulator += perishable.PerishUpdateRate * GetRotRate(uid);
  76. if (perishable.RotAccumulator >= perishable.RotAfter)
  77. {
  78. var rot = AddComp<RottingComponent>(uid);
  79. rot.NextRotUpdate = _timing.CurTime + rot.RotUpdateRate;
  80. }
  81. }
  82. var rotQuery = EntityQueryEnumerator<RottingComponent, PerishableComponent, TransformComponent>();
  83. while (rotQuery.MoveNext(out var uid, out var rotting, out var perishable, out var xform))
  84. {
  85. if (_timing.CurTime < rotting.NextRotUpdate) // This is where it starts to get noticable on larger animals, no need to run every second
  86. continue;
  87. rotting.NextRotUpdate += rotting.RotUpdateRate;
  88. if (!IsRotProgressing(uid, perishable))
  89. continue;
  90. rotting.TotalRotTime += rotting.RotUpdateRate * GetRotRate(uid);
  91. if (rotting.DealDamage)
  92. {
  93. var damage = rotting.Damage * rotting.RotUpdateRate.TotalSeconds;
  94. _damageable.TryChangeDamage(uid, damage, true, false);
  95. }
  96. if (TryComp<RotIntoComponent>(uid, out var rotInto))
  97. {
  98. var rotStageEnd = rotInto.Stage;
  99. //on tdm, faster rotting
  100. var query = EntityQueryEnumerator<TeamDeathMatchRuleComponent, GameRuleComponent>();
  101. while (query.MoveNext(out var nuid, out var tdm, out var gameRule))
  102. {
  103. if (_gameTicker.IsGameRuleActive(nuid, gameRule))
  104. {
  105. if (perishable.RotAccumulator.TotalSeconds >= 300)
  106. {
  107. Spawn(rotInto.Entity, xform.Coordinates);
  108. QueueDel(uid);
  109. return;
  110. }
  111. }
  112. }
  113. var stage = RotStage(uid, rotting, perishable);
  114. if (stage >= rotStageEnd)
  115. {
  116. Spawn(rotInto.Entity, xform.Coordinates);
  117. QueueDel(uid);
  118. continue;
  119. }
  120. }
  121. if (!TryComp<PhysicsComponent>(uid, out var physics))
  122. continue;
  123. // We need a way to get the mass of the mob alone without armor etc in the future
  124. // or just remove the mass mechanics altogether because they aren't good.
  125. var molRate = perishable.MolsPerSecondPerUnitMass * (float)rotting.RotUpdateRate.TotalSeconds;
  126. var tileMix = _atmosphere.GetTileMixture(uid, excite: true);
  127. tileMix?.AdjustMoles(Gas.Ammonia, molRate * physics.FixturesMass);
  128. }
  129. }
  130. }