Re-organize all projects (#4166)
This commit is contained in:
29
Content.Server/StationEvents/Events/FalseAlarm.cs
Normal file
29
Content.Server/StationEvents/Events/FalseAlarm.cs
Normal file
@@ -0,0 +1,29 @@
|
||||
#nullable enable
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class FalseAlarm : StationEvent
|
||||
{
|
||||
public override string Name => "FalseAlarm";
|
||||
public override float Weight => WeightHigh;
|
||||
protected override float EndAfter => 1.0f;
|
||||
public override int? MaxOccurrences => 5;
|
||||
|
||||
public override void Announce()
|
||||
{
|
||||
var stationEventSystem = EntitySystem.Get<StationEventSystem>();
|
||||
var randomEvent = stationEventSystem.PickRandomEvent();
|
||||
|
||||
if (randomEvent != null)
|
||||
{
|
||||
StartAnnouncement = randomEvent.StartAnnouncement;
|
||||
StartAudio = randomEvent.StartAudio;
|
||||
}
|
||||
|
||||
base.Announce();
|
||||
}
|
||||
}
|
||||
}
|
||||
198
Content.Server/StationEvents/Events/GasLeak.cs
Normal file
198
Content.Server/StationEvents/Events/GasLeak.cs
Normal file
@@ -0,0 +1,198 @@
|
||||
using Content.Server.GameObjects.Components.Atmos;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Log;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
|
||||
#nullable enable
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
internal sealed class GasLeak : StationEvent
|
||||
{
|
||||
public override string Name => "GasLeak";
|
||||
|
||||
public override string? StartAnnouncement =>
|
||||
"Attention crew, there is a gas leak on the station. We advise you to avoid the area and wear suit internals in the meantime.";
|
||||
|
||||
// Sourced from https://github.com/vgstation-coders/vgstation13/blob/2c5a491446ab824a8fbbf39bcf656b590e0228df/sound/misc/bloblarm.ogg
|
||||
public override string? StartAudio => "/Audio/Announcements/bloblarm.ogg";
|
||||
|
||||
protected override string? EndAnnouncement => "The source of the gas leak has been fixed. Please be cautious around areas with gas remaining.";
|
||||
|
||||
private static readonly Gas[] LeakableGases = {
|
||||
Gas.Plasma,
|
||||
Gas.Tritium,
|
||||
};
|
||||
|
||||
public override int EarliestStart => 10;
|
||||
|
||||
public override int MinimumPlayers => 5;
|
||||
|
||||
/// <summary>
|
||||
/// Give people time to get their internals on.
|
||||
/// </summary>
|
||||
protected override float StartAfter => 20f;
|
||||
|
||||
/// <summary>
|
||||
/// Don't know how long the event will be until we calculate the leak amount.
|
||||
/// </summary>
|
||||
protected override float EndAfter { get; set; } = float.MaxValue;
|
||||
|
||||
/// <summary>
|
||||
/// Running cooldown of how much time until another leak.
|
||||
/// </summary>
|
||||
private float _timeUntilLeak;
|
||||
|
||||
/// <summary>
|
||||
/// How long between more gas being added to the tile.
|
||||
/// </summary>
|
||||
private const float LeakCooldown = 1.0f;
|
||||
|
||||
// Event variables
|
||||
|
||||
private IEntity? _targetGrid;
|
||||
|
||||
private Vector2i _targetTile;
|
||||
|
||||
private EntityCoordinates _targetCoords;
|
||||
|
||||
private bool _foundTile;
|
||||
|
||||
private Gas _leakGas;
|
||||
|
||||
private float _molesPerSecond;
|
||||
|
||||
private const int MinimumMolesPerSecond = 20;
|
||||
|
||||
/// <summary>
|
||||
/// Don't want to make it too fast to give people time to flee.
|
||||
/// </summary>
|
||||
private const int MaximumMolesPerSecond = 50;
|
||||
|
||||
private const int MinimumGas = 250;
|
||||
|
||||
private const int MaximumGas = 1000;
|
||||
|
||||
private const float SparkChance = 0.05f;
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
base.Startup();
|
||||
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
||||
|
||||
// Essentially we'll pick out a target amount of gas to leak, then a rate to leak it at, then work out the duration from there.
|
||||
if (TryFindRandomTile(out _targetTile, robustRandom))
|
||||
{
|
||||
_foundTile = true;
|
||||
|
||||
_leakGas = robustRandom.Pick(LeakableGases);
|
||||
// Was 50-50 on using normal distribution.
|
||||
var totalGas = (float) robustRandom.Next(MinimumGas, MaximumGas);
|
||||
_molesPerSecond = robustRandom.Next(MinimumMolesPerSecond, MaximumMolesPerSecond);
|
||||
EndAfter = totalGas / _molesPerSecond + StartAfter;
|
||||
Logger.InfoS("stationevents", $"Leaking {totalGas} of {_leakGas} over {EndAfter - StartAfter} seconds at {_targetTile}");
|
||||
}
|
||||
|
||||
// Look technically if you wanted to guarantee a leak you'd do this in announcement but having the announcement
|
||||
// there just to fuck with people even if there is no valid tile is funny.
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!Started || !Running) return;
|
||||
|
||||
_timeUntilLeak -= frameTime;
|
||||
|
||||
if (_timeUntilLeak > 0f) return;
|
||||
_timeUntilLeak += LeakCooldown;
|
||||
|
||||
if (!_foundTile ||
|
||||
_targetGrid == null ||
|
||||
_targetGrid.Deleted ||
|
||||
!_targetGrid.TryGetComponent(out GridAtmosphereComponent? gridAtmos))
|
||||
{
|
||||
Running = false;
|
||||
return;
|
||||
}
|
||||
|
||||
var atmos = gridAtmos.GetTile(_targetTile);
|
||||
|
||||
atmos?.Air?.AdjustMoles(_leakGas, LeakCooldown * _molesPerSecond);
|
||||
atmos?.Invalidate();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
|
||||
Spark();
|
||||
|
||||
_foundTile = false;
|
||||
_targetGrid = null;
|
||||
_targetTile = default;
|
||||
_targetCoords = default;
|
||||
_leakGas = Gas.Oxygen;
|
||||
EndAfter = float.MaxValue;
|
||||
}
|
||||
|
||||
private void Spark()
|
||||
{
|
||||
var robustRandom = IoCManager.Resolve<IRobustRandom>();
|
||||
if (robustRandom.NextFloat() <= SparkChance)
|
||||
{
|
||||
if (!_foundTile ||
|
||||
_targetGrid == null ||
|
||||
_targetGrid.Deleted ||
|
||||
!_targetGrid.TryGetComponent(out GridAtmosphereComponent? gridAtmos))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var atmos = gridAtmos.GetTile(_targetTile);
|
||||
// Don't want it to be so obnoxious as to instantly murder anyone in the area but enough that
|
||||
// it COULD start potentially start a bigger fire.
|
||||
atmos?.HotspotExpose(700f, 50f, true);
|
||||
SoundSystem.Play(Filter.Pvs(_targetCoords), "/Audio/Effects/sparks4.ogg", _targetCoords);
|
||||
}
|
||||
}
|
||||
|
||||
private bool TryFindRandomTile(out Vector2i tile, IRobustRandom? robustRandom = null)
|
||||
{
|
||||
tile = default;
|
||||
var defaultGridId = IoCManager.Resolve<IGameTicker>().DefaultGridId;
|
||||
|
||||
if (!IoCManager.Resolve<IMapManager>().TryGetGrid(defaultGridId, out var grid) ||
|
||||
!IoCManager.Resolve<IEntityManager>().TryGetEntity(grid.GridEntityId, out _targetGrid)) return false;
|
||||
|
||||
_targetGrid.EnsureComponent(out GridAtmosphereComponent gridAtmos);
|
||||
robustRandom ??= IoCManager.Resolve<IRobustRandom>();
|
||||
var found = false;
|
||||
var gridBounds = grid.WorldBounds;
|
||||
var gridPos = grid.WorldPosition;
|
||||
|
||||
for (var i = 0; i < 10; i++)
|
||||
{
|
||||
var randomX = robustRandom.Next((int) gridBounds.Left, (int) gridBounds.Right);
|
||||
var randomY = robustRandom.Next((int) gridBounds.Bottom, (int) gridBounds.Top);
|
||||
|
||||
tile = new Vector2i(randomX - (int) gridPos.X, randomY - (int) gridPos.Y);
|
||||
if (gridAtmos.IsSpace(tile) || gridAtmos.IsAirBlocked(tile)) continue;
|
||||
found = true;
|
||||
_targetCoords = grid.GridTileToLocal(tile);
|
||||
break;
|
||||
}
|
||||
|
||||
if (!found) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
79
Content.Server/StationEvents/Events/PowerGridCheck.cs
Normal file
79
Content.Server/StationEvents/Events/PowerGridCheck.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
#nullable enable
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using Content.Server.Power.Components;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Random;
|
||||
using Timer = Robust.Shared.Timing.Timer;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class PowerGridCheck : StationEvent
|
||||
{
|
||||
public override string Name => "PowerGridCheck";
|
||||
public override float Weight => WeightNormal;
|
||||
public override int? MaxOccurrences => 3;
|
||||
public override string StartAnnouncement => Loc.GetString(
|
||||
"Abnormal activity detected in the station's powernet. As a precautionary measure, the station's power will be shut off for an indeterminate duration.");
|
||||
protected override string EndAnnouncement => Loc.GetString(
|
||||
"Power has been restored to the station. We apologize for the inconvenience.");
|
||||
public override string? StartAudio => "/Audio/Announcements/power_off.ogg";
|
||||
|
||||
// If you need EndAudio it's down below. Not set here because we can't play it at the normal time without spamming sounds.
|
||||
|
||||
protected override float StartAfter => 12.0f;
|
||||
|
||||
private CancellationTokenSource? _announceCancelToken;
|
||||
|
||||
private readonly List<IEntity> _powered = new();
|
||||
|
||||
public override void Announce()
|
||||
{
|
||||
base.Announce();
|
||||
EndAfter = IoCManager.Resolve<IRobustRandom>().Next(60, 120);
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
var componentManager = IoCManager.Resolve<IComponentManager>();
|
||||
|
||||
foreach (var component in componentManager.EntityQuery<PowerReceiverComponent>(true))
|
||||
{
|
||||
component.PowerDisabled = true;
|
||||
_powered.Add(component.Owner);
|
||||
}
|
||||
|
||||
base.Startup();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
foreach (var entity in _powered)
|
||||
{
|
||||
if (entity.Deleted) continue;
|
||||
|
||||
if (entity.TryGetComponent(out PowerReceiverComponent? powerReceiverComponent))
|
||||
{
|
||||
powerReceiverComponent.PowerDisabled = false;
|
||||
}
|
||||
}
|
||||
|
||||
// Can't use the default EndAudio
|
||||
_announceCancelToken?.Cancel();
|
||||
_announceCancelToken = new CancellationTokenSource();
|
||||
Timer.Spawn(3000, () =>
|
||||
{
|
||||
SoundSystem.Play(Filter.Broadcast(), "/Audio/Announcements/power_on.ogg");
|
||||
}, _announceCancelToken.Token);
|
||||
_powered.Clear();
|
||||
|
||||
base.Shutdown();
|
||||
}
|
||||
}
|
||||
}
|
||||
112
Content.Server/StationEvents/Events/RadiationStorm.cs
Normal file
112
Content.Server/StationEvents/Events/RadiationStorm.cs
Normal file
@@ -0,0 +1,112 @@
|
||||
#nullable enable
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Radiation;
|
||||
using Content.Shared.Coordinates;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Localization;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Random;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
[UsedImplicitly]
|
||||
public sealed class RadiationStorm : StationEvent
|
||||
{
|
||||
// Based on Goonstation style radiation storm with some TG elements (announcer, etc.)
|
||||
|
||||
[Dependency] private IEntityManager _entityManager = default!;
|
||||
[Dependency] private IRobustRandom _robustRandom = default!;
|
||||
|
||||
public override string Name => "RadiationStorm";
|
||||
public override string StartAnnouncement => Loc.GetString(
|
||||
"High levels of radiation detected near the station. Evacuate any areas containing abnormal green energy fields.");
|
||||
protected override string EndAnnouncement => Loc.GetString(
|
||||
"The radiation threat has passed. Please return to your workplaces.");
|
||||
public override string StartAudio => "/Audio/Announcements/radiation.ogg";
|
||||
protected override float StartAfter => 10.0f;
|
||||
|
||||
// Event specific details
|
||||
private float _timeUntilPulse;
|
||||
private const float MinPulseDelay = 0.2f;
|
||||
private const float MaxPulseDelay = 0.8f;
|
||||
|
||||
private void ResetTimeUntilPulse()
|
||||
{
|
||||
_timeUntilPulse = _robustRandom.NextFloat() * (MaxPulseDelay - MinPulseDelay) + MinPulseDelay;
|
||||
}
|
||||
|
||||
public override void Announce()
|
||||
{
|
||||
base.Announce();
|
||||
EndAfter = _robustRandom.Next(30, 80) + StartAfter; // We want to be forgiving about the radstorm.
|
||||
}
|
||||
|
||||
public override void Startup()
|
||||
{
|
||||
ResetTimeUntilPulse();
|
||||
base.Startup();
|
||||
}
|
||||
|
||||
public override void Shutdown()
|
||||
{
|
||||
base.Shutdown();
|
||||
}
|
||||
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
base.Update(frameTime);
|
||||
|
||||
if (!Started || !Running) return;
|
||||
|
||||
_timeUntilPulse -= frameTime;
|
||||
|
||||
if (_timeUntilPulse <= 0.0f)
|
||||
{
|
||||
var pauseManager = IoCManager.Resolve<IPauseManager>();
|
||||
var gameTicker = IoCManager.Resolve<IGameTicker>();
|
||||
var defaultGrid = IoCManager.Resolve<IMapManager>().GetGrid(gameTicker.DefaultGridId);
|
||||
|
||||
if (pauseManager.IsGridPaused(defaultGrid))
|
||||
return;
|
||||
|
||||
SpawnPulse(defaultGrid);
|
||||
}
|
||||
}
|
||||
|
||||
private void SpawnPulse(IMapGrid mapGrid)
|
||||
{
|
||||
if (!TryFindRandomGrid(mapGrid, out var coordinates))
|
||||
return;
|
||||
|
||||
var pulse = _entityManager.SpawnEntity("RadiationPulse", coordinates);
|
||||
pulse.GetComponent<RadiationPulseComponent>().DoPulse();
|
||||
ResetTimeUntilPulse();
|
||||
}
|
||||
|
||||
private bool TryFindRandomGrid(IMapGrid mapGrid, out EntityCoordinates coordinates)
|
||||
{
|
||||
if (!mapGrid.Index.IsValid())
|
||||
{
|
||||
coordinates = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
var randomX = _robustRandom.Next((int) mapGrid.WorldBounds.Left, (int) mapGrid.WorldBounds.Right);
|
||||
var randomY = _robustRandom.Next((int) mapGrid.WorldBounds.Bottom, (int) mapGrid.WorldBounds.Top);
|
||||
|
||||
coordinates = mapGrid.ToCoordinates(randomX, randomY);
|
||||
|
||||
// TODO: Need to get valid tiles? (maybe just move right if the tile we chose is invalid?)
|
||||
if (!coordinates.IsValid(_entityManager))
|
||||
{
|
||||
coordinates = default;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
170
Content.Server/StationEvents/Events/StationEvent.cs
Normal file
170
Content.Server/StationEvents/Events/StationEvent.cs
Normal file
@@ -0,0 +1,170 @@
|
||||
#nullable enable
|
||||
using Content.Server.Chat.Managers;
|
||||
using Robust.Shared.Audio;
|
||||
using Robust.Shared.IoC;
|
||||
using Robust.Shared.Player;
|
||||
|
||||
namespace Content.Server.StationEvents.Events
|
||||
{
|
||||
public abstract class StationEvent
|
||||
{
|
||||
public const float WeightVeryLow = 0.0f;
|
||||
public const float WeightLow = 5.0f;
|
||||
public const float WeightNormal = 10.0f;
|
||||
public const float WeightHigh = 15.0f;
|
||||
public const float WeightVeryHigh = 20.0f;
|
||||
|
||||
/// <summary>
|
||||
/// If the event has started and is currently running.
|
||||
/// </summary>
|
||||
public bool Running { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Human-readable name for the event.
|
||||
/// </summary>
|
||||
public abstract string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// The weight this event has in the random-selection process.
|
||||
/// </summary>
|
||||
public virtual float Weight => WeightNormal;
|
||||
|
||||
/// <summary>
|
||||
/// What should be said in chat when the event starts (if anything).
|
||||
/// </summary>
|
||||
public virtual string? StartAnnouncement { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// What should be said in chat when the event ends (if anything).
|
||||
/// </summary>
|
||||
protected virtual string? EndAnnouncement { get; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Starting audio of the event.
|
||||
/// </summary>
|
||||
public virtual string? StartAudio { get; set; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Ending audio of the event.
|
||||
/// </summary>
|
||||
public virtual string? EndAudio { get; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// In minutes, when is the first round time this event can start
|
||||
/// </summary>
|
||||
public virtual int EarliestStart { get; } = 5;
|
||||
|
||||
/// <summary>
|
||||
/// When in the lifetime to call Start().
|
||||
/// </summary>
|
||||
protected virtual float StartAfter { get; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// When in the lifetime the event should end.
|
||||
/// </summary>
|
||||
protected virtual float EndAfter { get; set; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How long has the event existed. Do not change this.
|
||||
/// </summary>
|
||||
private float Elapsed { get; set; } = 0.0f;
|
||||
|
||||
/// <summary>
|
||||
/// How many players need to be present on station for the event to run
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// To avoid running deadly events with low-pop
|
||||
/// </remarks>
|
||||
public virtual int MinimumPlayers { get; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// How many times this event has run this round
|
||||
/// </summary>
|
||||
public int Occurrences { get; set; } = 0;
|
||||
|
||||
/// <summary>
|
||||
/// How many times this even can occur in a single round
|
||||
/// </summary>
|
||||
public virtual int? MaxOccurrences { get; } = null;
|
||||
|
||||
/// <summary>
|
||||
/// Has the startup time elapsed?
|
||||
/// </summary>
|
||||
protected bool Started { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Has this event commenced (announcement may or may not be used)?
|
||||
/// </summary>
|
||||
private bool Announced { get; set; } = false;
|
||||
|
||||
/// <summary>
|
||||
/// Called once to setup the event after StartAfter has elapsed.
|
||||
/// </summary>
|
||||
public virtual void Startup()
|
||||
{
|
||||
Started = true;
|
||||
Occurrences += 1;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once as soon as an event is active.
|
||||
/// Can also be used for some initial setup.
|
||||
/// </summary>
|
||||
public virtual void Announce()
|
||||
{
|
||||
if (StartAnnouncement != null)
|
||||
{
|
||||
var chatManager = IoCManager.Resolve<IChatManager>();
|
||||
chatManager.DispatchStationAnnouncement(StartAnnouncement);
|
||||
}
|
||||
|
||||
if (StartAudio != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Broadcast(), StartAudio, AudioParams.Default.WithVolume(-10f));
|
||||
}
|
||||
|
||||
Announced = true;
|
||||
Running = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called once when the station event ends for any reason.
|
||||
/// </summary>
|
||||
public virtual void Shutdown()
|
||||
{
|
||||
if (EndAnnouncement != null)
|
||||
{
|
||||
var chatManager = IoCManager.Resolve<IChatManager>();
|
||||
chatManager.DispatchStationAnnouncement(EndAnnouncement);
|
||||
}
|
||||
|
||||
if (EndAudio != null)
|
||||
{
|
||||
SoundSystem.Play(Filter.Broadcast(), EndAudio, AudioParams.Default.WithVolume(-10f));
|
||||
}
|
||||
|
||||
Started = false;
|
||||
Announced = false;
|
||||
Elapsed = 0;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Called every tick when this event is running.
|
||||
/// </summary>
|
||||
/// <param name="frameTime"></param>
|
||||
public virtual void Update(float frameTime)
|
||||
{
|
||||
Elapsed += frameTime;
|
||||
|
||||
if (!Started && Elapsed >= StartAfter)
|
||||
{
|
||||
Startup();
|
||||
}
|
||||
|
||||
if (EndAfter <= Elapsed)
|
||||
{
|
||||
Running = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user