]> git.smokeofanarchy.ru Git - space-station-14.git/commitdiff
Cargo shuttle changes (#14363)
authormetalgearsloth <31366439+metalgearsloth@users.noreply.github.com>
Thu, 23 Mar 2023 05:10:49 +0000 (16:10 +1100)
committerGitHub <noreply@github.com>
Thu, 23 Mar 2023 05:10:49 +0000 (16:10 +1100)
26 files changed:
Content.Client/Cargo/BUI/CargoShuttleConsoleBoundUserInterface.cs
Content.Client/Cargo/UI/CargoShuttleMenu.xaml
Content.Client/Cargo/UI/CargoShuttleMenu.xaml.cs
Content.Client/Shuttles/UI/RadarControl.cs
Content.Server/Alert/Click/StopPiloting.cs
Content.Server/Cargo/Systems/CargoSystem.Orders.cs
Content.Server/Cargo/Systems/CargoSystem.Shuttle.cs
Content.Server/Shuttles/Components/EmergencyDockComponent.cs [deleted file]
Content.Server/Shuttles/Components/FTLComponent.cs
Content.Server/Shuttles/Components/PriorityDockComponent.cs [new file with mode: 0644]
Content.Server/Shuttles/Components/ShuttleComponent.cs
Content.Server/Shuttles/DockCommand.cs
Content.Server/Shuttles/Events/FTLTagEvent.cs [new file with mode: 0644]
Content.Server/Shuttles/Systems/DockingSystem.AutoDock.cs
Content.Server/Shuttles/Systems/DockingSystem.cs
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.cs
Content.Server/Shuttles/Systems/ShuttleSystem.EmergencyConsole.cs
Content.Server/Shuttles/Systems/ShuttleSystem.EmergencyShuttle.cs
Content.Server/Shuttles/Systems/ShuttleSystem.FasterThanLight.cs
Content.Shared/Cargo/BUI/CargoShuttleConsoleBoundUserInterfaceState.cs
Content.Shared/Cargo/Components/CargoShuttleComponent.cs
Content.Shared/Cargo/Events/CargoCallShuttleMessage.cs [deleted file]
Content.Shared/Cargo/Events/CargoRecallShuttleMessage.cs [deleted file]
Content.Shared/Shuttles/Systems/SharedRadarConsoleSystem.cs
Resources/Prototypes/Entities/Structures/Doors/Airlocks/access.yml
Resources/Prototypes/tags.yml

index 86b583206e26b26a5aa2cf2598bdf98307159227..f1f102985e66e170c57c6a99e16d3982df359d20 100644 (file)
@@ -1,12 +1,12 @@
 using Content.Client.Cargo.UI;
 using Content.Shared.Cargo.BUI;
-using Content.Shared.Cargo.Events;
+using JetBrains.Annotations;
 using Robust.Client.GameObjects;
 using Robust.Shared.Prototypes;
-using Robust.Shared.Timing;
 
 namespace Content.Client.Cargo.BUI;
 
+[UsedImplicitly]
 public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
 {
     private CargoShuttleMenu? _menu;
@@ -21,9 +21,7 @@ public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
         if (collection == null)
             return;
 
-        _menu = new CargoShuttleMenu(collection.Resolve<IGameTiming>(), collection.Resolve<IPrototypeManager>(), collection.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>());
-        _menu.ShuttleCallRequested += OnShuttleCall;
-        _menu.ShuttleRecallRequested += OnShuttleRecall;
+        _menu = new CargoShuttleMenu(collection.Resolve<IPrototypeManager>(), collection.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>());
         _menu.OnClose += Close;
 
         _menu.OpenCentered();
@@ -38,24 +36,12 @@ public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
         }
     }
 
-    private void OnShuttleRecall()
-    {
-        SendMessage(new CargoRecallShuttleMessage());
-    }
-
-    private void OnShuttleCall()
-    {
-        SendMessage(new CargoCallShuttleMessage());
-    }
-
     protected override void UpdateState(BoundUserInterfaceState state)
     {
         base.UpdateState(state);
         if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return;
         _menu?.SetAccountName(cargoState.AccountName);
         _menu?.SetShuttleName(cargoState.ShuttleName);
-        _menu?.SetShuttleETA(cargoState.ShuttleETA);
         _menu?.SetOrders(cargoState.Orders);
-        _menu?.SetCanRecall(cargoState.CanRecall);
     }
 }
index f789197fa86c93649df24c1f8bdc45c5eb3625b8..c070b908238fc79678cb067e6d459be529664c60 100644 (file)
             <Label Name="ShuttleStatusLabel"
                    Text="{Loc 'cargo-console-menu-shuttle-status-away-text'}" />
         </BoxContainer>
-        <Button Name="ShuttleCallButton"
-                Text="Call Shuttle"/>
-        <Button Name="ShuttleRecallButton"
-                Text="Recall Shuttle"
-                ToolTip="Needs to be out of range to recall."
-                Visible="False"/>
         <Label Text="{Loc 'cargo-console-menu-orders-label'}" />
         <PanelContainer VerticalExpand="True"
                         SizeFlagsStretchRatio="6">
index 27a378ebe1e185e440a45a306e6c7707d3252646..64d1e383b2ff0a1d17961c8b3bdb7e12fc4c27de 100644 (file)
@@ -3,8 +3,6 @@ using Content.Shared.Cargo;
 using Content.Shared.Cargo.Prototypes;
 using Robust.Client.AutoGenerated;
 using Robust.Client.GameObjects;
-using Robust.Client.Graphics;
-using Robust.Client.UserInterface.Controls;
 using Robust.Client.UserInterface.XAML;
 using Robust.Shared.Prototypes;
 using Robust.Shared.Timing;
@@ -14,23 +12,14 @@ namespace Content.Client.Cargo.UI
     [GenerateTypedNameReferences]
     public sealed partial class CargoShuttleMenu : FancyWindow
     {
-        private readonly IGameTiming _timing;
         private readonly IPrototypeManager _protoManager;
         private readonly SpriteSystem _spriteSystem;
 
-        public Action? ShuttleCallRequested;
-        public Action? ShuttleRecallRequested;
-
-        private TimeSpan? _shuttleEta;
-
-        public CargoShuttleMenu(IGameTiming timing, IPrototypeManager protoManager, SpriteSystem spriteSystem)
+        public CargoShuttleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem)
         {
             RobustXamlLoader.Load(this);
-            _timing = timing;
             _protoManager = protoManager;
             _spriteSystem = spriteSystem;
-            ShuttleCallButton.OnPressed += OnCallPressed;
-            ShuttleRecallButton.OnPressed += OnRecallPressed;
             Title = Loc.GetString("cargo-shuttle-console-menu-title");
         }
 
@@ -44,33 +33,6 @@ namespace Content.Client.Cargo.UI
             ShuttleNameLabel.Text = name;
         }
 
-        public void SetShuttleETA(TimeSpan? eta)
-        {
-            _shuttleEta = eta;
-
-            if (eta == null)
-            {
-                ShuttleCallButton.Visible = false;
-                ShuttleRecallButton.Visible = true;
-            }
-            else
-            {
-                ShuttleRecallButton.Visible = false;
-                ShuttleCallButton.Visible = true;
-                ShuttleCallButton.Disabled = true;
-            }
-        }
-
-        private void OnRecallPressed(BaseButton.ButtonEventArgs obj)
-        {
-            ShuttleRecallRequested?.Invoke();
-        }
-
-        private void OnCallPressed(BaseButton.ButtonEventArgs obj)
-        {
-            ShuttleCallRequested?.Invoke();
-        }
-
         public void SetOrders(List<CargoOrderData> orders)
         {
             Orders.DisposeAllChildren();
@@ -102,27 +64,5 @@ namespace Content.Client.Cargo.UI
                  Orders.AddChild(row);
             }
         }
-
-        public void SetCanRecall(bool canRecall)
-        {
-            ShuttleRecallButton.Disabled = !canRecall;
-        }
-
-        protected override void Draw(DrawingHandleScreen handle)
-        {
-            base.Draw(handle);
-
-            var remaining = _shuttleEta - _timing.CurTime;
-
-            if (remaining == null || remaining <= TimeSpan.Zero)
-            {
-                ShuttleStatusLabel.Text = $"Available";
-                ShuttleCallButton.Disabled = false;
-            }
-            else
-            {
-                ShuttleStatusLabel.Text = $"Available in: {remaining.Value.TotalSeconds:0.0}";
-            }
-        }
     }
 }
index 291ffbd23278cf62c6196d7b32623f706ed0eb4b..d6d0cb1164b58386e43461535c0e9da04bfacd09 100644 (file)
@@ -1,5 +1,6 @@
 using Content.Shared.Shuttles.BUIStates;
 using Content.Shared.Shuttles.Components;
+using Content.Shared.Shuttles.Systems;
 using Robust.Client.Graphics;
 using Robust.Client.UserInterface;
 using Robust.Client.UserInterface.Controls;
@@ -33,14 +34,14 @@ public sealed class RadarControl : Control
 
     private Angle? _rotation;
 
-    private float _radarMinRange = 64f;
-    private float _radarMaxRange = 256f;
-    public float RadarRange { get; private set; } = 256f;
+    private float _radarMinRange = SharedRadarConsoleSystem.DefaultMinRange;
+    private float _radarMaxRange = SharedRadarConsoleSystem.DefaultMaxRange;
+    public float RadarRange { get; private set; } = SharedRadarConsoleSystem.DefaultMinRange;
 
     /// <summary>
     /// We'll lerp between the radarrange and actual range
     /// </summary>
-    private float _actualRadarRange = 256f;
+    private float _actualRadarRange = SharedRadarConsoleSystem.DefaultMinRange;
 
     /// <summary>
     /// Controls the maximum distance that IFF labels will display.
@@ -87,14 +88,11 @@ public sealed class RadarControl : Control
     {
         _radarMaxRange = ls.MaxRange;
 
-        if (_radarMaxRange < RadarRange)
-        {
-            _actualRadarRange = _radarMaxRange;
-        }
-
         if (_radarMaxRange < _radarMinRange)
             _radarMinRange = _radarMaxRange;
 
+        _actualRadarRange = Math.Clamp(_actualRadarRange, _radarMinRange, _radarMaxRange);
+
         _docks.Clear();
 
         foreach (var state in ls.Docks)
index 446389da25b48d9f631aca8ccecffde711f63819..8384dec258eeaf9c39fd4ca1392b6b9d1af05669 100644 (file)
@@ -19,7 +19,7 @@ namespace Content.Server.Alert.Click
             if (entManager.TryGetComponent(player, out PilotComponent? pilotComponent) &&
                 pilotComponent.Console != null)
             {
-                entManager.System<ShuttleConsoleSystem>().RemovePilot(pilotComponent);
+                entManager.System<ShuttleConsoleSystem>().RemovePilot(player, pilotComponent);
             }
         }
     }
index f309ae3d721bc56758cdf1df26b8bfc4ccb1e8d6..2af83644dc42a9b477de2e12b75d8242002e82ab 100644 (file)
@@ -263,20 +263,25 @@ namespace Content.Server.Cargo.Systems
         private void UpdateOrders(StationCargoOrderDatabaseComponent component)
         {
             // Order added so all consoles need updating.
-            foreach (var comp in EntityQuery<CargoOrderConsoleComponent>(true))
+            var orderQuery = AllEntityQuery<CargoOrderConsoleComponent>();
+
+            while (orderQuery.MoveNext(out var uid, out var comp))
             {
-                var station = _station.GetOwningStation(component.Owner);
-                if (station != component.Owner) continue;
+                var station = _station.GetOwningStation(uid);
+                if (station != component.Owner)
+                    continue;
 
                 UpdateOrderState(comp, station);
             }
 
-            foreach (var comp in EntityQuery<CargoShuttleConsoleComponent>(true))
+            var consoleQuery = AllEntityQuery<CargoShuttleConsoleComponent>();
+            while (consoleQuery.MoveNext(out var uid, out var comp))
             {
-                var station = _station.GetOwningStation(component.Owner);
-                if (station != component.Owner) continue;
+                var station = _station.GetOwningStation(uid);
+                if (station != component.Owner)
+                    continue;
 
-                UpdateShuttleState(comp, station);
+                UpdateShuttleState(uid, comp, station);
             }
         }
 
index 3bd90aeb3eb1f4e5d5a08f56154722690c163373..8b150812fe9f423e2cd6304374f2ab8fe737be70 100644 (file)
@@ -17,20 +17,16 @@ using Content.Shared.Cargo.Prototypes;
 using Content.Shared.CCVar;
 using Content.Shared.Dataset;
 using Content.Shared.GameTicking;
-using Content.Shared.Mobs.Components;
-using Content.Shared.Mobs.Systems;
+using Content.Shared.Whitelist;
 using Robust.Server.GameObjects;
-using Robust.Server.Maps;
-using Robust.Shared.Audio;
 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;
 using Robust.Shared.Utility;
 using Robust.Shared.Prototypes;
 using Content.Shared.Coordinates;
+using Content.Shared.Mobs.Components;
+using Robust.Shared.Map.Components;
 
 namespace Content.Server.Cargo.Systems;
 
@@ -40,22 +36,19 @@ public sealed partial class CargoSystem
      * Handles cargo shuttle mechanics, including cargo shuttle consoles.
      */
 
+    [Dependency] private readonly IComponentFactory _factory = 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 IPrototypeManager _prototypeManager = default!;
     [Dependency] private readonly EntityLookupSystem _lookup = default!;
     [Dependency] private readonly MapLoaderSystem _map = default!;
-    [Dependency] private readonly MobStateSystem _mobState = default!;
     [Dependency] private readonly PricingSystem _pricing = default!;
     [Dependency] private readonly ShuttleConsoleSystem _console = default!;
     [Dependency] private readonly ShuttleSystem _shuttle = default!;
     [Dependency] private readonly StackSystem _stack = default!;
     public MapId? CargoMap { get; private set; }
 
-    private const float CallOffset = 50f;
-
     private int _index;
 
     /// <summary>
@@ -69,10 +62,11 @@ public sealed partial class CargoSystem
         // Don't want to immediately call this as shuttles will get setup in the natural course of things.
         _configManager.OnValueChanged(CCVars.CargoShuttles, SetCargoShuttleEnabled);
 
-        SubscribeLocalEvent<CargoShuttleComponent, MoveEvent>(OnCargoShuttleMove);
+        SubscribeLocalEvent<CargoShuttleComponent, FTLStartedEvent>(OnCargoFTLStarted);
+        SubscribeLocalEvent<CargoShuttleComponent, FTLCompletedEvent>(OnCargoFTLCompleted);
+        SubscribeLocalEvent<CargoShuttleComponent, FTLTagEvent>(OnCargoFTLTag);
+
         SubscribeLocalEvent<CargoShuttleConsoleComponent, ComponentStartup>(OnCargoShuttleConsoleStartup);
-        SubscribeLocalEvent<CargoShuttleConsoleComponent, CargoCallShuttleMessage>(OnCargoShuttleCall);
-        SubscribeLocalEvent<CargoShuttleConsoleComponent, CargoRecallShuttleMessage>(RecallCargoShuttle);
 
         SubscribeLocalEvent<CargoPalletConsoleComponent, CargoPalletSellMessage>(OnPalletSale);
         SubscribeLocalEvent<CargoPalletConsoleComponent, CargoPalletAppraiseMessage>(OnPalletAppraise);
@@ -87,6 +81,16 @@ public sealed partial class CargoSystem
         SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
     }
 
+    private void OnCargoFTLTag(EntityUid uid, CargoShuttleComponent component, ref FTLTagEvent args)
+    {
+        if (args.Handled)
+            return;
+
+        // Just saves mappers forgetting.
+        args.Handled = true;
+        args.Tag = "DockCargo";
+    }
+
     private void ShutdownShuttle()
     {
         _configManager.UnsubValueChanged(CCVars.CargoShuttles, SetCargoShuttleEnabled);
@@ -94,7 +98,9 @@ public sealed partial class CargoSystem
 
     private void SetCargoShuttleEnabled(bool value)
     {
-        if (_enabled == value) return;
+        if (_enabled == value)
+            return;
+
         _enabled = value;
 
         if (value)
@@ -116,7 +122,7 @@ public sealed partial class CargoSystem
 
     private void OnCargoPilotConsoleOpen(EntityUid uid, CargoPilotConsoleComponent component, AfterActivatableUIOpenEvent args)
     {
-        component.Entity = GetShuttleConsole(component);
+        component.Entity = GetShuttleConsole(uid);
     }
 
     private void OnCargoPilotConsoleClose(EntityUid uid, CargoPilotConsoleComponent component, BoundUIClosedEvent args)
@@ -126,30 +132,50 @@ public sealed partial class CargoSystem
 
     private void OnCargoGetConsole(EntityUid uid, CargoPilotConsoleComponent component, ref ConsoleShuttleEvent args)
     {
-        args.Console = GetShuttleConsole(component);
+        args.Console = GetShuttleConsole(uid);
     }
 
-    private EntityUid? GetShuttleConsole(CargoPilotConsoleComponent component)
+    private EntityUid? GetShuttleConsole(EntityUid uid)
     {
-        var stationUid = _station.GetOwningStation(component.Owner);
+        var stationUid = _station.GetOwningStation(uid);
 
         if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase) ||
-            !TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle)) return null;
+            !TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
+        {
+            return null;
+        }
 
-        return GetShuttleConsole(shuttle);
+        return GetShuttleConsole(orderDatabase.Shuttle.Value, shuttle);
     }
 
     #endregion
 
     #region Console
 
-    private void UpdateShuttleCargoConsoles(CargoShuttleComponent component)
+    private void UpdateCargoShuttleConsoles(CargoShuttleComponent component)
     {
-        foreach (var console in EntityQuery<CargoShuttleConsoleComponent>(true))
+        // Update pilot consoles that are already open.
+        var pilotConsoleQuery = AllEntityQuery<CargoPilotConsoleComponent>();
+
+        while (pilotConsoleQuery.MoveNext(out var uid, out var console))
         {
-            var stationUid = _station.GetOwningStation(console.Owner);
-            if (stationUid != component.Station) continue;
-            UpdateShuttleState(console, stationUid);
+            var stationUid = _station.GetOwningStation(uid);
+            if (stationUid == null || stationUid != component.Station)
+                continue;
+
+            console.Entity = GetShuttleConsole(stationUid.Value);
+        }
+
+        // Update order consoles.
+        var shuttleConsoleQuery = AllEntityQuery<CargoShuttleConsoleComponent>();
+
+        while (shuttleConsoleQuery.MoveNext(out var uid, out var console))
+        {
+            var stationUid = _station.GetOwningStation(uid);
+            if (stationUid != component.Station)
+                continue;
+
+            UpdateShuttleState(uid, console, stationUid);
         }
     }
 
@@ -198,10 +224,10 @@ public sealed partial class CargoSystem
     private void OnCargoShuttleConsoleStartup(EntityUid uid, CargoShuttleConsoleComponent component, ComponentStartup args)
     {
         var station = _station.GetOwningStation(uid);
-        UpdateShuttleState(component, station);
+        UpdateShuttleState(uid, component, station);
     }
 
-    private void UpdateShuttleState(CargoShuttleConsoleComponent component, EntityUid? station = null)
+    private void UpdateShuttleState(EntityUid uid, CargoShuttleConsoleComponent component, EntityUid? station = null)
     {
         TryComp<StationCargoOrderDatabaseComponent>(station, out var orderDatabase);
         TryComp<CargoShuttleComponent>(orderDatabase?.Shuttle, out var shuttle);
@@ -209,12 +235,10 @@ public sealed partial class CargoSystem
         var orders = GetProjectedOrders(orderDatabase, shuttle);
         var shuttleName = orderDatabase?.Shuttle != null ? MetaData(orderDatabase.Shuttle.Value).EntityName : string.Empty;
 
-        _uiSystem.GetUiOrNull(component.Owner, CargoConsoleUiKey.Shuttle)?.SetState(
+        _uiSystem.GetUiOrNull(uid, CargoConsoleUiKey.Shuttle)?.SetState(
             new CargoShuttleConsoleBoundUserInterfaceState(
                 station != null ? MetaData(station.Value).EntityName : Loc.GetString("cargo-shuttle-console-station-unknown"),
                 string.IsNullOrEmpty(shuttleName) ? Loc.GetString("cargo-shuttle-console-shuttle-not-found") : shuttleName,
-                _shuttle.CanFTL(shuttle?.Owner, out _),
-                shuttle?.NextCall,
                 orders));
     }
 
@@ -222,32 +246,21 @@ public sealed partial class CargoSystem
 
     #region Shuttle
 
-    public EntityUid? GetShuttleConsole(CargoShuttleComponent component)
+    public EntityUid? GetShuttleConsole(EntityUid uid, CargoShuttleComponent component)
     {
-        foreach (var (comp, xform) in EntityQuery<ShuttleConsoleComponent, TransformComponent>(true))
+        var query = AllEntityQuery<ShuttleConsoleComponent, TransformComponent>();
+
+        while (query.MoveNext(out var cUid, out var comp, out var xform))
         {
-            if (xform.ParentUid != component.Owner) continue;
-            return comp.Owner;
+            if (xform.ParentUid != uid)
+                continue;
+
+            return cUid;
         }
 
         return null;
     }
 
-    private void OnCargoShuttleMove(EntityUid uid, CargoShuttleComponent component, ref MoveEvent args)
-    {
-        if (component.Station == null) return;
-
-        var oldCanRecall = component.CanRecall;
-
-        // Check if we can update the recall status.
-        var canRecall = _shuttle.CanFTL(uid, out _, args.Component);
-        if (oldCanRecall == canRecall) return;
-
-        component.CanRecall = canRecall;
-        _sawmill.Debug($"Updated CanRecall for {ToPrettyString(uid)}");
-        UpdateShuttleCargoConsoles(component);
-    }
-
     /// <summary>
     /// Returns the orders that can fit on the cargo shuttle.
     /// </summary>
@@ -329,7 +342,10 @@ public sealed partial class CargoSystem
 
         if (CargoMap == null ||
             component.Shuttle != null ||
-            component.CargoShuttleProto == null) return;
+            component.CargoShuttleProto == null)
+        {
+            return;
+        }
 
         var prototype = _protoMan.Index<CargoShuttlePrototype>(component.CargoShuttleProto);
         var possibleNames = _protoMan.Index<DatasetPrototype>(prototype.NameDataset).Values;
@@ -348,11 +364,9 @@ public sealed partial class CargoSystem
         xform.LocalPosition += 100 * _index;
         var comp = EnsureComp<CargoShuttleComponent>(shuttleUid);
         comp.Station = component.Owner;
-        comp.Coordinates = xform.Coordinates;
 
         component.Shuttle = shuttleUid;
-        comp.NextCall = _timing.CurTime + TimeSpan.FromSeconds(comp.Cooldown);
-        UpdateShuttleCargoConsoles(comp);
+        UpdateCargoShuttleConsoles(comp);
         _index++;
         _sawmill.Info($"Added cargo shuttle to {ToPrettyString(shuttleUid)}");
     }
@@ -400,80 +414,6 @@ public sealed partial class CargoSystem
         }
     }
 
-    private void SendToCargoMap(EntityUid uid, CargoShuttleComponent? component = null)
-    {
-        if (!Resolve(uid, ref component))
-            return;
-
-        component.NextCall = _timing.CurTime + TimeSpan.FromSeconds(component.Cooldown);
-        Transform(uid).Coordinates = component.Coordinates;
-        DebugTools.Assert(MetaData(uid).EntityPaused);
-
-        UpdateShuttleCargoConsoles(component);
-        _sawmill.Info($"Stashed cargo shuttle {ToPrettyString(uid)}");
-    }
-
-    /// <summary>
-    /// Retrieves a shuttle for delivery.
-    /// </summary>
-    public void CallShuttle(StationCargoOrderDatabaseComponent orderDatabase)
-    {
-        if (!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
-            return;
-
-        // Already called / not available
-        if (shuttle.NextCall == null || _timing.CurTime < shuttle.NextCall)
-            return;
-
-        if (!TryComp<StationDataComponent>(orderDatabase.Owner, out var stationData))
-            return;
-
-        var targetGrid = _station.GetLargestGrid(stationData);
-
-        // Nowhere to warp in to.
-        if (!TryComp<TransformComponent>(targetGrid, out var xform))
-            return;
-
-        shuttle.NextCall = null;
-
-        // Find a valid free area nearby to spawn in on
-        // TODO: Make this use hyperspace now.
-        var center = new Vector2();
-        var minRadius = 0f;
-        Box2? aabb = null;
-        var xformQuery = GetEntityQuery<TransformComponent>();
-
-        foreach (var grid in _mapManager.GetAllMapGrids(xform.MapID))
-        {
-            var worldAABB = xformQuery.GetComponent(grid.Owner).WorldMatrix.TransformBox(grid.LocalAABB);
-            aabb = aabb?.Union(worldAABB) ?? worldAABB;
-        }
-
-        if (aabb != null)
-        {
-            center = aabb.Value.Center;
-            minRadius = MathF.Max(aabb.Value.Width, aabb.Value.Height);
-        }
-
-        var offset = 0f;
-        if (TryComp<MapGridComponent>(orderDatabase.Shuttle, out var shuttleGrid))
-        {
-            var bounds = shuttleGrid.LocalAABB;
-            offset = MathF.Max(bounds.Width, bounds.Height) / 2f;
-        }
-
-        Transform(shuttle.Owner).Coordinates = new EntityCoordinates(xform.ParentUid,
-            center + _random.NextVector2(minRadius + offset, minRadius + CallOffset + offset));
-        DebugTools.Assert(!MetaData(shuttle.Owner).EntityPaused);
-
-        AddCargoContents(shuttle, orderDatabase);
-        UpdateOrders(orderDatabase);
-        UpdateShuttleCargoConsoles(shuttle);
-        _console.RefreshShuttleConsoles();
-
-        _sawmill.Info($"Retrieved cargo shuttle {ToPrettyString(shuttle.Owner)} from {ToPrettyString(orderDatabase.Owner)}");
-    }
-
     private void AddCargoContents(CargoShuttleComponent shuttle, StationCargoOrderDatabaseComponent orderDatabase)
     {
         var xformQuery = GetEntityQuery<TransformComponent>();
@@ -510,74 +450,37 @@ public sealed partial class CargoSystem
         UpdatePalletConsoleInterface(uid, component);
     }
 
-    private void RecallCargoShuttle(EntityUid uid, CargoShuttleConsoleComponent component, CargoRecallShuttleMessage args)
+    private void OnCargoFTLStarted(EntityUid uid, CargoShuttleComponent component, ref FTLStartedEvent args)
     {
-        var player = args.Session.AttachedEntity;
-
-        if (player == null) return;
-
-        var stationUid = _station.GetOwningStation(uid);
-
-        if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase) ||
-            !TryComp<StationBankAccountComponent>(stationUid, out var bank)) return;
-
-        if (!TryComp<CargoShuttleComponent>(orderDatabase.Shuttle, out var shuttle))
-        {
-            _popup.PopupEntity(Loc.GetString("cargo-no-shuttle"), args.Entity, args.Entity);
-            return;
-        }
+        var xform = Transform(uid);
+        var stationUid = component.Station;
 
-        if (!_shuttle.CanFTL(shuttle.Owner, out var reason))
+        // Called
+        if (xform.MapID != CargoMap ||
+            !TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase))
         {
-            _popup.PopupEntity(reason, args.Entity, args.Entity);
             return;
         }
 
-        if (IsBlocked(shuttle))
-        {
-            _popup.PopupEntity(Loc.GetString("cargo-shuttle-console-organics"), player.Value, player.Value);
-            _audio.PlayPvs(_audio.GetSound(component.DenySound), uid);
-            return;
-        }
-
-        SellPallets((EntityUid) orderDatabase.Shuttle, out double price);
-        bank.Balance += (int) price;
-        _console.RefreshShuttleConsoles();
-        SendToCargoMap(orderDatabase.Shuttle.Value);
+        AddCargoContents(component, orderDatabase);
+        UpdateOrders(orderDatabase);
+        UpdateCargoShuttleConsoles(component);
     }
 
-    /// <summary>
-    ///
-    /// </summary>
-    /// <param name="component"></param>
-    private bool IsBlocked(CargoShuttleComponent component)
+    private void OnCargoFTLCompleted(EntityUid uid, CargoShuttleComponent component, ref FTLCompletedEvent args)
     {
-        // TODO: Would be good to rate-limit this on the console.
-        var mobQuery = GetEntityQuery<MobStateComponent>();
-        var xformQuery = GetEntityQuery<TransformComponent>();
-
-        return FoundOrganics(component.Owner, mobQuery, xformQuery);
-    }
+        var xform = Transform(uid);
+        // Recalled
+        if (xform.MapID != CargoMap)
+            return;
 
-    public bool FoundOrganics(EntityUid uid, EntityQuery<MobStateComponent> mobQuery, EntityQuery<TransformComponent> xformQuery)
-    {
-        var xform = xformQuery.GetComponent(uid);
-        var childEnumerator = xform.ChildEnumerator;
+        var stationUid = component.Station;
 
-        while (childEnumerator.MoveNext(out var child))
+        if (TryComp<StationBankAccountComponent>(stationUid, out var bank))
         {
-            if ((mobQuery.TryGetComponent(child.Value, out var mobState) && !_mobState.IsDead(child.Value, mobState))
-                || FoundOrganics(child.Value, mobQuery, xformQuery)) return true;
+            SellPallets(uid, out var amount);
+            bank.Balance += (int) amount;
         }
-
-        return false;
-    }
-
-    private void OnCargoShuttleCall(EntityUid uid, CargoShuttleConsoleComponent component, CargoCallShuttleMessage args)
-    {
-        var stationUid = _station.GetOwningStation(args.Entity);
-        if (!TryComp<StationCargoOrderDatabaseComponent>(stationUid, out var orderDatabase)) return;
-        CallShuttle(orderDatabase);
     }
 
     #endregion
@@ -600,13 +503,15 @@ public sealed partial class CargoSystem
         CargoMap = null;
 
         // Shuttle may not have been in the cargo dimension (e.g. on the station map) so need to delete.
-        foreach (var comp in EntityQuery<CargoShuttleComponent>())
+        var query = AllEntityQuery<CargoShuttleComponent>();
+
+        while (query.MoveNext(out var uid, out var comp))
         {
             if (TryComp<StationCargoOrderDatabaseComponent>(comp.Station, out var station))
             {
                 station.Shuttle = null;
             }
-            QueueDel(comp.Owner);
+            QueueDel(uid);
         }
     }
 
@@ -619,11 +524,23 @@ public sealed partial class CargoSystem
 
         // It gets mapinit which is okay... buuutt we still want it paused to avoid power draining.
         CargoMap = _mapManager.CreateMap();
-        _mapManager.SetMapPaused(CargoMap!.Value, true);
+        var mapUid = _mapManager.GetMapEntityId(CargoMap.Value);
+        var ftl = EnsureComp<FTLDestinationComponent>(_mapManager.GetMapEntityId(CargoMap.Value));
+        ftl.Whitelist = new EntityWhitelist()
+        {
+            Components = new[]
+            {
+                _factory.GetComponentName(typeof(CargoShuttleComponent))
+            }
+        };
+
+        MetaData(mapUid).EntityName = $"Trading post {_random.Next(1000):000}";
 
         foreach (var comp in EntityQuery<StationCargoOrderDatabaseComponent>(true))
         {
             AddShuttle(comp);
         }
+
+        _console.RefreshShuttleConsoles();
     }
 }
diff --git a/Content.Server/Shuttles/Components/EmergencyDockComponent.cs b/Content.Server/Shuttles/Components/EmergencyDockComponent.cs
deleted file mode 100644 (file)
index a736877..0000000
+++ /dev/null
@@ -1,7 +0,0 @@
-namespace Content.Server.Shuttles.Components;
-
-/// <summary>
-/// Given priority when considering where to dock an emergency shuttle.
-/// </summary>
-[RegisterComponent]
-public sealed class EmergencyDockComponent : Component {}
index 5d102d1ef76c47c30ffcd03780d37a4b52bf9f9e..7563b083aa33ed6cff978d6efd2dc32479f04660 100644 (file)
@@ -1,6 +1,8 @@
 using Content.Shared.Shuttles.Systems;
+using Content.Shared.Tag;
 using Robust.Shared.Audio;
 using Robust.Shared.Map;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
 
 namespace Content.Server.Shuttles.Components;
 
@@ -37,6 +39,12 @@ public sealed class FTLComponent : Component
     [ViewVariables(VVAccess.ReadWrite), DataField("dock")]
     public bool Dock;
 
+    /// <summary>
+    /// If we're docking after FTL what is the prioritised dock tag (if applicable).
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite), DataField("priorityTag", customTypeSerializer:typeof(PrototypeIdSerializer<TagPrototype>))]
+    public string? PriorityTag;
+
     [ViewVariables(VVAccess.ReadWrite), DataField("soundTravel")]
     public SoundSpecifier? TravelSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_progress.ogg")
     {
diff --git a/Content.Server/Shuttles/Components/PriorityDockComponent.cs b/Content.Server/Shuttles/Components/PriorityDockComponent.cs
new file mode 100644 (file)
index 0000000..4f19d72
--- /dev/null
@@ -0,0 +1,18 @@
+using Content.Shared.Tag;
+using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
+
+namespace Content.Server.Shuttles.Components;
+
+/// <summary>
+/// Given priority when considering where to dock.
+/// </summary>
+[RegisterComponent]
+public sealed class PriorityDockComponent : Component
+{
+    /// <summary>
+    /// Tag to match on the docking request, if this dock is to be prioritised.
+    /// </summary>
+    [ViewVariables(VVAccess.ReadWrite),
+     DataField("tag", customTypeSerializer: typeof(PrototypeIdSerializer<TagPrototype>))]
+    public string? Tag;
+}
index b5fe77ebf60a1ae308d605d121093366a5771a3e..83a78fc61d536ba02fd4a0cfdac44bc17ca09b70 100644 (file)
@@ -1,5 +1,3 @@
-using Content.Shared.Shuttles.Components;
-
 namespace Content.Server.Shuttles.Components
 {
     [RegisterComponent]
index 88e39e3b61bda918ed6e52642f2b54e45bccedf0..7b6c354584f055e0f51db39a7fc3dcab4702d24b 100644 (file)
@@ -9,6 +9,8 @@ namespace Content.Server.Shuttles;
 [AdminCommand(AdminFlags.Mapping)]
 public sealed class DockCommand : IConsoleCommand
 {
+    [Dependency] private readonly IEntityManager _entManager = default!;
+
     public string Command => "dock";
     public string Description => $"Attempts to dock 2 airlocks together. Doesn't check whether it is valid.";
     public string Help => $"{Command} <airlock entityuid1> <airlock entityuid2>";
@@ -32,21 +34,19 @@ public sealed class DockCommand : IConsoleCommand
             return;
         }
 
-        var entManager = IoCManager.Resolve<IEntityManager>();
-
-        if (!entManager.TryGetComponent(airlock1, out DockingComponent? dock1))
+        if (!_entManager.TryGetComponent(airlock1, out DockingComponent? dock1))
         {
             shell.WriteError($"No docking component found on {airlock1}");
             return;
         }
 
-        if (!entManager.TryGetComponent(airlock2, out DockingComponent? dock2))
+        if (!_entManager.TryGetComponent(airlock2, out DockingComponent? dock2))
         {
             shell.WriteError($"No docking component found on {airlock2}");
             return;
         }
 
-        var dockSystem = EntitySystem.Get<DockingSystem>();
-        dockSystem.Dock(dock1, dock2);
+        var dockSystem = _entManager.System<DockingSystem>();
+        dockSystem.Dock(airlock1, dock1, airlock2, dock2);
     }
 }
diff --git a/Content.Server/Shuttles/Events/FTLTagEvent.cs b/Content.Server/Shuttles/Events/FTLTagEvent.cs
new file mode 100644 (file)
index 0000000..913a8fc
--- /dev/null
@@ -0,0 +1,7 @@
+namespace Content.Server.Shuttles.Events;
+
+/// <summary>
+/// Raised when trying to get a priority tag for docking.
+/// </summary>
+[ByRefEvent]
+public record struct FTLTagEvent(bool Handled, string? Tag);
index 3b56894ed4e1e6f71fb5ff07aa82d7a4ee55bf18..fc8d331e2d8c78bcca0d0d66578e3ffa21ba801b 100644 (file)
@@ -16,23 +16,25 @@ public sealed partial class DockingSystem
         var dockingQuery = GetEntityQuery<DockingComponent>();
         var xformQuery = GetEntityQuery<TransformComponent>();
         var recentQuery = GetEntityQuery<RecentlyDockedComponent>();
+        var query = EntityQueryEnumerator<AutoDockComponent, PhysicsComponent>();
 
-        foreach (var (comp, body) in EntityQuery<AutoDockComponent, PhysicsComponent>())
+        while (query.MoveNext(out var dockUid, out var comp, out var body))
         {
-            if (comp.Requesters.Count == 0 || !dockingQuery.TryGetComponent(comp.Owner, out var dock))
+            if (comp.Requesters.Count == 0 || !dockingQuery.TryGetComponent(dockUid, out var dock))
             {
-                RemComp<AutoDockComponent>(comp.Owner);
+                RemComp<AutoDockComponent>(dockUid);
                 continue;
             }
 
             // Don't re-dock if we're already docked or recently were.
-            if (dock.Docked || recentQuery.HasComponent(comp.Owner)) continue;
+            if (dock.Docked || recentQuery.HasComponent(dockUid))
+                continue;
 
-            var dockable = GetDockable(body, xformQuery.GetComponent(comp.Owner));
+            var dockable = GetDockable(body, xformQuery.GetComponent(dockUid));
 
             if (dockable == null) continue;
 
-            TryDock(dock, dockable);
+            TryDock(dockUid, dock, dockable.Owner, dockable);
         }
 
         // Work out recent docks that have gone past their designated threshold.
index 571246a5146fda818f8db6b6b44f85324d4416c8..24471d9bc6ce61dc26e4df03e6fd8a1c2a8601f9 100644 (file)
@@ -144,12 +144,13 @@ namespace Content.Server.Shuttles.Systems
         private void Cleanup(DockingComponent dockA)
         {
             _pathfinding.RemovePortal(dockA.PathfindHandle);
-            _jointSystem.RemoveJoint(dockA.DockJoint!);
+
+            if (dockA.DockJoint != null)
+                _jointSystem.RemoveJoint(dockA.DockJoint);
 
             var dockBUid = dockA.DockedWith;
 
             if (dockBUid == null ||
-                dockA.DockJoint == null ||
                 !TryComp(dockBUid, out DockingComponent? dockB))
             {
                 DebugTools.Assert(false);
@@ -187,9 +188,9 @@ namespace Content.Server.Shuttles.Systems
                 GridBUid = gridBUid!.Value,
             };
 
-            EntityManager.EventBus.RaiseLocalEvent(dockA.Owner, msg, false);
-            EntityManager.EventBus.RaiseLocalEvent(dockB.Owner, msg, false);
-            EntityManager.EventBus.RaiseEvent(EventSource.Local, msg);
+            RaiseLocalEvent(dockA.Owner, msg);
+            RaiseLocalEvent(dockB.Owner, msg);
+            RaiseLocalEvent(msg);
         }
 
         private void OnStartup(EntityUid uid, DockingComponent component, ComponentStartup args)
@@ -208,7 +209,7 @@ namespace Content.Server.Shuttles.Systems
                 var otherDock = EntityManager.GetComponent<DockingComponent>(component.DockedWith.Value);
                 DebugTools.Assert(otherDock.DockedWith != null);
 
-                Dock(component, otherDock);
+                Dock(uid, component, component.DockedWith.Value, otherDock);
                 DebugTools.Assert(component.Docked && otherDock.Docked);
             }
         }
@@ -234,7 +235,7 @@ namespace Content.Server.Shuttles.Systems
             var other = Comp<DockingComponent>(component.DockedWith!.Value);
 
             Undock(component);
-            Dock(component, other);
+            Dock(uid, component, component.DockedWith.Value, other);
             _console.RefreshShuttleConsoles();
         }
 
@@ -278,91 +279,96 @@ namespace Content.Server.Shuttles.Systems
         /// <summary>
         /// Docks 2 ports together and assumes it is valid.
         /// </summary>
-        public void Dock(DockingComponent dockA, DockingComponent dockB)
+        public void Dock(EntityUid dockAUid, DockingComponent dockA, EntityUid dockBUid, DockingComponent dockB)
         {
-            if (dockB.Owner.GetHashCode() < dockA.Owner.GetHashCode())
+            if (dockBUid.GetHashCode() < dockAUid.GetHashCode())
             {
                 (dockA, dockB) = (dockB, dockA);
             }
 
-            _sawmill.Debug($"Docking between {dockA.Owner} and {dockB.Owner}");
+            _sawmill.Debug($"Docking between {dockAUid} and {dockBUid}");
 
             // https://gamedev.stackexchange.com/questions/98772/b2distancejoint-with-frequency-equal-to-0-vs-b2weldjoint
 
             // We could also potentially use a prismatic joint? Depending if we want clamps that can extend or whatever
-            var dockAXform = EntityManager.GetComponent<TransformComponent>(dockA.Owner);
-            var dockBXform = EntityManager.GetComponent<TransformComponent>(dockB.Owner);
+            var dockAXform = EntityManager.GetComponent<TransformComponent>(dockAUid);
+            var dockBXform = EntityManager.GetComponent<TransformComponent>(dockBUid);
 
             DebugTools.Assert(dockAXform.GridUid != null);
             DebugTools.Assert(dockBXform.GridUid != null);
             var gridA = dockAXform.GridUid!.Value;
             var gridB = dockBXform.GridUid!.Value;
 
-            SharedJointSystem.LinearStiffness(
-                2f,
-                0.7f,
-                EntityManager.GetComponent<PhysicsComponent>(gridA).Mass,
-                EntityManager.GetComponent<PhysicsComponent>(gridB).Mass,
-                out var stiffness,
-                out var damping);
-
-            // These need playing around with
-            // Could also potentially have collideconnected false and stiffness 0 but it was a bit more suss???
-            WeldJoint joint;
-
-            // Pre-existing joint so use that.
-            if (dockA.DockJointId != null)
-            {
-                DebugTools.Assert(dockB.DockJointId == dockA.DockJointId);
-                joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, dockA.DockJointId);
-            }
-            else
+            // May not be possible if map or the likes.
+            if (TryComp<PhysicsComponent>(gridA, out var gridPhysicsA) &&
+                TryComp<PhysicsComponent>(gridB, out var gridPhysicsB))
             {
-                joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, DockingJoint + dockA.Owner);
-            }
+                SharedJointSystem.LinearStiffness(
+                    2f,
+                    0.7f,
+                    EntityManager.GetComponent<PhysicsComponent>(gridA).Mass,
+                    EntityManager.GetComponent<PhysicsComponent>(gridB).Mass,
+                    out var stiffness,
+                    out var damping);
+
+                // These need playing around with
+                // Could also potentially have collideconnected false and stiffness 0 but it was a bit more suss???
+                WeldJoint joint;
+
+                // Pre-existing joint so use that.
+                if (dockA.DockJointId != null)
+                {
+                    DebugTools.Assert(dockB.DockJointId == dockA.DockJointId);
+                    joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, dockA.DockJointId);
+                }
+                else
+                {
+                    joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, DockingJoint + dockAUid);
+                }
 
-            var gridAXform = EntityManager.GetComponent<TransformComponent>(gridA);
-            var gridBXform = EntityManager.GetComponent<TransformComponent>(gridB);
+                var gridAXform = EntityManager.GetComponent<TransformComponent>(gridA);
+                var gridBXform = EntityManager.GetComponent<TransformComponent>(gridB);
 
-            var anchorA = dockAXform.LocalPosition + dockAXform.LocalRotation.ToWorldVec() / 2f;
-            var anchorB = dockBXform.LocalPosition + dockBXform.LocalRotation.ToWorldVec() / 2f;
+                var anchorA = dockAXform.LocalPosition + dockAXform.LocalRotation.ToWorldVec() / 2f;
+                var anchorB = dockBXform.LocalPosition + dockBXform.LocalRotation.ToWorldVec() / 2f;
 
-            joint.LocalAnchorA = anchorA;
-            joint.LocalAnchorB = anchorB;
-            joint.ReferenceAngle = (float) (gridBXform.WorldRotation - gridAXform.WorldRotation);
-            joint.CollideConnected = true;
-            joint.Stiffness = stiffness;
-            joint.Damping = damping;
+                joint.LocalAnchorA = anchorA;
+                joint.LocalAnchorB = anchorB;
+                joint.ReferenceAngle = (float) (gridBXform.WorldRotation - gridAXform.WorldRotation);
+                joint.CollideConnected = true;
+                joint.Stiffness = stiffness;
+                joint.Damping = damping;
 
-            dockA.DockedWith = dockB.Owner;
-            dockB.DockedWith = dockA.Owner;
+                dockA.DockJoint = joint;
+                dockA.DockJointId = joint.ID;
 
-            dockA.DockJoint = joint;
-            dockA.DockJointId = joint.ID;
+                dockB.DockJoint = joint;
+                dockB.DockJointId = joint.ID;
+            }
 
-            dockB.DockJoint = joint;
-            dockB.DockJointId = joint.ID;
+            dockA.DockedWith = dockBUid;
+            dockB.DockedWith = dockAUid;
 
-            if (TryComp(dockA.Owner, out DoorComponent? doorA))
+            if (TryComp(dockAUid, out DoorComponent? doorA))
             {
                 if (_doorSystem.TryOpen(doorA.Owner, doorA))
                 {
                     doorA.ChangeAirtight = false;
-                    if (TryComp<AirlockComponent>(dockA.Owner, out var airlockA))
+                    if (TryComp<AirlockComponent>(dockAUid, out var airlockA))
                     {
-                        _airlocks.SetBoltsWithAudio(dockA.Owner, airlockA, true);
+                        _airlocks.SetBoltsWithAudio(dockAUid, airlockA, true);
                     }
                 }
             }
 
-            if (TryComp(dockB.Owner, out DoorComponent? doorB))
+            if (TryComp(dockBUid, out DoorComponent? doorB))
             {
                 if (_doorSystem.TryOpen(doorB.Owner, doorB))
                 {
                     doorB.ChangeAirtight = false;
-                    if (TryComp<AirlockComponent>(dockB.Owner, out var airlockB))
+                    if (TryComp<AirlockComponent>(dockBUid, out var airlockB))
                     {
-                        _airlocks.SetBoltsWithAudio(dockB.Owner, airlockB, true);
+                        _airlocks.SetBoltsWithAudio(dockBUid, airlockB, true);
                     }
                 }
             }
@@ -381,9 +387,9 @@ namespace Content.Server.Shuttles.Systems
                 GridBUid = gridB,
             };
 
-            EntityManager.EventBus.RaiseLocalEvent(dockA.Owner, msg, false);
-            EntityManager.EventBus.RaiseLocalEvent(dockB.Owner, msg, false);
-            EntityManager.EventBus.RaiseEvent(EventSource.Local, msg);
+            RaiseLocalEvent(dockAUid, msg);
+            RaiseLocalEvent(dockBUid, msg);
+            RaiseLocalEvent(msg);
         }
 
         private bool CanDock(DockingComponent dockA, DockingComponent dockB)
@@ -433,11 +439,11 @@ namespace Content.Server.Shuttles.Systems
         /// <summary>
         /// Attempts to dock 2 ports together and will return early if it's not possible.
         /// </summary>
-        private void TryDock(DockingComponent dockA, DockingComponent dockB)
+        private void TryDock(EntityUid dockAUid, DockingComponent dockA, EntityUid dockBUid, DockingComponent dockB)
         {
             if (!CanDock(dockA, dockB)) return;
 
-            Dock(dockA, dockB);
+            Dock(dockAUid, dockA, dockBUid, dockB);
         }
 
         public void Undock(DockingComponent dock)
index 3147dc01c2b86b38c20861afd351bfd8114d04e9..d4b52de29aafb46f85ffc3f23722ce9201ed9aa8 100644 (file)
@@ -13,8 +13,8 @@ using Content.Shared.Shuttles.Systems;
 using Content.Shared.Tag;
 using Robust.Server.GameObjects;
 using Robust.Shared.GameStates;
+using Robust.Shared.Map.Components;
 using Robust.Shared.Physics.Components;
-using Robust.Shared.Player;
 using Robust.Shared.Timing;
 using Robust.Shared.Utility;
 
@@ -68,9 +68,10 @@ namespace Content.Server.Shuttles.Systems
                 return;
             }
 
-            if (!dest.Enabled) return;
+            if (!dest.Enabled)
+                return;
 
-            EntityUid? entity = component.Owner;
+            EntityUid? entity = uid;
 
             var getShuttleEv = new ConsoleShuttleEvent
             {
@@ -80,13 +81,14 @@ namespace Content.Server.Shuttles.Systems
             RaiseLocalEvent(entity.Value, ref getShuttleEv);
             entity = getShuttleEv.Console;
 
-            if (entity == null || dest.Whitelist?.IsValid(entity.Value, EntityManager) == false)
+            if (!TryComp<TransformComponent>(entity, out var xform) ||
+                !TryComp<ShuttleComponent>(xform.GridUid, out var shuttle))
             {
                 return;
             }
 
-            if (!TryComp<TransformComponent>(entity, out var xform) ||
-                !TryComp<ShuttleComponent>(xform.GridUid, out var shuttle))
+            if (dest.Whitelist?.IsValid(entity.Value, EntityManager) == false &&
+                dest.Whitelist?.IsValid(xform.GridUid.Value, EntityManager) == false)
             {
                 return;
             }
@@ -97,13 +99,17 @@ namespace Content.Server.Shuttles.Systems
                 return;
             }
 
-            if (!_shuttle.CanFTL(shuttle.Owner, out var reason))
+            if (!_shuttle.CanFTL(xform.GridUid, out var reason))
             {
                 _popup.PopupCursor(reason, args.Session);
                 return;
             }
 
-            _shuttle.FTLTravel(shuttle, args.Destination);
+            var dock = HasComp<MapComponent>(args.Destination) && HasComp<MapGridComponent>(args.Destination);
+            var tagEv = new FTLTagEvent();
+            RaiseLocalEvent(xform.GridUid.Value, ref tagEv);
+
+            _shuttle.FTLTravel(shuttle, args.Destination, dock: dock, priorityTag: tagEv.Tag);
         }
 
         private void OnDock(DockEvent ev)
@@ -128,10 +134,11 @@ namespace Content.Server.Shuttles.Systems
         public void RefreshShuttleConsoles()
         {
             var docks = GetAllDocks();
+            var query = AllEntityQuery<ShuttleConsoleComponent>();
 
-            foreach (var comp in EntityQuery<ShuttleConsoleComponent>(true))
+            while (query.MoveNext(out var uid, out var comp))
             {
-                UpdateState(comp, docks);
+                UpdateState(uid, comp, docks);
             }
         }
 
@@ -141,7 +148,10 @@ namespace Content.Server.Shuttles.Systems
         private void OnConsoleUIClose(EntityUid uid, ShuttleConsoleComponent component, BoundUIClosedEvent args)
         {
             if ((ShuttleConsoleUiKey) args.UiKey != ShuttleConsoleUiKey.Key ||
-                args.Session.AttachedEntity is not {} user) return;
+                args.Session.AttachedEntity is not { } user)
+            {
+                return;
+            }
 
             // In case they D/C should still clean them up.
             foreach (var comp in EntityQuery<AutoDockComponent>(true))
@@ -160,12 +170,12 @@ namespace Content.Server.Shuttles.Systems
 
         private void OnConsoleAnchorChange(EntityUid uid, ShuttleConsoleComponent component, ref AnchorStateChangedEvent args)
         {
-            UpdateState(component);
+            UpdateState(uid, component);
         }
 
         private void OnConsolePowerChange(EntityUid uid, ShuttleConsoleComponent component, ref PowerChangedEvent args)
         {
-            UpdateState(component);
+            UpdateState(uid, component);
         }
 
         private bool TryPilot(EntityUid user, EntityUid uid)
@@ -184,7 +194,7 @@ namespace Content.Server.Shuttles.Systems
 
             if (console != null)
             {
-                RemovePilot(pilotComponent);
+                RemovePilot(user, pilotComponent);
 
                 if (console == component)
                 {
@@ -208,16 +218,18 @@ namespace Content.Server.Shuttles.Systems
         {
             // TODO: NEED TO MAKE SURE THIS UPDATES ON ANCHORING CHANGES!
             var result = new List<DockingInterfaceState>();
+            var query = AllEntityQuery<DockingComponent, TransformComponent>();
 
-            foreach (var (comp, xform) in EntityQuery<DockingComponent, TransformComponent>(true))
+            while (query.MoveNext(out var uid, out var comp, out var xform))
             {
-                if (xform.ParentUid != xform.GridUid) continue;
+                if (xform.ParentUid != xform.GridUid)
+                    continue;
 
                 var state = new DockingInterfaceState()
                 {
                     Coordinates = xform.Coordinates,
                     Angle = xform.LocalRotation,
-                    Entity = comp.Owner,
+                    Entity = uid,
                     Connected = comp.Docked,
                     Color = comp.RadarColor,
                     HighlightedColor = comp.HighlightedRadarColor,
@@ -228,9 +240,9 @@ namespace Content.Server.Shuttles.Systems
             return result;
         }
 
-        private void UpdateState(ShuttleConsoleComponent component, List<DockingInterfaceState>? docks = null)
+        private void UpdateState(EntityUid consoleUid, ShuttleConsoleComponent component, List<DockingInterfaceState>? docks = null)
         {
-            EntityUid? entity = component.Owner;
+            EntityUid? entity = consoleUid;
 
             var getShuttleEv = new ConsoleShuttleEvent
             {
@@ -242,38 +254,44 @@ namespace Content.Server.Shuttles.Systems
 
             TryComp<TransformComponent>(entity, out var consoleXform);
             TryComp<RadarConsoleComponent>(entity, out var radar);
-            var range = radar?.MaxRange ?? 0f;
+            var range = radar?.MaxRange ?? SharedRadarConsoleSystem.DefaultMaxRange;
 
-            TryComp<ShuttleComponent>(consoleXform?.GridUid, out var shuttle);
+            var shuttleGridUid = consoleXform?.GridUid;
 
             var destinations = new List<(EntityUid, string, bool)>();
             var ftlState = FTLState.Available;
             var ftlTime = TimeSpan.Zero;
 
-            if (TryComp<FTLComponent>(shuttle?.Owner, out var shuttleFtl))
+            if (TryComp<FTLComponent>(shuttleGridUid, out var shuttleFtl))
             {
                 ftlState = shuttleFtl.State;
                 ftlTime = _timing.CurTime + TimeSpan.FromSeconds(shuttleFtl.Accumulator);
             }
 
             // Mass too large
-            if (entity != null && shuttle?.Owner != null && (!TryComp<PhysicsComponent>(shuttle?.Owner, out var shuttleBody) ||
-                shuttleBody.Mass < 1000f))
+            if (entity != null && shuttleGridUid != null &&
+                (!TryComp<PhysicsComponent>(shuttleGridUid, out var shuttleBody) || shuttleBody.Mass < 1000f))
             {
                 var metaQuery = GetEntityQuery<MetaDataComponent>();
 
                 // Can't go anywhere when in FTL.
-                var locked = shuttleFtl != null || Paused(shuttle!.Owner);
+                var locked = shuttleFtl != null || Paused(shuttleGridUid.Value);
 
                 // Can't cache it because it may have a whitelist for the particular console.
                 // Include paused as we still want to show CentCom.
-                foreach (var comp in EntityQuery<FTLDestinationComponent>(true))
+                var destQuery = AllEntityQuery<FTLDestinationComponent>();
+
+                while (destQuery.MoveNext(out var destUid, out var comp))
                 {
-                    // Can't warp to itself or if it's not on the whitelist.
-                    if (comp.Owner == shuttle?.Owner ||
-                        comp.Whitelist?.IsValid(entity.Value) == false) continue;
+                    // Can't warp to itself or if it's not on the whitelist (console or shuttle).
+                    if (destUid == shuttleGridUid ||
+                        comp.Whitelist?.IsValid(entity.Value) == false &&
+                        (shuttleGridUid == null || comp.Whitelist?.IsValid(shuttleGridUid.Value, EntityManager) == false))
+                    {
+                        continue;
+                    }
 
-                    var meta = metaQuery.GetComponent(comp.Owner);
+                    var meta = metaQuery.GetComponent(destUid);
                     var name = meta.EntityName;
 
                     if (string.IsNullOrEmpty(name))
@@ -283,19 +301,19 @@ namespace Content.Server.Shuttles.Systems
                                     comp.Enabled &&
                                     (!TryComp<FTLComponent>(comp.Owner, out var ftl) || ftl.State == FTLState.Cooldown);
 
-                    // Can't travel to same map.
-                    if (canTravel && consoleXform?.MapUid == Transform(comp.Owner).MapUid)
+                    // Can't travel to same map (yet)
+                    if (canTravel && consoleXform?.MapUid == Transform(destUid).MapUid)
                     {
                         canTravel = false;
                     }
 
-                    destinations.Add((comp.Owner, name, canTravel));
+                    destinations.Add((destUid, name, canTravel));
                 }
             }
 
             docks ??= GetAllDocks();
 
-            _ui.GetUiOrNull(component.Owner, ShuttleConsoleUiKey.Key)
+            _ui.GetUiOrNull(consoleUid, ShuttleConsoleUiKey.Key)
                 ?.SetState(new ShuttleConsoleBoundInterfaceState(
                     ftlState,
                     ftlTime,
@@ -311,12 +329,14 @@ namespace Content.Server.Shuttles.Systems
             base.Update(frameTime);
 
             var toRemove = new RemQueue<PilotComponent>();
+            var query = EntityQueryEnumerator<PilotComponent>();
 
-            foreach (var comp in EntityManager.EntityQuery<PilotComponent>())
+            while (query.MoveNext(out var uid, out var comp))
             {
-                if (comp.Console == null) continue;
+                if (comp.Console == null)
+                    continue;
 
-                if (!_blocker.CanInteract(comp.Owner, comp.Console.Owner))
+                if (!_blocker.CanInteract(uid, comp.Console.Owner))
                 {
                     toRemove.Add(comp);
                 }
@@ -324,7 +344,7 @@ namespace Content.Server.Shuttles.Systems
 
             foreach (var comp in toRemove)
             {
-                RemovePilot(comp);
+                RemovePilot(comp.Owner, comp);
             }
         }
 
@@ -341,15 +361,18 @@ namespace Content.Server.Shuttles.Systems
             }
 
             if (args.NewPosition.TryDistance(EntityManager, component.Position.Value, out var distance) &&
-                distance < PilotComponent.BreakDistance) return;
+                distance < PilotComponent.BreakDistance)
+            {
+                return;
+            }
 
-            RemovePilot(component);
+            RemovePilot(uid, component);
         }
 
         protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
         {
             base.HandlePilotShutdown(uid, component, args);
-            RemovePilot(component);
+            RemovePilot(uid, component);
         }
 
         private void OnConsoleShutdown(EntityUid uid, ShuttleConsoleComponent component, ComponentShutdown args)
@@ -380,42 +403,43 @@ namespace Content.Server.Shuttles.Systems
             Dirty(pilotComponent);
         }
 
-        public void RemovePilot(PilotComponent pilotComponent)
+        public void RemovePilot(EntityUid pilotUid, PilotComponent pilotComponent)
         {
             var console = pilotComponent.Console;
 
-            if (console is not ShuttleConsoleComponent helmsman) return;
+            if (console is not ShuttleConsoleComponent helmsman)
+                return;
 
             pilotComponent.Console = null;
             pilotComponent.Position = null;
 
-            if (TryComp<SharedEyeComponent>(pilotComponent.Owner, out var eye))
+            if (TryComp<SharedEyeComponent>(pilotUid, out var eye))
             {
                 eye.Zoom = new(1.0f, 1.0f);
             }
 
             if (!helmsman.SubscribedPilots.Remove(pilotComponent)) return;
 
-            _alertsSystem.ClearAlert(pilotComponent.Owner, AlertType.PilotingShuttle);
+            _alertsSystem.ClearAlert(pilotUid, AlertType.PilotingShuttle);
 
             pilotComponent.Owner.PopupMessage(Loc.GetString("shuttle-pilot-end"));
 
             if (pilotComponent.LifeStage < ComponentLifeStage.Stopping)
-                EntityManager.RemoveComponent<PilotComponent>(pilotComponent.Owner);
+                EntityManager.RemoveComponent<PilotComponent>(pilotUid);
         }
 
         public void RemovePilot(EntityUid entity)
         {
             if (!EntityManager.TryGetComponent(entity, out PilotComponent? pilotComponent)) return;
 
-            RemovePilot(pilotComponent);
+            RemovePilot(entity, pilotComponent);
         }
 
         public void ClearPilots(ShuttleConsoleComponent component)
         {
             while (component.SubscribedPilots.TryGetValue(0, out var pilot))
             {
-                RemovePilot(pilot);
+                RemovePilot(pilot.Owner, pilot);
             }
         }
     }
index 0991048a77727bdaaf184dcbf2fa50aa277d7202..536de0c1d8186117a6354581ad099f50ee2ca93b 100644 (file)
@@ -152,7 +152,7 @@ public sealed partial class ShuttleSystem
                     else
                     {
                         FTLTravel(shuttle,
-                            CentCom.Value, _consoleAccumulator, TransitTime, dock: true);
+                            CentCom.Value, _consoleAccumulator, TransitTime, true);
                     }
                 }
             }
index fa8338b1008f663eb5da2c0bd61921a8671f8345..ebe7b0e7ddf6acffcc4e51b8ef1b25ebdf013301 100644 (file)
@@ -12,6 +12,7 @@ using Content.Shared.CCVar;
 using Content.Shared.Database;
 using Content.Shared.Shuttles.Events;
 using Content.Shared.Tiles;
+using Content.Shared.Tag;
 using Robust.Server.GameObjects;
 using Robust.Server.Maps;
 using Robust.Server.Player;
@@ -109,126 +110,6 @@ public sealed partial class ShuttleSystem
        });
    }
 
-   /// <summary>
-   /// Checks whether the emergency shuttle can warp to the specified position.
-   /// </summary>
-   private bool ValidSpawn(MapGridComponent grid, Box2 area)
-   {
-       return !grid.GetLocalTilesIntersecting(area).Any();
-   }
-
-   private DockingConfig? GetDockingConfig(ShuttleComponent component, EntityUid targetGrid)
-   {
-       var gridDocks = GetDocks(targetGrid);
-
-       if (gridDocks.Count <= 0) return null;
-
-       var xformQuery = GetEntityQuery<TransformComponent>();
-       var targetGridGrid = Comp<MapGridComponent>(targetGrid);
-       var targetGridXform = xformQuery.GetComponent(targetGrid);
-       var targetGridAngle = targetGridXform.WorldRotation.Reduced();
-
-       var shuttleDocks = GetDocks(component.Owner);
-       var shuttleAABB = Comp<MapGridComponent>(component.Owner).LocalAABB;
-
-       var validDockConfigs = new List<DockingConfig>();
-
-       if (shuttleDocks.Count > 0)
-       {
-           // We'll try all combinations of shuttle docks and see which one is most suitable
-           foreach (var shuttleDock in shuttleDocks)
-           {
-               var shuttleDockXform = xformQuery.GetComponent(shuttleDock.Owner);
-
-               foreach (var gridDock in gridDocks)
-               {
-                   var gridXform = xformQuery.GetComponent(gridDock.Owner);
-
-                   if (!CanDock(
-                           shuttleDock, shuttleDockXform,
-                           gridDock, gridXform,
-                           targetGridAngle,
-                           shuttleAABB,
-                           targetGridGrid,
-                           out var dockedAABB,
-                           out var matty,
-                           out var targetAngle)) continue;
-
-                   // Can't just use the AABB as we want to get bounds as tight as possible.
-                   var spawnPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero));
-                   spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, spawnPosition.ToMapPos(EntityManager));
-
-                   var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetGridAngle, spawnPosition.Position);
-
-                   // Check if there's no intersecting grids (AKA oh god it's docking at cargo).
-                   if (_mapManager.FindGridsIntersecting(targetGridXform.MapID,
-                           dockedBounds).Any(o => o.Owner != targetGrid))
-                   {
-                       continue;
-                   }
-
-                   // Alright well the spawn is valid now to check how many we can connect
-                   // Get the matrix for each shuttle dock and test it against the grid docks to see
-                   // if the connected position / direction matches.
-
-                   var dockedPorts = new List<(DockingComponent DockA, DockingComponent DockB)>()
-                   {
-                       (shuttleDock, gridDock),
-                   };
-
-                   // TODO: Check shuttle orientation as the tiebreaker.
-
-                   foreach (var other in shuttleDocks)
-                   {
-                       if (other == shuttleDock) continue;
-
-                       foreach (var otherGrid in gridDocks)
-                       {
-                           if (otherGrid == gridDock) continue;
-
-                           if (!CanDock(
-                                   other,
-                                   xformQuery.GetComponent(other.Owner),
-                                   otherGrid,
-                                   xformQuery.GetComponent(otherGrid.Owner),
-                                   targetGridAngle,
-                                   shuttleAABB, targetGridGrid,
-                                   out var otherDockedAABB,
-                                   out _,
-                                   out var otherTargetAngle) ||
-                               !otherDockedAABB.Equals(dockedAABB) ||
-                               !targetAngle.Equals(otherTargetAngle)) continue;
-
-                           dockedPorts.Add((other, otherGrid));
-                       }
-                   }
-
-                   validDockConfigs.Add(new DockingConfig()
-                   {
-                       Docks = dockedPorts,
-                       Area = dockedAABB.Value,
-                       Coordinates = spawnPosition,
-                       Angle = targetAngle,
-                   });
-               }
-           }
-       }
-
-       if (validDockConfigs.Count <= 0) return null;
-
-       // Prioritise by priority docks, then by maximum connected ports, then by most similar angle.
-       validDockConfigs = validDockConfigs
-           .OrderByDescending(x => x.Docks.Any(docks => HasComp<EmergencyDockComponent>(docks.DockB.Owner)))
-           .ThenByDescending(x => x.Docks.Count)
-           .ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList();
-
-       var location = validDockConfigs.First();
-       location.TargetGrid = targetGrid;
-       // TODO: Ideally do a hyperspace warpin, just have it run on like a 10 second timer.
-
-       return location;
-   }
-
    /// <summary>
    /// Calls the emergency shuttle for the station.
    /// </summary>
@@ -305,6 +186,7 @@ public sealed partial class ShuttleSystem
        TransformComponent gridDockXform,
        Angle targetGridRotation,
        Box2 shuttleAABB,
+       EntityUid gridUid,
        MapGridComponent grid,
        [NotNullWhen(true)] out Box2? shuttleDockedAABB,
        out Matrix3 matty,
@@ -337,7 +219,8 @@ public sealed partial class ShuttleSystem
        // Rounding moment
        shuttleDockedAABB = shuttleDockedAABB.Value.Enlarged(-0.01f);
 
-       if (!ValidSpawn(grid, shuttleDockedAABB.Value)) return false;
+       if (!ValidSpawn(gridUid, grid, shuttleDockedAABB.Value))
+           return false;
 
        gridRotation = targetGridRotation + gridDockAngle - shuttleDockAngle;
        return true;
index bada13d9a0aa40470d3b27347bce8d48a5719302..75ddccce48fb498fe370c72c25adfb69b68a182e 100644 (file)
@@ -1,4 +1,3 @@
-using Content.Server.Doors.Components;
 using Content.Server.Doors.Systems;
 using Content.Server.Shuttles.Components;
 using Content.Server.Station.Systems;
@@ -12,10 +11,12 @@ using Robust.Shared.Map;
 using Robust.Shared.Player;
 using Robust.Shared.Utility;
 using System.Diagnostics.CodeAnalysis;
+using System.Linq;
 using Content.Server.Shuttles.Events;
 using Content.Shared.Buckle.Components;
 using Content.Shared.Doors.Components;
 using Content.Shared.Shuttles.Components;
+using JetBrains.Annotations;
 using Robust.Shared.Map.Components;
 using Robust.Shared.Physics;
 using Robust.Shared.Physics.Components;
@@ -46,7 +47,7 @@ public sealed partial class ShuttleSystem
     /// <summary>
     /// Minimum mass a grid needs to be to block a shuttle recall.
     /// </summary>
-    private const float ShuttleFTLMassThreshold = 300f;
+    public const float ShuttleFTLMassThreshold = 300f;
 
     // I'm too lazy to make CVars.
 
@@ -69,6 +70,11 @@ public sealed partial class ShuttleSystem
     /// </summary>
     private const int FTLProximityIterations = 3;
 
+    /// <summary>
+    /// Minimum mass for an FTL destination
+    /// </summary>
+    public const float FTLDestinationMass = 500f;
+
     private void InitializeFTL()
     {
         SubscribeLocalEvent<StationGridAddedEvent>(OnStationGridAdd);
@@ -76,7 +82,9 @@ public sealed partial class ShuttleSystem
 
     private void OnStationGridAdd(StationGridAddedEvent ev)
     {
-        if (TryComp<PhysicsComponent>(ev.GridId, out var body) && body.Mass > 500f)
+        if (HasComp<MapComponent>(ev.GridId) ||
+            TryComp<PhysicsComponent>(ev.GridId, out var body) &&
+            body.Mass > FTLDestinationMass)
         {
             AddFTLDestination(ev.GridId, true);
         }
@@ -133,9 +141,11 @@ public sealed partial class ShuttleSystem
         return destination;
     }
 
+    [PublicAPI]
     public void RemoveFTLDestination(EntityUid uid)
     {
-        if (!RemComp<FTLDestinationComponent>(uid)) return;
+        if (!RemComp<FTLDestinationComponent>(uid))
+            return;
         _console.RefreshShuttleConsoles();
     }
 
@@ -145,7 +155,8 @@ public sealed partial class ShuttleSystem
     public void FTLTravel(ShuttleComponent component,
         EntityCoordinates coordinates,
         float startupTime = DefaultStartupTime,
-        float hyperspaceTime = DefaultTravelTime)
+        float hyperspaceTime = DefaultTravelTime,
+        string? priorityTag = null)
     {
         if (!TrySetupFTL(component, out var hyperspace))
            return;
@@ -155,6 +166,7 @@ public sealed partial class ShuttleSystem
         hyperspace.Accumulator = hyperspace.StartupTime;
         hyperspace.TargetCoordinates = coordinates;
         hyperspace.Dock = false;
+        hyperspace.PriorityTag = priorityTag;
         _console.RefreshShuttleConsoles();
     }
 
@@ -165,7 +177,8 @@ public sealed partial class ShuttleSystem
         EntityUid target,
         float startupTime = DefaultStartupTime,
         float hyperspaceTime = DefaultTravelTime,
-        bool dock = false)
+        bool dock = false,
+        string? priorityTag = null)
     {
         if (!TrySetupFTL(component, out var hyperspace))
             return;
@@ -175,6 +188,7 @@ public sealed partial class ShuttleSystem
         hyperspace.Accumulator = hyperspace.StartupTime;
         hyperspace.TargetUid = target;
         hyperspace.Dock = dock;
+        hyperspace.PriorityTag = priorityTag;
         _console.RefreshShuttleConsoles();
     }
 
@@ -206,18 +220,23 @@ public sealed partial class ShuttleSystem
         SoundSystem.Play(_startupSound.GetSound(), Filter.Empty().AddInRange(Transform(uid).MapPosition, GetSoundRange(component.Owner)), _startupSound.Params);
         // Make sure the map is setup before we leave to avoid pop-in (e.g. parallax).
         SetupHyperspace();
+
+        var ev = new FTLStartedEvent();
+        RaiseLocalEvent(uid, ref ev);
         return true;
     }
 
     private void UpdateHyperspace(float frameTime)
     {
-        foreach (var comp in EntityQuery<FTLComponent>())
+        var query = EntityQueryEnumerator<FTLComponent>();
+
+        while (query.MoveNext(out var uid, out var comp))
         {
             comp.Accumulator -= frameTime;
 
-            if (comp.Accumulator > 0f) continue;
+            if (comp.Accumulator > 0f)
+                continue;
 
-            var uid = comp.Owner;
             var xform = Transform(uid);
             PhysicsComponent? body;
             ShuttleComponent? shuttle;
@@ -280,13 +299,21 @@ public sealed partial class ShuttleSystem
                     SetDockBolts(uid, false);
                     SetDocks(uid, true);
 
+                    if (TryComp(uid, out body))
+                    {
+                        _physics.SetLinearVelocity(uid, Vector2.Zero, body: body);
+                        _physics.SetAngularVelocity(uid, 0f, body: body);
+                        _physics.SetLinearDamping(body, ShuttleLinearDamping);
+                        _physics.SetAngularDamping(body, ShuttleAngularDamping);
+                    }
+
                     TryComp(uid, out shuttle);
                     MapId mapId;
 
                     if (comp.TargetUid != null && shuttle != null)
                     {
                         if (comp.Dock)
-                            TryFTLDock(shuttle, comp.TargetUid.Value);
+                            TryFTLDock(shuttle, comp.TargetUid.Value, comp.PriorityTag);
                         else
                             TryFTLProximity(shuttle, comp.TargetUid.Value);
 
@@ -439,7 +466,8 @@ public sealed partial class ShuttleSystem
     /// <summary>
     /// Tries to dock with the target grid, otherwise falls back to proximity.
     /// </summary>
-    public bool TryFTLDock(ShuttleComponent component, EntityUid targetUid)
+    /// <param name="priorityTag">Priority docking tag to prefer, e.g. for emergency shuttle</param>
+    public bool TryFTLDock(ShuttleComponent component, EntityUid targetUid, string? priorityTag = null)
     {
         if (!TryComp<TransformComponent>(component.Owner, out var xform) ||
             !TryComp<TransformComponent>(targetUid, out var targetXform) ||
@@ -449,7 +477,7 @@ public sealed partial class ShuttleSystem
             return false;
         }
 
-        var config = GetDockingConfig(component, targetUid);
+        var config = GetDockingConfig(component, targetUid, priorityTag);
 
         if (config != null)
         {
@@ -460,7 +488,7 @@ public sealed partial class ShuttleSystem
            // Connect everything
            foreach (var (dockA, dockB) in config.Docks)
            {
-               _dockSystem.Dock(dockA, dockB);
+               _dockSystem.Dock(dockA.Owner, dockA, dockB.Owner, dockB);
            }
 
            return true;
@@ -476,7 +504,6 @@ public sealed partial class ShuttleSystem
     public bool TryFTLProximity(ShuttleComponent component, EntityUid targetUid, TransformComponent? xform = null, TransformComponent? targetXform = null)
     {
         if (!Resolve(targetUid, ref targetXform) ||
-            targetXform.GridUid == null ||
             targetXform.MapUid == null ||
             !targetXform.MapUid.Value.IsValid() ||
             !Resolve(component.Owner, ref xform))
@@ -502,9 +529,9 @@ public sealed partial class ShuttleSystem
 
         var targetAABB = _transform.GetWorldMatrix(targetXform, xformQuery)
             .TransformBox(targetLocalAABB).Enlarged(shuttleAABB.Size.Length);
-        var nearbyGrids = new HashSet<EntityUid>(1) { targetXform.GridUid.Value };
+        var nearbyGrids = new HashSet<EntityUid>();
         var iteration = 0;
-        var lastCount = 1;
+        var lastCount = nearbyGrids.Count;
         var mapId = targetXform.MapID;
 
         while (iteration < FTLProximityIterations)
@@ -552,7 +579,7 @@ public sealed partial class ShuttleSystem
         }
 
         // TODO: This is pretty crude for multiple landings.
-        if (nearbyGrids.Count > 1 || !HasComp<MapComponent>(targetXform.GridUid.Value))
+        if (nearbyGrids.Count > 1 || !HasComp<MapComponent>(targetXform.GridUid))
         {
             var minRadius = (MathF.Max(targetAABB.Width, targetAABB.Height) + MathF.Max(shuttleAABB.Width, shuttleAABB.Height)) / 2f;
             spawnPos = targetAABB.Center + _random.NextVector2(minRadius, minRadius + 64f);
@@ -570,7 +597,7 @@ public sealed partial class ShuttleSystem
 
         xform.Coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos);
 
-        if (!HasComp<MapComponent>(targetXform.GridUid.Value))
+        if (!HasComp<MapComponent>(targetXform.GridUid))
         {
             _transform.SetLocalRotation(xform, _random.NextAngle());
         }
@@ -581,4 +608,138 @@ public sealed partial class ShuttleSystem
 
         return true;
     }
+
+    /// <summary>
+   /// Checks whether the emergency shuttle can warp to the specified position.
+   /// </summary>
+   private bool ValidSpawn(EntityUid gridUid, MapGridComponent grid, Box2 area)
+   {
+       // If the target is a map then any tile is valid.
+       // TODO: We already need the entities-under check
+       if (HasComp<MapComponent>(gridUid))
+           return true;
+
+       return !grid.GetLocalTilesIntersecting(area).Any();
+   }
+
+   /// <summary>
+   /// Tries to get a valid docking configuration for the shuttle to the target grid.
+   /// </summary>
+   /// <param name="priorityTag">Priority docking tag to prefer, e.g. for emergency shuttle</param>
+   private DockingConfig? GetDockingConfig(ShuttleComponent component, EntityUid targetGrid, string? priorityTag = null)
+   {
+       var gridDocks = GetDocks(targetGrid);
+
+       if (gridDocks.Count <= 0)
+           return null;
+
+       var xformQuery = GetEntityQuery<TransformComponent>();
+       var targetGridGrid = Comp<MapGridComponent>(targetGrid);
+       var targetGridXform = xformQuery.GetComponent(targetGrid);
+       var targetGridAngle = targetGridXform.WorldRotation.Reduced();
+
+       var shuttleDocks = GetDocks(component.Owner);
+       var shuttleAABB = Comp<MapGridComponent>(component.Owner).LocalAABB;
+
+       var validDockConfigs = new List<DockingConfig>();
+
+       if (shuttleDocks.Count > 0)
+       {
+           // We'll try all combinations of shuttle docks and see which one is most suitable
+           foreach (var shuttleDock in shuttleDocks)
+           {
+               var shuttleDockXform = xformQuery.GetComponent(shuttleDock.Owner);
+
+               foreach (var gridDock in gridDocks)
+               {
+                   var gridXform = xformQuery.GetComponent(gridDock.Owner);
+
+                   if (!CanDock(
+                           shuttleDock, shuttleDockXform,
+                           gridDock, gridXform,
+                           targetGridAngle,
+                           shuttleAABB,
+                           targetGrid,
+                           targetGridGrid,
+                           out var dockedAABB,
+                           out var matty,
+                           out var targetAngle)) continue;
+
+                   // Can't just use the AABB as we want to get bounds as tight as possible.
+                   var spawnPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero));
+                   spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, spawnPosition.ToMapPos(EntityManager));
+
+                   var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetGridAngle, spawnPosition.Position);
+
+                   // Check if there's no intersecting grids (AKA oh god it's docking at cargo).
+                   if (_mapManager.FindGridsIntersecting(targetGridXform.MapID,
+                           dockedBounds).Any(o => o.Owner != targetGrid))
+                   {
+                       continue;
+                   }
+
+                   // Alright well the spawn is valid now to check how many we can connect
+                   // Get the matrix for each shuttle dock and test it against the grid docks to see
+                   // if the connected position / direction matches.
+
+                   var dockedPorts = new List<(DockingComponent DockA, DockingComponent DockB)>()
+                   {
+                       (shuttleDock, gridDock),
+                   };
+
+                   foreach (var other in shuttleDocks)
+                   {
+                       if (other == shuttleDock) continue;
+
+                       foreach (var otherGrid in gridDocks)
+                       {
+                           if (otherGrid == gridDock) continue;
+
+                           if (!CanDock(
+                                   other,
+                                   xformQuery.GetComponent(other.Owner),
+                                   otherGrid,
+                                   xformQuery.GetComponent(otherGrid.Owner),
+                                   targetGridAngle,
+                                   shuttleAABB,
+                                   targetGrid,
+                                   targetGridGrid,
+                                   out var otherDockedAABB,
+                                   out _,
+                                   out var otherTargetAngle) ||
+                               !otherDockedAABB.Equals(dockedAABB) ||
+                               !targetAngle.Equals(otherTargetAngle)) continue;
+
+                           dockedPorts.Add((other, otherGrid));
+                       }
+                   }
+
+                   validDockConfigs.Add(new DockingConfig()
+                   {
+                       Docks = dockedPorts,
+                       Area = dockedAABB.Value,
+                       Coordinates = spawnPosition,
+                       Angle = targetAngle,
+                   });
+               }
+           }
+       }
+
+       if (validDockConfigs.Count <= 0)
+           return null;
+
+       // 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.DockB.Owner, out var priority) &&
+               priority.Tag?.Equals(priorityTag) == true))
+           .ThenByDescending(x => x.Docks.Count)
+           .ThenBy(x => Math.Abs(Angle.ShortestDistance(x.Angle.Reduced(), targetGridAngle).Theta)).ToList();
+
+       var location = validDockConfigs.First();
+       location.TargetGrid = targetGrid;
+       // TODO: Ideally do a hyperspace warpin, just have it run on like a 10 second timer.
+
+       return location;
+   }
 }
index 75e0aee60f0d861e3ca8059ec3e57998fa045a66..8654fffdd83da68e637323a6b303af5ed6b74be4 100644 (file)
@@ -8,18 +8,6 @@ public sealed class CargoShuttleConsoleBoundUserInterfaceState : BoundUserInterf
     public string AccountName;
     public string ShuttleName;
 
-    // Unfortunately shuttles have essentially 3 states so can't just use a nullable var for it:
-    // 1. stowed
-    // 2. called but not recallable
-    // 3. called and recallable
-    // The reason we have 2 is so people don't spam the recall button in the UI.
-    public bool CanRecall;
-
-    /// <summary>
-    /// When the shuttle is expected to be usable.
-    /// </summary>
-    public TimeSpan? ShuttleETA;
-
     /// <summary>
     /// List of orders expected on the delivery.
     /// </summary>
@@ -28,14 +16,10 @@ public sealed class CargoShuttleConsoleBoundUserInterfaceState : BoundUserInterf
     public CargoShuttleConsoleBoundUserInterfaceState(
         string accountName,
         string shuttleName,
-        bool canRecall,
-        TimeSpan? shuttleETA,
         List<CargoOrderData> orders)
     {
         AccountName = accountName;
         ShuttleName = shuttleName;
-        CanRecall = canRecall;
-        ShuttleETA = shuttleETA;
         Orders = orders;
     }
 }
index d3c5b693cd4eff4513a5eda780a3ea1300b3d456..b6d503a7569a905bc1ba170c94a6efdc34f2239d 100644 (file)
@@ -10,21 +10,6 @@ namespace Content.Shared.Cargo.Components;
 [RegisterComponent, Access(typeof(SharedCargoSystem))]
 public sealed class CargoShuttleComponent : Component
 {
-    [ViewVariables(VVAccess.ReadWrite), DataField("nextCall")]
-    public TimeSpan? NextCall;
-
-    [ViewVariables(VVAccess.ReadWrite), DataField("cooldown")]
-    public float Cooldown = 30f;
-
-    [ViewVariables]
-    public bool CanRecall;
-
-    /// <summary>
-    /// The shuttle's assigned coordinates on the cargo map.
-    /// </summary>
-    [ViewVariables]
-    public EntityCoordinates Coordinates;
-
     /// <summary>
     /// The assigned station for this cargo shuttle.
     /// </summary>
diff --git a/Content.Shared/Cargo/Events/CargoCallShuttleMessage.cs b/Content.Shared/Cargo/Events/CargoCallShuttleMessage.cs
deleted file mode 100644 (file)
index e06b388..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Cargo.Events;
-
-/// <summary>
-/// Raised on a cargo console requesting the cargo shuttle.
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class CargoCallShuttleMessage : BoundUserInterfaceMessage
-{
-
-}
diff --git a/Content.Shared/Cargo/Events/CargoRecallShuttleMessage.cs b/Content.Shared/Cargo/Events/CargoRecallShuttleMessage.cs
deleted file mode 100644 (file)
index 041ddeb..0000000
+++ /dev/null
@@ -1,12 +0,0 @@
-using Robust.Shared.Serialization;
-
-namespace Content.Shared.Cargo.Events;
-
-/// <summary>
-/// Raised on a client request cargo shuttle recall
-/// </summary>
-[Serializable, NetSerializable]
-public sealed class CargoRecallShuttleMessage : BoundUserInterfaceMessage
-{
-
-}
index c0754df1e132800e154df41eca319b2de6d509ee..826eaf13b3eb01862bdee057ae54624734acce33 100644 (file)
@@ -6,6 +6,9 @@ namespace Content.Shared.Shuttles.Systems;
 
 public abstract class SharedRadarConsoleSystem : EntitySystem
 {
+    public const float DefaultMinRange = 64f;
+    public const float DefaultMaxRange = 256f;
+
     public override void Initialize()
     {
         base.Initialize();
index c14cf062f516a6be0d477da2454e4cd75bf7bd09..4c62f0d77c72cff59b62ce2dd38326ddee4e0c93 100644 (file)
   components:
   - type: AccessReader
     access: [["External"]]
-    
+
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalCargoLocked
   suffix: External, Cargo, Locked
   components:
   - type: AccessReader
-    access: [["Cargo"]]    
-    
+    access: [["Cargo"]]
+
 - type: entity
   parent: AirlockExternal
   id: AirlockExternalEngineeringLocked
@@ -94,7 +94,7 @@
   components:
   - type: AccessReader
     access: [["Atmospherics"]]
-    
+
 - type: entity
   parent: AirlockFreezer
   id: AirlockFreezerLocked
   components:
   - type: AccessReader
     access: [["Cargo"]]
-    
+
 - type: entity
   parent: AirlockExternalGlass
   id: AirlockExternalGlassEngineeringLocked
   suffix: External, Glass, Engineering, Locked
   components:
   - type: AccessReader
-    access: [["Engineering"]]    
+    access: [["Engineering"]]
 
 - type: entity
   parent: AirlockExternalGlass
   suffix: External, Glass, Atmospherics, Locked
   components:
   - type: AccessReader
-    access: [["Atmospherics"]]   
-    
+    access: [["Atmospherics"]]
+
 - type: entity
   parent: AirlockGlass
   id: AirlockKitchenGlassLocked
   id: AirlockExternalGlassShuttleEmergencyLocked
   suffix: External, Emergency, Glass, Docking, Locked
   components:
-    - type: EmergencyDock
+    - type: PriorityDock
+      tag: DockEmergency
     - type: AccessReader
       access: [["External"]]
 
index 88480aa133ca0e16ed7142cf5f7e4ee82e88ed9f..60c0c78f7a61f236bc2fd85e6d1b27d1490abbc5 100644 (file)
 - type: Tag
   id: DiscreteHealthAnalyzer #So construction recipes don't eat medical PDAs
 
+- type: Tag
+  id: DockCargo
+
+- type: Tag
+  id: DockEmergency
+
 - type: Tag
   id: Document