SharedRottingSystem.cs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. using Content.Shared.Examine;
  2. using Content.Shared.IdentityManagement;
  3. using Content.Shared.Mobs;
  4. using Content.Shared.Mobs.Components;
  5. using Content.Shared.Mobs.Systems;
  6. using Content.Shared.Rejuvenate;
  7. using Robust.Shared.Containers;
  8. using Robust.Shared.Timing;
  9. namespace Content.Shared.Atmos.Rotting;
  10. public abstract class SharedRottingSystem : EntitySystem
  11. {
  12. [Dependency] private readonly IGameTiming _timing = default!;
  13. [Dependency] private readonly SharedContainerSystem _container = default!;
  14. [Dependency] private readonly MobStateSystem _mobState = default!;
  15. public const int MaxStages = 3;
  16. public override void Initialize()
  17. {
  18. base.Initialize();
  19. SubscribeLocalEvent<PerishableComponent, MapInitEvent>(OnPerishableMapInit);
  20. SubscribeLocalEvent<PerishableComponent, MobStateChangedEvent>(OnMobStateChanged);
  21. SubscribeLocalEvent<PerishableComponent, ExaminedEvent>(OnPerishableExamined);
  22. SubscribeLocalEvent<RottingComponent, ComponentShutdown>(OnShutdown);
  23. SubscribeLocalEvent<RottingComponent, MobStateChangedEvent>(OnRottingMobStateChanged);
  24. SubscribeLocalEvent<RottingComponent, RejuvenateEvent>(OnRejuvenate);
  25. SubscribeLocalEvent<RottingComponent, ExaminedEvent>(OnExamined);
  26. }
  27. private void OnPerishableMapInit(EntityUid uid, PerishableComponent component, MapInitEvent args)
  28. {
  29. component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
  30. }
  31. private void OnMobStateChanged(EntityUid uid, PerishableComponent component, MobStateChangedEvent args)
  32. {
  33. if (args.NewMobState != MobState.Dead && args.OldMobState != MobState.Dead)
  34. return;
  35. if (HasComp<RottingComponent>(uid))
  36. return;
  37. component.RotAccumulator = TimeSpan.Zero;
  38. component.RotNextUpdate = _timing.CurTime + component.PerishUpdateRate;
  39. }
  40. private void OnPerishableExamined(Entity<PerishableComponent> perishable, ref ExaminedEvent args)
  41. {
  42. int stage = PerishStage(perishable, MaxStages);
  43. if (stage < 1 || stage > MaxStages)
  44. {
  45. // We dont push an examined string if it hasen't started "perishing" or it's already rotting
  46. return;
  47. }
  48. var isMob = HasComp<MobStateComponent>(perishable);
  49. var description = "perishable-" + stage + (!isMob ? "-nonmob" : string.Empty);
  50. args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(perishable, EntityManager))));
  51. }
  52. private void OnShutdown(EntityUid uid, RottingComponent component, ComponentShutdown args)
  53. {
  54. if (TryComp<PerishableComponent>(uid, out var perishable))
  55. {
  56. perishable.RotNextUpdate = TimeSpan.Zero;
  57. }
  58. }
  59. private void OnRottingMobStateChanged(EntityUid uid, RottingComponent component, MobStateChangedEvent args)
  60. {
  61. if (args.NewMobState == MobState.Dead)
  62. return;
  63. RemCompDeferred(uid, component);
  64. }
  65. private void OnRejuvenate(EntityUid uid, RottingComponent component, RejuvenateEvent args)
  66. {
  67. RemCompDeferred<RottingComponent>(uid);
  68. }
  69. private void OnExamined(EntityUid uid, RottingComponent component, ExaminedEvent args)
  70. {
  71. var stage = RotStage(uid, component);
  72. var description = stage switch
  73. {
  74. >= 2 => "rotting-extremely-bloated",
  75. >= 1 => "rotting-bloated",
  76. _ => "rotting-rotting"
  77. };
  78. if (!HasComp<MobStateComponent>(uid))
  79. description += "-nonmob";
  80. args.PushMarkup(Loc.GetString(description, ("target", Identity.Entity(uid, EntityManager))));
  81. }
  82. /// <summary>
  83. /// Return an integer from 0 to maxStage representing how close to rotting an entity is. Used to
  84. /// generate examine messages for items that are starting to rot.
  85. /// </summary>
  86. public int PerishStage(Entity<PerishableComponent> perishable, int maxStages)
  87. {
  88. if (perishable.Comp.RotAfter.TotalSeconds == 0 || perishable.Comp.RotAccumulator.TotalSeconds == 0)
  89. return 0;
  90. return (int)(1 + maxStages * perishable.Comp.RotAccumulator.TotalSeconds / perishable.Comp.RotAfter.TotalSeconds);
  91. }
  92. public bool IsRotProgressing(EntityUid uid, PerishableComponent? perishable)
  93. {
  94. // things don't perish by default.
  95. if (!Resolve(uid, ref perishable, false))
  96. return false;
  97. // Overrides all the other checks.
  98. if (perishable.ForceRotProgression)
  99. return true;
  100. // only dead things or inanimate objects can rot
  101. if (TryComp<MobStateComponent>(uid, out var mobState) && !_mobState.IsDead(uid, mobState))
  102. return false;
  103. if (_container.TryGetOuterContainer(uid, Transform(uid), out var container) &&
  104. HasComp<AntiRottingContainerComponent>(container.Owner))
  105. {
  106. return false;
  107. }
  108. var ev = new IsRottingEvent();
  109. RaiseLocalEvent(uid, ref ev);
  110. return !ev.Handled;
  111. }
  112. public bool IsRotten(EntityUid uid, RottingComponent? rotting = null)
  113. {
  114. return Resolve(uid, ref rotting, false);
  115. }
  116. public void ReduceAccumulator(EntityUid uid, TimeSpan time)
  117. {
  118. if (!TryComp<PerishableComponent>(uid, out var perishable))
  119. return;
  120. if (!TryComp<RottingComponent>(uid, out var rotting))
  121. {
  122. perishable.RotAccumulator -= time;
  123. return;
  124. }
  125. var total = (rotting.TotalRotTime + perishable.RotAccumulator) - time;
  126. if (total < perishable.RotAfter)
  127. {
  128. RemCompDeferred(uid, rotting);
  129. perishable.RotAccumulator = total;
  130. }
  131. else
  132. rotting.TotalRotTime = total - perishable.RotAfter;
  133. }
  134. /// <summary>
  135. /// Return the rot stage, usually from 0 to 2 inclusive.
  136. /// </summary>
  137. public int RotStage(EntityUid uid, RottingComponent? comp = null, PerishableComponent? perishable = null)
  138. {
  139. if (!Resolve(uid, ref comp, ref perishable))
  140. return 0;
  141. return (int)(comp.TotalRotTime.TotalSeconds / perishable.RotAfter.TotalSeconds);
  142. }
  143. }