Files
tbd-station-14/Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs
avery 1679ef1004 Shuttle timers (#19471)
* sync

* sync

* no more squiggles..

* last build error

* updated maps for testing

* some issue with receiving arrivals setup

* networkpayload refactor. TODO: accurate timings

* timings accurate. TODO: backport old bugfix

* all set?

* cleaned up source. TODO: diff arrivals methods

* cleaned component. TODO: escape polish, docs

* first documentation pass

* escape timers work

* no more magic numbers

* removed dead code leads

* sync

sync

* Automatic changelog update

* shuttle timer groundwork

no more squiggles..

last build error

updated maps for testing

some issue with receiving arrivals setup

networkpayload refactor. TODO: accurate timings

timings accurate. TODO: backport old bugfix

all set?

cleaned up source. TODO: diff arrivals methods

cleaned component. TODO: escape polish, docs

first documentation pass

escape timers work

no more magic numbers

* BBQ rib sandwich (#21180)

* Fix missing toggle fullscreen loc string (#21264)

* Cave Decoration pack  (#21265)

* add chromite chasm

* add desert chasm

* snow chasm

* finish

* fixes and tweaks (#21172)

* Automatic changelog update

* Fix ItemPlacer (#21160)

This is going to lead to many entities being ticked unnecessarily and performance problems.

* headrev spawn music (#21119)

* headrev spawn music

* :trollface:

* skill issue

* double skill issue

* :trollface:

* :trollface:

* :trollface:

* Automatic changelog update

* Techfab resprite + department fab sprites (#21136)

* Fix popup messages appearing when someone tries to open a door without a tool. (#21099)

* The fixTM

* typo fix

* addressing review

* Show "departed and moved on" for when a ghost role is taken (#21092)

* fix ghost role not counting for "departed and moved on"

* I don't think that bit was needed so away it goes

* hopefully finish the upsream merge

* Automatic changelog update

* Implant whitelist/blacklisting (#20678)

* add whitelist and blacklist to implant and implanter components

* handle whitelist and blacklist in systems

* move hardcoded whitelist/blacklist to base implanter + add admeme implanter

* give implants sensible whitelists

* cleaner CheckTarget and fix

* remove unused imports

* network lists

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>

* Automatic changelog update

* Ion storm event (#20277)

* ion storm event prototype + locale

* add lawsets

* use lawsets, make borgs ion storm targets

* ion storm rule and ion storm target

* lawset prototype

* use lawsets

* update silicon law system to use lawsets and support ion storm event

* new toys

* fix

* more fix

* fixy

* ion storm admin logging

* assigning laws makes borg provide its own laws, other stuff

* 1h reoccurence

* 50% chance

* better call saul

* emagLaws is required

* add announcment audio

* fixy

* family friendly gaming

* fixy

* address reviews

* fixy

* more fixy and no erp

* pro

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>

* Automatic changelog update

* A return to foam (foam rework) (#20831)

* Automatic changelog update

* ERT Loadout overhaul + Real deathsquad mobs + ERT fixes (#21230)

* "assist with medical efforts"

* CentComm official description change

* give cburn ert mask

* Ert medic hardsuit uses blood-red medic values

* description changes, they all used to use the blood-red description

* ert engineer hardsuit uses cburn values, good for handling all possible engineering problems.

* janitor hardsuit uses cburn values for extreme messes, otherwise we'd send the non eva variant.

* spawn suffix changes

* shorten suffix

* drop armor from ert jumpsuits

* drop armor from DS jumpsuit

* add more armor to death squad to make up for removed armor in the uniform.

* give sec gas masks armor, give syndicate gas masks armor. ERT gas mask uses syndicate mask values

* add nanotrasen

* removed duplicate

* give centcom IDs their hud icon

* replace all ert bulletproof armor with basic universal armor

* replace all oxygen tanks with air tanks; species is random.

* remove gun and meds from ert engineer kit

* give ert engineer materials

* remove weapons and meds from janitor ert

* give ert janitor light replacer

* remove ert sec pulse weapons, admins will assign loadout. Either the lecter or enforcer, probably.

* Give ert sec the security pistol kit

* typo

* give eva ert sec pistol

* give eva janitor ert gas mask

* give jani purple gloves

* medical gloves for medical ert

* replicate security loadout to leader

* quick ert lecter spawns for lazy admins

* better suffixes to find them easier

* add cburn to ertspawn

* Replace "Spawn" with "role"

* Add death squad. Give ert engineer gas analyzer.

* death squad using wrong equipment

* typo

* missing ghost roles on lecter loadouts

* add freedom implanter to deathsquad

* deathsquad ghost role text

* Operative sounds better

* give Ds flashbang box (why isn't it entirely filled?)

* fix typo. add energy shield to DS

* fix typos

* all centcomm roles are now mindshielded. These cannot be removed.

* Rider didnt include some of the changes ?

* give zipties instead of cuffs for mass arrests!

* upgrade ERT survival boxes to extended capacity

* give cburn extended oxygen too

* Automatic changelog update

* Restore Leviathan's 80 pop cap (#21281)

* Un-revert IPlayerManager refactor (#21244)

* Update engine to v172.0.0 (#21288)

* Bandaid tests (#21292)

* rename the rocks (#21275)

* Make crystals noRot (#21279)

IDK might be better. Ideally the anchoring would be offset 0-0 but this is the world we live in atm.

* Fix nukies sound not played  (#21268)

* Play sound and sending greeting message works for nukies now!!!!!

* oops

* silly change

* Automatic changelog update

* Fix hijack objective (#21241)

* Fix hijack

* Max difficulty

* Remove GridModifiedEvent (#21291)

* Update submodule to 173.0.0 (#21296)

* Fix namespace error (#21298)

* Update yaml sequence option in editorconfig (#21297)

* Fix namespace (#21299)

* fix cburn bag issue, make new bag entity for them and filled bag entity (#21295)

* Health analyzer UI improve (#17280)

* Automatic changelog update

* User accessible playtime (#21242)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>

* Automatic changelog update

* Moves cloning comp & cloning event to shared (#21253)

* Generalizes solution overflow & slightly increases space lube yield (#21094)

* generalize SolutionSpikeOverflowEvent

* let reactions overflow

* spacelube: 3 -> 5

* restore TryMixAndOverflow threshold cap

* Automatic changelog update

* Move ActorComponent to shared (#21293)

* Update engine to v174.0.0 (#21311)

* Fix planet command help message (#21312)

* Wearable bee plush (#20623)

* add

* fix

* temporary change, needs fixing

* mayb fix

* actually fix FR

* yes

* Automatic changelog update

* remove pulse rifle from ert medic (#21310)

* Content audio (#20862)

* Automatic changelog update

* Update submodule to 175.0.0 (#21318)

* Revert "Update submodule to 175.0.0 (#21318)" (#21319)

* Atomic bomb add uranium  (#21143)

* fix: Incendiary bullets no longer deal cold, acid, or shock damage that ignores all armor.

* Atomic bomb

* Action bugfixes (#21321)

* Disable OOC during round (#21323)

* Fix PDA notifications when creating a news entry using the Mass-Media console. (#21320)

* Automatic changelog update

* Update belt.yml (#21317)

changes the chief engineer's belt to remove the lv wires (they take up a lot of space and are easy to get anyways) in exchange for a holofan, a t-ray, and a gas analyzer (first time coding ever this might have to be edited)

* New foam sprites (edge sprites) (#21308)

* New foam sprites (icon smoothing)

* changed to edge sprites for foam

* fix

* edges for metal foams

* fix

* Fix bola stam damage, bring back old construction requirements (#21340)

* Automatic changelog update

* Added thermal insulation to flannel jackets (#21273)

* Automatic changelog update

* Space Asshole Gear (#21243)

* Add Space Asshole Coat

* Add sledgehammer

* Adjust sledgehammer damage values

* Add copyright string to sledgehammer

* Fix broken slot highlight in midnight theme. (#21331)

* removed dead code leads

removed redundant ensurecomps

* textscreen documentation

* remove redundant ensurecomps

* Add hint for the examine trigger effect (#21166)

* examine locale

* examine trigger desc

Automatic changelog update

cornmeal is actually obtainable now (#21162)

* do the thing

* lets find out

* Update Resources/Prototypes/Entities/Objects/Consumable/Food/produce.yml

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>

---------

Co-authored-by: ShadowCommander <10494922+ShadowCommander@users.noreply.github.com>

Automatic changelog update

Healing skeletons by pouring milk over them (and clean pie remains off their skulls) (#21231)

* mvp done - skellies can heal by spillink regular milk on themselves and clean themselves off creaming with water

* added other types of healing milk, also made a separate reaction to oat milk - it has almost no calcium in it

* fixed indent error, made a dumb mistake

Automatic changelog update

Fix anomaly locators frantically beeping when entering detection range. (#21178)

* reset beep timer when out of range

* prevent deficit from impacting beep timing

Automatic changelog update

Remove "mk --> mmm, okay" and "u --> you" to chatsan anti slang (#21177)

Automatic changelog update

Adds AttemptEntity(Uns|S)tickEvent. (#20728)

* try-stick

* convert spider charge to attempt-stick-events

Change ListContainer to send null when selected is removed from the data (#20595)

fix feeding unremovable items (#21234)

Automatic changelog update

Power switchable refactor (#20419)

Co-authored-by: deltanedas <@deltanedas:kde.org>

simple space mobs cant be flashed (#20784)

Co-authored-by: deltanedas <@deltanedas:kde.org>

give roundstart borgs names (#20081)

Co-authored-by: deltanedas <@deltanedas:kde.org>

fix searching on vending machines (#21233)

syndicate snack box (#21024)

Co-authored-by: deltanedas <@deltanedas:kde.org>

Fix DockingControl (#21238)

Shadow Dimension visual pack (#21237)

Co-authored-by: metalgearsloth <31366439+metalgearsloth@users.noreply.github.com>

Potato battery update + potato AI (#21142)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>

[TEST MERGE] Slot-based Storage (#21212)

Automatic changelog update

Some mild item size balancing + fixes (#21250)

Automatic changelog update

Revert "Storage TEST MERGE" (#21258)

Add missing changelog for storage refactor revert (#21259)

* sync

sync

* Automatic changelog update

shuttle timer groundwork

no more squiggles..

last build error

updated maps for testing

some issue with receiving arrivals setup

networkpayload refactor. TODO: accurate timings

timings accurate. TODO: backport old bugfix

all set?

cleaned up source. TODO: diff arrivals methods

cleaned component. TODO: escape polish, docs

first documentation pass

escape timers work

no more magic numbers

BBQ rib sandwich (#21180)

Fix missing toggle fullscreen loc string (#21264)

Cave Decoration pack  (#21265)

* add chromite chasm

* add desert chasm

* snow chasm

* finish

fixes and tweaks (#21172)

Automatic changelog update

Fix ItemPlacer (#21160)

This is going to lead to many entities being ticked unnecessarily and performance problems.

headrev spawn music (#21119)

* headrev spawn music

* :trollface:

* skill issue

* double skill issue

* :trollface:

* :trollface:

* :trollface:

Automatic changelog update

Techfab resprite + department fab sprites (#21136)

Fix popup messages appearing when someone tries to open a door without a tool. (#21099)

* The fixTM

* typo fix

* addressing review

Show "departed and moved on" for when a ghost role is taken (#21092)

* fix ghost role not counting for "departed and moved on"

* I don't think that bit was needed so away it goes

* hopefully finish the upsream merge

Automatic changelog update

Implant whitelist/blacklisting (#20678)

* add whitelist and blacklist to implant and implanter components

* handle whitelist and blacklist in systems

* move hardcoded whitelist/blacklist to base implanter + add admeme implanter

* give implants sensible whitelists

* cleaner CheckTarget and fix

* remove unused imports

* network lists

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>

Automatic changelog update

Ion storm event (#20277)

* ion storm event prototype + locale

* add lawsets

* use lawsets, make borgs ion storm targets

* ion storm rule and ion storm target

* lawset prototype

* use lawsets

* update silicon law system to use lawsets and support ion storm event

* new toys

* fix

* more fix

* fixy

* ion storm admin logging

* assigning laws makes borg provide its own laws, other stuff

* 1h reoccurence

* 50% chance

* better call saul

* emagLaws is required

* add announcment audio

* fixy

* family friendly gaming

* fixy

* address reviews

* fixy

* more fixy and no erp

* pro

---------

Co-authored-by: deltanedas <@deltanedas:kde.org>

Automatic changelog update

A return to foam (foam rework) (#20831)

Automatic changelog update

ERT Loadout overhaul + Real deathsquad mobs + ERT fixes (#21230)

* "assist with medical efforts"

* CentComm official description change

* give cburn ert mask

* Ert medic hardsuit uses blood-red medic values

* description changes, they all used to use the blood-red description

* ert engineer hardsuit uses cburn values, good for handling all possible engineering problems.

* janitor hardsuit uses cburn values for extreme messes, otherwise we'd send the non eva variant.

* spawn suffix changes

* shorten suffix

* drop armor from ert jumpsuits

* drop armor from DS jumpsuit

* add more armor to death squad to make up for removed armor in the uniform.

* give sec gas masks armor, give syndicate gas masks armor. ERT gas mask uses syndicate mask values

* add nanotrasen

* removed duplicate

* give centcom IDs their hud icon

* replace all ert bulletproof armor with basic universal armor

* replace all oxygen tanks with air tanks; species is random.

* remove gun and meds from ert engineer kit

* give ert engineer materials

* remove weapons and meds from janitor ert

* give ert janitor light replacer

* remove ert sec pulse weapons, admins will assign loadout. Either the lecter or enforcer, probably.

* Give ert sec the security pistol kit

* typo

* give eva ert sec pistol

* give eva janitor ert gas mask

* give jani purple gloves

* medical gloves for medical ert

* replicate security loadout to leader

* quick ert lecter spawns for lazy admins

* better suffixes to find them easier

* add cburn to ertspawn

* Replace "Spawn" with "role"

* Add death squad. Give ert engineer gas analyzer.

* death squad using wrong equipment

* typo

* missing ghost roles on lecter loadouts

* add freedom implanter to deathsquad

* deathsquad ghost role text

* Operative sounds better

* give Ds flashbang box (why isn't it entirely filled?)

* fix typo. add energy shield to DS

* fix typos

* all centcomm roles are now mindshielded. These cannot be removed.

* Rider didnt include some of the changes ?

* give zipties instead of cuffs for mass arrests!

* upgrade ERT survival boxes to extended capacity

* give cburn extended oxygen too

Automatic changelog update

Restore Leviathan's 80 pop cap (#21281)

Un-revert IPlayerManager refactor (#21244)

Update engine to v172.0.0 (#21288)

Bandaid tests (#21292)

rename the rocks (#21275)

Make crystals noRot (#21279)

IDK might be better. Ideally the anchoring would be offset 0-0 but this is the world we live in atm.

Fix nukies sound not played  (#21268)

* Play sound and sending greeting message works for nukies now!!!!!

* oops

* silly change

Automatic changelog update

Fix hijack objective (#21241)

* Fix hijack

* Max difficulty

Remove GridModifiedEvent (#21291)

Update submodule to 173.0.0 (#21296)

Fix namespace error (#21298)

Update yaml sequence option in editorconfig (#21297)

Fix namespace (#21299)

fix cburn bag issue, make new bag entity for them and filled bag entity (#21295)

Health analyzer UI improve (#17280)

Automatic changelog update

User accessible playtime (#21242)

Co-authored-by: metalgearsloth <comedian_vs_clown@hotmail.com>

Automatic changelog update

Moves cloning comp & cloning event to shared (#21253)

Generalizes solution overflow & slightly increases space lube yield (#21094)

* generalize SolutionSpikeOverflowEvent

* let reactions overflow

* spacelube: 3 -> 5

* restore TryMixAndOverflow threshold cap

Automatic changelog update

Move ActorComponent to shared (#21293)

Update engine to v174.0.0 (#21311)

Fix planet command help message (#21312)

Wearable bee plush (#20623)

* add

* fix

* temporary change, needs fixing

* mayb fix

* actually fix FR

* yes

Automatic changelog update

remove pulse rifle from ert medic (#21310)

Content audio (#20862)

Automatic changelog update

Update submodule to 175.0.0 (#21318)

Revert "Update submodule to 175.0.0 (#21318)" (#21319)

Atomic bomb add uranium  (#21143)

* fix: Incendiary bullets no longer deal cold, acid, or shock damage that ignores all armor.

* Atomic bomb

Action bugfixes (#21321)

Disable OOC during round (#21323)

Fix PDA notifications when creating a news entry using the Mass-Media console. (#21320)

Automatic changelog update

Update belt.yml (#21317)

changes the chief engineer's belt to remove the lv wires (they take up a lot of space and are easy to get anyways) in exchange for a holofan, a t-ray, and a gas analyzer (first time coding ever this might have to be edited)

New foam sprites (edge sprites) (#21308)

* New foam sprites (icon smoothing)

* changed to edge sprites for foam

* fix

* edges for metal foams

* fix

Fix bola stam damage, bring back old construction requirements (#21340)

Automatic changelog update

Added thermal insulation to flannel jackets (#21273)

Automatic changelog update

Space Asshole Gear (#21243)

* Add Space Asshole Coat

* Add sledgehammer

* Adjust sledgehammer damage values

* Add copyright string to sledgehammer

Fix broken slot highlight in midnight theme. (#21331)

* removed old ensurecomps, stubs

removed dead code leads

removed redundant ensurecomps

sync

sync

no more squiggles..

last build error

updated maps for testing

some issue with receiving arrivals setup

networkpayload refactor. TODO: accurate timings

timings accurate. TODO: backport old bugfix

all set?

cleaned up source. TODO: diff arrivals methods

cleaned component. TODO: escape polish, docs

first documentation pass

escape timers work

no more magic numbers

removed dead code leads

textscreen documentation

remove redundant ensurecomps

* sync

* new shuttletimer sprite/offset

* sync

* sync

* sync

* sync

* sync

* builds

* sync

* sync

* sync

* offset adjustments

* timer pattern implemented

* arrivals timer system seems to work

* sync

* sync

* sync

* sync

* sync

* sync

* sync

* sync

* sync

* sync

* implements shuttletimers

* centcomm timers

* set color to tg 1097fb

* textscreen -> signalscreen, shuttletimer -> screen

* move screens out of timer.yml

* pruned unused properties/imports

* license+copyright for screen.rsi

* forgor change signaltimer event handler doc

* roundstart arrivals ftl logic

* block signaltimer screentext updates during timing

* merged robust 185

* centcomm carpet uid collision PLEASE MERGE MY PR

* remove arrivalssystem entitymanager dependency

* refactored magic strings, added roundend getters

* specific vgstation sprite commit licensing

---------
2023-12-20 09:30:29 +11:00

541 lines
20 KiB
C#

using System.Numerics;
using System.Threading;
using Content.Server.Access.Systems;
using Content.Server.Administration.Logs;
using Content.Server.Administration.Managers;
using Content.Server.Chat.Systems;
using Content.Server.Communications;
using Content.Server.DeviceNetwork;
using Content.Server.DeviceNetwork.Components;
using Content.Server.DeviceNetwork.Systems;
using Content.Server.GameTicking.Events;
using Content.Server.Popups;
using Content.Server.RoundEnd;
using Content.Server.Shuttles.Components;
using Content.Server.Shuttles.Events;
using Content.Server.Station.Components;
using Content.Server.Station.Systems;
using Content.Shared.Access.Systems;
using Content.Shared.CCVar;
using Content.Shared.Database;
using Content.Shared.Shuttles.Components;
using Content.Shared.Shuttles.Events;
using Content.Shared.Tag;
using Content.Shared.Tiles;
using Robust.Server.GameObjects;
using Robust.Server.Maps;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Player;
using Robust.Shared.Random;
using Robust.Shared.Timing;
namespace Content.Server.Shuttles.Systems;
public sealed partial class EmergencyShuttleSystem : EntitySystem
{
/*
* Handles the escape shuttle + CentCom.
*/
[Dependency] private readonly IAdminLogManager _logger = default!;
[Dependency] private readonly IAdminManager _admin = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly AccessReaderSystem _reader = default!;
[Dependency] private readonly ChatSystem _chatSystem = default!;
[Dependency] private readonly CommunicationsConsoleSystem _commsConsole = default!;
[Dependency] private readonly DeviceNetworkSystem _deviceNetworkSystem = default!;
[Dependency] private readonly DockingSystem _dock = default!;
[Dependency] private readonly EntityManager _entityManager = default!;
[Dependency] private readonly IdCardSystem _idSystem = default!;
[Dependency] private readonly MapLoaderSystem _map = default!;
[Dependency] private readonly PopupSystem _popup = default!;
[Dependency] private readonly RoundEndSystem _roundEnd = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly ShuttleSystem _shuttle = default!;
[Dependency] private readonly StationSystem _station = default!;
[Dependency] private readonly TransformSystem _transformSystem = default!;
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
private ISawmill _sawmill = default!;
private const float ShuttleSpawnBuffer = 1f;
private bool _emergencyShuttleEnabled;
[ValidatePrototypeId<TagPrototype>]
private const string DockTag = "DockEmergency";
public override void Initialize()
{
_sawmill = Logger.GetSawmill("shuttle.emergency");
_emergencyShuttleEnabled = _configManager.GetCVar(CCVars.EmergencyShuttleEnabled);
// Don't immediately invoke as roundstart will just handle it.
_configManager.OnValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
SubscribeLocalEvent<RoundStartingEvent>(OnRoundStart);
SubscribeLocalEvent<StationEmergencyShuttleComponent, ComponentStartup>(OnStationStartup);
SubscribeLocalEvent<StationCentcommComponent, ComponentShutdown>(OnCentcommShutdown);
SubscribeLocalEvent<StationCentcommComponent, ComponentInit>(OnCentcommInit);
SubscribeLocalEvent<EmergencyShuttleComponent, FTLStartedEvent>(OnEmergencyFTL);
SubscribeLocalEvent<EmergencyShuttleComponent, FTLCompletedEvent>(OnEmergencyFTLComplete);
SubscribeNetworkEvent<EmergencyShuttleRequestPositionMessage>(OnShuttleRequestPosition);
InitializeEmergencyConsole();
}
private void OnRoundStart(RoundStartingEvent ev)
{
CleanupEmergencyConsole();
_roundEndCancelToken?.Cancel();
_roundEndCancelToken = new CancellationTokenSource();
}
private void OnCentcommShutdown(EntityUid uid, StationCentcommComponent component, ComponentShutdown args)
{
ClearCentcomm(component);
}
private void ClearCentcomm(StationCentcommComponent component)
{
QueueDel(component.Entity);
QueueDel(component.MapEntity);
component.Entity = null;
component.MapEntity = null;
}
/// <summary>
/// Attempts to get the EntityUid of the emergency shuttle
/// </summary>
public EntityUid? GetShuttle()
{
AllEntityQuery<EmergencyShuttleComponent>().MoveNext(out var shuttle, out _);
return shuttle;
}
private void SetEmergencyShuttleEnabled(bool value)
{
if (_emergencyShuttleEnabled == value)
return;
_emergencyShuttleEnabled = value;
if (value)
{
SetupEmergencyShuttle();
}
else
{
CleanupEmergencyShuttle();
}
}
private void CleanupEmergencyShuttle()
{
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var uid, out _))
{
RemCompDeferred<StationCentcommComponent>(uid);
}
}
public override void Update(float frameTime)
{
base.Update(frameTime);
UpdateEmergencyConsole(frameTime);
}
public override void Shutdown()
{
_configManager.UnsubValueChanged(CCVars.EmergencyShuttleEnabled, SetEmergencyShuttleEnabled);
ShutdownEmergencyConsole();
}
/// <summary>
/// If the client is requesting debug info on where an emergency shuttle would dock.
/// </summary>
private void OnShuttleRequestPosition(EmergencyShuttleRequestPositionMessage msg, EntitySessionEventArgs args)
{
if (!_admin.IsAdmin(args.SenderSession))
return;
var player = args.SenderSession.AttachedEntity;
if (player is null)
return;
var station = _station.GetOwningStation(player.Value);
if (!TryComp<StationEmergencyShuttleComponent>(station, out var stationShuttle) ||
!HasComp<ShuttleComponent>(stationShuttle.EmergencyShuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(station.Value));
if (targetGrid == null)
return;
var config = _dock.GetDockingConfig(stationShuttle.EmergencyShuttle.Value, targetGrid.Value, DockTag);
if (config == null)
return;
RaiseNetworkEvent(new EmergencyShuttlePositionMessage()
{
StationUid = GetNetEntity(targetGrid),
Position = config.Area,
});
}
/// <summary>
/// Escape shuttle FTL event handler. The only escape shuttle FTL transit should be from station to centcomm at round end
/// </summary>
private void OnEmergencyFTL(EntityUid uid, EmergencyShuttleComponent component, ref FTLStartedEvent args)
{
TimeSpan ftlTime = TimeSpan.FromSeconds
(
TryComp<FTLComponent>(uid, out var ftlComp) ?
ftlComp.TravelTime : ShuttleSystem.DefaultTravelTime
);
if (TryComp<DeviceNetworkComponent>(uid, out var netComp))
{
var payload = new NetworkPayload
{
[ShuttleTimerMasks.ShuttleMap] = uid,
[ShuttleTimerMasks.SourceMap] = args.FromMapUid,
[ShuttleTimerMasks.DestMap] = args.TargetCoordinates.GetMapUid(_entityManager),
[ShuttleTimerMasks.ShuttleTime] = ftlTime,
[ShuttleTimerMasks.SourceTime] = ftlTime,
[ShuttleTimerMasks.DestTime] = ftlTime
};
_deviceNetworkSystem.QueuePacket(uid, null, payload, netComp.TransmitFrequency);
}
}
/// <summary>
/// When the escape shuttle finishes FTL (docks at centcomm), have the timers display the round end countdown
/// </summary>
private void OnEmergencyFTLComplete(EntityUid uid, EmergencyShuttleComponent component, ref FTLCompletedEvent args)
{
var countdownTime = TimeSpan.FromSeconds(_configManager.GetCVar(CCVars.RoundRestartTime));
var shuttle = args.Entity;
if (TryComp<DeviceNetworkComponent>(shuttle, out var net))
{
var payload = new NetworkPayload
{
[ShuttleTimerMasks.ShuttleMap] = shuttle,
[ShuttleTimerMasks.SourceMap] = _roundEnd.GetCentcomm(),
[ShuttleTimerMasks.DestMap] = _roundEnd.GetStation(),
[ShuttleTimerMasks.ShuttleTime] = countdownTime,
[ShuttleTimerMasks.SourceTime] = countdownTime,
[ShuttleTimerMasks.DestTime] = countdownTime,
[ShuttleTimerMasks.Text] = new string?[] { "BYE!" }
};
_deviceNetworkSystem.QueuePacket(shuttle, null, payload, net.TransmitFrequency);
}
}
/// <summary>
/// Attempts to dock the emergency shuttle to the station.
/// </summary>
public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
{
if (!Resolve(stationUid, ref stationShuttle) ||
!TryComp<TransformComponent>(stationShuttle.EmergencyShuttle, out var xform) ||
!TryComp<ShuttleComponent>(stationShuttle.EmergencyShuttle, out var shuttle))
{
return;
}
var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(stationUid));
// UHH GOOD LUCK
if (targetGrid == null)
{
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to dock with station {ToPrettyString(stationUid)}");
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-good-luck"), playDefaultSound: false);
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
return;
}
var xformQuery = GetEntityQuery<TransformComponent>();
if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag))
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", angle.GetDir())), playDefaultSound: false);
}
// shuttle timers
var time = TimeSpan.FromSeconds(_consoleAccumulator);
if (TryComp<DeviceNetworkComponent>(stationShuttle.EmergencyShuttle.Value, out var netComp))
{
var payload = new NetworkPayload
{
[ShuttleTimerMasks.ShuttleMap] = stationShuttle.EmergencyShuttle.Value,
[ShuttleTimerMasks.SourceMap] = targetXform?.MapUid,
[ShuttleTimerMasks.DestMap] = _roundEnd.GetCentcomm(),
[ShuttleTimerMasks.ShuttleTime] = time,
[ShuttleTimerMasks.SourceTime] = time,
[ShuttleTimerMasks.DestTime] = time + TimeSpan.FromSeconds(TransitTime),
[ShuttleTimerMasks.Docked] = true
};
_deviceNetworkSystem.QueuePacket(stationShuttle.EmergencyShuttle.Value, null, payload, netComp.TransmitFrequency);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Announcements/shuttle_dock.ogg", Filter.Broadcast(), true);
}
else
{
if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
{
var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
_chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("direction", angle.GetDir())), playDefaultSound: false);
}
_logger.Add(LogType.EmergencyShuttle, LogImpact.High, $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}");
// TODO: Need filter extensions or something don't blame me.
_audio.PlayGlobal("/Audio/Misc/notice1.ogg", Filter.Broadcast(), true);
}
}
private void OnCentcommInit(EntityUid uid, StationCentcommComponent component, ComponentInit args)
{
if (!_emergencyShuttleEnabled)
return;
// Post mapinit? fancy
if (TryComp<TransformComponent>(component.Entity, out var xform))
{
component.MapEntity = xform.MapUid;
return;
}
AddCentcomm(component);
}
private void OnStationStartup(EntityUid uid, StationEmergencyShuttleComponent component, ComponentStartup args)
{
AddEmergencyShuttle(uid, component);
}
/// <summary>
/// Spawns the emergency shuttle for each station and starts the countdown until controls unlock.
/// </summary>
public void CallEmergencyShuttle()
{
if (EmergencyShuttleArrived)
return;
if (!_emergencyShuttleEnabled)
{
_roundEnd.EndRound();
return;
}
_consoleAccumulator = _configManager.GetCVar(CCVars.EmergencyShuttleDockTime);
EmergencyShuttleArrived = true;
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
{
CallEmergencyShuttle(uid, comp);
}
_commsConsole.UpdateCommsConsoleInterface();
}
private void SetupEmergencyShuttle()
{
if (!_emergencyShuttleEnabled)
return;
var centcommQuery = AllEntityQuery<StationCentcommComponent>();
while (centcommQuery.MoveNext(out var centcomm))
{
AddCentcomm(centcomm);
}
var query = AllEntityQuery<StationEmergencyShuttleComponent>();
while (query.MoveNext(out var uid, out var comp))
AddEmergencyShuttle(uid, comp);
}
private void AddCentcomm(StationCentcommComponent component)
{
if (component.MapEntity != null || component.Entity != null)
{
_sawmill.Warning("Attempted to re-add an existing centcomm map.");
return;
}
// Check for existing centcomms and just point to that
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var otherComp))
{
if (otherComp == component)
continue;
if (!Exists(otherComp.MapEntity) || !Exists(otherComp.Entity))
{
Log.Error($"Disconvered invalid centcomm component?");
ClearCentcomm(otherComp);
continue;
}
component.MapEntity = otherComp.MapEntity;
component.ShuttleIndex = otherComp.ShuttleIndex;
return;
}
if (string.IsNullOrEmpty(component.Map.ToString()))
{
_sawmill.Warning("No CentComm map found, skipping setup.");
return;
}
var mapId = _mapManager.CreateMap();
var grid = _map.LoadGrid(mapId, component.Map.ToString());
var map = _mapManager.GetMapEntityId(mapId);
if (!Exists(map))
{
Log.Error($"Failed to set up centcomm map!");
QueueDel(grid);
return;
}
if (!Exists(grid))
{
Log.Error($"Failed to set up centcomm grid!");
QueueDel(map);
return;
}
var xform = Transform(grid.Value);
if (xform.ParentUid != map || xform.MapUid != map)
{
Log.Error($"Centcomm grid is not parented to its own map?");
QueueDel(map);
QueueDel(grid);
return;
}
component.MapEntity = map;
component.Entity = grid;
_shuttle.AddFTLDestination(grid.Value, false);
}
public HashSet<EntityUid> GetCentcommMaps()
{
var query = AllEntityQuery<StationCentcommComponent>();
var maps = new HashSet<EntityUid>(Count<StationCentcommComponent>());
while (query.MoveNext(out var comp))
{
if (comp.MapEntity != null)
maps.Add(comp.MapEntity.Value);
}
return maps;
}
private void AddEmergencyShuttle(EntityUid uid, StationEmergencyShuttleComponent component)
{
if (!_emergencyShuttleEnabled
|| component.EmergencyShuttle != null ||
!TryComp<StationCentcommComponent>(uid, out var centcomm)
|| !TryComp(centcomm.MapEntity, out MapComponent? map))
{
return;
}
// Load escape shuttle
var shuttlePath = component.EmergencyShuttlePath;
var shuttle = _map.LoadGrid(map.MapId, shuttlePath.ToString(), new MapLoadOptions()
{
// Should be far enough... right? I'm too lazy to bounds check CentCom rn.
Offset = new Vector2(500f + centcomm.ShuttleIndex, 0f)
});
if (shuttle == null)
{
_sawmill.Error($"Unable to spawn emergency shuttle {shuttlePath} for {ToPrettyString(uid)}");
return;
}
centcomm.ShuttleIndex += _mapManager.GetGrid(shuttle.Value).LocalAABB.Width + ShuttleSpawnBuffer;
// Update indices for all centcomm comps pointing to same map
var query = AllEntityQuery<StationCentcommComponent>();
while (query.MoveNext(out var comp))
{
if (comp == centcomm || comp.MapEntity != centcomm.MapEntity)
continue;
comp.ShuttleIndex = centcomm.ShuttleIndex;
}
component.EmergencyShuttle = shuttle;
EnsureComp<ProtectedGridComponent>(shuttle.Value);
EnsureComp<PreventPilotComponent>(shuttle.Value);
EnsureComp<EmergencyShuttleComponent>(shuttle.Value);
}
private void OnEscapeUnpaused(EntityUid uid, EscapePodComponent component, ref EntityUnpausedEvent args)
{
if (component.LaunchTime == null)
return;
component.LaunchTime = component.LaunchTime.Value + args.PausedTime;
}
/// <summary>
/// Returns whether a target is escaping on the emergency shuttle, but only if evac has arrived.
/// </summary>
public bool IsTargetEscaping(EntityUid target)
{
// if evac isn't here then sitting in a pod doesn't return true
if (!EmergencyShuttleArrived)
return false;
// check each emergency shuttle
var xform = Transform(target);
foreach (var stationData in EntityQuery<StationEmergencyShuttleComponent>())
{
if (stationData.EmergencyShuttle == null)
continue;
if (IsOnGrid(xform, stationData.EmergencyShuttle.Value))
{
return true;
}
}
return false;
}
private bool IsOnGrid(TransformComponent xform, EntityUid shuttle, MapGridComponent? grid = null, TransformComponent? shuttleXform = null)
{
if (!Resolve(shuttle, ref grid, ref shuttleXform))
return false;
return _transformSystem.GetWorldMatrix(shuttleXform).TransformBox(grid.LocalAABB).Contains(_transformSystem.GetWorldPosition(xform));
}
}