]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Extend shuttle dock time if the shuttle doesn't dock at evac. (#31496)
authorPieter-Jan Briers <pieterjan.briers+git@gmail.com>
Mon, 9 Sep 2024 18:10:28 +0000 (20:10 +0200)
committerGitHub <noreply@github.com>
Mon, 9 Sep 2024 18:10:28 +0000 (20:10 +0200)
* Extend shuttle dock time if the shuttle doesn't dock at evac.

If the shuttle can't dock at evac for *some reason*, it will instead try to dock at another port on the station. And if that fails it goes somewhere random on the station.

Because of the chaos and confusion caused by this, many people will frequently not get to the shuttle in time under these circumstances. This sucks for everybody.

To alleviate this, the shuttle launch timer will now be extended if the station doesn't dock at its ideal spot. The default values (controlled via CVar) are 1.667x and 2x respectively for "wrong dock" and "no dock at all" scenarios.

The code around here was a mess, so I fixed that too. "CallEmergencyShuttle" has been renamed to "DockEmergencyShuttle", the overload that did the actual docking has been renamed to DockSingleEmergencyShuttle. Code has been split into separate dock -> announce methods so we can calculate shuttle delay in between the case of multiple stations.

Also made the "shuttle couldn't find a dock" text announce the time until it launches and fix the shuttle timers not triggering for it.

* Minor review

---------

Content.Server/RoundEnd/RoundEndSystem.cs
Content.Server/Shuttles/Commands/DockEmergencyShuttleCommand.cs
Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs
Content.Server/Shuttles/Systems/EmergencyShuttleSystem.cs
Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
Content.Shared/CCVar/CCVars.cs
Resources/Locale/en-US/shuttles/emergency.ftl

index 42783f163bfe244fbbe784ad1a7507e9725e4d6a..82bdb78816f0c775d7e834d77feb49a479bf3b0f 100644 (file)
@@ -194,7 +194,7 @@ namespace Content.Server.RoundEnd
             ExpectedCountdownEnd = _gameTiming.CurTime + countdownTime;
 
             // TODO full game saves
-            Timer.Spawn(countdownTime, _shuttle.CallEmergencyShuttle, _countdownTokenSource.Token);
+            Timer.Spawn(countdownTime, _shuttle.DockEmergencyShuttle, _countdownTokenSource.Token);
 
             ActivateCooldown();
             RaiseLocalEvent(RoundEndSystemChangedEvent.Default);
index 8febe51f5a76d19f40881da041e65350b1f18a73..f219602bcb6c88e264e58df6a42c4621ee8b635d 100644 (file)
@@ -19,6 +19,6 @@ public sealed class DockEmergencyShuttleCommand : IConsoleCommand
     public void Execute(IConsoleShell shell, string argStr, string[] args)
     {
         var system = _sysManager.GetEntitySystem<EmergencyShuttleSystem>();
-        system.CallEmergencyShuttle();
+        system.DockEmergencyShuttle();
     }
 }
index 597d74dcc7a056fafaf7fd5f86948e11ace21860..aabfaa31dd84c553aec9b9bf45831ede80402931 100644 (file)
@@ -17,7 +17,7 @@ public sealed partial class DockingSystem
 
     private const int DockRoundingDigits = 2;
 
-    public Angle GetAngle(EntityUid uid, TransformComponent xform, EntityUid targetUid, TransformComponent targetXform, EntityQuery<TransformComponent> xformQuery)
+    public Angle GetAngle(EntityUid uid, TransformComponent xform, EntityUid targetUid, TransformComponent targetXform)
     {
         var (shuttlePos, shuttleRot) = _transform.GetWorldPositionRotation(xform);
         var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetXform);
@@ -288,9 +288,7 @@ public sealed partial class DockingSystem
 
         // Prioritise by priority docks, then by maximum connected ports, then by most similar angle.
         validDockConfigs = validDockConfigs
-           .OrderByDescending(x => x.Docks.Any(docks =>
-               TryComp<PriorityDockComponent>(docks.DockBUid, out var priority) &&
-               priority.Tag?.Equals(priorityTag) == true))
+           .OrderByDescending(x => IsConfigPriority(x, priorityTag))
            .ThenByDescending(x => x.Docks.Count)
            .ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList();
 
@@ -301,6 +299,13 @@ public sealed partial class DockingSystem
         return location;
     }
 
+    public bool IsConfigPriority(DockingConfig config, string? priorityTag)
+    {
+        return config.Docks.Any(docks =>
+            TryComp<PriorityDockComponent>(docks.DockBUid, out var priority)
+            && priority.Tag?.Equals(priorityTag) == true);
+    }
+
     /// <summary>
     /// Checks whether the shuttle can warp to the specified position.
     /// </summary>
index 4c13a2cc826ff653b1b4ccca121193c91dd3d4f1..6c4bdc08148995bff74bff56d41b9511d1eeb1d4 100644 (file)
@@ -1,3 +1,4 @@
+using System.Linq;
 using System.Numerics;
 using System.Threading;
 using Content.Server.Access.Systems;
@@ -255,18 +256,19 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
     }
 
     /// <summary>
-    ///     Attempts to dock the emergency shuttle to the station.
+    ///     Attempts to dock a station's emergency shuttle.
     /// </summary>
-    public void CallEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
+    /// <seealso cref="DockEmergencyShuttle"/>
+    public ShuttleDockResult? DockSingleEmergencyShuttle(EntityUid stationUid, StationEmergencyShuttleComponent? stationShuttle = null)
     {
         if (!Resolve(stationUid, ref stationShuttle))
-            return;
+            return null;
 
         if (!TryComp(stationShuttle.EmergencyShuttle, out TransformComponent? xform) ||
             !TryComp<ShuttleComponent>(stationShuttle.EmergencyShuttle, out var shuttle))
         {
             Log.Error($"Attempted to call an emergency shuttle for an uninitialized station? Station: {ToPrettyString(stationUid)}. Shuttle: {ToPrettyString(stationShuttle.EmergencyShuttle)}");
-            return;
+            return null;
         }
 
         var targetGrid = _station.GetLargestGrid(Comp<StationDataComponent>(stationUid));
@@ -274,60 +276,126 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
         // 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;
+            _logger.Add(
+                LogType.EmergencyShuttle,
+                LogImpact.High,
+                $"Emergency shuttle {ToPrettyString(stationUid)} unable to dock with station {ToPrettyString(stationUid)}");
+
+            return new ShuttleDockResult
+            {
+                Station = (stationUid, stationShuttle),
+                ResultType = ShuttleDockResultType.GoodLuck,
+            };
         }
 
-        var xformQuery = GetEntityQuery<TransformComponent>();
+        ShuttleDockResultType resultType;
+        if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, out var config, DockTag))
+        {
+            _logger.Add(
+                LogType.EmergencyShuttle,
+                LogImpact.High,
+                $"Emergency shuttle {ToPrettyString(stationUid)} docked with stations");
+
+            resultType = _dock.IsConfigPriority(config, DockTag)
+                ? ShuttleDockResultType.PriorityDock
+                : ShuttleDockResultType.OtherDock;
+        }
+        else
+        {
+            _logger.Add(
+                LogType.EmergencyShuttle,
+                LogImpact.High,
+                $"Emergency shuttle {ToPrettyString(stationUid)} unable to find a valid docking port for {ToPrettyString(stationUid)}");
 
-        if (_shuttle.TryFTLDock(stationShuttle.EmergencyShuttle.Value, shuttle, targetGrid.Value, DockTag))
+            resultType = ShuttleDockResultType.NoDock;
+        }
+
+        return new ShuttleDockResult
         {
-            if (TryComp(targetGrid.Value, out TransformComponent? targetXform))
-            {
-                var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
-                var direction = ContentLocalizationManager.FormatDirection(angle.GetDir());
-                var location = FormattedMessage.RemoveMarkupPermissive(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform)));
-                _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-docked", ("time", $"{_consoleAccumulator:0}"), ("direction", direction), ("location", location)), playDefaultSound: false);
-            }
+            Station = (stationUid, stationShuttle),
+            DockingConfig = config,
+            ResultType = resultType,
+            TargetGrid = targetGrid,
+        };
+    }
 
-            // 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);
-            }
+    /// <summary>
+    /// Do post-shuttle-dock setup. Announce to the crew and set up shuttle timers.
+    /// </summary>
+    public void AnnounceShuttleDock(ShuttleDockResult result, bool extended)
+    {
+        var shuttle = result.Station.Comp.EmergencyShuttle;
 
-            _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
+        DebugTools.Assert(shuttle != null);
+
+        if (result.ResultType == ShuttleDockResultType.GoodLuck)
         {
-            if (TryComp<TransformComponent>(targetGrid.Value, out var targetXform))
-            {
-                var angle = _dock.GetAngle(stationShuttle.EmergencyShuttle.Value, xform, targetGrid.Value, targetXform, xformQuery);
-                var direction = ContentLocalizationManager.FormatDirection(angle.GetDir());
-                var location = FormattedMessage.RemoveMarkupPermissive(_navMap.GetNearestBeaconString((stationShuttle.EmergencyShuttle.Value, xform)));
-                _chatSystem.DispatchStationAnnouncement(stationUid, Loc.GetString("emergency-shuttle-nearby", ("time", $"{_consoleAccumulator:0}"), ("direction", direction), ("location", location)), playDefaultSound: false);
-            }
+            _chatSystem.DispatchStationAnnouncement(
+                result.Station,
+                Loc.GetString("emergency-shuttle-good-luck"),
+                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);
+            return;
         }
+
+        DebugTools.Assert(result.TargetGrid != null);
+
+        // Send station announcement.
+
+        var targetXform = Transform(result.TargetGrid.Value);
+        var angle = _dock.GetAngle(
+            shuttle.Value,
+            Transform(shuttle.Value),
+            result.TargetGrid.Value,
+            targetXform);
+
+        var direction = ContentLocalizationManager.FormatDirection(angle.GetDir());
+        var location = FormattedMessage.RemoveMarkupPermissive(
+            _navMap.GetNearestBeaconString((shuttle.Value, Transform(shuttle.Value))));
+
+        var extendedText = extended ? Loc.GetString("emergency-shuttle-extended") : "";
+        var locKey = result.ResultType == ShuttleDockResultType.NoDock
+            ? "emergency-shuttle-nearby"
+            : "emergency-shuttle-docked";
+
+        _chatSystem.DispatchStationAnnouncement(
+            result.Station,
+            Loc.GetString(
+                locKey,
+                ("time", $"{_consoleAccumulator:0}"),
+                ("direction", direction),
+                ("location", location),
+                ("extended", extendedText)),
+            playDefaultSound: false);
+
+        // Trigger shuttle timers on the shuttle.
+
+        var time = TimeSpan.FromSeconds(_consoleAccumulator);
+        if (TryComp<DeviceNetworkComponent>(shuttle, out var netComp))
+        {
+            var payload = new NetworkPayload
+            {
+                [ShuttleTimerMasks.ShuttleMap] = shuttle,
+                [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(shuttle.Value, null, payload, netComp.TransmitFrequency);
+        }
+
+        // Play announcement audio.
+
+        var audioFile = result.ResultType == ShuttleDockResultType.NoDock
+            ? "/Audio/Misc/notice1.ogg"
+            : "/Audio/Announcements/shuttle_dock.ogg";
+
+        // TODO: Need filter extensions or something don't blame me.
+        _audio.PlayGlobal(audioFile, Filter.Broadcast(), true);
     }
 
     private void OnStationInit(EntityUid uid, StationCentcommComponent component, MapInitEvent args)
@@ -353,9 +421,12 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
     }
 
     /// <summary>
-    ///     Spawns the emergency shuttle for each station and starts the countdown until controls unlock.
+    /// Teleports the emergency shuttle to its station and starts the countdown until it launches.
     /// </summary>
-    public void CallEmergencyShuttle()
+    /// <remarks>
+    /// If the emergency shuttle is disabled, this immediately ends the round.
+    /// </remarks>
+    public void DockEmergencyShuttle()
     {
         if (EmergencyShuttleArrived)
             return;
@@ -371,9 +442,34 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
 
         var query = AllEntityQuery<StationEmergencyShuttleComponent>();
 
+        var dockResults = new List<ShuttleDockResult>();
+
         while (query.MoveNext(out var uid, out var comp))
         {
-            CallEmergencyShuttle(uid, comp);
+            if (DockSingleEmergencyShuttle(uid, comp) is { } dockResult)
+                dockResults.Add(dockResult);
+        }
+
+        // Make the shuttle wait longer if it couldn't dock in the normal spot.
+        // We have to handle the possibility of there being multiple stations, so since the shuttle timer is global,
+        // use the WORST value we have.
+        var worstResult = dockResults.Max(x => x.ResultType);
+        var multiplier = worstResult switch
+        {
+            ShuttleDockResultType.OtherDock => _configManager.GetCVar(
+                CCVars.EmergencyShuttleDockTimeMultiplierOtherDock),
+            ShuttleDockResultType.NoDock => _configManager.GetCVar(
+                CCVars.EmergencyShuttleDockTimeMultiplierNoDock),
+            // GoodLuck doesn't get a multiplier.
+            // Quite frankly at that point the round is probably so fucked that you'd rather it be over ASAP.
+            _ => 1,
+        };
+
+        _consoleAccumulator *= multiplier;
+
+        foreach (var shuttleDockResult in dockResults)
+        {
+            AnnounceShuttleDock(shuttleDockResult, multiplier > 1);
         }
 
         _commsConsole.UpdateCommsConsoleInterface();
@@ -579,4 +675,66 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
 
         return _transformSystem.GetWorldMatrix(shuttleXform).TransformBox(grid.LocalAABB).Contains(_transformSystem.GetWorldPosition(xform));
     }
+
+    /// <summary>
+    /// A result of a shuttle dock operation done by <see cref="EmergencyShuttleSystem.DockSingleEmergencyShuttle"/>.
+    /// </summary>
+    /// <seealso cref="ShuttleDockResultType"/>
+    public sealed class ShuttleDockResult
+    {
+        /// <summary>
+        /// The station for which the emergency shuttle got docked.
+        /// </summary>
+        public Entity<StationEmergencyShuttleComponent> Station;
+
+        /// <summary>
+        /// The target grid of the station that the shuttle tried to dock to.
+        /// </summary>
+        /// <remarks>
+        /// Not present if <see cref="ResultType"/> is <see cref="ShuttleDockResultType.GoodLuck"/>.
+        /// </remarks>
+        public EntityUid? TargetGrid;
+
+        /// <summary>
+        /// Enum code describing the dock result.
+        /// </summary>
+        public ShuttleDockResultType ResultType;
+
+        /// <summary>
+        /// The docking config used to actually dock to the station.
+        /// </summary>
+        /// <remarks>
+        /// Only present if <see cref="ResultType"/> is <see cref="ShuttleDockResultType.PriorityDock"/>
+        /// or <see cref="ShuttleDockResultType.NoDock"/>.
+        /// </remarks>
+        public DockingConfig? DockingConfig;
+    }
+
+    /// <summary>
+    /// Emergency shuttle dock result codes used by <see cref="ShuttleDockResult"/>.
+    /// </summary>
+    public enum ShuttleDockResultType : byte
+    {
+        // This enum is ordered from "best" to "worst". This is used to sort the results.
+
+        /// <summary>
+        /// The shuttle was docked at a priority dock, which is the intended destination.
+        /// </summary>
+        PriorityDock,
+
+        /// <summary>
+        /// The shuttle docked at another dock on the station then the intended priority dock.
+        /// </summary>
+        OtherDock,
+
+        /// <summary>
+        /// The shuttle couldn't find any suitable dock on the station at all, it did not dock.
+        /// </summary>
+        NoDock,
+
+        /// <summary>
+        /// No station grid was found at all, shuttle did not get moved.
+        /// </summary>
+        GoodLuck,
+    }
 }
index 8da7aaa641e55a630fe1e7abaca67a874e25aaac..f30cab253a0ec44ae5a36ed9b4b77cf803c41d55 100644 (file)
@@ -669,8 +669,28 @@ public sealed partial class ShuttleSystem
     /// Tries to dock with the target grid, otherwise falls back to proximity.
     /// This bypasses FTL travel time.
     /// </summary>
-    public bool TryFTLDock(EntityUid shuttleUid, ShuttleComponent component, EntityUid targetUid, string? priorityTag = null)
+    public bool TryFTLDock(
+        EntityUid shuttleUid,
+        ShuttleComponent component,
+        EntityUid targetUid,
+        string? priorityTag = null)
+    {
+        return TryFTLDock(shuttleUid, component, targetUid, out _, priorityTag);
+    }
+
+    /// <summary>
+    /// Tries to dock with the target grid, otherwise falls back to proximity.
+    /// This bypasses FTL travel time.
+    /// </summary>
+    public bool TryFTLDock(
+        EntityUid shuttleUid,
+        ShuttleComponent component,
+        EntityUid targetUid,
+        [NotNullWhen(true)] out DockingConfig? config,
+        string? priorityTag = null)
     {
+        config = null;
+
         if (!_xformQuery.TryGetComponent(shuttleUid, out var shuttleXform) ||
             !_xformQuery.TryGetComponent(targetUid, out var targetXform) ||
             targetXform.MapUid == null ||
@@ -679,7 +699,7 @@ public sealed partial class ShuttleSystem
             return false;
         }
 
-        var config = _dockSystem.GetDockingConfig(shuttleUid, targetUid, priorityTag);
+        config = _dockSystem.GetDockingConfig(shuttleUid, targetUid, priorityTag);
 
         if (config != null)
         {
index 95fb7bd692c597f835fb49b5ac62abc24e98e30f..9e95231c84ade81fed6ce30506fbfa81a4ae84c6 100644 (file)
@@ -1556,6 +1556,18 @@ namespace Content.Shared.CCVar
         public static readonly CVarDef<float> EmergencyShuttleDockTime =
             CVarDef.Create("shuttle.emergency_dock_time", 180f, CVar.SERVERONLY);
 
+        /// <summary>
+        /// If the emergency shuttle can't dock at a priority port, the dock time will be multiplied with this value.
+        /// </summary>
+        public static readonly CVarDef<float> EmergencyShuttleDockTimeMultiplierOtherDock =
+            CVarDef.Create("shuttle.emergency_dock_time_multiplier_other_dock", 1.6667f, CVar.SERVERONLY);
+
+        /// <summary>
+        /// If the emergency shuttle can't dock at all, the dock time will be multiplied with this value.
+        /// </summary>
+        public static readonly CVarDef<float> EmergencyShuttleDockTimeMultiplierNoDock =
+            CVarDef.Create("shuttle.emergency_dock_time_multiplier_no_dock", 2f, CVar.SERVERONLY);
+
         /// <summary>
         /// How long after the console is authorized for the shuttle to early launch.
         /// </summary>
index be3f0962fa7ced4829b6d06d2348a68aa99d0b2d..ef3582c623c4ac7e5ccd0dd5a28227900f22f5c6 100644 (file)
@@ -13,9 +13,10 @@ emergency-shuttle-command-launch-desc = Early launches the emergency shuttle if
 # Emergency shuttle
 emergency-shuttle-left = The Emergency Shuttle has left the station. Estimate {$transitTime} seconds until the shuttle arrives at CentComm.
 emergency-shuttle-launch-time = The emergency shuttle will launch in {$consoleAccumulator} seconds.
-emergency-shuttle-docked = The Emergency Shuttle has docked {$direction} of the station, {$location}. It will leave in {$time} seconds.
+emergency-shuttle-docked = The Emergency Shuttle has docked {$direction} of the station, {$location}. It will leave in {$time} seconds.{$extended}
 emergency-shuttle-good-luck = The Emergency Shuttle is unable to find a station. Good luck.
-emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped in {$direction} of the station, {$location}.
+emergency-shuttle-nearby = The Emergency Shuttle is unable to find a valid docking port. It has warped in {$direction} of the station, {$location}. It will leave in {$time} seconds.{$extended}
+emergency-shuttle-extended = {" "}Launch time has been extended due to inconvenient circumstances.
 
 # Emergency shuttle console popup / announcement
 emergency-shuttle-console-no-early-launches = Early launch is disabled