Files
wwdpublic/Content.Server/PowerSink/PowerSinkSystem.cs
DEATHB4DEFEAT e3bc8d4c0e Random Announcer System (#415)
# Description

Replaces every instance of station announcements with an announcer
system meant to handle audio and messages for various announcers defined
in prototypes instead of each server replacing the scattered files
inconsistently with whatever singular thing they want to hear announce
messages.

# TODO

- [x] Systems
- [x] CVars
- [x] Sounds
- [x] Client volume slider
- [x] Collections
- [x] Prototypes
- [x] Events
- [x] Commands
- [x] PR media
- [x] Deglobalize
- [x] Passthrough localization parameters to overrides
- [x] Make every announcer follow the template
- [x] Move sounds into subdirectories
- [x] Make announcement IDs camelCased
- [x] Test announcement localizations
- [x] Weighted announcer lists

---

<details><summary><h1>Media</h1></summary>
<p>


https://github.com/Simple-Station/Parkstation-Friendly-Chainsaw/assets/77995199/caf5805d-acb0-4140-b344-875a8f79e5ee

</p>
</details>

---

# Changelog

🆑
- add: Added 4 new announcers that will randomly be selected every shift
2024-07-12 16:13:50 -04:00

137 lines
5.6 KiB
C#

using Content.Server.Explosion.EntitySystems;
using Content.Server.Power.Components;
using Content.Shared.Examine;
using Robust.Shared.Utility;
using Content.Server.Chat.Systems;
using Content.Server.Station.Systems;
using Robust.Shared.Timing;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Content.Server.Power.EntitySystems;
using Content.Server.Announcements.Systems;
namespace Content.Server.PowerSink
{
public sealed class PowerSinkSystem : EntitySystem
{
/// <summary>
/// Percentage of battery full to trigger the announcement warning at.
/// </summary>
private const float WarningMessageThreshold = 0.70f;
private readonly float[] _warningSoundThresholds = new[] { .80f, .90f, .95f, .98f };
/// <summary>
/// Length of time to delay explosion from battery full state -- this is used to play
/// a brief SFX winding up the explosion.
/// </summary>
/// <returns></returns>
private readonly TimeSpan _explosionDelayTime = TimeSpan.FromSeconds(1.465);
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly ChatSystem _chat = default!;
[Dependency] private readonly ExplosionSystem _explosionSystem = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly BatterySystem _battery = default!;
[Dependency] private readonly AnnouncerSystem _announcer = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PowerSinkComponent, ExaminedEvent>(OnExamine);
}
private void OnExamine(EntityUid uid, PowerSinkComponent component, ExaminedEvent args)
{
if (!args.IsInDetailsRange || !TryComp<PowerConsumerComponent>(uid, out var consumer))
return;
var drainAmount = (int) consumer.NetworkLoad.ReceivingPower / 1000;
args.PushMarkup(
Loc.GetString(
"powersink-examine-drain-amount",
("amount", drainAmount),
("markupDrainColor", "orange"))
);
}
public override void Update(float frameTime)
{
var toRemove = new RemQueue<(EntityUid Entity, PowerSinkComponent Sink)>();
var query = EntityQueryEnumerator<PowerSinkComponent, PowerConsumerComponent, BatteryComponent, TransformComponent>();
// Realistically it's gonna be like <5 per station.
while (query.MoveNext(out var entity, out var component, out var networkLoad, out var battery, out var transform))
{
if (!transform.Anchored)
continue;
_battery.SetCharge(entity, battery.CurrentCharge + networkLoad.NetworkLoad.ReceivingPower / 1000, battery);
var currentBatteryThreshold = battery.CurrentCharge / battery.MaxCharge;
// Check for warning message threshold
if (!component.SentImminentExplosionWarningMessage &&
currentBatteryThreshold >= WarningMessageThreshold)
{
NotifyStationOfImminentExplosion(entity, component);
}
// Check for warning sound threshold
foreach (var testThreshold in _warningSoundThresholds)
{
if (currentBatteryThreshold >= testThreshold &&
testThreshold > component.HighestWarningSoundThreshold)
{
component.HighestWarningSoundThreshold = currentBatteryThreshold; // Don't re-play in future until next threshold hit
_audio.PlayPvs(component.ElectricSound, entity); // Play SFX
break;
}
}
// Check for explosion
if (battery.CurrentCharge < battery.MaxCharge)
continue;
if (component.ExplosionTime == null)
{
// Set explosion sequence to start soon
component.ExplosionTime = _gameTiming.CurTime.Add(_explosionDelayTime);
// Wind-up SFX
_audio.PlayPvs(component.ChargeFireSound, entity); // Play SFX
}
else if (_gameTiming.CurTime >= component.ExplosionTime)
{
// Explode!
toRemove.Add((entity, component));
}
}
foreach (var (entity, component) in toRemove)
{
_explosionSystem.QueueExplosion(entity, "PowerSink", 2000f, 4f, 20f, canCreateVacuum: true);
EntityManager.RemoveComponent(entity, component);
}
}
private void NotifyStationOfImminentExplosion(EntityUid uid, PowerSinkComponent powerSinkComponent)
{
if (powerSinkComponent.SentImminentExplosionWarningMessage)
return;
powerSinkComponent.SentImminentExplosionWarningMessage = true;
var station = _station.GetOwningStation(uid);
if (station == null)
return;
_announcer.SendAnnouncement(_announcer.GetAnnouncementId("PowerSinkExplosion"),
_station.GetInOwningStation(station.Value), "powersink-immiment-explosion-announcement",
colorOverride: Color.Yellow, station: station.Value);
}
}
}