PowerSinkSystem.cs 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. using Content.Server.Explosion.EntitySystems;
  2. using Content.Server.Power.Components;
  3. using Content.Shared.Examine;
  4. using Robust.Shared.Utility;
  5. using Content.Server.Chat.Systems;
  6. using Content.Server.Station.Systems;
  7. using Robust.Shared.Timing;
  8. using Robust.Shared.Audio;
  9. using Robust.Shared.Audio.Systems;
  10. using Content.Server.Power.EntitySystems;
  11. namespace Content.Server.PowerSink
  12. {
  13. public sealed class PowerSinkSystem : EntitySystem
  14. {
  15. /// <summary>
  16. /// Percentage of battery full to trigger the announcement warning at.
  17. /// </summary>
  18. private const float WarningMessageThreshold = 0.70f;
  19. private readonly float[] _warningSoundThresholds = new[] { .80f, .90f, .95f, .98f };
  20. /// <summary>
  21. /// Length of time to delay explosion from battery full state -- this is used to play
  22. /// a brief SFX winding up the explosion.
  23. /// </summary>
  24. /// <returns></returns>
  25. private readonly TimeSpan _explosionDelayTime = TimeSpan.FromSeconds(1.465);
  26. [Dependency] private readonly IGameTiming _gameTiming = default!;
  27. [Dependency] private readonly ChatSystem _chat = default!;
  28. [Dependency] private readonly ExplosionSystem _explosionSystem = default!;
  29. [Dependency] private readonly SharedAudioSystem _audio = default!;
  30. [Dependency] private readonly StationSystem _station = default!;
  31. [Dependency] private readonly BatterySystem _battery = default!;
  32. public override void Initialize()
  33. {
  34. base.Initialize();
  35. SubscribeLocalEvent<PowerSinkComponent, ExaminedEvent>(OnExamine);
  36. }
  37. private void OnExamine(EntityUid uid, PowerSinkComponent component, ExaminedEvent args)
  38. {
  39. if (!args.IsInDetailsRange || !TryComp<PowerConsumerComponent>(uid, out var consumer))
  40. return;
  41. var drainAmount = (int) consumer.NetworkLoad.ReceivingPower / 1000;
  42. args.PushMarkup(
  43. Loc.GetString(
  44. "powersink-examine-drain-amount",
  45. ("amount", drainAmount),
  46. ("markupDrainColor", "orange"))
  47. );
  48. }
  49. public override void Update(float frameTime)
  50. {
  51. var toRemove = new RemQueue<(EntityUid Entity, PowerSinkComponent Sink)>();
  52. var query = EntityQueryEnumerator<PowerSinkComponent, PowerConsumerComponent, BatteryComponent, TransformComponent>();
  53. // Realistically it's gonna be like <5 per station.
  54. while (query.MoveNext(out var entity, out var component, out var networkLoad, out var battery, out var transform))
  55. {
  56. if (!transform.Anchored)
  57. continue;
  58. _battery.SetCharge(entity, battery.CurrentCharge + networkLoad.NetworkLoad.ReceivingPower / 1000, battery);
  59. var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
  60. // Check for warning message threshold
  61. if (!component.SentImminentExplosionWarningMessage &&
  62. currentBatteryThreshold >= WarningMessageThreshold)
  63. {
  64. NotifyStationOfImminentExplosion(entity, component);
  65. }
  66. // Check for warning sound threshold
  67. foreach (var testThreshold in _warningSoundThresholds)
  68. {
  69. if (currentBatteryThreshold >= testThreshold &&
  70. testThreshold > component.HighestWarningSoundThreshold)
  71. {
  72. component.HighestWarningSoundThreshold = currentBatteryThreshold; // Don't re-play in future until next threshold hit
  73. _audio.PlayPvs(component.ElectricSound, entity); // Play SFX
  74. break;
  75. }
  76. }
  77. // Check for explosion
  78. if (battery.CurrentCharge < battery.MaxCharge)
  79. continue;
  80. if (component.ExplosionTime == null)
  81. {
  82. // Set explosion sequence to start soon
  83. component.ExplosionTime = _gameTiming.CurTime.Add(_explosionDelayTime);
  84. // Wind-up SFX
  85. _audio.PlayPvs(component.ChargeFireSound, entity); // Play SFX
  86. }
  87. else if (_gameTiming.CurTime >= component.ExplosionTime)
  88. {
  89. // Explode!
  90. toRemove.Add((entity, component));
  91. }
  92. }
  93. foreach (var (entity, component) in toRemove)
  94. {
  95. _explosionSystem.QueueExplosion(entity, "PowerSink", 2000f, 4f, 20f, canCreateVacuum: true);
  96. EntityManager.RemoveComponent(entity, component);
  97. }
  98. }
  99. private void NotifyStationOfImminentExplosion(EntityUid uid, PowerSinkComponent powerSinkComponent)
  100. {
  101. if (powerSinkComponent.SentImminentExplosionWarningMessage)
  102. return;
  103. powerSinkComponent.SentImminentExplosionWarningMessage = true;
  104. var station = _station.GetOwningStation(uid);
  105. if (station == null)
  106. return;
  107. _chat.DispatchStationAnnouncement(
  108. station.Value,
  109. Loc.GetString("powersink-imminent-explosion-announcement"),
  110. playDefaultSound: true,
  111. colorOverride: Color.Yellow
  112. );
  113. }
  114. }
  115. }