Shuttle console + FTL rework (#24430)
* Add shuttle interior drawing back Just do it per-tile she'll be right, at least it's done with 1 draw call. * Revamp shuttle console * Bunch of cleanup work * Lables sortito * dok * Pixel alignment and colours * Fix a bunch of drawing bugs * Shuttle map drawing * Drawing fixes * Map parallax working finally * weh * Commit all my stuff * mic * deez * Update everything * Xamlify everything * uh * Rudimentary blocker range * My enemies have succeeded * Bunch of changes to FTL * Heaps of cleanup * Fix FTL bugs * FTL * weewoo * FTL fallback * wew * weh * Basic FTL working * FTL working * FTL destination fixes * a * Exclusion zones * Fix drawing / FTL * Beacons working * Coordinates drawing * Fix unknown map names * Dorks beginning * State + docking cleanup start * Basic dock drawing * Bunch of drawing fixes * Batching / color fixes * Cleanup and beacons support * weh * weh * Begin pings * First draft at map objects * Map fixup * Faster drawing * Fix perf + FTL * Cached drawing * Fix drawing * Best I got * strips * Back to lists but with caching * Final optimisation * Fix dock bounds * Docking work * stinker * kobolds * Btns * Docking vis working * Fix docking pre-vis * canasses * Helldivers 2 * a * Array life * Fix * Fix TODOs * liltenhead feature club * dorking * Merge artifacts * Last-minute touchup
This commit is contained in:
@@ -12,7 +12,7 @@
|
|||||||
<BoxContainer Name="VolumeBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
<BoxContainer Name="VolumeBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
||||||
|
|
||||||
<!-- The temperature / heat capacity / thermal energy of the solution -->
|
<!-- The temperature / heat capacity / thermal energy of the solution -->
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Name="ThermalHeading" Title="{Loc 'admin-solutions-window-thermals'}" />
|
<CollapsibleHeading Name="ThermalHeading" Title="{Loc 'admin-solutions-window-thermals'}" />
|
||||||
<CollapsibleBody>
|
<CollapsibleBody>
|
||||||
<BoxContainer Name="ThermalBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
<BoxContainer Name="ThermalBox" Orientation="Vertical" HorizontalExpand="True" Margin="0 4"/>
|
||||||
@@ -23,7 +23,7 @@
|
|||||||
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="0 4">
|
<ScrollContainer HorizontalExpand="True" VerticalExpand="True" Margin="0 4">
|
||||||
<BoxContainer Name="ReagentList" Orientation="Vertical"/>
|
<BoxContainer Name="ReagentList" Orientation="Vertical"/>
|
||||||
</ScrollContainer>
|
</ScrollContainer>
|
||||||
|
|
||||||
<Button Name="AddButton" Text="{Loc 'admin-solutions-window-add-new-button'}" HorizontalExpand="True" Margin="0 4"/>
|
<Button Name="AddButton" Text="{Loc 'admin-solutions-window-add-new-button'}" HorizontalExpand="True" Margin="0 4"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</DefaultWindow>
|
</DefaultWindow>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io"
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Orientation="Vertical" Margin="2 0 2 4">
|
Orientation="Vertical" Margin="2 0 2 4">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Name="CAddress" />
|
<CollapsibleHeading Name="CAddress" />
|
||||||
<!-- Upper row: toggle, direction, checks -->
|
<!-- Upper row: toggle, direction, checks -->
|
||||||
<CollapsibleBody Margin="20 0 0 0">
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io"
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Orientation="Vertical" Margin="2 0 2 4">
|
Orientation="Vertical" Margin="2 0 2 4">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Name="CAddress" />
|
<CollapsibleHeading Name="CAddress" />
|
||||||
<CollapsibleBody Margin="20 0 0 0">
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
@@ -26,7 +26,7 @@
|
|||||||
<Button Name="CCopySettings" Text="{Loc 'air-alarm-ui-widget-copy'}" ToolTip="{Loc 'air-alarm-ui-widget-copy-tooltip'}" />
|
<Button Name="CCopySettings" Text="{Loc 'air-alarm-ui-widget-copy'}" ToolTip="{Loc 'air-alarm-ui-widget-copy-tooltip'}" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<!-- Lower row: every single gas -->
|
<!-- Lower row: every single gas -->
|
||||||
<Collapsible Orientation="Vertical" Margin="2 2 2 2">
|
<Collapsible Margin="2 2 2 2">
|
||||||
<CollapsibleHeading Title="Gas filters" />
|
<CollapsibleHeading Title="Gas filters" />
|
||||||
<CollapsibleBody Margin="20 0 0 0">
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
|
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io" HorizontalExpand="True">
|
<BoxContainer xmlns="https://spacestation14.io" HorizontalExpand="True">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Name="SensorAddress" />
|
<CollapsibleHeading Name="SensorAddress" />
|
||||||
<CollapsibleBody Margin="20 2 2 2">
|
<CollapsibleBody Margin="20 2 2 2">
|
||||||
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
<BoxContainer Orientation="Vertical" HorizontalExpand="True">
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
<RichTextLabel Name="TemperatureLabel" />
|
<RichTextLabel Name="TemperatureLabel" />
|
||||||
<Control Name="TemperatureThresholdContainer" Margin="20 0 2 0" />
|
<Control Name="TemperatureThresholdContainer" Margin="20 0 2 0" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Collapsible Orientation="Vertical" Margin="2">
|
<Collapsible Margin="2">
|
||||||
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-gases'}" />
|
<CollapsibleHeading Title="{Loc 'air-alarm-ui-sensor-gases'}" />
|
||||||
<CollapsibleBody Margin="20 0 0 0">
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Name="GasContainer" Orientation="Vertical" Margin="2" />
|
<BoxContainer Name="GasContainer" Orientation="Vertical" Margin="2" />
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<BoxContainer xmlns="https://spacestation14.io"
|
<BoxContainer xmlns="https://spacestation14.io"
|
||||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||||
Orientation="Vertical" Margin="0 0 0 4">
|
Orientation="Vertical" Margin="0 0 0 4">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Name="CName" />
|
<CollapsibleHeading Name="CName" />
|
||||||
<CollapsibleBody Margin="20 0 0 0">
|
<CollapsibleBody Margin="20 0 0 0">
|
||||||
<BoxContainer Orientation="Vertical">
|
<BoxContainer Orientation="Vertical">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<PanelContainer.PanelOverride>
|
<PanelContainer.PanelOverride>
|
||||||
<graphics:StyleBoxFlat BackgroundColor="{xNamespace:Static style:StyleNano.ButtonColorDisabled}" />
|
<graphics:StyleBoxFlat BackgroundColor="{xNamespace:Static style:StyleNano.ButtonColorDisabled}" />
|
||||||
</PanelContainer.PanelOverride>
|
</PanelContainer.PanelOverride>
|
||||||
<Collapsible Orientation="Vertical" Name="Collapsible">
|
<Collapsible Name="Collapsible">
|
||||||
<CollapsibleHeading Name="Heading" MinHeight="35"/>
|
<CollapsibleHeading Name="Heading" MinHeight="35"/>
|
||||||
<CollapsibleBody Name="Body">
|
<CollapsibleBody Name="Body">
|
||||||
<BoxContainer Name="ItemsContainer" Orientation="Vertical" HorizontalExpand="True"/>
|
<BoxContainer Name="ItemsContainer" Orientation="Vertical" HorizontalExpand="True"/>
|
||||||
|
|||||||
@@ -11,7 +11,7 @@
|
|||||||
<RichTextLabel Name="ReagentName" HorizontalAlignment="Center"/>
|
<RichTextLabel Name="ReagentName" HorizontalAlignment="Center"/>
|
||||||
</PanelContainer>
|
</PanelContainer>
|
||||||
<BoxContainer Name="RecipesContainer" HorizontalExpand="True">
|
<BoxContainer Name="RecipesContainer" HorizontalExpand="True">
|
||||||
<Collapsible Orientation="Vertical" HorizontalExpand="True">
|
<Collapsible HorizontalExpand="True">
|
||||||
<CollapsibleHeading Title="{Loc 'guidebook-reagent-recipes-header'}"/>
|
<CollapsibleHeading Title="{Loc 'guidebook-reagent-recipes-header'}"/>
|
||||||
<CollapsibleBody>
|
<CollapsibleBody>
|
||||||
<GridContainer Name="RecipesDescriptionContainer"
|
<GridContainer Name="RecipesDescriptionContainer"
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
</Collapsible>
|
</Collapsible>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Name="SourcesContainer" HorizontalExpand="True">
|
<BoxContainer Name="SourcesContainer" HorizontalExpand="True">
|
||||||
<Collapsible Orientation="Vertical" HorizontalExpand="True">
|
<Collapsible HorizontalExpand="True">
|
||||||
<CollapsibleHeading Title="{Loc 'guidebook-reagent-sources-header'}"/>
|
<CollapsibleHeading Title="{Loc 'guidebook-reagent-sources-header'}"/>
|
||||||
<CollapsibleBody>
|
<CollapsibleBody>
|
||||||
<GridContainer Name="SourcesDescriptionContainer"
|
<GridContainer Name="SourcesDescriptionContainer"
|
||||||
@@ -37,7 +37,7 @@
|
|||||||
</Collapsible>
|
</Collapsible>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<BoxContainer Name="EffectsContainer" HorizontalExpand="True">
|
<BoxContainer Name="EffectsContainer" HorizontalExpand="True">
|
||||||
<Collapsible Orientation="Vertical">
|
<Collapsible>
|
||||||
<CollapsibleHeading Title="{Loc 'guidebook-reagent-effects-header'}"/>
|
<CollapsibleHeading Title="{Loc 'guidebook-reagent-effects-header'}"/>
|
||||||
<CollapsibleBody>
|
<CollapsibleBody>
|
||||||
<BoxContainer Name="EffectsDescriptionContainer"
|
<BoxContainer Name="EffectsDescriptionContainer"
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<CheckBox Name="MarkingForced" Text="Force" Pressed="True" />
|
<CheckBox Name="MarkingForced" Text="Force" Pressed="True" />
|
||||||
<CheckBox Name="MarkingIgnoreSpecies" Text="Ignore Species" Pressed="True" />
|
<CheckBox Name="MarkingIgnoreSpecies" Text="Ignore Species" Pressed="True" />
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<Collapsible Orientation="Vertical" HorizontalExpand="True">
|
<Collapsible HorizontalExpand="True">
|
||||||
<CollapsibleHeading Title="Base layers" />
|
<CollapsibleHeading Title="Base layers" />
|
||||||
<CollapsibleBody HorizontalExpand="True">
|
<CollapsibleBody HorizontalExpand="True">
|
||||||
<BoxContainer Name="BaseLayersContainer" Orientation="Vertical" HorizontalExpand="True" />
|
<BoxContainer Name="BaseLayersContainer" Orientation="Vertical" HorizontalExpand="True" />
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
using Content.Client.Pinpointer.UI;
|
using Content.Client.Pinpointer.UI;
|
||||||
using Robust.Client.Graphics;
|
using Robust.Client.Graphics;
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
namespace Content.Client.Medical.CrewMonitoring;
|
namespace Content.Client.Medical.CrewMonitoring;
|
||||||
|
|
||||||
@@ -16,7 +17,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||||||
{
|
{
|
||||||
WallColor = new Color(192, 122, 196);
|
WallColor = new Color(192, 122, 196);
|
||||||
TileColor = new(71, 42, 72);
|
TileColor = new(71, 42, 72);
|
||||||
_backgroundColor = Color.FromSrgb(TileColor.WithAlpha(_backgroundOpacity));
|
BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity));
|
||||||
|
|
||||||
_trackedEntityLabel = new Label
|
_trackedEntityLabel = new Label
|
||||||
{
|
{
|
||||||
@@ -30,7 +31,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||||||
{
|
{
|
||||||
PanelOverride = new StyleBoxFlat
|
PanelOverride = new StyleBoxFlat
|
||||||
{
|
{
|
||||||
BackgroundColor = _backgroundColor,
|
BackgroundColor = BackgroundColor,
|
||||||
},
|
},
|
||||||
|
|
||||||
Margin = new Thickness(5f, 10f),
|
Margin = new Thickness(5f, 10f),
|
||||||
@@ -43,9 +44,9 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
|
|||||||
this.AddChild(_trackedEntityPanel);
|
this.AddChild(_trackedEntityPanel);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
{
|
{
|
||||||
base.Draw(handle);
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
if (Focus == null)
|
if (Focus == null)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ namespace Content.Client.Medical.CrewMonitoring;
|
|||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class CrewMonitoringWindow : FancyWindow
|
public sealed partial class CrewMonitoringWindow : FancyWindow
|
||||||
{
|
{
|
||||||
private List<Control> _rowsContent = new();
|
|
||||||
private readonly IEntityManager _entManager;
|
private readonly IEntityManager _entManager;
|
||||||
private readonly IPrototypeManager _prototypeManager;
|
private readonly IPrototypeManager _prototypeManager;
|
||||||
private readonly SpriteSystem _spriteSystem;
|
private readonly SpriteSystem _spriteSystem;
|
||||||
@@ -100,7 +99,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
};
|
};
|
||||||
|
|
||||||
SensorsTable.AddChild(spacer);
|
SensorsTable.AddChild(spacer);
|
||||||
_rowsContent.Add(spacer);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
var deparmentLabel = new RichTextLabel()
|
var deparmentLabel = new RichTextLabel()
|
||||||
@@ -113,7 +111,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
||||||
|
|
||||||
SensorsTable.AddChild(deparmentLabel);
|
SensorsTable.AddChild(deparmentLabel);
|
||||||
_rowsContent.Add(deparmentLabel);
|
|
||||||
|
|
||||||
PopulateDepartmentList(departmentSensors);
|
PopulateDepartmentList(departmentSensors);
|
||||||
}
|
}
|
||||||
@@ -129,7 +126,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
};
|
};
|
||||||
|
|
||||||
SensorsTable.AddChild(spacer);
|
SensorsTable.AddChild(spacer);
|
||||||
_rowsContent.Add(spacer);
|
|
||||||
|
|
||||||
var deparmentLabel = new RichTextLabel()
|
var deparmentLabel = new RichTextLabel()
|
||||||
{
|
{
|
||||||
@@ -141,7 +137,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
deparmentLabel.StyleClasses.Add(StyleNano.StyleClassTooltipActionDescription);
|
||||||
|
|
||||||
SensorsTable.AddChild(deparmentLabel);
|
SensorsTable.AddChild(deparmentLabel);
|
||||||
_rowsContent.Add(deparmentLabel);
|
|
||||||
|
|
||||||
PopulateDepartmentList(remainingSensors);
|
PopulateDepartmentList(remainingSensors);
|
||||||
}
|
}
|
||||||
@@ -175,7 +170,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
sensorButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
sensorButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
|
||||||
|
|
||||||
SensorsTable.AddChild(sensorButton);
|
SensorsTable.AddChild(sensorButton);
|
||||||
_rowsContent.Add(sensorButton);
|
|
||||||
|
|
||||||
// Primary container to hold the button UI elements
|
// Primary container to hold the button UI elements
|
||||||
var mainContainer = new BoxContainer()
|
var mainContainer = new BoxContainer()
|
||||||
@@ -422,7 +416,6 @@ public sealed partial class CrewMonitoringWindow : FancyWindow
|
|||||||
private void ClearOutDatedData()
|
private void ClearOutDatedData()
|
||||||
{
|
{
|
||||||
SensorsTable.RemoveAllChildren();
|
SensorsTable.RemoveAllChildren();
|
||||||
_rowsContent.Clear();
|
|
||||||
NavMap.TrackedCoordinates.Clear();
|
NavMap.TrackedCoordinates.Clear();
|
||||||
NavMap.TrackedEntities.Clear();
|
NavMap.TrackedEntities.Clear();
|
||||||
NavMap.LocalizedNames.Clear();
|
NavMap.LocalizedNames.Clear();
|
||||||
|
|||||||
@@ -25,12 +25,14 @@ namespace Content.Client.Pinpointer.UI;
|
|||||||
[UsedImplicitly, Virtual]
|
[UsedImplicitly, Virtual]
|
||||||
public partial class NavMapControl : MapGridControl
|
public partial class NavMapControl : MapGridControl
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
[Dependency] private IResourceCache _cache = default!;
|
||||||
private readonly SharedTransformSystem _transformSystem;
|
private readonly SharedTransformSystem _transformSystem;
|
||||||
|
|
||||||
public EntityUid? Owner;
|
public EntityUid? Owner;
|
||||||
public EntityUid? MapUid;
|
public EntityUid? MapUid;
|
||||||
|
|
||||||
|
protected override bool Draggable => true;
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
public event Action<NetEntity?>? TrackedEntitySelectedAction;
|
public event Action<NetEntity?>? TrackedEntitySelectedAction;
|
||||||
public event Action<DrawingHandleScreen>? PostWallDrawingAction;
|
public event Action<DrawingHandleScreen>? PostWallDrawingAction;
|
||||||
@@ -47,23 +49,17 @@ public partial class NavMapControl : MapGridControl
|
|||||||
// Constants
|
// Constants
|
||||||
protected float UpdateTime = 1.0f;
|
protected float UpdateTime = 1.0f;
|
||||||
protected float MaxSelectableDistance = 10f;
|
protected float MaxSelectableDistance = 10f;
|
||||||
protected float RecenterMinimum = 0.05f;
|
|
||||||
protected float MinDragDistance = 5f;
|
protected float MinDragDistance = 5f;
|
||||||
protected static float MinDisplayedRange = 8f;
|
protected static float MinDisplayedRange = 8f;
|
||||||
protected static float MaxDisplayedRange = 128f;
|
protected static float MaxDisplayedRange = 128f;
|
||||||
protected static float DefaultDisplayedRange = 48f;
|
protected static float DefaultDisplayedRange = 48f;
|
||||||
|
|
||||||
// Local variables
|
// Local variables
|
||||||
private Vector2 _offset;
|
|
||||||
private bool _draggin;
|
|
||||||
private Vector2 _startDragPosition = default!;
|
|
||||||
private bool _recentering = false;
|
|
||||||
private float _updateTimer = 0.25f;
|
private float _updateTimer = 0.25f;
|
||||||
private Dictionary<Color, Color> _sRGBLookUp = new Dictionary<Color, Color>();
|
private Dictionary<Color, Color> _sRGBLookUp = new();
|
||||||
public Color _backgroundColor;
|
protected Color BackgroundColor;
|
||||||
public float _backgroundOpacity = 0.9f;
|
protected float BackgroundOpacity = 0.9f;
|
||||||
private int _targetFontsize = 8;
|
private int _targetFontsize = 8;
|
||||||
private IResourceCache _cache;
|
|
||||||
|
|
||||||
// Components
|
// Components
|
||||||
private NavMapComponent? _navMap;
|
private NavMapComponent? _navMap;
|
||||||
@@ -100,10 +96,9 @@ public partial class NavMapControl : MapGridControl
|
|||||||
public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDisplayedRange)
|
public NavMapControl() : base(MinDisplayedRange, MaxDisplayedRange, DefaultDisplayedRange)
|
||||||
{
|
{
|
||||||
IoCManager.InjectDependencies(this);
|
IoCManager.InjectDependencies(this);
|
||||||
_cache = IoCManager.Resolve<IResourceCache>();
|
|
||||||
|
|
||||||
_transformSystem = _entManager.System<SharedTransformSystem>();
|
_transformSystem = EntManager.System<SharedTransformSystem>();
|
||||||
_backgroundColor = Color.FromSrgb(TileColor.WithAlpha(_backgroundOpacity));
|
BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity));
|
||||||
|
|
||||||
RectClipContent = true;
|
RectClipContent = true;
|
||||||
HorizontalExpand = true;
|
HorizontalExpand = true;
|
||||||
@@ -145,21 +140,16 @@ public partial class NavMapControl : MapGridControl
|
|||||||
|
|
||||||
_recenter.OnPressed += args =>
|
_recenter.OnPressed += args =>
|
||||||
{
|
{
|
||||||
_recentering = true;
|
Recentering = true;
|
||||||
};
|
};
|
||||||
|
|
||||||
ForceNavMapUpdate();
|
ForceNavMapUpdate();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void ForceRecenter()
|
|
||||||
{
|
|
||||||
_recentering = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void ForceNavMapUpdate()
|
public void ForceNavMapUpdate()
|
||||||
{
|
{
|
||||||
_entManager.TryGetComponent(MapUid, out _navMap);
|
EntManager.TryGetComponent(MapUid, out _navMap);
|
||||||
_entManager.TryGetComponent(MapUid, out _grid);
|
EntManager.TryGetComponent(MapUid, out _grid);
|
||||||
|
|
||||||
UpdateNavMap();
|
UpdateNavMap();
|
||||||
}
|
}
|
||||||
@@ -167,29 +157,15 @@ public partial class NavMapControl : MapGridControl
|
|||||||
public void CenterToCoordinates(EntityCoordinates coordinates)
|
public void CenterToCoordinates(EntityCoordinates coordinates)
|
||||||
{
|
{
|
||||||
if (_physics != null)
|
if (_physics != null)
|
||||||
_offset = new Vector2(coordinates.X, coordinates.Y) - _physics.LocalCenter;
|
Offset = new Vector2(coordinates.X, coordinates.Y) - _physics.LocalCenter;
|
||||||
|
|
||||||
_recenter.Disabled = false;
|
_recenter.Disabled = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
|
||||||
{
|
|
||||||
base.KeyBindDown(args);
|
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.Use)
|
|
||||||
{
|
|
||||||
_startDragPosition = args.PointerLocation.Position;
|
|
||||||
_draggin = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
{
|
{
|
||||||
base.KeyBindUp(args);
|
base.KeyBindUp(args);
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.Use)
|
|
||||||
_draggin = false;
|
|
||||||
|
|
||||||
if (args.Function == EngineKeyFunctions.UIClick)
|
if (args.Function == EngineKeyFunctions.UIClick)
|
||||||
{
|
{
|
||||||
if (TrackedEntitySelectedAction == null)
|
if (TrackedEntitySelectedAction == null)
|
||||||
@@ -199,15 +175,15 @@ public partial class NavMapControl : MapGridControl
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// If the cursor has moved a significant distance, exit
|
// If the cursor has moved a significant distance, exit
|
||||||
if ((_startDragPosition - args.PointerLocation.Position).Length() > MinDragDistance)
|
if ((StartDragPosition - args.PointerLocation.Position).Length() > MinDragDistance)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Get the clicked position
|
// Get the clicked position
|
||||||
var offset = _offset + _physics.LocalCenter;
|
var offset = Offset + _physics.LocalCenter;
|
||||||
var localPosition = args.PointerLocation.Position - GlobalPixelPosition;
|
var localPosition = args.PointerLocation.Position - GlobalPixelPosition;
|
||||||
|
|
||||||
// Convert to a world position
|
// Convert to a world position
|
||||||
var unscaledPosition = (localPosition - MidpointVector) / MinimapScale;
|
var unscaledPosition = (localPosition - MidPointVector) / MinimapScale;
|
||||||
var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset);
|
var worldPosition = _transformSystem.GetWorldMatrix(_xform).Transform(new Vector2(unscaledPosition.X, -unscaledPosition.Y) + offset);
|
||||||
|
|
||||||
// Find closest tracked entity in range
|
// Find closest tracked entity in range
|
||||||
@@ -219,7 +195,7 @@ public partial class NavMapControl : MapGridControl
|
|||||||
if (!blip.Selectable)
|
if (!blip.Selectable)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var currentDistance = (blip.Coordinates.ToMapPos(_entManager, _transformSystem) - worldPosition).Length();
|
var currentDistance = (blip.Coordinates.ToMapPos(EntManager, _transformSystem) - worldPosition).Length();
|
||||||
|
|
||||||
if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance)
|
if (closestDistance < currentDistance || currentDistance * MinimapScale > MaxSelectableDistance)
|
||||||
continue;
|
continue;
|
||||||
@@ -251,15 +227,8 @@ public partial class NavMapControl : MapGridControl
|
|||||||
{
|
{
|
||||||
base.MouseMove(args);
|
base.MouseMove(args);
|
||||||
|
|
||||||
if (!_draggin)
|
if (Offset != Vector2.Zero)
|
||||||
return;
|
|
||||||
|
|
||||||
_recentering = false;
|
|
||||||
_offset -= new Vector2(args.Relative.X, -args.Relative.Y) / MidPoint * WorldRange;
|
|
||||||
|
|
||||||
if (_offset != Vector2.Zero)
|
|
||||||
_recenter.Disabled = false;
|
_recenter.Disabled = false;
|
||||||
|
|
||||||
else
|
else
|
||||||
_recenter.Disabled = true;
|
_recenter.Disabled = true;
|
||||||
}
|
}
|
||||||
@@ -269,36 +238,21 @@ public partial class NavMapControl : MapGridControl
|
|||||||
base.Draw(handle);
|
base.Draw(handle);
|
||||||
|
|
||||||
// Get the components necessary for drawing the navmap
|
// Get the components necessary for drawing the navmap
|
||||||
_entManager.TryGetComponent(MapUid, out _navMap);
|
EntManager.TryGetComponent(MapUid, out _navMap);
|
||||||
_entManager.TryGetComponent(MapUid, out _grid);
|
EntManager.TryGetComponent(MapUid, out _grid);
|
||||||
_entManager.TryGetComponent(MapUid, out _xform);
|
EntManager.TryGetComponent(MapUid, out _xform);
|
||||||
_entManager.TryGetComponent(MapUid, out _physics);
|
EntManager.TryGetComponent(MapUid, out _physics);
|
||||||
_entManager.TryGetComponent(MapUid, out _fixtures);
|
EntManager.TryGetComponent(MapUid, out _fixtures);
|
||||||
|
|
||||||
// Map re-centering
|
// Map re-centering
|
||||||
if (_recentering)
|
_recenter.Disabled = DrawRecenter();
|
||||||
{
|
|
||||||
var frameTime = Timing.FrameTime;
|
|
||||||
var diff = _offset * (float) frameTime.TotalSeconds;
|
|
||||||
|
|
||||||
if (_offset.LengthSquared() < RecenterMinimum)
|
|
||||||
{
|
|
||||||
_offset = Vector2.Zero;
|
|
||||||
_recentering = false;
|
|
||||||
_recenter.Disabled = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
_offset -= diff * 5f;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange ):0.0}"));
|
_zoom.Text = Loc.GetString("navmap-zoom", ("value", $"{(DefaultDisplayedRange / WorldRange ):0.0}"));
|
||||||
|
|
||||||
if (_navMap == null || _xform == null)
|
if (_navMap == null || _xform == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var offset = _offset;
|
var offset = Offset;
|
||||||
|
|
||||||
if (_physics != null)
|
if (_physics != null)
|
||||||
offset += _physics.LocalCenter;
|
offset += _physics.LocalCenter;
|
||||||
@@ -317,7 +271,7 @@ public partial class NavMapControl : MapGridControl
|
|||||||
{
|
{
|
||||||
var vert = poly.Vertices[i] - offset;
|
var vert = poly.Vertices[i] - offset;
|
||||||
|
|
||||||
verts[i] = Scale(new Vector2(vert.X, -vert.Y));
|
verts[i] = ScalePosition(new Vector2(vert.X, -vert.Y));
|
||||||
}
|
}
|
||||||
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..poly.VertexCount], TileColor);
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts[..poly.VertexCount], TileColor);
|
||||||
@@ -348,8 +302,8 @@ public partial class NavMapControl : MapGridControl
|
|||||||
|
|
||||||
foreach (var chunkedLine in chunkedLines)
|
foreach (var chunkedLine in chunkedLines)
|
||||||
{
|
{
|
||||||
var start = Scale(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
var start = ScalePosition(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||||
var end = Scale(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
var end = ScalePosition(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
walls.Add(start);
|
walls.Add(start);
|
||||||
walls.Add(end);
|
walls.Add(end);
|
||||||
@@ -375,7 +329,7 @@ public partial class NavMapControl : MapGridControl
|
|||||||
foreach (var airlock in _navMap.Airlocks)
|
foreach (var airlock in _navMap.Airlocks)
|
||||||
{
|
{
|
||||||
var position = airlock.Position - offset;
|
var position = airlock.Position - offset;
|
||||||
position = Scale(position with { Y = -position.Y });
|
position = ScalePosition(position with { Y = -position.Y });
|
||||||
airlockLines.Add(position + airlockBuffer);
|
airlockLines.Add(position + airlockBuffer);
|
||||||
airlockLines.Add(position - airlockBuffer * foobarVec);
|
airlockLines.Add(position - airlockBuffer * foobarVec);
|
||||||
|
|
||||||
@@ -418,10 +372,10 @@ public partial class NavMapControl : MapGridControl
|
|||||||
foreach (var beacon in _navMap.Beacons)
|
foreach (var beacon in _navMap.Beacons)
|
||||||
{
|
{
|
||||||
var position = beacon.Position - offset;
|
var position = beacon.Position - offset;
|
||||||
position = Scale(position with { Y = -position.Y });
|
position = ScalePosition(position with { Y = -position.Y });
|
||||||
|
|
||||||
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
var textDimensions = handle.GetDimensions(font, beacon.Text, 1f);
|
||||||
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), _backgroundColor);
|
handle.DrawRect(new UIBox2(position - textDimensions / 2 - rectBuffer, position + textDimensions / 2 + rectBuffer), BackgroundColor);
|
||||||
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
handle.DrawString(font, position - textDimensions / 2, beacon.Text, beacon.Color);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -435,12 +389,12 @@ public partial class NavMapControl : MapGridControl
|
|||||||
{
|
{
|
||||||
if (lit && value.Visible)
|
if (lit && value.Visible)
|
||||||
{
|
{
|
||||||
var mapPos = coord.ToMap(_entManager, _transformSystem);
|
var mapPos = coord.ToMap(EntManager, _transformSystem);
|
||||||
|
|
||||||
if (mapPos.MapId != MapId.Nullspace)
|
if (mapPos.MapId != MapId.Nullspace)
|
||||||
{
|
{
|
||||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||||
position = Scale(new Vector2(position.X, -position.Y));
|
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||||
|
|
||||||
handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color);
|
handle.DrawCircle(position, float.Sqrt(MinimapScale) * 2f, value.Color);
|
||||||
}
|
}
|
||||||
@@ -461,12 +415,12 @@ public partial class NavMapControl : MapGridControl
|
|||||||
if (!iconVertexUVs.TryGetValue((blip.Texture, blip.Color), out var vertexUVs))
|
if (!iconVertexUVs.TryGetValue((blip.Texture, blip.Color), out var vertexUVs))
|
||||||
vertexUVs = new();
|
vertexUVs = new();
|
||||||
|
|
||||||
var mapPos = blip.Coordinates.ToMap(_entManager, _transformSystem);
|
var mapPos = blip.Coordinates.ToMap(EntManager, _transformSystem);
|
||||||
|
|
||||||
if (mapPos.MapId != MapId.Nullspace)
|
if (mapPos.MapId != MapId.Nullspace)
|
||||||
{
|
{
|
||||||
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
var position = _transformSystem.GetInvWorldMatrix(_xform).Transform(mapPos.Position) - offset;
|
||||||
position = Scale(new Vector2(position.X, -position.Y));
|
position = ScalePosition(new Vector2(position.X, -position.Y));
|
||||||
|
|
||||||
var scalingCoefficient = 2.5f;
|
var scalingCoefficient = 2.5f;
|
||||||
var positionOffset = scalingCoefficient * float.Sqrt(MinimapScale);
|
var positionOffset = scalingCoefficient * float.Sqrt(MinimapScale);
|
||||||
@@ -628,14 +582,9 @@ public partial class NavMapControl : MapGridControl
|
|||||||
return decodedOutput;
|
return decodedOutput;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected Vector2 Scale(Vector2 position)
|
|
||||||
{
|
|
||||||
return position * MinimapScale + MidpointVector;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected Vector2 GetOffset()
|
protected Vector2 GetOffset()
|
||||||
{
|
{
|
||||||
return _offset + (_physics != null ? _physics.LocalCenter : new Vector2());
|
return Offset + (_physics?.LocalCenter ?? new Vector2());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,7 +33,7 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
|||||||
// Set colors
|
// Set colors
|
||||||
TileColor = new Color(30, 57, 67);
|
TileColor = new Color(30, 57, 67);
|
||||||
WallColor = new Color(102, 164, 217);
|
WallColor = new Color(102, 164, 217);
|
||||||
_backgroundColor = Color.FromSrgb(TileColor.WithAlpha(_backgroundOpacity));
|
BackgroundColor = Color.FromSrgb(TileColor.WithAlpha(BackgroundOpacity));
|
||||||
|
|
||||||
PostWallDrawingAction += DrawAllCableNetworks;
|
PostWallDrawingAction += DrawAllCableNetworks;
|
||||||
}
|
}
|
||||||
@@ -93,8 +93,8 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
|||||||
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var start = Scale(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
var start = ScalePosition(chunkedLine.Origin - new Vector2(offset.X, -offset.Y));
|
||||||
var end = Scale(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
var end = ScalePosition(chunkedLine.Terminus - new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
cableNetworks[(int) chunkedLine.Group].Add(start);
|
cableNetworks[(int) chunkedLine.Group].Add(start);
|
||||||
cableNetworks[(int) chunkedLine.Group].Add(end);
|
cableNetworks[(int) chunkedLine.Group].Add(end);
|
||||||
@@ -139,22 +139,22 @@ public sealed partial class PowerMonitoringConsoleNavMapControl : NavMapControl
|
|||||||
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
if (HiddenLineGroups.Contains(chunkedLine.Group))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var leftTop = Scale(new Vector2
|
var leftTop = ScalePosition(new Vector2
|
||||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||||
- new Vector2(offset.X, -offset.Y));
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
var rightTop = Scale(new Vector2
|
var rightTop = ScalePosition(new Vector2
|
||||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||||
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
Math.Min(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) - 0.1f)
|
||||||
- new Vector2(offset.X, -offset.Y));
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
var leftBottom = Scale(new Vector2
|
var leftBottom = ScalePosition(new Vector2
|
||||||
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
(Math.Min(chunkedLine.Origin.X, chunkedLine.Terminus.X) - 0.1f,
|
||||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||||
- new Vector2(offset.X, -offset.Y));
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|
||||||
var rightBottom = Scale(new Vector2
|
var rightBottom = ScalePosition(new Vector2
|
||||||
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
(Math.Max(chunkedLine.Origin.X, chunkedLine.Terminus.X) + 0.1f,
|
||||||
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
Math.Max(chunkedLine.Origin.Y, chunkedLine.Terminus.Y) + 0.1f)
|
||||||
- new Vector2(offset.X, -offset.Y));
|
- new Vector2(offset.X, -offset.Y));
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ using Content.Client.Shuttles.UI;
|
|||||||
using Content.Shared.Shuttles.BUIStates;
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Client.GameObjects;
|
||||||
|
using RadarConsoleWindow = Content.Client.Shuttles.UI.RadarConsoleWindow;
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.BUI;
|
namespace Content.Client.Shuttles.BUI;
|
||||||
|
|
||||||
@@ -36,9 +37,9 @@ public sealed class RadarConsoleBoundUserInterface : BoundUserInterface
|
|||||||
protected override void UpdateState(BoundUserInterfaceState state)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
base.UpdateState(state);
|
base.UpdateState(state);
|
||||||
if (state is not RadarConsoleBoundInterfaceState cState) return;
|
if (state is not NavBoundUserInterfaceState cState)
|
||||||
|
return;
|
||||||
|
|
||||||
_window?.SetMatrix(EntMan.GetCoordinates(cState.Coordinates), cState.Angle);
|
_window?.UpdateState(cState.State);
|
||||||
_window?.UpdateState(cState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ using Content.Client.Shuttles.UI;
|
|||||||
using Content.Shared.Shuttles.BUIStates;
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using Content.Shared.Shuttles.Events;
|
using Content.Shared.Shuttles.Events;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Client.GameObjects;
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.BUI;
|
namespace Content.Client.Shuttles.BUI;
|
||||||
|
|
||||||
@@ -20,25 +20,48 @@ public sealed class ShuttleConsoleBoundUserInterface : BoundUserInterface
|
|||||||
{
|
{
|
||||||
base.Open();
|
base.Open();
|
||||||
_window = new ShuttleConsoleWindow();
|
_window = new ShuttleConsoleWindow();
|
||||||
_window.UndockPressed += OnUndockPressed;
|
|
||||||
_window.StartAutodockPressed += OnAutodockPressed;
|
|
||||||
_window.StopAutodockPressed += OnStopAutodockPressed;
|
|
||||||
_window.DestinationPressed += OnDestinationPressed;
|
|
||||||
_window.OpenCentered();
|
_window.OpenCentered();
|
||||||
_window.OnClose += OnClose;
|
_window.OnClose += Close;
|
||||||
|
|
||||||
|
_window.RequestFTL += OnFTLRequest;
|
||||||
|
_window.RequestBeaconFTL += OnFTLBeaconRequest;
|
||||||
|
_window.DockRequest += OnDockRequest;
|
||||||
|
_window.UndockRequest += OnUndockRequest;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestinationPressed(NetEntity obj)
|
private void OnUndockRequest(NetEntity entity)
|
||||||
{
|
{
|
||||||
SendMessage(new ShuttleConsoleFTLRequestMessage()
|
SendMessage(new UndockRequestMessage()
|
||||||
{
|
{
|
||||||
Destination = obj,
|
DockEntity = entity,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnClose()
|
private void OnDockRequest(NetEntity entity, NetEntity target)
|
||||||
{
|
{
|
||||||
Close();
|
SendMessage(new DockRequestMessage()
|
||||||
|
{
|
||||||
|
DockEntity = entity,
|
||||||
|
TargetDockEntity = target,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFTLBeaconRequest(NetEntity ent, Angle angle)
|
||||||
|
{
|
||||||
|
SendMessage(new ShuttleConsoleFTLBeaconMessage()
|
||||||
|
{
|
||||||
|
Beacon = ent,
|
||||||
|
Angle = angle,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnFTLRequest(MapCoordinates obj, Angle angle)
|
||||||
|
{
|
||||||
|
SendMessage(new ShuttleConsoleFTLPositionMessage()
|
||||||
|
{
|
||||||
|
Coordinates = obj,
|
||||||
|
Angle = angle,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
protected override void Dispose(bool disposing)
|
protected override void Dispose(bool disposing)
|
||||||
@@ -51,27 +74,12 @@ public sealed class ShuttleConsoleBoundUserInterface : BoundUserInterface
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStopAutodockPressed(NetEntity obj)
|
|
||||||
{
|
|
||||||
SendMessage(new StopAutodockRequestMessage() { DockEntity = obj });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnAutodockPressed(NetEntity obj)
|
|
||||||
{
|
|
||||||
SendMessage(new AutodockRequestMessage() { DockEntity = obj });
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUndockPressed(NetEntity obj)
|
|
||||||
{
|
|
||||||
SendMessage(new UndockRequestMessage() { DockEntity = obj });
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void UpdateState(BoundUserInterfaceState state)
|
protected override void UpdateState(BoundUserInterfaceState state)
|
||||||
{
|
{
|
||||||
base.UpdateState(state);
|
base.UpdateState(state);
|
||||||
if (state is not ShuttleConsoleBoundInterfaceState cState) return;
|
if (state is not ShuttleBoundUserInterfaceState cState)
|
||||||
|
return;
|
||||||
|
|
||||||
_window?.SetMatrix(EntMan.GetCoordinates(cState.Coordinates), cState.Angle);
|
_window?.UpdateState(Owner, cState);
|
||||||
_window?.UpdateState(cState);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,7 +1,6 @@
|
|||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
|
|
||||||
namespace Content.Client.Shuttles
|
namespace Content.Client.Shuttles;
|
||||||
{
|
|
||||||
[RegisterComponent]
|
[RegisterComponent]
|
||||||
public sealed partial class ShuttleConsoleComponent : SharedShuttleConsoleComponent {}
|
public sealed partial class ShuttleConsoleComponent : SharedShuttleConsoleComponent {}
|
||||||
}
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
using Content.Shared.Shuttles.Events;
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.Systems;
|
namespace Content.Client.Shuttles.Systems;
|
||||||
|
|
||||||
public sealed class DockingSystem : EntitySystem {}
|
public sealed class DockingSystem : SharedDockingSystem
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|||||||
48
Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs
Normal file
48
Content.Client/Shuttles/Systems/ShuttleSystem.Console.cs
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
using Content.Client.Resources;
|
||||||
|
using Content.Client.Shuttles.UI;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed partial class ShuttleSystem
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IResourceCache _resource = default!;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the parallax to use for the specified map or uses the fallback if not available.
|
||||||
|
/// </summary>
|
||||||
|
public Texture GetTexture(Entity<ShuttleMapParallaxComponent?> entity)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp, false))
|
||||||
|
{
|
||||||
|
return _resource.GetTexture(ShuttleMapParallaxComponent.FallbackTexture);
|
||||||
|
}
|
||||||
|
|
||||||
|
return _resource.GetTexture(entity.Comp.TexturePath);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the map coordinates of a map object.
|
||||||
|
/// </summary>
|
||||||
|
public MapCoordinates GetMapCoordinates(IMapObject mapObj)
|
||||||
|
{
|
||||||
|
switch (mapObj)
|
||||||
|
{
|
||||||
|
case ShuttleBeaconObject beacon:
|
||||||
|
return GetCoordinates(beacon.Coordinates).ToMap(EntityManager, XformSystem);
|
||||||
|
case ShuttleExclusionObject exclusion:
|
||||||
|
return GetCoordinates(exclusion.Coordinates).ToMap(EntityManager, XformSystem);
|
||||||
|
case GridMapObject grid:
|
||||||
|
var gridXform = Transform(grid.Entity);
|
||||||
|
Entity<PhysicsComponent?, TransformComponent?> gridEnt = (grid.Entity, null, gridXform);
|
||||||
|
return new MapCoordinates(Maps.GetGridPosition(gridEnt), gridXform.MapID);
|
||||||
|
default:
|
||||||
|
throw new ArgumentOutOfRangeException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
2
Content.Client/Shuttles/UI/BaseShuttleControl.xaml
Normal file
2
Content.Client/Shuttles/UI/BaseShuttleControl.xaml
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
<controls1:MapGridControl
|
||||||
|
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls" />
|
||||||
324
Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs
Normal file
324
Content.Client/Shuttles/UI/BaseShuttleControl.xaml.cs
Normal file
@@ -0,0 +1,324 @@
|
|||||||
|
using Content.Client.UserInterface.Controls;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Threading;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
using Vector2 = System.Numerics.Vector2;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Provides common functionality for radar-like displays on shuttle consoles.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
[Virtual]
|
||||||
|
public partial class BaseShuttleControl : MapGridControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IParallelManager _parallel = default!;
|
||||||
|
protected readonly SharedMapSystem Maps;
|
||||||
|
|
||||||
|
protected readonly Font Font;
|
||||||
|
|
||||||
|
private GridDrawJob _drawJob;
|
||||||
|
|
||||||
|
// Cache grid drawing data as it can be expensive to build
|
||||||
|
public readonly Dictionary<EntityUid, GridDrawData> GridData = new();
|
||||||
|
|
||||||
|
// Per-draw caching
|
||||||
|
private readonly List<Vector2i> _gridTileList = new();
|
||||||
|
private readonly HashSet<Vector2i> _gridNeighborSet = new();
|
||||||
|
private readonly List<(Vector2 Start, Vector2 End)> _edges = new();
|
||||||
|
|
||||||
|
private Vector2[] _allVertices = Array.Empty<Vector2>();
|
||||||
|
|
||||||
|
private (DirectionFlag, Vector2i)[] _neighborDirections;
|
||||||
|
|
||||||
|
public BaseShuttleControl() : this(32f, 32f, 32f)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
public BaseShuttleControl(float minRange, float maxRange, float range) : base(minRange, maxRange, range)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
Maps = EntManager.System<SharedMapSystem>();
|
||||||
|
Font = new VectorFont(IoCManager.Resolve<IResourceCache>().GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 12);
|
||||||
|
|
||||||
|
_drawJob = new GridDrawJob()
|
||||||
|
{
|
||||||
|
ScaledVertices = _allVertices,
|
||||||
|
};
|
||||||
|
|
||||||
|
_neighborDirections = new (DirectionFlag, Vector2i)[4];
|
||||||
|
|
||||||
|
for (var i = 0; i < 4; i++)
|
||||||
|
{
|
||||||
|
var dir = (DirectionFlag) Math.Pow(2, i);
|
||||||
|
var dirVec = dir.AsDir().ToIntVec();
|
||||||
|
_neighborDirections[i] = (dir, dirVec);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawData(DrawingHandleScreen handle, string text)
|
||||||
|
{
|
||||||
|
var coordsDimensions = handle.GetDimensions(Font, text, UIScale);
|
||||||
|
const float coordsMargins = 5f;
|
||||||
|
|
||||||
|
handle.DrawString(Font,
|
||||||
|
new Vector2(coordsMargins, Height) - new Vector2(0f, coordsDimensions.Y + coordsMargins),
|
||||||
|
text,
|
||||||
|
Color.FromSrgb(IFFComponent.SelfColor));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawCircles(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
// Equatorial lines
|
||||||
|
var gridLines = Color.LightGray.WithAlpha(0.01f);
|
||||||
|
|
||||||
|
// Each circle is this x distance of the last one.
|
||||||
|
const float EquatorialMultiplier = 2f;
|
||||||
|
|
||||||
|
var minDistance = MathF.Pow(EquatorialMultiplier, EquatorialMultiplier * 1.5f);
|
||||||
|
var maxDistance = MathF.Pow(2f, EquatorialMultiplier * 6f);
|
||||||
|
var cornerDistance = MathF.Sqrt(WorldRange * WorldRange + WorldRange * WorldRange);
|
||||||
|
|
||||||
|
var origin = ScalePosition(-new Vector2(Offset.X, -Offset.Y));
|
||||||
|
var distOffset = -24f;
|
||||||
|
|
||||||
|
for (var radius = minDistance; radius <= maxDistance; radius *= EquatorialMultiplier)
|
||||||
|
{
|
||||||
|
if (radius > cornerDistance)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var color = Color.ToSrgb(gridLines).WithAlpha(0.05f);
|
||||||
|
var scaledRadius = MinimapScale * radius;
|
||||||
|
var text = $"{radius:0}m";
|
||||||
|
var textDimensions = handle.GetDimensions(Font, text, UIScale);
|
||||||
|
|
||||||
|
handle.DrawCircle(origin, scaledRadius, color, false);
|
||||||
|
handle.DrawString(Font, ScalePosition(new Vector2(0f, -radius)) - new Vector2(0f, textDimensions.Y), text, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int gridLinesRadial = 8;
|
||||||
|
|
||||||
|
for (var i = 0; i < gridLinesRadial; i++)
|
||||||
|
{
|
||||||
|
Angle angle = (Math.PI / gridLinesRadial) * i;
|
||||||
|
// TODO: Handle distance properly.
|
||||||
|
var aExtent = angle.ToVec() * ScaledMinimapRadius * 1.42f;
|
||||||
|
var lineColor = Color.MediumSpringGreen.WithAlpha(0.02f);
|
||||||
|
handle.DrawLine(origin - aExtent, origin + aExtent, lineColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, Entity<MapGridComponent> grid, Color color, float alpha = 0.01f)
|
||||||
|
{
|
||||||
|
var rator = Maps.GetAllTilesEnumerator(grid.Owner, grid.Comp);
|
||||||
|
var minimapScale = MinimapScale;
|
||||||
|
var midpoint = new Vector2(MidPoint, MidPoint);
|
||||||
|
var tileSize = grid.Comp.TileSize;
|
||||||
|
|
||||||
|
// Check if we even have data
|
||||||
|
// TODO: Need to prune old grid-data if we don't draw it.
|
||||||
|
var gridData = GridData.GetOrNew(grid.Owner);
|
||||||
|
|
||||||
|
if (gridData.LastBuild < grid.Comp.LastTileModifiedTick)
|
||||||
|
{
|
||||||
|
gridData.Vertices.Clear();
|
||||||
|
_gridTileList.Clear();
|
||||||
|
_gridNeighborSet.Clear();
|
||||||
|
|
||||||
|
// Okay so there's 2 steps to this
|
||||||
|
// 1. Is that get we get a set of all tiles. This is used to decompose into triangle-strips
|
||||||
|
// 2. Is that we get a list of all tiles. This is used for edge data to decompose into line-strips.
|
||||||
|
while (rator.MoveNext(out var tileRef))
|
||||||
|
{
|
||||||
|
var index = tileRef.Value.GridIndices;
|
||||||
|
_gridNeighborSet.Add(index);
|
||||||
|
_gridTileList.Add(index);
|
||||||
|
|
||||||
|
var bl = Maps.TileToVector(grid, index);
|
||||||
|
var br = bl + new Vector2(tileSize, 0f);
|
||||||
|
var tr = bl + new Vector2(tileSize, tileSize);
|
||||||
|
var tl = bl + new Vector2(0f, tileSize);
|
||||||
|
|
||||||
|
gridData.Vertices.Add(bl);
|
||||||
|
gridData.Vertices.Add(br);
|
||||||
|
gridData.Vertices.Add(tl);
|
||||||
|
|
||||||
|
gridData.Vertices.Add(br);
|
||||||
|
gridData.Vertices.Add(tl);
|
||||||
|
gridData.Vertices.Add(tr);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridData.EdgeIndex = gridData.Vertices.Count;
|
||||||
|
_edges.Clear();
|
||||||
|
|
||||||
|
foreach (var index in _gridTileList)
|
||||||
|
{
|
||||||
|
// We get all of the raw lines up front
|
||||||
|
// then we decompose them into longer lines in a separate step.
|
||||||
|
foreach (var (dir, dirVec) in _neighborDirections)
|
||||||
|
{
|
||||||
|
var neighbor = index + dirVec;
|
||||||
|
|
||||||
|
if (_gridNeighborSet.Contains(neighbor))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var bl = Maps.TileToVector(grid, index);
|
||||||
|
var br = bl + new Vector2(tileSize, 0f);
|
||||||
|
var tr = bl + new Vector2(tileSize, tileSize);
|
||||||
|
var tl = bl + new Vector2(0f, tileSize);
|
||||||
|
|
||||||
|
// Could probably rotate this but this might be faster?
|
||||||
|
Vector2 actualStart;
|
||||||
|
Vector2 actualEnd;
|
||||||
|
|
||||||
|
switch (dir)
|
||||||
|
{
|
||||||
|
case DirectionFlag.South:
|
||||||
|
actualStart = bl;
|
||||||
|
actualEnd = br;
|
||||||
|
break;
|
||||||
|
case DirectionFlag.East:
|
||||||
|
actualStart = br;
|
||||||
|
actualEnd = tr;
|
||||||
|
break;
|
||||||
|
case DirectionFlag.North:
|
||||||
|
actualStart = tr;
|
||||||
|
actualEnd = tl;
|
||||||
|
break;
|
||||||
|
case DirectionFlag.West:
|
||||||
|
actualStart = tl;
|
||||||
|
actualEnd = bl;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
_edges.Add((actualStart, actualEnd));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Decompose the edges into longer lines to save data.
|
||||||
|
// Now we decompose the lines into longer lines (less data to send to the GPU)
|
||||||
|
var decomposed = true;
|
||||||
|
|
||||||
|
while (decomposed)
|
||||||
|
{
|
||||||
|
decomposed = false;
|
||||||
|
|
||||||
|
for (var i = 0; i < _edges.Count; i++)
|
||||||
|
{
|
||||||
|
var (start, end) = _edges[i];
|
||||||
|
var neighborFound = false;
|
||||||
|
var neighborIndex = 0;
|
||||||
|
Vector2 neighborStart;
|
||||||
|
Vector2 neighborEnd = Vector2.Zero;
|
||||||
|
|
||||||
|
// Does our end correspond with another start?
|
||||||
|
for (var j = i + 1; j < _edges.Count; j++)
|
||||||
|
{
|
||||||
|
(neighborStart, neighborEnd) = _edges[j];
|
||||||
|
|
||||||
|
if (!end.Equals(neighborStart))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
neighborFound = true;
|
||||||
|
neighborIndex = j;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!neighborFound)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if our start and the neighbor's end are collinear
|
||||||
|
if (!CollinearSimplifier.IsCollinear(start, end, neighborEnd, 10f * float.Epsilon))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
decomposed = true;
|
||||||
|
_edges[i] = (start, neighborEnd);
|
||||||
|
_edges.RemoveAt(neighborIndex);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
gridData.Vertices.EnsureCapacity(_edges.Count * 2);
|
||||||
|
|
||||||
|
foreach (var edge in _edges)
|
||||||
|
{
|
||||||
|
gridData.Vertices.Add(edge.Start);
|
||||||
|
gridData.Vertices.Add(edge.End);
|
||||||
|
}
|
||||||
|
|
||||||
|
gridData.LastBuild = grid.Comp.LastTileModifiedTick;
|
||||||
|
}
|
||||||
|
|
||||||
|
var totalData = gridData.Vertices.Count;
|
||||||
|
var triCount = gridData.EdgeIndex;
|
||||||
|
var edgeCount = totalData - gridData.EdgeIndex;
|
||||||
|
Extensions.EnsureLength(ref _allVertices, totalData);
|
||||||
|
|
||||||
|
_drawJob.MidPoint = midpoint;
|
||||||
|
_drawJob.Matrix = matrix;
|
||||||
|
_drawJob.MinimapScale = minimapScale;
|
||||||
|
_drawJob.Vertices = gridData.Vertices;
|
||||||
|
_drawJob.ScaledVertices = _allVertices;
|
||||||
|
|
||||||
|
_parallel.ProcessNow(_drawJob, totalData);
|
||||||
|
|
||||||
|
const float BatchSize = 3f * 4096;
|
||||||
|
|
||||||
|
for (var i = 0; i < Math.Ceiling(triCount / BatchSize); i++)
|
||||||
|
{
|
||||||
|
var start = (int) (i * BatchSize);
|
||||||
|
var end = (int) Math.Min(triCount, start + BatchSize);
|
||||||
|
var count = end - start;
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, new Span<Vector2>(_allVertices, start, count), color.WithAlpha(alpha));
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, new Span<Vector2>(_allVertices, gridData.EdgeIndex, edgeCount), color);
|
||||||
|
}
|
||||||
|
|
||||||
|
private record struct GridDrawJob : IParallelRobustJob
|
||||||
|
{
|
||||||
|
public int BatchSize => 16;
|
||||||
|
|
||||||
|
public float MinimapScale;
|
||||||
|
public Vector2 MidPoint;
|
||||||
|
public Matrix3 Matrix;
|
||||||
|
|
||||||
|
public List<Vector2> Vertices;
|
||||||
|
public Vector2[] ScaledVertices;
|
||||||
|
|
||||||
|
public void Execute(int index)
|
||||||
|
{
|
||||||
|
var vert = Vertices[index];
|
||||||
|
var adjustedVert = Matrix.Transform(vert);
|
||||||
|
adjustedVert = adjustedVert with { Y = -adjustedVert.Y };
|
||||||
|
|
||||||
|
var scaledVert = ScalePosition(adjustedVert, MinimapScale, MidPoint);
|
||||||
|
ScaledVertices[index] = scaledVert;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public sealed class GridDrawData
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* List of lists because we use LineStrip and TriangleStrip respectively (less data to pass to the GPU).
|
||||||
|
*/
|
||||||
|
|
||||||
|
public List<Vector2> Vertices = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Vertices index from when edges start.
|
||||||
|
/// </summary>
|
||||||
|
public int EdgeIndex;
|
||||||
|
|
||||||
|
public GameTick LastBuild;
|
||||||
|
}
|
||||||
11
Content.Client/Shuttles/UI/DockObject.xaml
Normal file
11
Content.Client/Shuttles/UI/DockObject.xaml
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
<controls:PanelContainer HorizontalExpand="True"
|
||||||
|
xmlns:controls="https://spacestation14.io"
|
||||||
|
Margin="0 3">
|
||||||
|
<controls:BoxContainer Name="Contents" Orientation="Vertical"
|
||||||
|
Margin="3">
|
||||||
|
<controls:Label Name="DockedLabel"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
Margin="3 5"/>
|
||||||
|
<controls:BoxContainer Orientation="Vertical" Name="DockContainer"/>
|
||||||
|
</controls:BoxContainer>
|
||||||
|
</controls:PanelContainer>
|
||||||
61
Content.Client/Shuttles/UI/DockObject.xaml.cs
Normal file
61
Content.Client/Shuttles/UI/DockObject.xaml.cs
Normal file
@@ -0,0 +1,61 @@
|
|||||||
|
using System.Text;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class DockObject : PanelContainer
|
||||||
|
{
|
||||||
|
public event Action? UndockPressed;
|
||||||
|
public event Action? ViewPressed;
|
||||||
|
|
||||||
|
public BoxContainer ContentsContainer => Contents;
|
||||||
|
|
||||||
|
public DockObject()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
PanelOverride = new StyleBoxFlat(new Color(30, 30, 34));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddDock(DockingPortState state, ShuttleDockControl dockControl)
|
||||||
|
{
|
||||||
|
var viewButton = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("shuttle-console-view"),
|
||||||
|
};
|
||||||
|
|
||||||
|
viewButton.OnPressed += args =>
|
||||||
|
{
|
||||||
|
dockControl.SetViewedDock(state);
|
||||||
|
};
|
||||||
|
|
||||||
|
var container = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new Label()
|
||||||
|
{
|
||||||
|
Text = state.Name,
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
},
|
||||||
|
viewButton
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
DockContainer.AddChild(container);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetName(string value)
|
||||||
|
{
|
||||||
|
DockedLabel.Text = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,272 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.Shuttles.BUIStates;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Collision.Shapes;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.UI;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Displays the docking view from a specific docking port
|
|
||||||
/// </summary>
|
|
||||||
[Virtual]
|
|
||||||
public class DockingControl : Control
|
|
||||||
{
|
|
||||||
private readonly IEntityManager _entManager;
|
|
||||||
private readonly IMapManager _mapManager;
|
|
||||||
|
|
||||||
private float _range = 8f;
|
|
||||||
private float _rangeSquared = 0f;
|
|
||||||
|
|
||||||
private Vector2 RangeVector => new Vector2(_range, _range);
|
|
||||||
|
|
||||||
private const float GridLinesDistance = 32f;
|
|
||||||
|
|
||||||
private int MidPoint => SizeFull / 2;
|
|
||||||
private Vector2 MidPointVector => new Vector2(MidPoint, MidPoint);
|
|
||||||
|
|
||||||
private int SizeFull => (int) (MapGridControl.UIDisplayRadius * 2 * UIScale);
|
|
||||||
private int ScaledMinimapRadius => (int) (MapGridControl.UIDisplayRadius * UIScale);
|
|
||||||
private float MinimapScale => _range != 0 ? ScaledMinimapRadius / _range : 0f;
|
|
||||||
|
|
||||||
public NetEntity? ViewedDock;
|
|
||||||
public EntityUid? GridEntity;
|
|
||||||
|
|
||||||
public EntityCoordinates? Coordinates;
|
|
||||||
public Angle? Angle;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Stored by GridID then by docks
|
|
||||||
/// </summary>
|
|
||||||
public Dictionary<NetEntity, List<DockingInterfaceState>> Docks = new();
|
|
||||||
|
|
||||||
private List<Entity<MapGridComponent>> _grids = new();
|
|
||||||
|
|
||||||
public DockingControl()
|
|
||||||
{
|
|
||||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
|
||||||
_mapManager = IoCManager.Resolve<IMapManager>();
|
|
||||||
_rangeSquared = _range * _range;
|
|
||||||
MinSize = new Vector2(SizeFull, SizeFull);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
|
||||||
{
|
|
||||||
base.Draw(handle);
|
|
||||||
|
|
||||||
var fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), ScaledMinimapRadius + 1, fakeAA);
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), ScaledMinimapRadius, Color.Black);
|
|
||||||
|
|
||||||
var gridLines = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
var gridLinesRadial = 8;
|
|
||||||
var gridLinesEquatorial = (int) Math.Floor(_range / GridLinesDistance);
|
|
||||||
|
|
||||||
for (var i = 1; i < gridLinesEquatorial + 1; i++)
|
|
||||||
{
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), GridLinesDistance * MinimapScale * i, gridLines, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < gridLinesRadial; i++)
|
|
||||||
{
|
|
||||||
Angle angle = (Math.PI / gridLinesRadial) * i;
|
|
||||||
var aExtent = angle.ToVec() * ScaledMinimapRadius;
|
|
||||||
handle.DrawLine(new Vector2(MidPoint, MidPoint) - aExtent, new Vector2(MidPoint, MidPoint) + aExtent, gridLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (Coordinates == null ||
|
|
||||||
Angle == null ||
|
|
||||||
!_entManager.TryGetComponent<TransformComponent>(GridEntity, out var gridXform)) return;
|
|
||||||
|
|
||||||
var rotation = Matrix3.CreateRotation(-Angle.Value + Math.PI);
|
|
||||||
var matrix = Matrix3.CreateTranslation(-Coordinates.Value.Position);
|
|
||||||
|
|
||||||
// Draw the fixtures around the dock before drawing it
|
|
||||||
if (_entManager.TryGetComponent<FixturesComponent>(GridEntity, out var fixtures))
|
|
||||||
{
|
|
||||||
foreach (var fixture in fixtures.Fixtures.Values)
|
|
||||||
{
|
|
||||||
var poly = (PolygonShape) fixture.Shape;
|
|
||||||
|
|
||||||
for (var i = 0; i < poly.VertexCount; i++)
|
|
||||||
{
|
|
||||||
var start = matrix.Transform(poly.Vertices[i]);
|
|
||||||
var end = matrix.Transform(poly.Vertices[(i + 1) % poly.VertexCount]);
|
|
||||||
|
|
||||||
var startOut = start.LengthSquared() > _rangeSquared;
|
|
||||||
var endOut = end.LengthSquared() > _rangeSquared;
|
|
||||||
|
|
||||||
// We need to draw to the radar border so we'll cap the range,
|
|
||||||
// but if none of the verts are in range then just leave it.
|
|
||||||
if (startOut && endOut)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
start.Y = -start.Y;
|
|
||||||
end.Y = -end.Y;
|
|
||||||
|
|
||||||
// If start is outside we draw capped from end to start
|
|
||||||
if (startOut)
|
|
||||||
{
|
|
||||||
// It's called Jobseeker now.
|
|
||||||
if (!MathHelper.TryGetIntersecting(start, end, _range, out var newStart))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
start = newStart.Value;
|
|
||||||
}
|
|
||||||
// otherwise vice versa
|
|
||||||
else if (endOut)
|
|
||||||
{
|
|
||||||
if (!MathHelper.TryGetIntersecting(end, start, _range, out var newEnd))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
end = newEnd.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawLine(ScalePosition(start), ScalePosition(end), Color.Goldenrod);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw the dock's collision
|
|
||||||
handle.DrawRect(new UIBox2(
|
|
||||||
ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))),
|
|
||||||
ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f)))), Color.Aquamarine);
|
|
||||||
|
|
||||||
// Draw the dock itself
|
|
||||||
handle.DrawRect(new UIBox2(
|
|
||||||
ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))),
|
|
||||||
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f)))), Color.Green);
|
|
||||||
|
|
||||||
// Draw nearby grids
|
|
||||||
var worldPos = gridXform.WorldMatrix.Transform(Coordinates.Value.Position);
|
|
||||||
var gridInvMatrix = gridXform.InvWorldMatrix;
|
|
||||||
Matrix3.Multiply(in gridInvMatrix, in matrix, out var invMatrix);
|
|
||||||
|
|
||||||
// TODO: Getting some overdraw so need to fix that.
|
|
||||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
|
||||||
|
|
||||||
_grids.Clear();
|
|
||||||
_mapManager.FindGridsIntersecting(gridXform.MapID, new Box2(worldPos - RangeVector, worldPos + RangeVector), ref _grids);
|
|
||||||
|
|
||||||
foreach (var grid in _grids)
|
|
||||||
{
|
|
||||||
if (grid.Owner == GridEntity)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// Draw the fixtures before drawing any docks in range.
|
|
||||||
if (!_entManager.TryGetComponent<FixturesComponent>(grid, out var gridFixtures))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var gridMatrix = xformQuery.GetComponent(grid).WorldMatrix;
|
|
||||||
|
|
||||||
Matrix3.Multiply(in gridMatrix, in invMatrix, out var matty);
|
|
||||||
|
|
||||||
foreach (var (_, fixture) in gridFixtures.Fixtures)
|
|
||||||
{
|
|
||||||
var poly = (PolygonShape) fixture.Shape;
|
|
||||||
|
|
||||||
for (var i = 0; i < poly.VertexCount; i++)
|
|
||||||
{
|
|
||||||
// This is because the same line might be on different fixtures so we don't want to draw it twice.
|
|
||||||
var startPos = poly.Vertices[i];
|
|
||||||
var endPos = poly.Vertices[(i + 1) % poly.VertexCount];
|
|
||||||
|
|
||||||
var start = matty.Transform(startPos);
|
|
||||||
var end = matty.Transform(endPos);
|
|
||||||
|
|
||||||
var startOut = start.LengthSquared() > _rangeSquared;
|
|
||||||
var endOut = end.LengthSquared() > _rangeSquared;
|
|
||||||
|
|
||||||
// We need to draw to the radar border so we'll cap the range,
|
|
||||||
// but if none of the verts are in range then just leave it.
|
|
||||||
if (startOut && endOut)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
start.Y = -start.Y;
|
|
||||||
end.Y = -end.Y;
|
|
||||||
|
|
||||||
// If start is outside we draw capped from end to start
|
|
||||||
if (startOut)
|
|
||||||
{
|
|
||||||
// It's called Jobseeker now.
|
|
||||||
if (!MathHelper.TryGetIntersecting(start, end, _range, out var newStart)) continue;
|
|
||||||
start = newStart.Value;
|
|
||||||
}
|
|
||||||
// otherwise vice versa
|
|
||||||
else if (endOut)
|
|
||||||
{
|
|
||||||
if (!MathHelper.TryGetIntersecting(end, start, _range, out var newEnd)) continue;
|
|
||||||
end = newEnd.Value;
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawLine(ScalePosition(start), ScalePosition(end), Color.Aquamarine);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Draw any docks on that grid
|
|
||||||
if (Docks.TryGetValue(_entManager.GetNetEntity(grid), out var gridDocks))
|
|
||||||
{
|
|
||||||
foreach (var dock in gridDocks)
|
|
||||||
{
|
|
||||||
var position = matty.Transform(dock.Coordinates.Position);
|
|
||||||
|
|
||||||
if (position.Length() > _range - 0.8f)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var otherDockRotation = Matrix3.CreateRotation(dock.Angle);
|
|
||||||
|
|
||||||
// Draw the dock's collision
|
|
||||||
var verts = new[]
|
|
||||||
{
|
|
||||||
matty.Transform(dock.Coordinates.Position +
|
|
||||||
otherDockRotation.Transform(new Vector2(-0.2f, -0.7f))),
|
|
||||||
matty.Transform(dock.Coordinates.Position +
|
|
||||||
otherDockRotation.Transform(new Vector2(0.2f, -0.7f))),
|
|
||||||
matty.Transform(dock.Coordinates.Position +
|
|
||||||
otherDockRotation.Transform(new Vector2(0.2f, -0.5f))),
|
|
||||||
matty.Transform(dock.Coordinates.Position +
|
|
||||||
otherDockRotation.Transform(new Vector2(-0.2f, -0.5f))),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < verts.Length; i++)
|
|
||||||
{
|
|
||||||
var vert = verts[i];
|
|
||||||
vert.Y = -vert.Y;
|
|
||||||
verts[i] = ScalePosition(vert);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Turquoise);
|
|
||||||
|
|
||||||
// Draw the dock itself
|
|
||||||
verts = new[]
|
|
||||||
{
|
|
||||||
matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f)),
|
|
||||||
matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f)),
|
|
||||||
matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f)),
|
|
||||||
matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f)),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < verts.Length; i++)
|
|
||||||
{
|
|
||||||
var vert = verts[i];
|
|
||||||
vert.Y = -vert.Y;
|
|
||||||
verts[i] = ScalePosition(vert);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, Color.Green);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 ScalePosition(Vector2 value)
|
|
||||||
{
|
|
||||||
return value * MinimapScale + MidPointVector;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
28
Content.Client/Shuttles/UI/DockingScreen.xaml
Normal file
28
Content.Client/Shuttles/UI/DockingScreen.xaml
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<controls:BoxContainer Visible="False"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
xmlns:controls="https://spacestation14.io"
|
||||||
|
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI">
|
||||||
|
<ui:ShuttleDockControl Name="DockingControl"
|
||||||
|
MouseFilter="Stop"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="5 4 10 5"/>
|
||||||
|
<controls:BoxContainer Name="RightDisplayDock"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
MinWidth="256"
|
||||||
|
MaxWidth="256"
|
||||||
|
Align="Center"
|
||||||
|
Margin="5 0 5 5"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<controls1:StripeBack MinSize="48 48">
|
||||||
|
<controls:Label Name="DockingPortsLabel" Text="{controls:Loc 'shuttle-console-docks-label'}" HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:ScrollContainer VerticalExpand="True" HScrollEnabled="False"
|
||||||
|
ReturnMeasure="True">
|
||||||
|
<controls:BoxContainer Name="DockPorts" Orientation="Vertical"/>
|
||||||
|
</controls:ScrollContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
183
Content.Client/Shuttles/UI/DockingScreen.xaml.cs
Normal file
183
Content.Client/Shuttles/UI/DockingScreen.xaml.cs
Normal file
@@ -0,0 +1,183 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class DockingScreen : BoxContainer
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
private readonly SharedShuttleSystem _shuttles;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Stored by GridID then by docks
|
||||||
|
/// </summary>
|
||||||
|
public Dictionary<NetEntity, List<DockingPortState>> Docks = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store the dock buttons for the side buttons.
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<NetEntity, Button> _ourDockButtons = new();
|
||||||
|
|
||||||
|
public event Action<NetEntity, NetEntity>? DockRequest;
|
||||||
|
public event Action<NetEntity>? UndockRequest;
|
||||||
|
|
||||||
|
public DockingScreen()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_shuttles = _entManager.System<SharedShuttleSystem>();
|
||||||
|
|
||||||
|
DockingControl.OnViewDock += OnView;
|
||||||
|
DockingControl.DockRequest += (entity, netEntity) =>
|
||||||
|
{
|
||||||
|
DockRequest?.Invoke(entity, netEntity);
|
||||||
|
};
|
||||||
|
DockingControl.UndockRequest += entity =>
|
||||||
|
{
|
||||||
|
UndockRequest?.Invoke(entity);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnView(NetEntity obj)
|
||||||
|
{
|
||||||
|
if (_ourDockButtons.TryGetValue(obj, out var viewed))
|
||||||
|
{
|
||||||
|
viewed.Pressed = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(EntityUid? shuttle, DockingInterfaceState state)
|
||||||
|
{
|
||||||
|
Docks = state.Docks;
|
||||||
|
DockingControl.DockState = state;
|
||||||
|
DockingControl.GridEntity = shuttle;
|
||||||
|
BuildDocks(shuttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BuildDocks(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
DockingControl.BuildDocks(shuttle);
|
||||||
|
var currentDock = DockingControl.ViewedDock;
|
||||||
|
// DockedWith.DisposeAllChildren();
|
||||||
|
DockPorts.DisposeAllChildren();
|
||||||
|
_ourDockButtons.Clear();
|
||||||
|
|
||||||
|
if (shuttle == null)
|
||||||
|
{
|
||||||
|
DockingControl.SetViewedDock(null);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var shuttleNent = _entManager.GetNetEntity(shuttle.Value);
|
||||||
|
|
||||||
|
if (!Docks.TryGetValue(shuttleNent, out var shuttleDocks) || shuttleDocks.Count <= 0)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var dockText = new StringBuilder();
|
||||||
|
var buttonGroup = new ButtonGroup();
|
||||||
|
var idx = 0;
|
||||||
|
var selected = false;
|
||||||
|
|
||||||
|
// Build the dock buttons for our docks.
|
||||||
|
foreach (var dock in shuttleDocks)
|
||||||
|
{
|
||||||
|
idx++;
|
||||||
|
dockText.Clear();
|
||||||
|
dockText.Append(dock.Name);
|
||||||
|
|
||||||
|
var button = new Button()
|
||||||
|
{
|
||||||
|
Text = dockText.ToString(),
|
||||||
|
ToggleMode = true,
|
||||||
|
Group = buttonGroup,
|
||||||
|
Margin = new Thickness(0f, 3f),
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnMouseEntered += args =>
|
||||||
|
{
|
||||||
|
DockingControl.HighlightedDock = dock.Entity;
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnMouseExited += args =>
|
||||||
|
{
|
||||||
|
DockingControl.HighlightedDock = null;
|
||||||
|
};
|
||||||
|
|
||||||
|
button.Label.Margin = new Thickness(3f);
|
||||||
|
|
||||||
|
if (currentDock == dock.Entity)
|
||||||
|
{
|
||||||
|
selected = true;
|
||||||
|
button.Pressed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
button.OnPressed += args =>
|
||||||
|
{
|
||||||
|
OnDockPress(dock);
|
||||||
|
};
|
||||||
|
|
||||||
|
_ourDockButtons[dock.Entity] = button;
|
||||||
|
DockPorts.AddChild(button);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Button group needs one selected so just show the first one.
|
||||||
|
if (!selected)
|
||||||
|
{
|
||||||
|
var buttonOne = shuttleDocks[0];
|
||||||
|
OnDockPress(buttonOne);
|
||||||
|
}
|
||||||
|
|
||||||
|
var shuttleContainers = new Dictionary<NetEntity, DockObject>();
|
||||||
|
|
||||||
|
foreach (var dock in shuttleDocks.OrderBy(x => x.GridDockedWith))
|
||||||
|
{
|
||||||
|
if (dock.GridDockedWith == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DockObject? dockContainer;
|
||||||
|
|
||||||
|
if (!shuttleContainers.TryGetValue(dock.GridDockedWith.Value, out dockContainer))
|
||||||
|
{
|
||||||
|
dockContainer = new DockObject();
|
||||||
|
shuttleContainers[dock.GridDockedWith.Value] = dockContainer;
|
||||||
|
var dockGrid = _entManager.GetEntity(dock.GridDockedWith);
|
||||||
|
string? iffLabel = null;
|
||||||
|
|
||||||
|
if (_entManager.EntityExists(dockGrid))
|
||||||
|
{
|
||||||
|
iffLabel = _shuttles.GetIFFLabel(dockGrid.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
iffLabel ??= Loc.GetString("shuttle-console-unknown");
|
||||||
|
dockContainer.SetName(iffLabel);
|
||||||
|
// DockedWith.AddChild(dockContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
dockContainer.AddDock(dock, DockingControl);
|
||||||
|
|
||||||
|
dockContainer.ViewPressed += () =>
|
||||||
|
{
|
||||||
|
OnDockPress(dock);
|
||||||
|
};
|
||||||
|
|
||||||
|
dockContainer.UndockPressed += () =>
|
||||||
|
{
|
||||||
|
UndockRequest?.Invoke(dock.Entity);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDockPress(DockingPortState state)
|
||||||
|
{
|
||||||
|
DockingControl.SetViewedDock(state);
|
||||||
|
}
|
||||||
|
}
|
||||||
66
Content.Client/Shuttles/UI/MapScreen.xaml
Normal file
66
Content.Client/Shuttles/UI/MapScreen.xaml
Normal file
@@ -0,0 +1,66 @@
|
|||||||
|
<controls:BoxContainer Visible="False"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
xmlns:controls="https://spacestation14.io"
|
||||||
|
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI">
|
||||||
|
<ui:ShuttleMapControl Name="MapRadar"
|
||||||
|
MouseFilter="Stop"
|
||||||
|
Margin="5 4 10 5"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
VerticalExpand="True"
|
||||||
|
VerticalAlignment="Stretch"/>
|
||||||
|
<controls:BoxContainer Name="RightDisplayMap"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
MinWidth="256"
|
||||||
|
MaxWidth="256"
|
||||||
|
Margin="5 0 5 5"
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalExpand="True">
|
||||||
|
<controls1:StripeBack
|
||||||
|
MinSize="48 48">
|
||||||
|
<controls:Label Name="MapDisplayLabel" Text="{controls:Loc 'shuttle-console-ftl-label'}"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:Label Name="MapFTLState"
|
||||||
|
Text="{controls:Loc 'shuttle-console-ftl-state-Available'}"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
<controls:ProgressBar Name="FTLBar" HorizontalExpand="True"
|
||||||
|
Margin="5"
|
||||||
|
MinValue="0.0"
|
||||||
|
MaxValue="1.0"
|
||||||
|
Value="1.0"/>
|
||||||
|
<controls:BoxContainer Orientation="Vertical">
|
||||||
|
<!-- Normal buttons -->
|
||||||
|
<controls1:StripeBack MinSize="48 48">
|
||||||
|
<controls:Label Name="SettingsLabel" Text="{controls:Loc 'shuttle-console-map-settings'}"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:Button Name="MapBeaconsButton"
|
||||||
|
Text="{controls:Loc 'shuttle-console-map-beacons'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"
|
||||||
|
Pressed="True"/>
|
||||||
|
<controls:Button Name="MapFTLButton"
|
||||||
|
ToggleMode="True"
|
||||||
|
Text="{controls:Loc 'shuttle-console-ftl-button'}"
|
||||||
|
TextAlign="Center"/>
|
||||||
|
<controls:Button Name="MapRebuildButton"
|
||||||
|
Text="{controls:Loc 'shuttle-console-map-rebuild'}"
|
||||||
|
TextAlign="Center"/>
|
||||||
|
<!-- Map objects -->
|
||||||
|
<controls1:StripeBack MinSize="48 48">
|
||||||
|
<controls:Label Name="HyperspaceLabel" Text="{controls:Loc 'shuttle-console-map-objects'}"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:ScrollContainer VerticalExpand="True" HScrollEnabled="False"
|
||||||
|
ReturnMeasure="True">
|
||||||
|
<controls:BoxContainer Name="HyperspaceDestinations"
|
||||||
|
Orientation="Vertical"
|
||||||
|
VerticalExpand="True"/>
|
||||||
|
</controls:ScrollContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
531
Content.Client/Shuttles/UI/MapScreen.xaml.cs
Normal file
531
Content.Client/Shuttles/UI/MapScreen.xaml.cs
Normal file
@@ -0,0 +1,531 @@
|
|||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Shuttles.Systems;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Audio;
|
||||||
|
using Robust.Shared.Audio.Systems;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Player;
|
||||||
|
using Robust.Shared.Random;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class MapScreen : BoxContainer
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] private readonly IRobustRandom _random = default!;
|
||||||
|
private readonly SharedAudioSystem _audio;
|
||||||
|
private readonly SharedMapSystem _maps;
|
||||||
|
private readonly ShuttleSystem _shuttles;
|
||||||
|
private readonly SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
private EntityUid? _console;
|
||||||
|
private EntityUid? _shuttleEntity;
|
||||||
|
|
||||||
|
private FTLState _state;
|
||||||
|
private float _ftlDuration;
|
||||||
|
|
||||||
|
private List<ShuttleBeaconObject> _beacons = new();
|
||||||
|
private List<ShuttleExclusionObject> _exclusions = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// When the next FTL state change happens.
|
||||||
|
/// </summary>
|
||||||
|
private TimeSpan _nextFtlTime;
|
||||||
|
|
||||||
|
private TimeSpan _nextPing;
|
||||||
|
private TimeSpan _pingCooldown = TimeSpan.FromSeconds(3);
|
||||||
|
private TimeSpan _nextMapDequeue;
|
||||||
|
|
||||||
|
private float _minMapDequeue = 0.05f;
|
||||||
|
private float _maxMapDequeue = 0.25f;
|
||||||
|
|
||||||
|
private StyleBoxFlat _ftlStyle;
|
||||||
|
|
||||||
|
public event Action<MapCoordinates, Angle>? RequestFTL;
|
||||||
|
public event Action<NetEntity, Angle>? RequestBeaconFTL;
|
||||||
|
|
||||||
|
private readonly Dictionary<MapId, BoxContainer> _mapHeadings = new();
|
||||||
|
private readonly Dictionary<MapId, List<IMapObject>> _mapObjects = new();
|
||||||
|
private readonly List<(MapId mapId, IMapObject mapobj)> _pendingMapObjects = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store the names of map object controls for re-sorting later.
|
||||||
|
/// </summary>
|
||||||
|
private Dictionary<Control, string> _mapObjectControls = new();
|
||||||
|
|
||||||
|
private List<Control> _sortChildren = new();
|
||||||
|
|
||||||
|
public MapScreen()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
|
||||||
|
_audio = _entManager.System<SharedAudioSystem>();
|
||||||
|
_maps = _entManager.System<SharedMapSystem>();
|
||||||
|
_shuttles = _entManager.System<ShuttleSystem>();
|
||||||
|
_xformSystem = _entManager.System<SharedTransformSystem>();
|
||||||
|
|
||||||
|
MapRebuildButton.OnPressed += MapRebuildPressed;
|
||||||
|
|
||||||
|
OnVisibilityChanged += OnVisChange;
|
||||||
|
|
||||||
|
MapFTLButton.OnToggled += FtlPreviewToggled;
|
||||||
|
|
||||||
|
_ftlStyle = new StyleBoxFlat(Color.LimeGreen);
|
||||||
|
FTLBar.ForegroundStyleBoxOverride = _ftlStyle;
|
||||||
|
|
||||||
|
// Just pass it on up.
|
||||||
|
MapRadar.RequestFTL += (coords, angle) =>
|
||||||
|
{
|
||||||
|
RequestFTL?.Invoke(coords, angle);
|
||||||
|
};
|
||||||
|
|
||||||
|
MapRadar.RequestBeaconFTL += (ent, angle) =>
|
||||||
|
{
|
||||||
|
RequestBeaconFTL?.Invoke(ent, angle);
|
||||||
|
};
|
||||||
|
|
||||||
|
MapBeaconsButton.OnToggled += args =>
|
||||||
|
{
|
||||||
|
MapRadar.ShowBeacons = args.Pressed;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(ShuttleMapInterfaceState state)
|
||||||
|
{
|
||||||
|
// Only network the accumulator due to ping making the thing fonky.
|
||||||
|
// This should work better with predicting network states as they come in.
|
||||||
|
_beacons = state.Destinations;
|
||||||
|
_exclusions = state.Exclusions;
|
||||||
|
_state = state.FTLState;
|
||||||
|
_ftlDuration = state.FTLDuration;
|
||||||
|
_nextFtlTime = _timing.CurTime + TimeSpan.FromSeconds(_ftlDuration);
|
||||||
|
MapRadar.InFtl = true;
|
||||||
|
MapFTLState.Text = Loc.GetString($"shuttle-console-ftl-state-{_state.ToString()}");
|
||||||
|
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case FTLState.Available:
|
||||||
|
SetFTLAllowed(true);
|
||||||
|
_ftlStyle.BackgroundColor = Color.FromHex("#80C71F");
|
||||||
|
MapRadar.InFtl = false;
|
||||||
|
break;
|
||||||
|
case FTLState.Starting:
|
||||||
|
SetFTLAllowed(false);
|
||||||
|
_ftlStyle.BackgroundColor = Color.FromHex("#169C9C");
|
||||||
|
break;
|
||||||
|
case FTLState.Travelling:
|
||||||
|
SetFTLAllowed(false);
|
||||||
|
_ftlStyle.BackgroundColor = Color.FromHex("#8932B8");
|
||||||
|
break;
|
||||||
|
case FTLState.Arriving:
|
||||||
|
SetFTLAllowed(false);
|
||||||
|
_ftlStyle.BackgroundColor = Color.FromHex("#F9801D");
|
||||||
|
break;
|
||||||
|
case FTLState.Cooldown:
|
||||||
|
SetFTLAllowed(false);
|
||||||
|
// Scroll to the FTL spot
|
||||||
|
if (_entManager.TryGetComponent(_shuttleEntity, out TransformComponent? shuttleXform))
|
||||||
|
{
|
||||||
|
var targetOffset = _maps.GetGridPosition(_shuttleEntity.Value);
|
||||||
|
MapRadar.SetMap(shuttleXform.MapID, targetOffset, recentering: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ftlStyle.BackgroundColor = Color.FromHex("#B02E26");
|
||||||
|
MapRadar.InFtl = false;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (IsFTLBlocked())
|
||||||
|
{
|
||||||
|
MapRebuildButton.Disabled = true;
|
||||||
|
ClearMapObjects();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetFTLAllowed(bool value)
|
||||||
|
{
|
||||||
|
if (value)
|
||||||
|
{
|
||||||
|
MapFTLButton.Disabled = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// Unselect FTL
|
||||||
|
MapFTLButton.Pressed = false;
|
||||||
|
MapRadar.FtlMode = false;
|
||||||
|
MapFTLButton.Disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void FtlPreviewToggled(BaseButton.ButtonToggledEventArgs obj)
|
||||||
|
{
|
||||||
|
MapRadar.FtlMode = obj.Pressed;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetConsole(EntityUid? console)
|
||||||
|
{
|
||||||
|
_console = console;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShuttle(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
_shuttleEntity = shuttle;
|
||||||
|
MapRadar.SetShuttle(shuttle);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnVisChange(Control obj)
|
||||||
|
{
|
||||||
|
if (!obj.Visible)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Centre map screen to the shuttle.
|
||||||
|
if (_shuttleEntity != null)
|
||||||
|
{
|
||||||
|
var mapPos = _xformSystem.GetMapCoordinates(_shuttleEntity.Value);
|
||||||
|
MapRadar.SetMap(mapPos.MapId, mapPos.Position);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Does a sonar-like effect on the map.
|
||||||
|
/// </summary>
|
||||||
|
public void PingMap()
|
||||||
|
{
|
||||||
|
if (_console != null)
|
||||||
|
{
|
||||||
|
_audio.PlayEntity(new SoundPathSpecifier("/Audio/Effects/Shuttle/radar_ping.ogg"), Filter.Local(), _console.Value, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
RebuildMapObjects();
|
||||||
|
BumpMapDequeue();
|
||||||
|
|
||||||
|
_nextPing = _timing.CurTime + _pingCooldown;
|
||||||
|
MapRebuildButton.Disabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void BumpMapDequeue()
|
||||||
|
{
|
||||||
|
_nextMapDequeue = _timing.CurTime + TimeSpan.FromSeconds(_random.NextFloat(_minMapDequeue, _maxMapDequeue));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MapRebuildPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
PingMap();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Clears all sector objects across all maps (e.g. if we start FTLing or need to re-ping).
|
||||||
|
/// </summary>
|
||||||
|
private void ClearMapObjects()
|
||||||
|
{
|
||||||
|
_mapObjectControls.Clear();
|
||||||
|
HyperspaceDestinations.DisposeAllChildren();
|
||||||
|
_pendingMapObjects.Clear();
|
||||||
|
_mapObjects.Clear();
|
||||||
|
_mapHeadings.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all map objects at time of ping and adds them to pending to be added over time.
|
||||||
|
/// </summary>
|
||||||
|
private void RebuildMapObjects()
|
||||||
|
{
|
||||||
|
ClearMapObjects();
|
||||||
|
|
||||||
|
if (_shuttleEntity == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mapComps = _entManager.AllEntityQueryEnumerator<MapComponent, TransformComponent, MetaDataComponent>();
|
||||||
|
MapId ourMap = MapId.Nullspace;
|
||||||
|
|
||||||
|
if (_entManager.TryGetComponent(_shuttleEntity, out TransformComponent? shuttleXform))
|
||||||
|
{
|
||||||
|
ourMap = shuttleXform.MapID;
|
||||||
|
}
|
||||||
|
|
||||||
|
while (mapComps.MoveNext(out var mapComp, out var mapXform, out var mapMetadata))
|
||||||
|
{
|
||||||
|
if (!_shuttles.CanFTLTo(_shuttleEntity.Value, mapComp.MapId))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var mapName = mapMetadata.EntityName;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(mapName))
|
||||||
|
{
|
||||||
|
mapName = Loc.GetString("shuttle-console-unknown");
|
||||||
|
}
|
||||||
|
|
||||||
|
var heading = new CollapsibleHeading(mapName);
|
||||||
|
|
||||||
|
heading.MinHeight = 32f;
|
||||||
|
heading.AddStyleClass(ContainerButton.StyleClassButton);
|
||||||
|
heading.HorizontalAlignment = HAlignment.Stretch;
|
||||||
|
heading.Label.HorizontalAlignment = HAlignment.Center;
|
||||||
|
heading.Label.HorizontalExpand = true;
|
||||||
|
heading.HorizontalExpand = true;
|
||||||
|
|
||||||
|
var gridContents = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = LayoutOrientation.Vertical,
|
||||||
|
VerticalExpand = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var body = new CollapsibleBody()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Stretch,
|
||||||
|
VerticalAlignment = VAlignment.Top,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
gridContents
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var mapButton = new Collapsible(heading, body);
|
||||||
|
|
||||||
|
heading.OnToggled += args =>
|
||||||
|
{
|
||||||
|
if (args.Pressed)
|
||||||
|
{
|
||||||
|
HideOtherCollapsibles(mapButton);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_mapHeadings.Add(mapComp.MapId, gridContents);
|
||||||
|
|
||||||
|
foreach (var grid in _mapManager.GetAllMapGrids(mapComp.MapId))
|
||||||
|
{
|
||||||
|
var gridObj = new GridMapObject()
|
||||||
|
{
|
||||||
|
Name = _entManager.GetComponent<MetaDataComponent>(grid.Owner).EntityName,
|
||||||
|
Entity = grid.Owner
|
||||||
|
};
|
||||||
|
|
||||||
|
// Always show our shuttle immediately
|
||||||
|
if (grid.Owner == _shuttleEntity)
|
||||||
|
{
|
||||||
|
AddMapObject(mapComp.MapId, gridObj);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_pendingMapObjects.Add((mapComp.MapId, gridObj));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (beacon, _) in _shuttles.GetExclusions(mapComp.MapId, _exclusions))
|
||||||
|
{
|
||||||
|
_pendingMapObjects.Add((mapComp.MapId, beacon));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (beacon, _) in _shuttles.GetBeacons(mapComp.MapId, _beacons))
|
||||||
|
{
|
||||||
|
_pendingMapObjects.Add((mapComp.MapId, beacon));
|
||||||
|
}
|
||||||
|
|
||||||
|
HyperspaceDestinations.AddChild(mapButton);
|
||||||
|
|
||||||
|
// Zoom in to our map
|
||||||
|
if (mapComp.MapId == MapRadar.ViewingMap)
|
||||||
|
{
|
||||||
|
mapButton.BodyVisible = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Need to sort from furthest way to nearest (as we will pop from the end of the list first).
|
||||||
|
// Also prioritise those on our map first.
|
||||||
|
var shuttlePos = _xformSystem.GetWorldPosition(_shuttleEntity.Value);
|
||||||
|
|
||||||
|
_pendingMapObjects.Sort((x, y) =>
|
||||||
|
{
|
||||||
|
if (x.mapId == ourMap && y.mapId != ourMap)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (y.mapId == ourMap && x.mapId != ourMap)
|
||||||
|
return -1;
|
||||||
|
|
||||||
|
var yMapPos = _shuttles.GetMapCoordinates(y.mapobj);
|
||||||
|
var xMapPos = _shuttles.GetMapCoordinates(x.mapobj);
|
||||||
|
|
||||||
|
return (yMapPos.Position - shuttlePos).Length().CompareTo((xMapPos.Position - shuttlePos).Length());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Hides other maps upon the specified collapsible being selected (AKA hacky collapsible groups).
|
||||||
|
/// </summary>
|
||||||
|
private void HideOtherCollapsibles(Collapsible collapsible)
|
||||||
|
{
|
||||||
|
foreach (var child in HyperspaceDestinations.Children)
|
||||||
|
{
|
||||||
|
if (child is not Collapsible childCollapse || childCollapse == collapsible)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
childCollapse.BodyVisible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if we shouldn't be able to select the FTL button.
|
||||||
|
/// </summary>
|
||||||
|
private bool IsFTLBlocked()
|
||||||
|
{
|
||||||
|
switch (_state)
|
||||||
|
{
|
||||||
|
case FTLState.Available:
|
||||||
|
return false;
|
||||||
|
default:
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnMapObjectPress(IMapObject mapObject)
|
||||||
|
{
|
||||||
|
if (IsFTLBlocked())
|
||||||
|
return;
|
||||||
|
|
||||||
|
var coordinates = _shuttles.GetMapCoordinates(mapObject);
|
||||||
|
|
||||||
|
// If it's our map then scroll, otherwise just set position there.
|
||||||
|
MapRadar.SetMap(coordinates.MapId, coordinates.Position, recentering: true);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMap(MapId mapId, Vector2 position)
|
||||||
|
{
|
||||||
|
MapRadar.SetMap(mapId, position);
|
||||||
|
MapRadar.Offset = position;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds a map object to the specified sector map.
|
||||||
|
/// </summary>
|
||||||
|
private void AddMapObject(MapId mapId, IMapObject mapObj)
|
||||||
|
{
|
||||||
|
var gridContents = _mapHeadings[mapId];
|
||||||
|
var existing = _mapObjects.GetOrNew(mapId);
|
||||||
|
existing.Add(mapObj);
|
||||||
|
|
||||||
|
var gridButton = new Button()
|
||||||
|
{
|
||||||
|
Text = mapObj.Name,
|
||||||
|
HorizontalExpand = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
var gridContainer = new BoxContainer()
|
||||||
|
{
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
new Control()
|
||||||
|
{
|
||||||
|
MinWidth = 32f,
|
||||||
|
},
|
||||||
|
gridButton
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
_mapObjectControls.Add(gridContainer, mapObj.Name);
|
||||||
|
gridContents.AddChild(gridContainer);
|
||||||
|
|
||||||
|
gridButton.OnPressed += args =>
|
||||||
|
{
|
||||||
|
OnMapObjectPress(mapObj);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (gridContents.ChildCount > 1)
|
||||||
|
{
|
||||||
|
// Re-sort the children
|
||||||
|
_sortChildren.Clear();
|
||||||
|
|
||||||
|
foreach (var child in gridContents.Children)
|
||||||
|
{
|
||||||
|
DebugTools.Assert(_mapObjectControls.ContainsKey(child));
|
||||||
|
_sortChildren.Add(child);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var child in _sortChildren)
|
||||||
|
{
|
||||||
|
child.Orphan();
|
||||||
|
}
|
||||||
|
|
||||||
|
_sortChildren.Sort((x, y) =>
|
||||||
|
{
|
||||||
|
var xText = _mapObjectControls[x];
|
||||||
|
var yText = _mapObjectControls[y];
|
||||||
|
|
||||||
|
return string.Compare(xText, yText, StringComparison.CurrentCultureIgnoreCase);
|
||||||
|
});
|
||||||
|
|
||||||
|
foreach (var control in _sortChildren)
|
||||||
|
{
|
||||||
|
gridContents.AddChild(control);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
|
||||||
|
var curTime = _timing.CurTime;
|
||||||
|
|
||||||
|
if (_nextMapDequeue < curTime && _pendingMapObjects.Count > 0)
|
||||||
|
{
|
||||||
|
var mapObj = _pendingMapObjects[^1];
|
||||||
|
_pendingMapObjects.RemoveAt(_pendingMapObjects.Count - 1);
|
||||||
|
AddMapObject(mapObj.mapId, mapObj.mapobj);
|
||||||
|
BumpMapDequeue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!IsFTLBlocked() && _nextPing < curTime)
|
||||||
|
{
|
||||||
|
MapRebuildButton.Disabled = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ftlDiff = (float) (_nextFtlTime - _timing.CurTime).TotalSeconds;
|
||||||
|
|
||||||
|
float ftlRatio;
|
||||||
|
|
||||||
|
if (_ftlDuration.Equals(0f))
|
||||||
|
{
|
||||||
|
ftlRatio = 1f;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
ftlRatio = Math.Clamp(1f - (ftlDiff / _ftlDuration), 0f, 1f);
|
||||||
|
}
|
||||||
|
|
||||||
|
FTLBar.Value = ftlRatio;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
MapRadar.SetMapObjects(_mapObjects);
|
||||||
|
base.Draw(handle);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void Startup()
|
||||||
|
{
|
||||||
|
if (_entManager.TryGetComponent(_shuttleEntity, out TransformComponent? shuttleXform))
|
||||||
|
{
|
||||||
|
SetMap(shuttleXform.MapID, _maps.GetGridPosition((_shuttleEntity.Value, null, shuttleXform)));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
69
Content.Client/Shuttles/UI/NavScreen.xaml
Normal file
69
Content.Client/Shuttles/UI/NavScreen.xaml
Normal file
@@ -0,0 +1,69 @@
|
|||||||
|
<controls:BoxContainer Visible="False"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
xmlns:controls="https://spacestation14.io"
|
||||||
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
||||||
|
xmlns:controls1="clr-namespace:Content.Client.UserInterface.Controls">
|
||||||
|
<ui:ShuttleNavControl Name="NavRadar"
|
||||||
|
MouseFilter="Stop"
|
||||||
|
VerticalAlignment="Stretch"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="5 4 10 5"/>
|
||||||
|
<!-- Nav controls -->
|
||||||
|
<controls:BoxContainer Name="RightDisplayNav"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalAlignment="Right"
|
||||||
|
VerticalExpand="True"
|
||||||
|
MinWidth="256"
|
||||||
|
MaxWidth="256"
|
||||||
|
Margin="5 0 5 5"
|
||||||
|
Orientation="Vertical">
|
||||||
|
<controls1:StripeBack
|
||||||
|
MinSize="48 48">
|
||||||
|
<controls:Label Name="NavDisplayLabel" Text="{controls:Loc 'shuttle-console-display-label'}"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:GridContainer Columns="2"
|
||||||
|
HorizontalAlignment="Stretch"
|
||||||
|
VerticalAlignment="Top"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Margin="3"
|
||||||
|
Name="ReadonlyDisplay">
|
||||||
|
<controls:Label Text="{controls:Loc 'shuttle-console-position'}"/>
|
||||||
|
<controls:Label Name="GridPosition"
|
||||||
|
Text="0.0, 0.0"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Right"/>
|
||||||
|
<controls:Label Text="{controls:Loc 'shuttle-console-orientation'}"/>
|
||||||
|
<controls:Label Name="GridOrientation"
|
||||||
|
Text="0.0"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Right"/>
|
||||||
|
<controls:Label Text="{controls:Loc 'shuttle-console-linear-velocity'}"/>
|
||||||
|
<controls:Label Name="GridLinearVelocity"
|
||||||
|
Text="0.0, 0.0"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Right"/>
|
||||||
|
<controls:Label Text="{controls:Loc 'shuttle-console-angular-velocity'}"/>
|
||||||
|
<controls:Label Name="GridAngularVelocity"
|
||||||
|
Text="0.0"
|
||||||
|
HorizontalExpand="True"
|
||||||
|
Align="Right"/>
|
||||||
|
</controls:GridContainer>
|
||||||
|
<controls1:StripeBack
|
||||||
|
MinSize="48 48">
|
||||||
|
<controls:Label Name="NavSettingsLabel" Text="{controls:Loc 'shuttle-console-nav-settings'}"
|
||||||
|
VerticalExpand="True"
|
||||||
|
HorizontalAlignment="Center"/>
|
||||||
|
</controls1:StripeBack>
|
||||||
|
<controls:Button Name="IFFToggle"
|
||||||
|
Text="{controls:Loc 'shuttle-console-iff-toggle'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"/>
|
||||||
|
<controls:Button Name="DockToggle"
|
||||||
|
Text="{controls:Loc 'shuttle-console-dock-toggle'}"
|
||||||
|
TextAlign="Center"
|
||||||
|
ToggleMode="True"/>
|
||||||
|
</controls:BoxContainer>
|
||||||
|
</controls:BoxContainer>
|
||||||
85
Content.Client/Shuttles/UI/NavScreen.xaml.cs
Normal file
85
Content.Client/Shuttles/UI/NavScreen.xaml.cs
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class NavScreen : BoxContainer
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
|
private SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
private EntityUid? _shuttleEntity;
|
||||||
|
|
||||||
|
public NavScreen()
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
_xformSystem = _entManager.System<SharedTransformSystem>();
|
||||||
|
|
||||||
|
IFFToggle.OnToggled += OnIFFTogglePressed;
|
||||||
|
IFFToggle.Pressed = NavRadar.ShowIFF;
|
||||||
|
|
||||||
|
DockToggle.OnToggled += OnDockTogglePressed;
|
||||||
|
DockToggle.Pressed = NavRadar.ShowDocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShuttle(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
_shuttleEntity = shuttle;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnIFFTogglePressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
NavRadar.ShowIFF ^= true;
|
||||||
|
args.Button.Pressed = NavRadar.ShowIFF;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnDockTogglePressed(BaseButton.ButtonEventArgs args)
|
||||||
|
{
|
||||||
|
NavRadar.ShowDocks ^= true;
|
||||||
|
args.Button.Pressed = NavRadar.ShowDocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(NavInterfaceState scc)
|
||||||
|
{
|
||||||
|
NavRadar.UpdateState(scc);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
||||||
|
{
|
||||||
|
_shuttleEntity = coordinates?.EntityId;
|
||||||
|
NavRadar.SetMatrix(coordinates, angle);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
if (!_entManager.TryGetComponent(_shuttleEntity, out TransformComponent? gridXform) ||
|
||||||
|
!_entManager.TryGetComponent(_shuttleEntity, out PhysicsComponent? gridBody))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var (_, worldRot, worldMatrix) = _xformSystem.GetWorldPositionRotationMatrix(gridXform);
|
||||||
|
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
|
||||||
|
|
||||||
|
// Get the positive reduced angle.
|
||||||
|
var displayRot = -worldRot.Reduced();
|
||||||
|
|
||||||
|
GridPosition.Text = $"{worldPos.X:0.0}, {worldPos.Y:0.0}";
|
||||||
|
GridOrientation.Text = $"{displayRot.Degrees:0.0}";
|
||||||
|
|
||||||
|
var gridVelocity = gridBody.LinearVelocity;
|
||||||
|
gridVelocity = displayRot.RotateVec(gridVelocity);
|
||||||
|
// Get linear velocity relative to the console entity
|
||||||
|
GridLinearVelocity.Text = $"{gridVelocity.X + 10f * float.Epsilon:0.0}, {gridVelocity.Y + 10f * float.Epsilon:0.0}";
|
||||||
|
GridAngularVelocity.Text = $"{-gridBody.AngularVelocity + 10f * float.Epsilon:0.0}";
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,7 @@
|
|||||||
Title="{Loc 'radar-console-window-title'}"
|
Title="{Loc 'radar-console-window-title'}"
|
||||||
SetSize="648 648"
|
SetSize="648 648"
|
||||||
MinSize="256 256">
|
MinSize="256 256">
|
||||||
<ui:RadarControl Name="RadarScreen"
|
<ui:ShuttleNavControl Name="RadarScreen"
|
||||||
Margin="4"
|
Margin="4"
|
||||||
HorizontalExpand = "True"
|
HorizontalExpand = "True"
|
||||||
VerticalExpand = "True"/>
|
VerticalExpand = "True"/>
|
||||||
|
|||||||
@@ -9,20 +9,15 @@ namespace Content.Client.Shuttles.UI;
|
|||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class RadarConsoleWindow : FancyWindow,
|
public sealed partial class RadarConsoleWindow : FancyWindow,
|
||||||
IComputerWindow<RadarConsoleBoundInterfaceState>
|
IComputerWindow<NavInterfaceState>
|
||||||
{
|
{
|
||||||
public RadarConsoleWindow()
|
public RadarConsoleWindow()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void UpdateState(RadarConsoleBoundInterfaceState scc)
|
public void UpdateState(NavInterfaceState scc)
|
||||||
{
|
{
|
||||||
RadarScreen.UpdateState(scc);
|
RadarScreen.UpdateState(scc);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
|
||||||
{
|
|
||||||
RadarScreen.SetMatrix(coordinates, angle);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,433 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Content.Client.UserInterface.Controls;
|
|
||||||
using Content.Shared.Shuttles.BUIStates;
|
|
||||||
using Content.Shared.Shuttles.Components;
|
|
||||||
using JetBrains.Annotations;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
|
||||||
using Robust.Shared.Collections;
|
|
||||||
using Robust.Shared.Input;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Map.Components;
|
|
||||||
using Robust.Shared.Physics;
|
|
||||||
using Robust.Shared.Physics.Components;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.UI;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Displays nearby grids inside of a control.
|
|
||||||
/// </summary>
|
|
||||||
public sealed class RadarControl : MapGridControl
|
|
||||||
{
|
|
||||||
[Dependency] private readonly IEntityManager _entManager = default!;
|
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
|
||||||
private SharedTransformSystem _transform;
|
|
||||||
|
|
||||||
private const float GridLinesDistance = 32f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid.
|
|
||||||
/// </summary>
|
|
||||||
private EntityCoordinates? _coordinates;
|
|
||||||
|
|
||||||
private Angle? _rotation;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows a label on each radar object.
|
|
||||||
/// </summary>
|
|
||||||
private Dictionary<EntityUid, Control> _iffControls = new();
|
|
||||||
|
|
||||||
private Dictionary<EntityUid, List<DockingInterfaceState>> _docks = new();
|
|
||||||
|
|
||||||
public bool ShowIFF { get; set; } = true;
|
|
||||||
public bool ShowDocks { get; set; } = true;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Currently hovered docked to show on the map.
|
|
||||||
/// </summary>
|
|
||||||
public NetEntity? HighlightedDock;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised if the user left-clicks on the radar control with the relevant entitycoordinates.
|
|
||||||
/// </summary>
|
|
||||||
public Action<EntityCoordinates>? OnRadarClick;
|
|
||||||
|
|
||||||
private List<Entity<MapGridComponent>> _grids = new();
|
|
||||||
|
|
||||||
public RadarControl() : base(64f, 256f, 256f)
|
|
||||||
{
|
|
||||||
_transform = _entManager.System<SharedTransformSystem>();
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
|
||||||
{
|
|
||||||
_coordinates = coordinates;
|
|
||||||
_rotation = angle;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
|
||||||
{
|
|
||||||
base.KeyBindUp(args);
|
|
||||||
|
|
||||||
if (_coordinates == null || _rotation == null || args.Function != EngineKeyFunctions.UIClick ||
|
|
||||||
OnRadarClick == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var a = InverseScalePosition(args.RelativePosition);
|
|
||||||
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
|
||||||
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
|
||||||
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
|
||||||
OnRadarClick?.Invoke(coords);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Gets the entitycoordinates of where the mouseposition is, relative to the control.
|
|
||||||
/// </summary>
|
|
||||||
[PublicAPI]
|
|
||||||
public EntityCoordinates GetMouseCoordinates(ScreenCoordinates screen)
|
|
||||||
{
|
|
||||||
if (_coordinates == null || _rotation == null)
|
|
||||||
{
|
|
||||||
return EntityCoordinates.Invalid;
|
|
||||||
}
|
|
||||||
|
|
||||||
var pos = screen.Position / UIScale - GlobalPosition;
|
|
||||||
|
|
||||||
var a = InverseScalePosition(pos);
|
|
||||||
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
|
||||||
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
|
||||||
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
|
||||||
return coords;
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateState(RadarConsoleBoundInterfaceState ls)
|
|
||||||
{
|
|
||||||
WorldMaxRange = ls.MaxRange;
|
|
||||||
|
|
||||||
if (WorldMaxRange < WorldRange)
|
|
||||||
{
|
|
||||||
ActualRadarRange = WorldMaxRange;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (WorldMaxRange < WorldMinRange)
|
|
||||||
WorldMinRange = WorldMaxRange;
|
|
||||||
|
|
||||||
ActualRadarRange = Math.Clamp(ActualRadarRange, WorldMinRange, WorldMaxRange);
|
|
||||||
|
|
||||||
_docks.Clear();
|
|
||||||
|
|
||||||
foreach (var state in ls.Docks)
|
|
||||||
{
|
|
||||||
var coordinates = state.Coordinates;
|
|
||||||
var grid = _docks.GetOrNew(_entManager.GetEntity(coordinates.NetEntity));
|
|
||||||
grid.Add(state);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
|
||||||
{
|
|
||||||
base.Draw(handle);
|
|
||||||
|
|
||||||
var fakeAA = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), ScaledMinimapRadius + 1, fakeAA);
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), ScaledMinimapRadius, Color.Black);
|
|
||||||
|
|
||||||
// No data
|
|
||||||
if (_coordinates == null || _rotation == null)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var gridLines = new Color(0.08f, 0.08f, 0.08f);
|
|
||||||
var gridLinesRadial = 8;
|
|
||||||
var gridLinesEquatorial = (int) Math.Floor(WorldRange / GridLinesDistance);
|
|
||||||
|
|
||||||
for (var i = 1; i < gridLinesEquatorial + 1; i++)
|
|
||||||
{
|
|
||||||
handle.DrawCircle(new Vector2(MidPoint, MidPoint), GridLinesDistance * MinimapScale * i, gridLines, false);
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < gridLinesRadial; i++)
|
|
||||||
{
|
|
||||||
Angle angle = (Math.PI / gridLinesRadial) * i;
|
|
||||||
var aExtent = angle.ToVec() * ScaledMinimapRadius;
|
|
||||||
handle.DrawLine(new Vector2(MidPoint, MidPoint) - aExtent, new Vector2(MidPoint, MidPoint) + aExtent, gridLines);
|
|
||||||
}
|
|
||||||
|
|
||||||
var metaQuery = _entManager.GetEntityQuery<MetaDataComponent>();
|
|
||||||
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
|
|
||||||
var fixturesQuery = _entManager.GetEntityQuery<FixturesComponent>();
|
|
||||||
var bodyQuery = _entManager.GetEntityQuery<PhysicsComponent>();
|
|
||||||
|
|
||||||
if (!xformQuery.TryGetComponent(_coordinates.Value.EntityId, out var xform)
|
|
||||||
|| xform.MapID == MapId.Nullspace)
|
|
||||||
{
|
|
||||||
Clear();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var (pos, rot) = _transform.GetWorldPositionRotation(xform);
|
|
||||||
var offset = _coordinates.Value.Position;
|
|
||||||
var offsetMatrix = Matrix3.CreateInverseTransform(pos, rot + _rotation.Value);
|
|
||||||
|
|
||||||
// Draw our grid in detail
|
|
||||||
var ourGridId = xform.GridUid;
|
|
||||||
if (_entManager.TryGetComponent<MapGridComponent>(ourGridId, out var ourGrid) &&
|
|
||||||
fixturesQuery.HasComponent(ourGridId.Value))
|
|
||||||
{
|
|
||||||
var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value);
|
|
||||||
Matrix3.Multiply(in ourGridMatrix, in offsetMatrix, out var matrix);
|
|
||||||
|
|
||||||
DrawGrid(handle, matrix, ourGrid, Color.MediumSpringGreen, true);
|
|
||||||
DrawDocks(handle, ourGridId.Value, matrix);
|
|
||||||
}
|
|
||||||
|
|
||||||
var invertedPosition = _coordinates.Value.Position - offset;
|
|
||||||
invertedPosition.Y = -invertedPosition.Y;
|
|
||||||
// Don't need to transform the InvWorldMatrix again as it's already offset to its position.
|
|
||||||
|
|
||||||
// Draw radar position on the station
|
|
||||||
handle.DrawCircle(ScalePosition(invertedPosition), 5f, Color.Lime);
|
|
||||||
|
|
||||||
var shown = new HashSet<EntityUid>();
|
|
||||||
|
|
||||||
_grids.Clear();
|
|
||||||
_mapManager.FindGridsIntersecting(xform.MapID, new Box2(pos - MaxRadarRangeVector, pos + MaxRadarRangeVector), ref _grids, approx: true, includeMap: false);
|
|
||||||
|
|
||||||
// Draw other grids... differently
|
|
||||||
foreach (var grid in _grids)
|
|
||||||
{
|
|
||||||
var gUid = grid.Owner;
|
|
||||||
if (gUid == ourGridId || !fixturesQuery.HasComponent(gUid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var gridBody = bodyQuery.GetComponent(gUid);
|
|
||||||
if (gridBody.Mass < 10f)
|
|
||||||
{
|
|
||||||
ClearLabel(gUid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
_entManager.TryGetComponent<IFFComponent>(gUid, out var iff);
|
|
||||||
|
|
||||||
// Hide it entirely.
|
|
||||||
if (iff != null &&
|
|
||||||
(iff.Flags & IFFFlags.Hide) != 0x0)
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
shown.Add(gUid);
|
|
||||||
var name = metaQuery.GetComponent(gUid).EntityName;
|
|
||||||
|
|
||||||
if (name == string.Empty)
|
|
||||||
name = Loc.GetString("shuttle-console-unknown");
|
|
||||||
|
|
||||||
var gridMatrix = _transform.GetWorldMatrix(gUid);
|
|
||||||
Matrix3.Multiply(in gridMatrix, in offsetMatrix, out var matty);
|
|
||||||
var color = iff?.Color ?? Color.Gold;
|
|
||||||
|
|
||||||
// Others default:
|
|
||||||
// Color.FromHex("#FFC000FF")
|
|
||||||
// Hostile default: Color.Firebrick
|
|
||||||
|
|
||||||
if (ShowIFF &&
|
|
||||||
(iff == null && IFFComponent.ShowIFFDefault ||
|
|
||||||
(iff.Flags & IFFFlags.HideLabel) == 0x0))
|
|
||||||
{
|
|
||||||
var gridBounds = grid.Comp.LocalAABB;
|
|
||||||
Label label;
|
|
||||||
|
|
||||||
if (!_iffControls.TryGetValue(gUid, out var control))
|
|
||||||
{
|
|
||||||
label = new Label()
|
|
||||||
{
|
|
||||||
HorizontalAlignment = HAlignment.Left,
|
|
||||||
};
|
|
||||||
|
|
||||||
_iffControls[gUid] = label;
|
|
||||||
AddChild(label);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
label = (Label) control;
|
|
||||||
}
|
|
||||||
|
|
||||||
label.FontColorOverride = color;
|
|
||||||
var gridCentre = matty.Transform(gridBody.LocalCenter);
|
|
||||||
gridCentre.Y = -gridCentre.Y;
|
|
||||||
var distance = gridCentre.Length();
|
|
||||||
|
|
||||||
// y-offset the control to always render below the grid (vertically)
|
|
||||||
var yOffset = Math.Max(gridBounds.Height, gridBounds.Width) * MinimapScale / 1.8f / UIScale;
|
|
||||||
|
|
||||||
// The actual position in the UI. We offset the matrix position to render it off by half its width
|
|
||||||
// plus by the offset.
|
|
||||||
var uiPosition = ScalePosition(gridCentre) / UIScale - new Vector2(label.Width / 2f, -yOffset);
|
|
||||||
|
|
||||||
// Look this is uggo so feel free to cleanup. We just need to clamp the UI position to within the viewport.
|
|
||||||
uiPosition = new Vector2(Math.Clamp(uiPosition.X, 0f, Width - label.Width),
|
|
||||||
Math.Clamp(uiPosition.Y, 10f, Height - label.Height));
|
|
||||||
|
|
||||||
label.Visible = true;
|
|
||||||
label.Text = Loc.GetString("shuttle-console-iff-label", ("name", name), ("distance", $"{distance:0.0}"));
|
|
||||||
LayoutContainer.SetPosition(label, uiPosition);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
ClearLabel(gUid);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Detailed view
|
|
||||||
DrawGrid(handle, matty, grid, color, true);
|
|
||||||
|
|
||||||
DrawDocks(handle, gUid, matty);
|
|
||||||
}
|
|
||||||
|
|
||||||
foreach (var (ent, _) in _iffControls)
|
|
||||||
{
|
|
||||||
if (shown.Contains(ent)) continue;
|
|
||||||
ClearLabel(ent);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void Clear()
|
|
||||||
{
|
|
||||||
foreach (var (_, label) in _iffControls)
|
|
||||||
{
|
|
||||||
label.Dispose();
|
|
||||||
}
|
|
||||||
|
|
||||||
_iffControls.Clear();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void ClearLabel(EntityUid uid)
|
|
||||||
{
|
|
||||||
if (!_iffControls.TryGetValue(uid, out var label)) return;
|
|
||||||
label.Dispose();
|
|
||||||
_iffControls.Remove(uid);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix)
|
|
||||||
{
|
|
||||||
if (!ShowDocks)
|
|
||||||
return;
|
|
||||||
|
|
||||||
const float DockScale = 1f;
|
|
||||||
|
|
||||||
if (_docks.TryGetValue(uid, out var docks))
|
|
||||||
{
|
|
||||||
foreach (var state in docks)
|
|
||||||
{
|
|
||||||
var position = state.Coordinates.Position;
|
|
||||||
var uiPosition = matrix.Transform(position);
|
|
||||||
|
|
||||||
if (uiPosition.Length() > WorldRange - DockScale)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var color = HighlightedDock == state.Entity ? state.HighlightedColor : state.Color;
|
|
||||||
|
|
||||||
uiPosition.Y = -uiPosition.Y;
|
|
||||||
|
|
||||||
var verts = new[]
|
|
||||||
{
|
|
||||||
matrix.Transform(position + new Vector2(-DockScale, -DockScale)),
|
|
||||||
matrix.Transform(position + new Vector2(DockScale, -DockScale)),
|
|
||||||
matrix.Transform(position + new Vector2(DockScale, DockScale)),
|
|
||||||
matrix.Transform(position + new Vector2(-DockScale, DockScale)),
|
|
||||||
};
|
|
||||||
|
|
||||||
for (var i = 0; i < verts.Length; i++)
|
|
||||||
{
|
|
||||||
var vert = verts[i];
|
|
||||||
vert.Y = -vert.Y;
|
|
||||||
verts[i] = ScalePosition(vert);
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, color.WithAlpha(0.8f));
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, verts, color);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void DrawGrid(DrawingHandleScreen handle, Matrix3 matrix, MapGridComponent grid, Color color, bool drawInterior)
|
|
||||||
{
|
|
||||||
var rator = grid.GetAllTilesEnumerator();
|
|
||||||
var edges = new ValueList<Vector2>();
|
|
||||||
|
|
||||||
while (rator.MoveNext(out var tileRef))
|
|
||||||
{
|
|
||||||
// TODO: Short-circuit interior chunk nodes
|
|
||||||
// This can be optimised a lot more if required.
|
|
||||||
Vector2? tileVec = null;
|
|
||||||
|
|
||||||
// Iterate edges and see which we can draw
|
|
||||||
for (var i = 0; i < 4; i++)
|
|
||||||
{
|
|
||||||
var dir = (DirectionFlag) Math.Pow(2, i);
|
|
||||||
var dirVec = dir.AsDir().ToIntVec();
|
|
||||||
|
|
||||||
if (!grid.GetTileRef(tileRef.Value.GridIndices + dirVec).Tile.IsEmpty)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Vector2 start;
|
|
||||||
Vector2 end;
|
|
||||||
tileVec ??= (Vector2) tileRef.Value.GridIndices * grid.TileSize;
|
|
||||||
|
|
||||||
// Draw line
|
|
||||||
// Could probably rotate this but this might be faster?
|
|
||||||
switch (dir)
|
|
||||||
{
|
|
||||||
case DirectionFlag.South:
|
|
||||||
start = tileVec.Value;
|
|
||||||
end = tileVec.Value + new Vector2(grid.TileSize, 0f);
|
|
||||||
break;
|
|
||||||
case DirectionFlag.East:
|
|
||||||
start = tileVec.Value + new Vector2(grid.TileSize, 0f);
|
|
||||||
end = tileVec.Value + new Vector2(grid.TileSize, grid.TileSize);
|
|
||||||
break;
|
|
||||||
case DirectionFlag.North:
|
|
||||||
start = tileVec.Value + new Vector2(grid.TileSize, grid.TileSize);
|
|
||||||
end = tileVec.Value + new Vector2(0f, grid.TileSize);
|
|
||||||
break;
|
|
||||||
case DirectionFlag.West:
|
|
||||||
start = tileVec.Value + new Vector2(0f, grid.TileSize);
|
|
||||||
end = tileVec.Value;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
throw new NotImplementedException();
|
|
||||||
}
|
|
||||||
|
|
||||||
var adjustedStart = matrix.Transform(start);
|
|
||||||
var adjustedEnd = matrix.Transform(end);
|
|
||||||
|
|
||||||
if (adjustedStart.Length() > ActualRadarRange || adjustedEnd.Length() > ActualRadarRange)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
start = ScalePosition(new Vector2(adjustedStart.X, -adjustedStart.Y));
|
|
||||||
end = ScalePosition(new Vector2(adjustedEnd.X, -adjustedEnd.Y));
|
|
||||||
|
|
||||||
edges.Add(start);
|
|
||||||
edges.Add(end);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, edges.Span, color);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 ScalePosition(Vector2 value)
|
|
||||||
{
|
|
||||||
return value * MinimapScale + MidpointVector;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Vector2 InverseScalePosition(Vector2 value)
|
|
||||||
{
|
|
||||||
return (value - MidpointVector) / MinimapScale;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2,109 +2,42 @@
|
|||||||
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
|
||||||
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"
|
||||||
Title="{Loc 'shuttle-console-window-title'}"
|
Title="{Loc 'shuttle-console-window-title'}"
|
||||||
SetSize="1180 648"
|
SetSize="960 762"
|
||||||
MinSize="788 320">
|
MinSize="960 762">
|
||||||
<GridContainer Columns="3"
|
<BoxContainer Orientation="Vertical">
|
||||||
HorizontalAlignment="Stretch"
|
<!-- Top row mode buttons -->
|
||||||
Margin="5 5 5 5">
|
<BoxContainer Name="ModeButtons"
|
||||||
<BoxContainer Name="LeftDisplay"
|
Orientation="Horizontal"
|
||||||
VerticalAlignment="Top"
|
HorizontalAlignment="Stretch"
|
||||||
HorizontalAlignment="Left"
|
MinSize="52 52"
|
||||||
MinWidth="256"
|
Margin="5">
|
||||||
MaxWidth="256"
|
<Button Name="NavModeButton"
|
||||||
Align="Center"
|
ToggleMode="True"
|
||||||
Orientation="Vertical">
|
HorizontalExpand="True"
|
||||||
<BoxContainer Orientation="Vertical">
|
VerticalExpand="True"
|
||||||
<controls:StripeBack>
|
Text="NAV"
|
||||||
<Label Name="DockingPortsLabel" Text="{Loc 'shuttle-console-dock-label'}" HorizontalAlignment="Center"/>
|
Margin="5"/>
|
||||||
</controls:StripeBack>
|
<Button Name="MapModeButton"
|
||||||
<BoxContainer Name="DockPorts"
|
ToggleMode="True"
|
||||||
Orientation="Vertical"/>
|
HorizontalExpand="True"
|
||||||
</BoxContainer>
|
VerticalExpand="True"
|
||||||
<BoxContainer Orientation="Vertical">
|
Text="MAP"
|
||||||
<controls:StripeBack>
|
Margin="5"/>
|
||||||
<Label Name="HyperspaceLabel" Text="{Loc 'shuttle-console-hyperspace-label'}" HorizontalAlignment="Center"/>
|
<Button Name="DockModeButton"
|
||||||
</controls:StripeBack>
|
ToggleMode="True"
|
||||||
<BoxContainer Name="HyperspaceDestinations"
|
HorizontalExpand="True"
|
||||||
Orientation="Vertical"/>
|
VerticalExpand="True"
|
||||||
</BoxContainer>
|
Text="DOCK"
|
||||||
|
Margin="5"/>
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
<PanelContainer MinSize="256 256"
|
<!-- Contents box -->
|
||||||
HorizontalAlignment = "Stretch"
|
<BoxContainer Name="Contents"
|
||||||
HorizontalExpand = "True"
|
HorizontalAlignment="Stretch"
|
||||||
VerticalExpand = "True">
|
VerticalExpand="True"
|
||||||
<ui:RadarControl Name="RadarScreen"
|
Margin="5">
|
||||||
MouseFilter="Stop"
|
<ui:NavScreen Name="NavContainer" Visible="False"/>
|
||||||
Margin="4"
|
<ui:MapScreen Name="MapContainer" Visible="False"/>
|
||||||
HorizontalExpand = "True"
|
<ui:DockingScreen Name="DockContainer" Visible="False"/>
|
||||||
VerticalExpand = "True"/>
|
|
||||||
<ui:DockingControl Name="DockingScreen"
|
|
||||||
Visible="False"
|
|
||||||
MouseFilter="Stop"
|
|
||||||
Margin="4"
|
|
||||||
HorizontalExpand = "True"
|
|
||||||
VerticalExpand = "True"/>
|
|
||||||
</PanelContainer>
|
|
||||||
<BoxContainer Name="RightDisplay"
|
|
||||||
VerticalAlignment="Top"
|
|
||||||
HorizontalAlignment="Right"
|
|
||||||
MinWidth="256"
|
|
||||||
MaxWidth="256"
|
|
||||||
Align="Center"
|
|
||||||
Orientation="Vertical">
|
|
||||||
<controls:StripeBack>
|
|
||||||
<Label Name="DisplayLabel" Text="{Loc 'shuttle-console-display-label'}" HorizontalAlignment="Center"/>
|
|
||||||
</controls:StripeBack>
|
|
||||||
<BoxContainer Name="ReadonlyDisplay">
|
|
||||||
<GridContainer Columns="2"
|
|
||||||
HorizontalAlignment="Stretch"
|
|
||||||
VerticalAlignment="Top">
|
|
||||||
<Label Text="{Loc 'shuttle-console-ftl-state'}"/>
|
|
||||||
<Label Name="FTLState"
|
|
||||||
Text="{Loc 'ftl-shuttle-console-available'}"
|
|
||||||
HorizontalAlignment="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-ftl-timer'}"/>
|
|
||||||
<Label Name="FTLTimer"
|
|
||||||
Text="0.0"
|
|
||||||
HorizontalAlignment="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-max-radar'}"/>
|
|
||||||
<Label Name="MaxRadarRange"
|
|
||||||
Text="0.0"
|
|
||||||
HorizontalAlignment="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-radar'}"/>
|
|
||||||
<Label Name="RadarRange"
|
|
||||||
Text="0.0"
|
|
||||||
HorizontalAlignment="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-position'}"/>
|
|
||||||
<Label Name="GridPosition"
|
|
||||||
Text="0.0, 0.0"
|
|
||||||
Align="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-orientation'}"/>
|
|
||||||
<Label Name="GridOrientation"
|
|
||||||
Text="0.0"
|
|
||||||
Align="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-linear-velocity'}"/>
|
|
||||||
<Label Name="GridLinearVelocity"
|
|
||||||
Text="0.0, 0.0"
|
|
||||||
Align="Right"/>
|
|
||||||
<Label Text="{Loc 'shuttle-console-angular-velocity'}"/>
|
|
||||||
<Label Name="GridAngularVelocity"
|
|
||||||
Text="0.0"
|
|
||||||
Align="Right"/>
|
|
||||||
</GridContainer>
|
|
||||||
</BoxContainer>
|
|
||||||
<Button Name="IFFToggle"
|
|
||||||
Text="{Loc 'shuttle-console-iff-toggle'}"
|
|
||||||
TextAlign="Center"
|
|
||||||
ToggleMode="True"/>
|
|
||||||
<Button Name="DockToggle"
|
|
||||||
Text="{Loc 'shuttle-console-dock-toggle'}"
|
|
||||||
TextAlign="Center"
|
|
||||||
ToggleMode="True"/>
|
|
||||||
<Button Name="UndockButton"
|
|
||||||
Text="{Loc 'shuttle-console-undock'}"
|
|
||||||
TextAlign="Center"
|
|
||||||
Disabled="True"/>
|
|
||||||
</BoxContainer>
|
</BoxContainer>
|
||||||
</GridContainer>
|
</BoxContainer>
|
||||||
</controls:FancyWindow>
|
</controls:FancyWindow>
|
||||||
|
|||||||
@@ -1,340 +1,148 @@
|
|||||||
|
using System.Numerics;
|
||||||
using Content.Client.Computer;
|
using Content.Client.Computer;
|
||||||
using Content.Client.UserInterface.Controls;
|
using Content.Client.UserInterface.Controls;
|
||||||
using Content.Shared.Shuttles.BUIStates;
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using Content.Shared.Shuttles.Components;
|
|
||||||
using Content.Shared.Shuttles.Systems;
|
|
||||||
using Robust.Client.AutoGenerated;
|
using Robust.Client.AutoGenerated;
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Client.UserInterface.Controls;
|
using Robust.Client.UserInterface.Controls;
|
||||||
using Robust.Client.UserInterface.XAML;
|
using Robust.Client.UserInterface.XAML;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics.Components;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.Utility;
|
|
||||||
|
|
||||||
namespace Content.Client.Shuttles.UI;
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
[GenerateTypedNameReferences]
|
[GenerateTypedNameReferences]
|
||||||
public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
public sealed partial class ShuttleConsoleWindow : FancyWindow,
|
||||||
IComputerWindow<ShuttleConsoleBoundInterfaceState>
|
IComputerWindow<ShuttleBoundUserInterfaceState>
|
||||||
{
|
{
|
||||||
private readonly IEntityManager _entManager;
|
[Dependency] private readonly IEntityManager _entManager = default!;
|
||||||
private readonly IGameTiming _timing;
|
|
||||||
|
|
||||||
private EntityUid? _shuttleEntity;
|
private ShuttleConsoleMode _mode = ShuttleConsoleMode.Nav;
|
||||||
|
|
||||||
/// <summary>
|
public event Action<MapCoordinates, Angle>? RequestFTL;
|
||||||
/// Currently selected dock button for camera.
|
public event Action<NetEntity, Angle>? RequestBeaconFTL;
|
||||||
/// </summary>
|
|
||||||
private BaseButton? _selectedDock;
|
|
||||||
|
|
||||||
/// <summary>
|
public event Action<NetEntity, NetEntity>? DockRequest;
|
||||||
/// Stored by grid entityid then by states
|
public event Action<NetEntity>? UndockRequest;
|
||||||
/// </summary>
|
|
||||||
private readonly Dictionary<NetEntity, List<DockingInterfaceState>> _docks = new();
|
|
||||||
|
|
||||||
private readonly Dictionary<BaseButton, NetEntity> _destinations = new();
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Next FTL state change.
|
|
||||||
/// </summary>
|
|
||||||
public TimeSpan FTLTime;
|
|
||||||
|
|
||||||
public Action<NetEntity>? UndockPressed;
|
|
||||||
public Action<NetEntity>? StartAutodockPressed;
|
|
||||||
public Action<NetEntity>? StopAutodockPressed;
|
|
||||||
public Action<NetEntity>? DestinationPressed;
|
|
||||||
|
|
||||||
public ShuttleConsoleWindow()
|
public ShuttleConsoleWindow()
|
||||||
{
|
{
|
||||||
RobustXamlLoader.Load(this);
|
RobustXamlLoader.Load(this);
|
||||||
_entManager = IoCManager.Resolve<IEntityManager>();
|
IoCManager.InjectDependencies(this);
|
||||||
_timing = IoCManager.Resolve<IGameTiming>();
|
|
||||||
|
|
||||||
WorldRangeChange(RadarScreen.WorldRange);
|
// Mode switching
|
||||||
RadarScreen.WorldRangeChanged += WorldRangeChange;
|
NavModeButton.OnPressed += NavPressed;
|
||||||
|
MapModeButton.OnPressed += MapPressed;
|
||||||
|
DockModeButton.OnPressed += DockPressed;
|
||||||
|
|
||||||
IFFToggle.OnToggled += OnIFFTogglePressed;
|
// Modes are exclusive
|
||||||
IFFToggle.Pressed = RadarScreen.ShowIFF;
|
var group = new ButtonGroup();
|
||||||
|
|
||||||
DockToggle.OnToggled += OnDockTogglePressed;
|
NavModeButton.Group = group;
|
||||||
DockToggle.Pressed = RadarScreen.ShowDocks;
|
MapModeButton.Group = group;
|
||||||
|
DockModeButton.Group = group;
|
||||||
|
|
||||||
UndockButton.OnPressed += OnUndockPressed;
|
NavModeButton.Pressed = true;
|
||||||
}
|
SetupMode(_mode);
|
||||||
|
|
||||||
private void WorldRangeChange(float value)
|
MapContainer.RequestFTL += (coords, angle) =>
|
||||||
{
|
|
||||||
RadarRange.Text = $"{value:0}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnIFFTogglePressed(BaseButton.ButtonEventArgs args)
|
|
||||||
{
|
|
||||||
RadarScreen.ShowIFF ^= true;
|
|
||||||
args.Button.Pressed = RadarScreen.ShowIFF;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDockTogglePressed(BaseButton.ButtonEventArgs args)
|
|
||||||
{
|
|
||||||
RadarScreen.ShowDocks ^= true;
|
|
||||||
args.Button.Pressed = RadarScreen.ShowDocks;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnUndockPressed(BaseButton.ButtonEventArgs args)
|
|
||||||
{
|
|
||||||
if (DockingScreen.ViewedDock == null) return;
|
|
||||||
UndockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
|
||||||
{
|
|
||||||
_shuttleEntity = coordinates?.EntityId;
|
|
||||||
RadarScreen.SetMatrix(coordinates, angle);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void UpdateState(ShuttleConsoleBoundInterfaceState scc)
|
|
||||||
{
|
|
||||||
UpdateDocks(scc.Docks);
|
|
||||||
UpdateFTL(scc.Destinations, scc.FTLState, scc.FTLTime);
|
|
||||||
RadarScreen.UpdateState(scc);
|
|
||||||
MaxRadarRange.Text = $"{scc.MaxRange:0}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void UpdateFTL(List<(NetEntity Entity, string Destination, bool Enabled)> destinations, FTLState state, TimeSpan time)
|
|
||||||
{
|
|
||||||
HyperspaceDestinations.DisposeAllChildren();
|
|
||||||
_destinations.Clear();
|
|
||||||
|
|
||||||
if (destinations.Count == 0)
|
|
||||||
{
|
{
|
||||||
HyperspaceDestinations.AddChild(new Label()
|
RequestFTL?.Invoke(coords, angle);
|
||||||
{
|
};
|
||||||
Text = Loc.GetString("shuttle-console-hyperspace-none"),
|
|
||||||
HorizontalAlignment = HAlignment.Center,
|
MapContainer.RequestBeaconFTL += (ent, angle) =>
|
||||||
});
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
{
|
||||||
destinations.Sort((x, y) => string.Compare(x.Destination, y.Destination, StringComparison.Ordinal));
|
RequestBeaconFTL?.Invoke(ent, angle);
|
||||||
|
};
|
||||||
|
|
||||||
foreach (var destination in destinations)
|
DockContainer.DockRequest += (entity, netEntity) =>
|
||||||
{
|
{
|
||||||
var button = new Button()
|
DockRequest?.Invoke(entity, netEntity);
|
||||||
{
|
};
|
||||||
Disabled = !destination.Enabled,
|
|
||||||
Text = destination.Destination,
|
|
||||||
};
|
|
||||||
|
|
||||||
_destinations[button] = destination.Entity;
|
DockContainer.UndockRequest += entity =>
|
||||||
button.OnPressed += OnHyperspacePressed;
|
{
|
||||||
HyperspaceDestinations.AddChild(button);
|
UndockRequest?.Invoke(entity);
|
||||||
}
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
private void ClearModes(ShuttleConsoleMode mode)
|
||||||
|
{
|
||||||
|
if (mode != ShuttleConsoleMode.Nav)
|
||||||
|
{
|
||||||
|
NavContainer.Visible = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
string stateText;
|
if (mode != ShuttleConsoleMode.Map)
|
||||||
|
|
||||||
switch (state)
|
|
||||||
{
|
{
|
||||||
case Shared.Shuttles.Systems.FTLState.Available:
|
MapContainer.Visible = false;
|
||||||
stateText = Loc.GetString("shuttle-console-ftl-available");
|
MapContainer.SetMap(MapId.Nullspace, Vector2.Zero);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mode != ShuttleConsoleMode.Dock)
|
||||||
|
{
|
||||||
|
DockContainer.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void NavPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
SwitchMode(ShuttleConsoleMode.Nav);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void MapPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
SwitchMode(ShuttleConsoleMode.Map);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DockPressed(BaseButton.ButtonEventArgs obj)
|
||||||
|
{
|
||||||
|
SwitchMode(ShuttleConsoleMode.Dock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetupMode(ShuttleConsoleMode mode)
|
||||||
|
{
|
||||||
|
switch (mode)
|
||||||
|
{
|
||||||
|
case ShuttleConsoleMode.Nav:
|
||||||
|
NavContainer.Visible = true;
|
||||||
break;
|
break;
|
||||||
case Shared.Shuttles.Systems.FTLState.Starting:
|
case ShuttleConsoleMode.Map:
|
||||||
stateText = Loc.GetString("shuttle-console-ftl-starting");
|
MapContainer.Visible = true;
|
||||||
|
MapContainer.Startup();
|
||||||
break;
|
break;
|
||||||
case Shared.Shuttles.Systems.FTLState.Travelling:
|
case ShuttleConsoleMode.Dock:
|
||||||
stateText = Loc.GetString("shuttle-console-ftl-travelling");
|
DockContainer.Visible = true;
|
||||||
break;
|
|
||||||
case Shared.Shuttles.Systems.FTLState.Cooldown:
|
|
||||||
stateText = Loc.GetString("shuttle-console-ftl-cooldown");
|
|
||||||
break;
|
|
||||||
case Shared.Shuttles.Systems.FTLState.Arriving:
|
|
||||||
stateText = Loc.GetString("shuttle-console-ftl-arriving");
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
throw new ArgumentOutOfRangeException(nameof(state), state, null);
|
throw new NotImplementedException();
|
||||||
}
|
|
||||||
|
|
||||||
FTLState.Text = stateText;
|
|
||||||
// Add a buffer due to lag or whatever
|
|
||||||
time += TimeSpan.FromSeconds(0.3);
|
|
||||||
FTLTime = time;
|
|
||||||
FTLTimer.Text = GetFTLText();
|
|
||||||
}
|
|
||||||
|
|
||||||
private string GetFTLText()
|
|
||||||
{
|
|
||||||
return $"{Math.Max(0, (FTLTime - _timing.CurTime).TotalSeconds):0.0}";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnHyperspacePressed(BaseButton.ButtonEventArgs obj)
|
|
||||||
{
|
|
||||||
var ent = _destinations[obj.Button];
|
|
||||||
DestinationPressed?.Invoke(ent);
|
|
||||||
}
|
|
||||||
|
|
||||||
#region Docking
|
|
||||||
|
|
||||||
private void UpdateDocks(List<DockingInterfaceState> docks)
|
|
||||||
{
|
|
||||||
// TODO: We should check for changes so any existing highlighted doesn't delete.
|
|
||||||
// We also need to make up some pseudonumber as well for these.
|
|
||||||
_docks.Clear();
|
|
||||||
|
|
||||||
foreach (var dock in docks)
|
|
||||||
{
|
|
||||||
var grid = _docks.GetOrNew(dock.Coordinates.NetEntity);
|
|
||||||
grid.Add(dock);
|
|
||||||
}
|
|
||||||
|
|
||||||
DockPorts.DisposeAllChildren();
|
|
||||||
DockingScreen.Docks = _docks;
|
|
||||||
var shuttleNetEntity = _entManager.GetNetEntity(_shuttleEntity);
|
|
||||||
|
|
||||||
if (shuttleNetEntity != null && _docks.TryGetValue(shuttleNetEntity.Value, out var gridDocks))
|
|
||||||
{
|
|
||||||
var index = 1;
|
|
||||||
|
|
||||||
foreach (var state in gridDocks)
|
|
||||||
{
|
|
||||||
var pressed = state.Entity == DockingScreen.ViewedDock;
|
|
||||||
|
|
||||||
string suffix;
|
|
||||||
|
|
||||||
if (state.Connected)
|
|
||||||
{
|
|
||||||
suffix = Loc.GetString("shuttle-console-docked", ("index", index));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
suffix = $"{index}";
|
|
||||||
}
|
|
||||||
|
|
||||||
var button = new Button()
|
|
||||||
{
|
|
||||||
Text = Loc.GetString("shuttle-console-dock-button", ("suffix", suffix)),
|
|
||||||
ToggleMode = true,
|
|
||||||
Pressed = pressed,
|
|
||||||
Margin = new Thickness(0f, 1f),
|
|
||||||
};
|
|
||||||
|
|
||||||
if (pressed)
|
|
||||||
{
|
|
||||||
_selectedDock = button;
|
|
||||||
}
|
|
||||||
|
|
||||||
button.OnMouseEntered += args => OnDockMouseEntered(args, state);
|
|
||||||
button.OnMouseExited += args => OnDockMouseExited(args, state);
|
|
||||||
button.OnToggled += args => OnDockToggled(args, state);
|
|
||||||
DockPorts.AddChild(button);
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDockMouseEntered(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
|
public void SwitchMode(ShuttleConsoleMode mode)
|
||||||
{
|
{
|
||||||
RadarScreen.HighlightedDock = state.Entity;
|
if (_mode == mode)
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDockMouseExited(GUIMouseHoverEventArgs obj, DockingInterfaceState state)
|
|
||||||
{
|
|
||||||
RadarScreen.HighlightedDock = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Shows a docking camera instead of radar screen.
|
|
||||||
/// </summary>
|
|
||||||
private void OnDockToggled(BaseButton.ButtonEventArgs obj, DockingInterfaceState state)
|
|
||||||
{
|
|
||||||
if (_selectedDock != null)
|
|
||||||
{
|
|
||||||
// If it got untoggled via other means then we'll stop viewing the old dock.
|
|
||||||
if (DockingScreen.ViewedDock != null && DockingScreen.ViewedDock != state.Entity)
|
|
||||||
{
|
|
||||||
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
_selectedDock.Pressed = false;
|
|
||||||
_selectedDock = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!obj.Button.Pressed)
|
|
||||||
{
|
|
||||||
if (DockingScreen.ViewedDock != null)
|
|
||||||
{
|
|
||||||
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
|
||||||
DockingScreen.ViewedDock = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
UndockButton.Disabled = true;
|
|
||||||
DockingScreen.Visible = false;
|
|
||||||
RadarScreen.Visible = true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
if (_shuttleEntity != null)
|
|
||||||
{
|
|
||||||
DockingScreen.Coordinates = _entManager.GetCoordinates(state.Coordinates);
|
|
||||||
DockingScreen.Angle = state.Angle;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DockingScreen.Coordinates = null;
|
|
||||||
DockingScreen.Angle = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
UndockButton.Disabled = false;
|
|
||||||
RadarScreen.Visible = false;
|
|
||||||
DockingScreen.Visible = true;
|
|
||||||
DockingScreen.ViewedDock = state.Entity;
|
|
||||||
StartAutodockPressed?.Invoke(state.Entity);
|
|
||||||
DockingScreen.GridEntity = _shuttleEntity;
|
|
||||||
_selectedDock = obj.Button;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public override void Close()
|
|
||||||
{
|
|
||||||
base.Close();
|
|
||||||
if (DockingScreen.ViewedDock != null)
|
|
||||||
{
|
|
||||||
StopAutodockPressed?.Invoke(DockingScreen.ViewedDock.Value);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#endregion
|
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
|
||||||
{
|
|
||||||
base.Draw(handle);
|
|
||||||
|
|
||||||
if (!_entManager.TryGetComponent<PhysicsComponent>(_shuttleEntity, out var gridBody) ||
|
|
||||||
!_entManager.TryGetComponent<TransformComponent>(_shuttleEntity, out var gridXform))
|
|
||||||
{
|
|
||||||
return;
|
return;
|
||||||
}
|
|
||||||
|
|
||||||
if (_entManager.TryGetComponent<MetaDataComponent>(_shuttleEntity, out var metadata) && metadata.EntityPaused)
|
_mode = mode;
|
||||||
{
|
ClearModes(mode);
|
||||||
FTLTime += _timing.FrameTime;
|
SetupMode(_mode);
|
||||||
}
|
}
|
||||||
|
|
||||||
FTLTimer.Text = GetFTLText();
|
public enum ShuttleConsoleMode : byte
|
||||||
|
{
|
||||||
|
Nav,
|
||||||
|
Map,
|
||||||
|
Dock,
|
||||||
|
}
|
||||||
|
|
||||||
var (_, worldRot, worldMatrix) = gridXform.GetWorldPositionRotationMatrix();
|
public void UpdateState(EntityUid owner, ShuttleBoundUserInterfaceState cState)
|
||||||
var worldPos = worldMatrix.Transform(gridBody.LocalCenter);
|
{
|
||||||
|
var coordinates = _entManager.GetCoordinates(cState.NavState.Coordinates);
|
||||||
|
NavContainer.SetShuttle(coordinates?.EntityId);
|
||||||
|
MapContainer.SetShuttle(coordinates?.EntityId);
|
||||||
|
MapContainer.SetConsole(owner);
|
||||||
|
|
||||||
// Get the positive reduced angle.
|
NavContainer.UpdateState(cState.NavState);
|
||||||
var displayRot = -worldRot.Reduced();
|
MapContainer.UpdateState(cState.MapState);
|
||||||
|
DockContainer.UpdateState(coordinates?.EntityId, cState.DockState);
|
||||||
GridPosition.Text = $"{worldPos.X:0.0}, {worldPos.Y:0.0}";
|
|
||||||
GridOrientation.Text = $"{displayRot.Degrees:0.0}";
|
|
||||||
|
|
||||||
var gridVelocity = gridBody.LinearVelocity;
|
|
||||||
gridVelocity = displayRot.RotateVec(gridVelocity);
|
|
||||||
// Get linear velocity relative to the console entity
|
|
||||||
GridLinearVelocity.Text = $"{gridVelocity.X:0.0}, {gridVelocity.Y:0.0}";
|
|
||||||
GridAngularVelocity.Text = $"{-gridBody.AngularVelocity:0.0}";
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
1
Content.Client/Shuttles/UI/ShuttleDockControl.xaml
Normal file
1
Content.Client/Shuttles/UI/ShuttleDockControl.xaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<ui:BaseShuttleControl xmlns:ui="clr-namespace:Content.Client.Shuttles.UI"/>
|
||||||
458
Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs
Normal file
458
Content.Client/Shuttles/UI/ShuttleDockControl.xaml.cs
Normal file
@@ -0,0 +1,458 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Shuttles.Systems;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ShuttleDockControl : BaseShuttleControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
private readonly DockingSystem _dockSystem;
|
||||||
|
private readonly SharedShuttleSystem _shuttles;
|
||||||
|
private readonly SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
public NetEntity? HighlightedDock;
|
||||||
|
|
||||||
|
public NetEntity? ViewedDock => _viewedState?.Entity;
|
||||||
|
private DockingPortState? _viewedState;
|
||||||
|
|
||||||
|
public EntityUid? GridEntity;
|
||||||
|
|
||||||
|
private EntityCoordinates? _coordinates;
|
||||||
|
private Angle? _angle;
|
||||||
|
|
||||||
|
public DockingInterfaceState? DockState = null;
|
||||||
|
|
||||||
|
private List<Entity<MapGridComponent>> _grids = new();
|
||||||
|
|
||||||
|
private readonly HashSet<DockingPortState> _drawnDocks = new();
|
||||||
|
private readonly Dictionary<DockingPortState, Button> _dockButtons = new();
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Store buttons for every other dock
|
||||||
|
/// </summary>
|
||||||
|
private readonly Dictionary<DockingPortState, Control> _dockContainers = new();
|
||||||
|
|
||||||
|
private static readonly TimeSpan DockChangeCooldown = TimeSpan.FromSeconds(0.5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Rate-limiting for docking changes
|
||||||
|
/// </summary>
|
||||||
|
private TimeSpan _nextDockChange;
|
||||||
|
|
||||||
|
public event Action<NetEntity>? OnViewDock;
|
||||||
|
public event Action<NetEntity, NetEntity>? DockRequest;
|
||||||
|
public event Action<NetEntity>? UndockRequest;
|
||||||
|
|
||||||
|
public ShuttleDockControl() : base(2f, 32f, 8f)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_dockSystem = EntManager.System<DockingSystem>();
|
||||||
|
_shuttles = EntManager.System<SharedShuttleSystem>();
|
||||||
|
_xformSystem = EntManager.System<SharedTransformSystem>();
|
||||||
|
MinSize = new Vector2(SizeFull, SizeFull);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetViewedDock(DockingPortState? dockState)
|
||||||
|
{
|
||||||
|
_viewedState = dockState;
|
||||||
|
|
||||||
|
if (dockState != null)
|
||||||
|
{
|
||||||
|
_coordinates = EntManager.GetCoordinates(dockState.Coordinates);
|
||||||
|
_angle = dockState.Angle;
|
||||||
|
OnViewDock?.Invoke(dockState.Entity);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
_coordinates = null;
|
||||||
|
_angle = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void FrameUpdate(FrameEventArgs args)
|
||||||
|
{
|
||||||
|
base.FrameUpdate(args);
|
||||||
|
HideDocks();
|
||||||
|
_drawnDocks.Clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
DrawBacking(handle);
|
||||||
|
|
||||||
|
if (_coordinates == null ||
|
||||||
|
_angle == null ||
|
||||||
|
DockState == null ||
|
||||||
|
!EntManager.TryGetComponent<TransformComponent>(GridEntity, out var gridXform))
|
||||||
|
{
|
||||||
|
DrawNoSignal(handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawCircles(handle);
|
||||||
|
var gridNent = EntManager.GetNetEntity(GridEntity);
|
||||||
|
var mapPos = _xformSystem.ToMapCoordinates(_coordinates.Value);
|
||||||
|
var ourGridMatrix = _xformSystem.GetWorldMatrix(gridXform.Owner);
|
||||||
|
var dockMatrix = Matrix3.CreateTransform(_coordinates.Value.Position, Angle.Zero);
|
||||||
|
Matrix3.Multiply(dockMatrix, ourGridMatrix, out var offsetMatrix);
|
||||||
|
|
||||||
|
offsetMatrix = offsetMatrix.Invert();
|
||||||
|
|
||||||
|
// Draw nearby grids
|
||||||
|
var controlBounds = SizeBox.Scale(1.25f);
|
||||||
|
_grids.Clear();
|
||||||
|
_mapManager.FindGridsIntersecting(gridXform.MapID, new Box2(mapPos.Position - WorldRangeVector, mapPos.Position + WorldRangeVector), ref _grids);
|
||||||
|
|
||||||
|
// offset the dotted-line position to the bounds.
|
||||||
|
Vector2? viewedDockPos = _viewedState != null ? MidPointVector : null;
|
||||||
|
|
||||||
|
if (viewedDockPos != null)
|
||||||
|
{
|
||||||
|
viewedDockPos = viewedDockPos.Value + _angle.Value.RotateVec(new Vector2(0f,-0.6f) * MinimapScale);
|
||||||
|
}
|
||||||
|
|
||||||
|
var canDockChange = _timing.CurTime > _nextDockChange;
|
||||||
|
var lineOffset = (float) _timing.RealTime.TotalSeconds * 30f;
|
||||||
|
|
||||||
|
foreach (var grid in _grids)
|
||||||
|
{
|
||||||
|
EntManager.TryGetComponent(grid.Owner, out IFFComponent? iffComp);
|
||||||
|
|
||||||
|
if (grid.Owner != GridEntity && !_shuttles.CanDraw(grid.Owner, iffComp: iffComp))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var gridMatrix = _xformSystem.GetWorldMatrix(grid.Owner);
|
||||||
|
Matrix3.Multiply(in gridMatrix, in offsetMatrix, out var matty);
|
||||||
|
var color = _shuttles.GetIFFColor(grid.Owner, grid.Owner == GridEntity, component: iffComp);
|
||||||
|
|
||||||
|
DrawGrid(handle, matty, grid, color);
|
||||||
|
|
||||||
|
// Draw any docks on that grid
|
||||||
|
if (!DockState.Docks.TryGetValue(EntManager.GetNetEntity(grid), out var gridDocks))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
foreach (var dock in gridDocks)
|
||||||
|
{
|
||||||
|
if (ViewedDock == dock.Entity)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var position = matty.Transform(dock.Coordinates.Position);
|
||||||
|
|
||||||
|
var otherDockRotation = Matrix3.CreateRotation(dock.Angle);
|
||||||
|
var scaledPos = ScalePosition(position with {Y = -position.Y});
|
||||||
|
|
||||||
|
if (!controlBounds.Contains(scaledPos.Floored()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Draw the dock's collision
|
||||||
|
var collisionBL = matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(-0.2f, -0.7f)));
|
||||||
|
var collisionBR = matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(0.2f, -0.7f)));
|
||||||
|
var collisionTR = matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(0.2f, -0.5f)));
|
||||||
|
var collisionTL = matty.Transform(dock.Coordinates.Position +
|
||||||
|
otherDockRotation.Transform(new Vector2(-0.2f, -0.5f)));
|
||||||
|
|
||||||
|
var verts = new[]
|
||||||
|
{
|
||||||
|
collisionBL,
|
||||||
|
collisionBR,
|
||||||
|
collisionBR,
|
||||||
|
collisionTR,
|
||||||
|
collisionTR,
|
||||||
|
collisionTL,
|
||||||
|
collisionTL,
|
||||||
|
collisionBL,
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
var collisionCenter = verts[0] + verts[1] + verts[3] + verts[5];
|
||||||
|
|
||||||
|
var otherDockConnection = Color.ToSrgb(Color.Pink);
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, otherDockConnection.WithAlpha(0.2f));
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockConnection);
|
||||||
|
|
||||||
|
// Draw the dock itself
|
||||||
|
var dockBL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, -0.5f));
|
||||||
|
var dockBR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, -0.5f));
|
||||||
|
var dockTR = matty.Transform(dock.Coordinates.Position + new Vector2(0.5f, 0.5f));
|
||||||
|
var dockTL = matty.Transform(dock.Coordinates.Position + new Vector2(-0.5f, 0.5f));
|
||||||
|
|
||||||
|
verts = new[]
|
||||||
|
{
|
||||||
|
dockBL,
|
||||||
|
dockBR,
|
||||||
|
dockBR,
|
||||||
|
dockTR,
|
||||||
|
dockTR,
|
||||||
|
dockTL,
|
||||||
|
dockTL,
|
||||||
|
dockBL
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
Color otherDockColor;
|
||||||
|
|
||||||
|
if (HighlightedDock == dock.Entity)
|
||||||
|
{
|
||||||
|
otherDockColor = Color.ToSrgb(Color.Magenta);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
otherDockColor = Color.ToSrgb(Color.Purple);
|
||||||
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Can draw in these conditions:
|
||||||
|
* 1. Same grid
|
||||||
|
* 2. It's in range
|
||||||
|
*
|
||||||
|
* We don't want to draw stuff far away that's docked because it will just overlap our buttons
|
||||||
|
*/
|
||||||
|
|
||||||
|
var canDraw = grid.Owner == GridEntity;
|
||||||
|
_dockButtons.TryGetValue(dock, out var dockButton);
|
||||||
|
|
||||||
|
// Rate limit
|
||||||
|
if (dockButton != null && dock.GridDockedWith != null)
|
||||||
|
{
|
||||||
|
dockButton.Disabled = !canDockChange;
|
||||||
|
}
|
||||||
|
|
||||||
|
// If the dock is in range then also do highlighting
|
||||||
|
if (viewedDockPos != null && dock.Coordinates.NetEntity != gridNent)
|
||||||
|
{
|
||||||
|
collisionCenter /= 4;
|
||||||
|
var range = viewedDockPos.Value - collisionCenter;
|
||||||
|
|
||||||
|
if (range.Length() < SharedDockingSystem.DockingHiglightRange * MinimapScale)
|
||||||
|
{
|
||||||
|
if (_viewedState?.GridDockedWith == null)
|
||||||
|
{
|
||||||
|
var coordsOne = EntManager.GetCoordinates(_viewedState!.Coordinates);
|
||||||
|
var coordsTwo = EntManager.GetCoordinates(dock.Coordinates);
|
||||||
|
var mapOne = _xformSystem.ToMapCoordinates(coordsOne);
|
||||||
|
var mapTwo = _xformSystem.ToMapCoordinates(coordsTwo);
|
||||||
|
|
||||||
|
var rotA = _xformSystem.GetWorldRotation(coordsOne.EntityId) + _viewedState!.Angle;
|
||||||
|
var rotB = _xformSystem.GetWorldRotation(coordsTwo.EntityId) + dock.Angle;
|
||||||
|
|
||||||
|
var distance = (mapOne.Position - mapTwo.Position).Length();
|
||||||
|
|
||||||
|
var inAlignment = _dockSystem.InAlignment(mapOne, rotA, mapTwo, rotB);
|
||||||
|
var canDock = distance < SharedDockingSystem.DockRange && inAlignment;
|
||||||
|
|
||||||
|
if (dockButton != null)
|
||||||
|
dockButton.Disabled = !canDock || !canDockChange;
|
||||||
|
|
||||||
|
var lineColor = inAlignment ? Color.Lime : Color.Red;
|
||||||
|
handle.DrawDottedLine(viewedDockPos.Value, collisionCenter, lineColor, offset: lineOffset);
|
||||||
|
}
|
||||||
|
|
||||||
|
canDraw = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dockButton != null)
|
||||||
|
dockButton.Disabled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, otherDockColor.WithAlpha(0.2f));
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, verts, otherDockColor);
|
||||||
|
|
||||||
|
// Position the dock control above it
|
||||||
|
var container = _dockContainers[dock];
|
||||||
|
container.Visible = canDraw;
|
||||||
|
|
||||||
|
if (canDraw)
|
||||||
|
{
|
||||||
|
// Because it's being layed out top-down we have to arrange for first frame.
|
||||||
|
container.Arrange(PixelRect);
|
||||||
|
var containerPos = scaledPos - container.DesiredSize / 2 - new Vector2(0f, 0.75f) * MinimapScale;
|
||||||
|
SetPosition(container, containerPos);
|
||||||
|
}
|
||||||
|
|
||||||
|
_drawnDocks.Add(dock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the dock's collision
|
||||||
|
var invertedPosition = Vector2.Zero;
|
||||||
|
invertedPosition.Y = -invertedPosition.Y;
|
||||||
|
var rotation = Matrix3.CreateRotation(-_angle.Value + MathF.PI);
|
||||||
|
var ourDockConnection = new UIBox2(
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(-0.2f, -0.7f))),
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(0.2f, -0.5f))));
|
||||||
|
|
||||||
|
var ourDock = new UIBox2(
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(-0.5f, 0.5f))),
|
||||||
|
ScalePosition(rotation.Transform(new Vector2(0.5f, -0.5f))));
|
||||||
|
|
||||||
|
var dockColor = Color.Magenta;
|
||||||
|
var connectionColor = Color.Pink;
|
||||||
|
|
||||||
|
handle.DrawRect(ourDockConnection, connectionColor.WithAlpha(0.2f));
|
||||||
|
handle.DrawRect(ourDockConnection, connectionColor, filled: false);
|
||||||
|
|
||||||
|
// Draw the dock itself
|
||||||
|
handle.DrawRect(ourDock, dockColor.WithAlpha(0.2f));
|
||||||
|
handle.DrawRect(ourDock, dockColor, filled: false);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void HideDocks()
|
||||||
|
{
|
||||||
|
foreach (var (dock, control) in _dockContainers)
|
||||||
|
{
|
||||||
|
if (_drawnDocks.Contains(dock))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
control.Visible = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void BuildDocks(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
var viewedEnt = ViewedDock;
|
||||||
|
_viewedState = null;
|
||||||
|
|
||||||
|
foreach (var btn in _dockButtons.Values)
|
||||||
|
{
|
||||||
|
btn.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var container in _dockContainers.Values)
|
||||||
|
{
|
||||||
|
container.Dispose();
|
||||||
|
}
|
||||||
|
|
||||||
|
_dockButtons.Clear();
|
||||||
|
_dockContainers.Clear();
|
||||||
|
|
||||||
|
if (DockState == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var gridNent = EntManager.GetNetEntity(GridEntity);
|
||||||
|
|
||||||
|
foreach (var (otherShuttle, docks) in DockState.Docks)
|
||||||
|
{
|
||||||
|
// If it's our shuttle we add a view button
|
||||||
|
|
||||||
|
foreach (var dock in docks)
|
||||||
|
{
|
||||||
|
if (dock.Entity == viewedEnt)
|
||||||
|
{
|
||||||
|
_viewedState = dock;
|
||||||
|
}
|
||||||
|
|
||||||
|
var container = new BoxContainer()
|
||||||
|
{
|
||||||
|
Orientation = BoxContainer.LayoutOrientation.Vertical,
|
||||||
|
Margin = new Thickness(3),
|
||||||
|
};
|
||||||
|
|
||||||
|
var panel = new PanelContainer()
|
||||||
|
{
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
VerticalAlignment = VAlignment.Center,
|
||||||
|
PanelOverride = new StyleBoxFlat(new Color(30, 30, 34, 200)),
|
||||||
|
Children =
|
||||||
|
{
|
||||||
|
container,
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Button button;
|
||||||
|
|
||||||
|
if (otherShuttle == gridNent)
|
||||||
|
{
|
||||||
|
button = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("shuttle-console-view"),
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnPressed += args =>
|
||||||
|
{
|
||||||
|
SetViewedDock(dock);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (dock.Connected)
|
||||||
|
{
|
||||||
|
button = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("shuttle-console-undock"),
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnPressed += args =>
|
||||||
|
{
|
||||||
|
_nextDockChange = _timing.CurTime + DockChangeCooldown;
|
||||||
|
UndockRequest?.Invoke(dock.Entity);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
button = new Button()
|
||||||
|
{
|
||||||
|
Text = Loc.GetString("shuttle-console-dock"),
|
||||||
|
Disabled = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
button.OnPressed += args =>
|
||||||
|
{
|
||||||
|
if (ViewedDock == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
_nextDockChange = _timing.CurTime + DockChangeCooldown;
|
||||||
|
DockRequest?.Invoke(ViewedDock.Value, dock.Entity);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
_dockButtons.Add(dock, button);
|
||||||
|
}
|
||||||
|
|
||||||
|
container.AddChild(new Label()
|
||||||
|
{
|
||||||
|
Text = dock.Name,
|
||||||
|
HorizontalAlignment = HAlignment.Center,
|
||||||
|
});
|
||||||
|
|
||||||
|
button.HorizontalAlignment = HAlignment.Center;
|
||||||
|
container.AddChild(button);
|
||||||
|
|
||||||
|
AddChild(panel);
|
||||||
|
panel.Measure(Vector2Helpers.Infinity);
|
||||||
|
_dockContainers[dock] = panel;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Content.Client/Shuttles/UI/ShuttleMapControl.xaml
Normal file
1
Content.Client/Shuttles/UI/ShuttleMapControl.xaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<ui:ShuttleMapControl xmlns:ui="clr-namespace:Content.Client.Shuttles.UI" />
|
||||||
609
Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
Normal file
609
Content.Client/Shuttles/UI/ShuttleMapControl.xaml.cs
Normal file
@@ -0,0 +1,609 @@
|
|||||||
|
using System.Buffers;
|
||||||
|
using System.Numerics;
|
||||||
|
using Content.Client.Shuttles.Systems;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.Input;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ShuttleMapControl : BaseShuttleControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IGameTiming _timing = default!;
|
||||||
|
[Dependency] private readonly IInputManager _inputs = default!;
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
private readonly ShuttleSystem _shuttles;
|
||||||
|
private readonly SharedTransformSystem _xformSystem;
|
||||||
|
|
||||||
|
protected override bool Draggable => true;
|
||||||
|
|
||||||
|
public bool ShowBeacons = true;
|
||||||
|
public MapId ViewingMap = MapId.Nullspace;
|
||||||
|
|
||||||
|
private EntityUid? _shuttleEntity;
|
||||||
|
|
||||||
|
private readonly Font _font;
|
||||||
|
|
||||||
|
private readonly EntityQuery<PhysicsComponent> _physicsQuery;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Toggles FTL mode on. This shows a pre-vis for FTLing a grid.
|
||||||
|
/// </summary>
|
||||||
|
public bool FtlMode;
|
||||||
|
|
||||||
|
private Angle _ftlAngle;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Are we currently in FTL.
|
||||||
|
/// </summary>
|
||||||
|
public bool InFtl;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised when a request to FTL to a particular spot is raised.
|
||||||
|
/// </summary>
|
||||||
|
public event Action<MapCoordinates, Angle>? RequestFTL;
|
||||||
|
|
||||||
|
public event Action<NetEntity, Angle>? RequestBeaconFTL;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Set every draw to determine the beacons that are clickable for mouse events
|
||||||
|
/// </summary>
|
||||||
|
private List<IMapObject> _beacons = new();
|
||||||
|
|
||||||
|
// Per frame data to avoid re-allocating
|
||||||
|
private readonly List<IMapObject> _mapObjects = new();
|
||||||
|
private readonly Dictionary<Color, List<Vector2>> _verts = new();
|
||||||
|
private readonly Dictionary<Color, List<Vector2>> _edges = new();
|
||||||
|
private readonly Dictionary<Color, List<(Vector2, string)>> _strings = new();
|
||||||
|
private readonly List<ShuttleExclusionObject> _viewportExclusions = new();
|
||||||
|
|
||||||
|
public ShuttleMapControl() : base(256f, 512f, 512f)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_shuttles = EntManager.System<ShuttleSystem>();
|
||||||
|
_xformSystem = EntManager.System<SharedTransformSystem>();
|
||||||
|
var cache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
|
||||||
|
_physicsQuery = EntManager.GetEntityQuery<PhysicsComponent>();
|
||||||
|
|
||||||
|
_font = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 10);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMap(MapId mapId, Vector2 offset, bool recentering = false)
|
||||||
|
{
|
||||||
|
ViewingMap = mapId;
|
||||||
|
TargetOffset = offset;
|
||||||
|
Recentering = recentering;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetShuttle(EntityUid? entity)
|
||||||
|
{
|
||||||
|
_shuttleEntity = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseMove(GUIMouseMoveEventArgs args)
|
||||||
|
{
|
||||||
|
// No move for you.
|
||||||
|
if (FtlMode)
|
||||||
|
return;
|
||||||
|
|
||||||
|
base.MouseMove(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
if (FtlMode && ViewingMap != MapId.Nullspace)
|
||||||
|
{
|
||||||
|
if (args.Function == EngineKeyFunctions.UIClick)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(ViewingMap);
|
||||||
|
|
||||||
|
var beaconsOnly = EntManager.TryGetComponent(mapUid, out FTLDestinationComponent? destComp) &&
|
||||||
|
destComp.BeaconsOnly;
|
||||||
|
|
||||||
|
var mapTransform = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
|
||||||
|
|
||||||
|
if (beaconsOnly && TryGetBeacon(_beacons, mapTransform, args.RelativePosition, PixelRect, out var foundBeacon, out _))
|
||||||
|
{
|
||||||
|
RequestBeaconFTL?.Invoke(foundBeacon.Entity, _ftlAngle);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// We'll send the "adjusted" position and server will adjust it back when relevant.
|
||||||
|
var mapCoords = new MapCoordinates(InverseMapPosition(args.RelativePosition), ViewingMap);
|
||||||
|
RequestFTL?.Invoke(mapCoords, _ftlAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
base.KeyBindUp(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||||
|
{
|
||||||
|
// Scroll handles FTL rotation if you're in FTL mode.
|
||||||
|
if (FtlMode)
|
||||||
|
{
|
||||||
|
_ftlAngle += Angle.FromDegrees(15f) * args.Delta.Y;
|
||||||
|
_ftlAngle = _ftlAngle.Reduced();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
base.MouseWheel(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawParallax(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
if (!EntManager.TryGetComponent(_shuttleEntity, out TransformComponent? shuttleXform) || shuttleXform.MapUid == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
// TODO: Figure out how the fuck to make this common between the 3 slightly different parallax methods and move to parallaxsystem.
|
||||||
|
// Draw background texture
|
||||||
|
var tex = _shuttles.GetTexture(shuttleXform.MapUid.Value);
|
||||||
|
|
||||||
|
// Size of the texture in world units.
|
||||||
|
var size = tex.Size * MinimapScale * 1f;
|
||||||
|
|
||||||
|
var position = ScalePosition(new Vector2(-Offset.X, Offset.Y));
|
||||||
|
var slowness = 1f;
|
||||||
|
|
||||||
|
// The "home" position is the effective origin of this layer.
|
||||||
|
// Parallax shifting is relative to the home, and shifts away from the home and towards the Eye centre.
|
||||||
|
// The effects of this are such that a slowness of 1 anchors the layer to the centre of the screen, while a slowness of 0 anchors the layer to the world.
|
||||||
|
// (For values 0.0 to 1.0 this is in effect a lerp, but it's deliberately unclamped.)
|
||||||
|
// The ParallaxAnchor adapts the parallax for station positioning and possibly map-specific tweaks.
|
||||||
|
var home = Vector2.Zero;
|
||||||
|
var scrolled = Vector2.Zero;
|
||||||
|
|
||||||
|
// Origin - start with the parallax shift itself.
|
||||||
|
var originBL = (position - home) * slowness + scrolled;
|
||||||
|
|
||||||
|
// Place at the home.
|
||||||
|
originBL += home;
|
||||||
|
|
||||||
|
// Centre the image.
|
||||||
|
originBL -= size / 2;
|
||||||
|
|
||||||
|
// Remove offset so we can floor.
|
||||||
|
var botLeft = new Vector2(0f, 0f);
|
||||||
|
var topRight = botLeft + Size;
|
||||||
|
|
||||||
|
var flooredBL = botLeft - originBL;
|
||||||
|
|
||||||
|
// Floor to background size.
|
||||||
|
flooredBL = (flooredBL / size).Floored() * size;
|
||||||
|
|
||||||
|
// Re-offset.
|
||||||
|
flooredBL += originBL;
|
||||||
|
|
||||||
|
for (var x = flooredBL.X; x < topRight.X; x += size.X)
|
||||||
|
{
|
||||||
|
for (var y = flooredBL.Y; y < topRight.Y; y += size.Y)
|
||||||
|
{
|
||||||
|
handle.DrawTextureRect(tex, new UIBox2(x, y, x + size.X, y + size.Y));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the map objects that intersect the viewport.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="mapObjects"></param>
|
||||||
|
/// <returns></returns>
|
||||||
|
private List<IMapObject> GetViewportMapObjects(Matrix3 matty, List<IMapObject> mapObjects)
|
||||||
|
{
|
||||||
|
var results = new List<IMapObject>();
|
||||||
|
var viewBox = SizeBox.Scale(1.2f);
|
||||||
|
|
||||||
|
foreach (var mapObj in mapObjects)
|
||||||
|
{
|
||||||
|
var mapCoords = _shuttles.GetMapCoordinates(mapObj);
|
||||||
|
|
||||||
|
var relativePos = matty.Transform(mapCoords.Position);
|
||||||
|
relativePos = relativePos with { Y = -relativePos.Y };
|
||||||
|
var uiPosition = ScalePosition(relativePos);
|
||||||
|
|
||||||
|
if (!viewBox.Contains(uiPosition.Floored()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
results.Add(mapObj);
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
if (ViewingMap == MapId.Nullspace)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var mapObjects = _mapObjects;
|
||||||
|
DrawRecenter();
|
||||||
|
|
||||||
|
if (InFtl || mapObjects.Count == 0)
|
||||||
|
{
|
||||||
|
DrawBacking(handle);
|
||||||
|
DrawNoSignal(handle);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DrawParallax(handle);
|
||||||
|
|
||||||
|
var viewedMapUid = _mapManager.GetMapEntityId(ViewingMap);
|
||||||
|
var matty = Matrix3.CreateInverseTransform(Offset, Angle.Zero);
|
||||||
|
var realTime = _timing.RealTime;
|
||||||
|
var viewBox = new Box2(Offset - WorldRangeVector, Offset + WorldRangeVector);
|
||||||
|
var viewportObjects = GetViewportMapObjects(matty, mapObjects);
|
||||||
|
_viewportExclusions.Clear();
|
||||||
|
|
||||||
|
// Draw our FTL range + no FTL zones
|
||||||
|
// Do it up here because we want this layered below most things.
|
||||||
|
if (FtlMode)
|
||||||
|
{
|
||||||
|
if (EntManager.TryGetComponent<TransformComponent>(_shuttleEntity, out var shuttleXform))
|
||||||
|
{
|
||||||
|
var gridUid = _shuttleEntity.Value;
|
||||||
|
var gridPhysics = _physicsQuery.GetComponent(gridUid);
|
||||||
|
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(shuttleXform);
|
||||||
|
gridPos = Maps.GetGridPosition((gridUid, gridPhysics), gridPos, gridRot);
|
||||||
|
|
||||||
|
var gridRelativePos = matty.Transform(gridPos);
|
||||||
|
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||||
|
var gridUiPos = ScalePosition(gridRelativePos);
|
||||||
|
|
||||||
|
var range = _shuttles.GetFTLRange(gridUid);
|
||||||
|
range *= MinimapScale;
|
||||||
|
handle.DrawCircle(gridUiPos, range, Color.Gold, filled: false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var exclusionColor = Color.Red;
|
||||||
|
|
||||||
|
// Exclusions need a bumped range so we check all the ones on the map.
|
||||||
|
foreach (var mapObj in mapObjects)
|
||||||
|
{
|
||||||
|
if (mapObj is not ShuttleExclusionObject exclusion)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Check if it even intersects the viewport.
|
||||||
|
var coords = EntManager.GetCoordinates(exclusion.Coordinates);
|
||||||
|
var mapCoords = _xformSystem.ToMapCoordinates(coords);
|
||||||
|
var enlargedBounds = viewBox.Enlarged(exclusion.Range);
|
||||||
|
|
||||||
|
if (mapCoords.MapId != ViewingMap ||
|
||||||
|
!enlargedBounds.Contains(mapCoords.Position))
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var adjustedPos = matty.Transform(mapCoords.Position);
|
||||||
|
var localPos = ScalePosition(adjustedPos with { Y = -adjustedPos.Y});
|
||||||
|
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor.WithAlpha(0.05f));
|
||||||
|
handle.DrawCircle(localPos, exclusion.Range * MinimapScale, exclusionColor, filled: false);
|
||||||
|
|
||||||
|
_viewportExclusions.Add(exclusion);
|
||||||
|
}
|
||||||
|
|
||||||
|
_verts.Clear();
|
||||||
|
_edges.Clear();
|
||||||
|
_strings.Clear();
|
||||||
|
|
||||||
|
// Add beacons if relevant.
|
||||||
|
var beaconsOnly = _shuttles.IsBeaconMap(viewedMapUid);
|
||||||
|
var controlLocalBounds = PixelRect;
|
||||||
|
_beacons.Clear();
|
||||||
|
|
||||||
|
if (ShowBeacons)
|
||||||
|
{
|
||||||
|
var beaconColor = Color.AliceBlue;
|
||||||
|
|
||||||
|
foreach (var (beaconName, coords, mapO) in GetBeacons(viewportObjects, matty, controlLocalBounds))
|
||||||
|
{
|
||||||
|
var localPos = matty.Transform(coords.Position);
|
||||||
|
localPos = localPos with { Y = -localPos.Y };
|
||||||
|
var beaconUiPos = ScalePosition(localPos);
|
||||||
|
var mapObject = GetMapObject(localPos, Angle.Zero, scale: 0.75f, scalePosition: true);
|
||||||
|
|
||||||
|
var existingVerts = _verts.GetOrNew(beaconColor);
|
||||||
|
var existingEdges = _edges.GetOrNew(beaconColor);
|
||||||
|
|
||||||
|
AddMapObject(existingEdges, existingVerts, mapObject);
|
||||||
|
_beacons.Add(mapO);
|
||||||
|
|
||||||
|
var existingStrings = _strings.GetOrNew(beaconColor);
|
||||||
|
existingStrings.Add((beaconUiPos, beaconName));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var mapObj in viewportObjects)
|
||||||
|
{
|
||||||
|
if (mapObj is not GridMapObject gridObj || !EntManager.TryGetComponent(gridObj.Entity, out MapGridComponent? mapGrid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
Entity<MapGridComponent> grid = (gridObj.Entity, mapGrid);
|
||||||
|
IFFComponent? iffComp = null;
|
||||||
|
|
||||||
|
// Rudimentary IFF for now, if IFF hiding on then we don't show on the map at all
|
||||||
|
if (grid.Owner != _shuttleEntity &&
|
||||||
|
EntManager.TryGetComponent(grid, out iffComp) &&
|
||||||
|
(iffComp.Flags & (IFFFlags.Hide | IFFFlags.HideLabel)) != 0x0)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
var gridColor = _shuttles.GetIFFColor(grid, self: _shuttleEntity == grid.Owner, component: iffComp);
|
||||||
|
|
||||||
|
var existingVerts = _verts.GetOrNew(gridColor);
|
||||||
|
var existingEdges = _edges.GetOrNew(gridColor);
|
||||||
|
|
||||||
|
var gridPhysics = _physicsQuery.GetComponent(grid.Owner);
|
||||||
|
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(grid.Owner);
|
||||||
|
gridPos = Maps.GetGridPosition((grid, gridPhysics), gridPos, gridRot);
|
||||||
|
|
||||||
|
var gridRelativePos = matty.Transform(gridPos);
|
||||||
|
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||||
|
var gridUiPos = ScalePosition(gridRelativePos);
|
||||||
|
|
||||||
|
var mapObject = GetMapObject(gridRelativePos, Angle.Zero, scalePosition: true);
|
||||||
|
AddMapObject(existingEdges, existingVerts, mapObject);
|
||||||
|
|
||||||
|
// Text
|
||||||
|
// Force drawing it at this point.
|
||||||
|
var iffText = _shuttles.GetIFFLabel(grid, self: true, component: iffComp);
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(iffText))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var existingStrings = _strings.GetOrNew(gridColor);
|
||||||
|
existingStrings.Add((gridUiPos, iffText));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Batch the colors whoopie
|
||||||
|
// really only affects forks with lots of grids.
|
||||||
|
foreach (var (color, sendVerts) in _verts)
|
||||||
|
{
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleList, sendVerts, color.WithAlpha(0.05f));
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (color, sendEdges) in _edges)
|
||||||
|
{
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineList, sendEdges, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
foreach (var (color, sendStrings) in _strings)
|
||||||
|
{
|
||||||
|
var adjustedColor = Color.FromSrgb(color);
|
||||||
|
|
||||||
|
foreach (var (gridUiPos, iffText) in sendStrings)
|
||||||
|
{
|
||||||
|
var textWidth = handle.GetDimensions(_font, iffText, UIScale);
|
||||||
|
handle.DrawString(_font, gridUiPos + textWidth with { X = -textWidth.X / 2f }, iffText, adjustedColor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var mousePos = _inputs.MouseScreenPosition;
|
||||||
|
var mouseLocalPos = GetLocalPosition(mousePos);
|
||||||
|
|
||||||
|
// Draw dotted line from our own shuttle entity to mouse.
|
||||||
|
if (FtlMode)
|
||||||
|
{
|
||||||
|
if (mousePos.Window != WindowId.Invalid)
|
||||||
|
{
|
||||||
|
// If mouse inbounds then draw it.
|
||||||
|
if (_shuttleEntity != null && controlLocalBounds.Contains(mouseLocalPos.Floored()) &&
|
||||||
|
EntManager.TryGetComponent(_shuttleEntity, out TransformComponent? shuttleXform) &&
|
||||||
|
shuttleXform.MapID != MapId.Nullspace)
|
||||||
|
{
|
||||||
|
// If it's a beacon only map then snap the mouse to a nearby spot.
|
||||||
|
ShuttleBeaconObject foundBeacon = default;
|
||||||
|
|
||||||
|
// Check for beacons around mouse and snap to that.
|
||||||
|
if (beaconsOnly && TryGetBeacon(viewportObjects, matty, mouseLocalPos, controlLocalBounds, out foundBeacon, out var foundLocalPos))
|
||||||
|
{
|
||||||
|
mouseLocalPos = foundLocalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
var grid = EntManager.GetComponent<MapGridComponent>(_shuttleEntity.Value);
|
||||||
|
|
||||||
|
var (gridPos, gridRot) = _xformSystem.GetWorldPositionRotation(shuttleXform);
|
||||||
|
gridPos = Maps.GetGridPosition(_shuttleEntity.Value, gridPos, gridRot);
|
||||||
|
|
||||||
|
// do NOT apply LocalCenter operation here because it will be adjusted in FTLFree.
|
||||||
|
var mouseMapPos = InverseMapPosition(mouseLocalPos);
|
||||||
|
|
||||||
|
var ftlFree = (!beaconsOnly || foundBeacon != default) &&
|
||||||
|
_shuttles.FTLFree(_shuttleEntity.Value, new EntityCoordinates(viewedMapUid, mouseMapPos), _ftlAngle, _viewportExclusions);
|
||||||
|
|
||||||
|
var color = ftlFree ? Color.LimeGreen : Color.Magenta;
|
||||||
|
|
||||||
|
var gridRelativePos = matty.Transform(gridPos);
|
||||||
|
gridRelativePos = gridRelativePos with { Y = -gridRelativePos.Y };
|
||||||
|
var gridUiPos = ScalePosition(gridRelativePos);
|
||||||
|
|
||||||
|
// Draw FTL buffer around the mouse.
|
||||||
|
var ourFTLBuffer = _shuttles.GetFTLBufferRange(_shuttleEntity.Value, grid);
|
||||||
|
ourFTLBuffer *= MinimapScale;
|
||||||
|
handle.DrawCircle(mouseLocalPos, ourFTLBuffer, Color.Magenta.WithAlpha(0.01f));
|
||||||
|
handle.DrawCircle(mouseLocalPos, ourFTLBuffer, Color.Magenta, filled: false);
|
||||||
|
|
||||||
|
// Draw line from our shuttle to target
|
||||||
|
// Might need to clip the line if it's too far? But my brain wasn't working so F.
|
||||||
|
handle.DrawDottedLine(gridUiPos, mouseLocalPos, color, (float) realTime.TotalSeconds * 30f);
|
||||||
|
|
||||||
|
// Draw shuttle pre-vis
|
||||||
|
var mouseVerts = GetMapObject(mouseLocalPos, _ftlAngle, scale: MinimapScale);
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, mouseVerts.Span, color.WithAlpha(0.05f));
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineLoop, mouseVerts.Span, color);
|
||||||
|
|
||||||
|
// Draw a notch indicating direction.
|
||||||
|
var ftlLength = GetMapObjectRadius() + 16f;
|
||||||
|
var ftlEnd = mouseLocalPos + _ftlAngle.RotateVec(new Vector2(0f, -ftlLength));
|
||||||
|
|
||||||
|
handle.DrawLine(mouseLocalPos, ftlEnd, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Draw the coordinates
|
||||||
|
var mapOffset = MidPointVector;
|
||||||
|
|
||||||
|
if (mousePos.Window != WindowId.Invalid &&
|
||||||
|
controlLocalBounds.Contains(mouseLocalPos.Floored()))
|
||||||
|
{
|
||||||
|
mapOffset = mouseLocalPos;
|
||||||
|
}
|
||||||
|
|
||||||
|
mapOffset = InverseMapPosition(mapOffset);
|
||||||
|
var coordsText = $"{mapOffset.X:0.0}, {mapOffset.Y:0.0}";
|
||||||
|
DrawData(handle, coordsText);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void AddMapObject(List<Vector2> edges, List<Vector2> verts, ValueList<Vector2> mapObject)
|
||||||
|
{
|
||||||
|
var bottom = mapObject[0];
|
||||||
|
var right = mapObject[1];
|
||||||
|
var top = mapObject[2];
|
||||||
|
var left = mapObject[3];
|
||||||
|
|
||||||
|
// Diamond interior
|
||||||
|
verts.Add(bottom);
|
||||||
|
verts.Add(right);
|
||||||
|
verts.Add(top);
|
||||||
|
|
||||||
|
verts.Add(bottom);
|
||||||
|
verts.Add(top);
|
||||||
|
verts.Add(left);
|
||||||
|
|
||||||
|
// Diamond edges
|
||||||
|
edges.Add(bottom);
|
||||||
|
edges.Add(right);
|
||||||
|
edges.Add(right);
|
||||||
|
edges.Add(top);
|
||||||
|
edges.Add(top);
|
||||||
|
edges.Add(left);
|
||||||
|
edges.Add(left);
|
||||||
|
edges.Add(bottom);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns the beacons that intersect the viewport.
|
||||||
|
/// </summary>
|
||||||
|
private IEnumerable<(string Beacon, MapCoordinates Coordinates, IMapObject MapObject)> GetBeacons(List<IMapObject> mapObjs, Matrix3 mapTransform, UIBox2i area)
|
||||||
|
{
|
||||||
|
foreach (var mapO in mapObjs)
|
||||||
|
{
|
||||||
|
if (mapO is not ShuttleBeaconObject beacon)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var beaconCoords = EntManager.GetCoordinates(beacon.Coordinates).ToMap(EntManager, _xformSystem);
|
||||||
|
var position = mapTransform.Transform(beaconCoords.Position);
|
||||||
|
var localPos = ScalePosition(position with {Y = -position.Y});
|
||||||
|
|
||||||
|
// If beacon not on screen then ignore it.
|
||||||
|
if (!area.Contains(localPos.Floored()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return (beacon.Name, beaconCoords, mapO);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private float GetMapObjectRadius(float scale = 1f) => WorldRange / 40f * scale;
|
||||||
|
|
||||||
|
private ValueList<Vector2> GetMapObject(Vector2 localPos, Angle angle, float scale = 1f, bool scalePosition = false)
|
||||||
|
{
|
||||||
|
// Constant size diamonds
|
||||||
|
var diamondRadius = GetMapObjectRadius();
|
||||||
|
|
||||||
|
var mapObj = new ValueList<Vector2>(4)
|
||||||
|
{
|
||||||
|
localPos + angle.RotateVec(new Vector2(0f, -2f * diamondRadius)) * scale,
|
||||||
|
localPos + angle.RotateVec(new Vector2(diamondRadius, 0f)) * scale,
|
||||||
|
localPos + angle.RotateVec(new Vector2(0f, 2f * diamondRadius)) * scale,
|
||||||
|
localPos + angle.RotateVec(new Vector2(-diamondRadius, 0f)) * scale,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (scalePosition)
|
||||||
|
{
|
||||||
|
for (var i = 0; i < mapObj.Count; i++)
|
||||||
|
{
|
||||||
|
mapObj[i] = ScalePosition(mapObj[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return mapObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryGetBeacon(IEnumerable<IMapObject> mapObjects, Matrix3 mapTransform, Vector2 mousePos, UIBox2i area, out ShuttleBeaconObject foundBeacon, out Vector2 foundLocalPos)
|
||||||
|
{
|
||||||
|
// In pixels
|
||||||
|
const float BeaconSnapRange = 32f;
|
||||||
|
float nearestValue = float.MaxValue;
|
||||||
|
foundLocalPos = Vector2.Zero;
|
||||||
|
foundBeacon = default;
|
||||||
|
|
||||||
|
foreach (var mapObj in mapObjects)
|
||||||
|
{
|
||||||
|
if (mapObj is not ShuttleBeaconObject beaconObj)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var beaconCoords = _xformSystem.ToMapCoordinates(EntManager.GetCoordinates(beaconObj.Coordinates));
|
||||||
|
|
||||||
|
if (beaconCoords.MapId != ViewingMap)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
// Invalid beacon?
|
||||||
|
if (!_shuttles.CanFTLBeacon(beaconObj.Coordinates))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var position = mapTransform.Transform(beaconCoords.Position);
|
||||||
|
var localPos = ScalePosition(position with {Y = -position.Y});
|
||||||
|
|
||||||
|
// If beacon not on screen then ignore it.
|
||||||
|
if (!area.Contains(localPos.Floored()))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var distance = (localPos - mousePos).Length();
|
||||||
|
|
||||||
|
if (distance > BeaconSnapRange ||
|
||||||
|
distance > nearestValue)
|
||||||
|
{
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
foundLocalPos = localPos;
|
||||||
|
nearestValue = distance;
|
||||||
|
foundBeacon = beaconObj;
|
||||||
|
}
|
||||||
|
|
||||||
|
return foundBeacon != default;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Sets the map objects for the next draw.
|
||||||
|
/// </summary>
|
||||||
|
public void SetMapObjects(Dictionary<MapId, List<IMapObject>> mapObjects)
|
||||||
|
{
|
||||||
|
_mapObjects.Clear();
|
||||||
|
|
||||||
|
if (mapObjects.TryGetValue(ViewingMap, out var obbies))
|
||||||
|
{
|
||||||
|
_mapObjects.AddRange(obbies);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
1
Content.Client/Shuttles/UI/ShuttleNavControl.xaml
Normal file
1
Content.Client/Shuttles/UI/ShuttleNavControl.xaml
Normal file
@@ -0,0 +1 @@
|
|||||||
|
<ui:BaseShuttleControl xmlns:ui="clr-namespace:Content.Client.Shuttles.UI" />
|
||||||
288
Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
Normal file
288
Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
Normal file
@@ -0,0 +1,288 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using JetBrains.Annotations;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Collections;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Client.Shuttles.UI;
|
||||||
|
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
public sealed partial class ShuttleNavControl : BaseShuttleControl
|
||||||
|
{
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
private readonly SharedShuttleSystem _shuttles;
|
||||||
|
private readonly SharedTransformSystem _transform;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Used to transform all of the radar objects. Typically is a shuttle console parented to a grid.
|
||||||
|
/// </summary>
|
||||||
|
private EntityCoordinates? _coordinates;
|
||||||
|
|
||||||
|
private Angle? _rotation;
|
||||||
|
|
||||||
|
private Dictionary<NetEntity, List<DockingPortState>> _docks = new();
|
||||||
|
|
||||||
|
public bool ShowIFF { get; set; } = true;
|
||||||
|
public bool ShowDocks { get; set; } = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised if the user left-clicks on the radar control with the relevant entitycoordinates.
|
||||||
|
/// </summary>
|
||||||
|
public Action<EntityCoordinates>? OnRadarClick;
|
||||||
|
|
||||||
|
private List<Entity<MapGridComponent>> _grids = new();
|
||||||
|
|
||||||
|
public ShuttleNavControl() : base(64f, 256f, 256f)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
_shuttles = EntManager.System<SharedShuttleSystem>();
|
||||||
|
_transform = EntManager.System<SharedTransformSystem>();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetMatrix(EntityCoordinates? coordinates, Angle? angle)
|
||||||
|
{
|
||||||
|
_coordinates = coordinates;
|
||||||
|
_rotation = angle;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindUp(args);
|
||||||
|
|
||||||
|
if (_coordinates == null || _rotation == null || args.Function != EngineKeyFunctions.UIClick ||
|
||||||
|
OnRadarClick == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var a = InverseScalePosition(args.RelativePosition);
|
||||||
|
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
||||||
|
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
||||||
|
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
||||||
|
OnRadarClick?.Invoke(coords);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the entitycoordinates of where the mouseposition is, relative to the control.
|
||||||
|
/// </summary>
|
||||||
|
[PublicAPI]
|
||||||
|
public EntityCoordinates GetMouseCoordinates(ScreenCoordinates screen)
|
||||||
|
{
|
||||||
|
if (_coordinates == null || _rotation == null)
|
||||||
|
{
|
||||||
|
return EntityCoordinates.Invalid;
|
||||||
|
}
|
||||||
|
|
||||||
|
var pos = screen.Position / UIScale - GlobalPosition;
|
||||||
|
|
||||||
|
var a = InverseScalePosition(pos);
|
||||||
|
var relativeWorldPos = new Vector2(a.X, -a.Y);
|
||||||
|
relativeWorldPos = _rotation.Value.RotateVec(relativeWorldPos);
|
||||||
|
var coords = _coordinates.Value.Offset(relativeWorldPos);
|
||||||
|
return coords;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void UpdateState(NavInterfaceState state)
|
||||||
|
{
|
||||||
|
SetMatrix(EntManager.GetCoordinates(state.Coordinates), state.Angle);
|
||||||
|
|
||||||
|
WorldMaxRange = state.MaxRange;
|
||||||
|
|
||||||
|
if (WorldMaxRange < WorldRange)
|
||||||
|
{
|
||||||
|
ActualRadarRange = WorldMaxRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (WorldMaxRange < WorldMinRange)
|
||||||
|
WorldMinRange = WorldMaxRange;
|
||||||
|
|
||||||
|
ActualRadarRange = Math.Clamp(ActualRadarRange, WorldMinRange, WorldMaxRange);
|
||||||
|
|
||||||
|
_docks = state.Docks;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
|
||||||
|
DrawBacking(handle);
|
||||||
|
DrawCircles(handle);
|
||||||
|
|
||||||
|
// No data
|
||||||
|
if (_coordinates == null || _rotation == null)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xformQuery = EntManager.GetEntityQuery<TransformComponent>();
|
||||||
|
var fixturesQuery = EntManager.GetEntityQuery<FixturesComponent>();
|
||||||
|
var bodyQuery = EntManager.GetEntityQuery<PhysicsComponent>();
|
||||||
|
|
||||||
|
if (!xformQuery.TryGetComponent(_coordinates.Value.EntityId, out var xform)
|
||||||
|
|| xform.MapID == MapId.Nullspace)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var mapPos = _transform.ToMapCoordinates(_coordinates.Value);
|
||||||
|
var offset = _coordinates.Value.Position;
|
||||||
|
var posMatrix = Matrix3.CreateTransform(offset, _rotation.Value);
|
||||||
|
var (_, ourEntRot, ourEntMatrix) = _transform.GetWorldPositionRotationMatrix(_coordinates.Value.EntityId);
|
||||||
|
Matrix3.Multiply(posMatrix, ourEntMatrix, out var ourWorldMatrix);
|
||||||
|
var ourWorldMatrixInvert = ourWorldMatrix.Invert();
|
||||||
|
|
||||||
|
// Draw our grid in detail
|
||||||
|
var ourGridId = xform.GridUid;
|
||||||
|
if (EntManager.TryGetComponent<MapGridComponent>(ourGridId, out var ourGrid) &&
|
||||||
|
fixturesQuery.HasComponent(ourGridId.Value))
|
||||||
|
{
|
||||||
|
var ourGridMatrix = _transform.GetWorldMatrix(ourGridId.Value);
|
||||||
|
Matrix3.Multiply(in ourGridMatrix, in ourWorldMatrixInvert, out var matrix);
|
||||||
|
var color = _shuttles.GetIFFColor(ourGridId.Value, self: true);
|
||||||
|
|
||||||
|
DrawGrid(handle, matrix, (ourGridId.Value, ourGrid), color);
|
||||||
|
DrawDocks(handle, ourGridId.Value, matrix);
|
||||||
|
}
|
||||||
|
|
||||||
|
var invertedPosition = _coordinates.Value.Position - offset;
|
||||||
|
invertedPosition.Y = -invertedPosition.Y;
|
||||||
|
// Don't need to transform the InvWorldMatrix again as it's already offset to its position.
|
||||||
|
|
||||||
|
// Draw radar position on the station
|
||||||
|
var radarPos = invertedPosition;
|
||||||
|
const float radarVertRadius = 2f;
|
||||||
|
|
||||||
|
var radarPosVerts = new Vector2[]
|
||||||
|
{
|
||||||
|
ScalePosition(radarPos + new Vector2(0f, -radarVertRadius)),
|
||||||
|
ScalePosition(radarPos + new Vector2(radarVertRadius / 2f, 0f)),
|
||||||
|
ScalePosition(radarPos + new Vector2(0f, radarVertRadius)),
|
||||||
|
ScalePosition(radarPos + new Vector2(radarVertRadius / -2f, 0f)),
|
||||||
|
};
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, radarPosVerts, Color.Lime);
|
||||||
|
|
||||||
|
var rot = ourEntRot + _rotation.Value;
|
||||||
|
var viewBounds = new Box2Rotated(new Box2(-WorldRange, -WorldRange, WorldRange, WorldRange).Translated(mapPos.Position), rot, mapPos.Position);
|
||||||
|
var viewAABB = viewBounds.CalcBoundingBox();
|
||||||
|
|
||||||
|
_grids.Clear();
|
||||||
|
_mapManager.FindGridsIntersecting(xform.MapID, new Box2(mapPos.Position - MaxRadarRangeVector, mapPos.Position + MaxRadarRangeVector), ref _grids, approx: true, includeMap: false);
|
||||||
|
|
||||||
|
// Draw other grids... differently
|
||||||
|
foreach (var grid in _grids)
|
||||||
|
{
|
||||||
|
var gUid = grid.Owner;
|
||||||
|
if (gUid == ourGridId || !fixturesQuery.HasComponent(gUid))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var gridBody = bodyQuery.GetComponent(gUid);
|
||||||
|
EntManager.TryGetComponent<IFFComponent>(gUid, out var iff);
|
||||||
|
|
||||||
|
if (!_shuttles.CanDraw(gUid, gridBody, iff))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var gridMatrix = _transform.GetWorldMatrix(gUid);
|
||||||
|
Matrix3.Multiply(in gridMatrix, in ourWorldMatrixInvert, out var matty);
|
||||||
|
var color = _shuttles.GetIFFColor(grid, self: false, iff);
|
||||||
|
|
||||||
|
// Others default:
|
||||||
|
// Color.FromHex("#FFC000FF")
|
||||||
|
// Hostile default: Color.Firebrick
|
||||||
|
var labelName = _shuttles.GetIFFLabel(grid, self: false, iff);
|
||||||
|
|
||||||
|
if (ShowIFF &&
|
||||||
|
labelName != null)
|
||||||
|
{
|
||||||
|
var gridBounds = grid.Comp.LocalAABB;
|
||||||
|
|
||||||
|
var gridCentre = matty.Transform(gridBody.LocalCenter);
|
||||||
|
gridCentre.Y = -gridCentre.Y;
|
||||||
|
var distance = gridCentre.Length();
|
||||||
|
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", labelName),
|
||||||
|
("distance", $"{distance:0.0}"));
|
||||||
|
var labelDimensions = handle.GetDimensions(Font, labelText, UIScale);
|
||||||
|
|
||||||
|
// y-offset the control to always render below the grid (vertically)
|
||||||
|
var yOffset = Math.Max(gridBounds.Height, gridBounds.Width) * MinimapScale / 1.8f / UIScale;
|
||||||
|
|
||||||
|
// The actual position in the UI. We offset the matrix position to render it off by half its width
|
||||||
|
// plus by the offset.
|
||||||
|
var uiPosition = ScalePosition(gridCentre) / UIScale - new Vector2(labelDimensions.X / 2f, -yOffset);
|
||||||
|
|
||||||
|
// Look this is uggo so feel free to cleanup. We just need to clamp the UI position to within the viewport.
|
||||||
|
uiPosition = new Vector2(Math.Clamp(uiPosition.X, 0f, Width - labelDimensions.X),
|
||||||
|
Math.Clamp(uiPosition.Y, 0f, Height - labelDimensions.Y));
|
||||||
|
|
||||||
|
handle.DrawString(Font, uiPosition, labelText, color);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Detailed view
|
||||||
|
var gridAABB = gridMatrix.TransformBox(grid.Comp.LocalAABB);
|
||||||
|
|
||||||
|
// Skip drawing if it's out of range.
|
||||||
|
if (!gridAABB.Intersects(viewAABB))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
DrawGrid(handle, matty, grid, color);
|
||||||
|
DrawDocks(handle, gUid, matty);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void DrawDocks(DrawingHandleScreen handle, EntityUid uid, Matrix3 matrix)
|
||||||
|
{
|
||||||
|
if (!ShowDocks)
|
||||||
|
return;
|
||||||
|
|
||||||
|
const float DockScale = 0.6f;
|
||||||
|
var nent = EntManager.GetNetEntity(uid);
|
||||||
|
|
||||||
|
if (_docks.TryGetValue(nent, out var docks))
|
||||||
|
{
|
||||||
|
foreach (var state in docks)
|
||||||
|
{
|
||||||
|
var position = state.Coordinates.Position;
|
||||||
|
var uiPosition = matrix.Transform(position);
|
||||||
|
|
||||||
|
if (uiPosition.Length() > (WorldRange * 2f) - DockScale)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
var color = Color.ToSrgb(Color.Magenta);
|
||||||
|
|
||||||
|
var verts = new[]
|
||||||
|
{
|
||||||
|
matrix.Transform(position + new Vector2(-DockScale, -DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(DockScale, -DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(DockScale, DockScale)),
|
||||||
|
matrix.Transform(position + new Vector2(-DockScale, DockScale)),
|
||||||
|
};
|
||||||
|
|
||||||
|
for (var i = 0; i < verts.Length; i++)
|
||||||
|
{
|
||||||
|
var vert = verts[i];
|
||||||
|
vert.Y = -vert.Y;
|
||||||
|
verts[i] = ScalePosition(vert);
|
||||||
|
}
|
||||||
|
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.TriangleFan, verts, color.WithAlpha(0.8f));
|
||||||
|
handle.DrawPrimitives(DrawPrimitiveTopology.LineStrip, verts, color);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private Vector2 InverseScalePosition(Vector2 value)
|
||||||
|
{
|
||||||
|
return (value - MidPointVector) / MinimapScale;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,84 +0,0 @@
|
|||||||
using System.Numerics;
|
|
||||||
using Robust.Client.Graphics;
|
|
||||||
using Robust.Client.UserInterface;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
|
|
||||||
namespace Content.Client.UserInterface.Controls;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Handles generic grid-drawing data, with zoom and dragging.
|
|
||||||
/// </summary>
|
|
||||||
public abstract class MapGridControl : Control
|
|
||||||
{
|
|
||||||
[Dependency] protected readonly IGameTiming Timing = default!;
|
|
||||||
|
|
||||||
protected const float ScrollSensitivity = 8f;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// UI pixel radius.
|
|
||||||
/// </summary>
|
|
||||||
public const int UIDisplayRadius = 320;
|
|
||||||
protected const int MinimapMargin = 4;
|
|
||||||
|
|
||||||
protected float WorldMinRange;
|
|
||||||
protected float WorldMaxRange;
|
|
||||||
public float WorldRange;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// We'll lerp between the radarrange and actual range
|
|
||||||
/// </summary>
|
|
||||||
protected float ActualRadarRange;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Controls the maximum distance that will display.
|
|
||||||
/// </summary>
|
|
||||||
public float MaxRadarRange { get; private set; } = 256f * 10f;
|
|
||||||
|
|
||||||
public Vector2 MaxRadarRangeVector => new Vector2(MaxRadarRange, MaxRadarRange);
|
|
||||||
|
|
||||||
protected Vector2 MidpointVector => new Vector2(MidPoint, MidPoint);
|
|
||||||
|
|
||||||
protected int MidPoint => SizeFull / 2;
|
|
||||||
protected int SizeFull => (int) ((UIDisplayRadius + MinimapMargin) * 2 * UIScale);
|
|
||||||
protected int ScaledMinimapRadius => (int) (UIDisplayRadius * UIScale);
|
|
||||||
protected float MinimapScale => WorldRange != 0 ? ScaledMinimapRadius / WorldRange : 0f;
|
|
||||||
|
|
||||||
public event Action<float>? WorldRangeChanged;
|
|
||||||
|
|
||||||
public MapGridControl(float minRange, float maxRange, float range)
|
|
||||||
{
|
|
||||||
IoCManager.InjectDependencies(this);
|
|
||||||
SetSize = new Vector2(SizeFull, SizeFull);
|
|
||||||
RectClipContent = true;
|
|
||||||
MouseFilter = MouseFilterMode.Stop;
|
|
||||||
ActualRadarRange = WorldRange;
|
|
||||||
WorldMinRange = minRange;
|
|
||||||
WorldMaxRange = maxRange;
|
|
||||||
WorldRange = range;
|
|
||||||
ActualRadarRange = range;
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
|
||||||
{
|
|
||||||
base.MouseWheel(args);
|
|
||||||
AddRadarRange(-args.Delta.Y * 1f / ScrollSensitivity * ActualRadarRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
public void AddRadarRange(float value)
|
|
||||||
{
|
|
||||||
ActualRadarRange = Math.Clamp(ActualRadarRange + value, WorldMinRange, WorldMaxRange);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void Draw(DrawingHandleScreen handle)
|
|
||||||
{
|
|
||||||
base.Draw(handle);
|
|
||||||
if (!ActualRadarRange.Equals(WorldRange))
|
|
||||||
{
|
|
||||||
var diff = ActualRadarRange - WorldRange;
|
|
||||||
const float lerpRate = 10f;
|
|
||||||
|
|
||||||
WorldRange += (float) Math.Clamp(diff, -lerpRate * MathF.Abs(diff) * Timing.FrameTime.TotalSeconds, lerpRate * MathF.Abs(diff) * Timing.FrameTime.TotalSeconds);
|
|
||||||
WorldRangeChanged?.Invoke(WorldRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
<controls:LayoutContainer xmlns:controls="https://spacestation14.io"/>
|
||||||
243
Content.Client/UserInterface/Controls/MapGridControl.xaml.cs
Normal file
243
Content.Client/UserInterface/Controls/MapGridControl.xaml.cs
Normal file
@@ -0,0 +1,243 @@
|
|||||||
|
using System.Numerics;
|
||||||
|
using Robust.Client.AutoGenerated;
|
||||||
|
using Robust.Client.Graphics;
|
||||||
|
using Robust.Client.ResourceManagement;
|
||||||
|
using Robust.Client.UserInterface;
|
||||||
|
using Robust.Client.UserInterface.Controls;
|
||||||
|
using Robust.Client.UserInterface.XAML;
|
||||||
|
using Robust.Shared.Input;
|
||||||
|
using Robust.Shared.Timing;
|
||||||
|
|
||||||
|
namespace Content.Client.UserInterface.Controls;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles generic grid-drawing data, with zoom and dragging.
|
||||||
|
/// </summary>
|
||||||
|
[GenerateTypedNameReferences]
|
||||||
|
[Virtual]
|
||||||
|
public partial class MapGridControl : LayoutContainer
|
||||||
|
{
|
||||||
|
[Dependency] protected readonly IEntityManager EntManager = default!;
|
||||||
|
[Dependency] protected readonly IGameTiming Timing = default!;
|
||||||
|
|
||||||
|
protected static readonly Color BackingColor = new Color(0.08f, 0.08f, 0.08f);
|
||||||
|
|
||||||
|
private Font _largerFont;
|
||||||
|
|
||||||
|
/* Dragging */
|
||||||
|
protected virtual bool Draggable { get; } = false;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Control offset from whatever is being tracked.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 Offset;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// If the control is being recentered what is the target offset to reach.
|
||||||
|
/// </summary>
|
||||||
|
public Vector2 TargetOffset;
|
||||||
|
|
||||||
|
private bool _draggin;
|
||||||
|
protected Vector2 StartDragPosition;
|
||||||
|
protected bool Recentering;
|
||||||
|
|
||||||
|
protected const float ScrollSensitivity = 8f;
|
||||||
|
|
||||||
|
protected float RecenterMinimum = 0.05f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// UI pixel radius.
|
||||||
|
/// </summary>
|
||||||
|
public const int UIDisplayRadius = 320;
|
||||||
|
protected const int MinimapMargin = 4;
|
||||||
|
|
||||||
|
protected float WorldMinRange;
|
||||||
|
protected float WorldMaxRange;
|
||||||
|
public float WorldRange;
|
||||||
|
public Vector2 WorldRangeVector => new Vector2(WorldRange, WorldRange);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// We'll lerp between the radarrange and actual range
|
||||||
|
/// </summary>
|
||||||
|
protected float ActualRadarRange;
|
||||||
|
|
||||||
|
protected float CornerRadarRange => MathF.Sqrt(ActualRadarRange * ActualRadarRange + ActualRadarRange * ActualRadarRange) * 1.1f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Controls the maximum distance that will display.
|
||||||
|
/// </summary>
|
||||||
|
public float MaxRadarRange { get; private set; } = 256f * 10f;
|
||||||
|
|
||||||
|
public Vector2 MaxRadarRangeVector => new Vector2(MaxRadarRange, MaxRadarRange);
|
||||||
|
|
||||||
|
protected Vector2 MidPointVector => new Vector2(MidPoint, MidPoint);
|
||||||
|
|
||||||
|
protected int MidPoint => SizeFull / 2;
|
||||||
|
protected int SizeFull => (int) ((UIDisplayRadius + MinimapMargin) * 2 * UIScale);
|
||||||
|
protected int ScaledMinimapRadius => (int) (UIDisplayRadius * UIScale);
|
||||||
|
protected float MinimapScale => WorldRange != 0 ? ScaledMinimapRadius / WorldRange : 0f;
|
||||||
|
|
||||||
|
public event Action<float>? WorldRangeChanged;
|
||||||
|
|
||||||
|
public MapGridControl() : this(32f, 32f, 32f) {}
|
||||||
|
|
||||||
|
public MapGridControl(float minRange, float maxRange, float range)
|
||||||
|
{
|
||||||
|
RobustXamlLoader.Load(this);
|
||||||
|
IoCManager.InjectDependencies(this);
|
||||||
|
SetSize = new Vector2(SizeFull, SizeFull);
|
||||||
|
RectClipContent = true;
|
||||||
|
MouseFilter = MouseFilterMode.Stop;
|
||||||
|
ActualRadarRange = WorldRange;
|
||||||
|
WorldMinRange = minRange;
|
||||||
|
WorldMaxRange = maxRange;
|
||||||
|
WorldRange = range;
|
||||||
|
ActualRadarRange = range;
|
||||||
|
|
||||||
|
var cache = IoCManager.Resolve<IResourceCache>();
|
||||||
|
_largerFont = new VectorFont(cache.GetResource<FontResource>("/EngineFonts/NotoSans/NotoSans-Regular.ttf"), 16);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void ForceRecenter()
|
||||||
|
{
|
||||||
|
Recentering = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
base.KeyBindDown(args);
|
||||||
|
|
||||||
|
if (!Draggable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Function == EngineKeyFunctions.Use)
|
||||||
|
{
|
||||||
|
StartDragPosition = args.PointerLocation.Position;
|
||||||
|
_draggin = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
|
||||||
|
{
|
||||||
|
if (!Draggable)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (args.Function == EngineKeyFunctions.Use)
|
||||||
|
_draggin = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseMove(GUIMouseMoveEventArgs args)
|
||||||
|
{
|
||||||
|
base.MouseMove(args);
|
||||||
|
|
||||||
|
if (!_draggin)
|
||||||
|
return;
|
||||||
|
|
||||||
|
Recentering = false;
|
||||||
|
Offset -= new Vector2(args.Relative.X, -args.Relative.Y) / MidPoint * WorldRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void MouseWheel(GUIMouseWheelEventArgs args)
|
||||||
|
{
|
||||||
|
base.MouseWheel(args);
|
||||||
|
AddRadarRange(-args.Delta.Y * 1f / ScrollSensitivity * ActualRadarRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void AddRadarRange(float value)
|
||||||
|
{
|
||||||
|
ActualRadarRange = Math.Clamp(ActualRadarRange + value, WorldMinRange, WorldMaxRange);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts map coordinates to the local control.
|
||||||
|
/// </summary>
|
||||||
|
protected Vector2 ScalePosition(Vector2 value)
|
||||||
|
{
|
||||||
|
return ScalePosition(value, MinimapScale, MidPointVector);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static Vector2 ScalePosition(Vector2 value, float minimapScale, Vector2 midpointVector)
|
||||||
|
{
|
||||||
|
return value * minimapScale + midpointVector;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Converts local coordinates on the control to map coordinates.
|
||||||
|
/// </summary>
|
||||||
|
protected Vector2 InverseMapPosition(Vector2 value)
|
||||||
|
{
|
||||||
|
var inversePos = (value - MidPointVector) / MinimapScale;
|
||||||
|
|
||||||
|
inversePos = inversePos with { Y = -inversePos.Y };
|
||||||
|
inversePos = Matrix3.CreateTransform(Offset, Angle.Zero).Transform(inversePos);
|
||||||
|
return inversePos;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles re-centering the control's offset.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public bool DrawRecenter()
|
||||||
|
{
|
||||||
|
// Map re-centering
|
||||||
|
if (Recentering)
|
||||||
|
{
|
||||||
|
var frameTime = Timing.FrameTime;
|
||||||
|
var diff = (TargetOffset - Offset) * (float) frameTime.TotalSeconds;
|
||||||
|
|
||||||
|
if (Offset.LengthSquared() < RecenterMinimum)
|
||||||
|
{
|
||||||
|
Offset = TargetOffset;
|
||||||
|
Recentering = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
Offset += diff * 5f;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Offset == TargetOffset;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawBacking(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
var backing = BackingColor;
|
||||||
|
handle.DrawRect(new UIBox2(0f, Height, Width, 0f), backing);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected void DrawNoSignal(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
var greyColor = Color.FromHex("#474F52");
|
||||||
|
|
||||||
|
// Draw funny lines
|
||||||
|
var lineCount = 4f;
|
||||||
|
|
||||||
|
for (var i = 0; i < lineCount; i++)
|
||||||
|
{
|
||||||
|
var angle = Angle.FromDegrees(45 + i * 360f / lineCount);
|
||||||
|
var distance = Width / 2f;
|
||||||
|
var start = MidPointVector + angle.RotateVec(new Vector2(0f, 2.5f * distance / 4f));
|
||||||
|
var end = MidPointVector + angle.RotateVec(new Vector2(0f, 4f * distance / 4f));
|
||||||
|
handle.DrawLine(start, end, greyColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
var signalText = Loc.GetString("shuttle-console-no-signal");
|
||||||
|
var dimensions = handle.GetDimensions(_largerFont, signalText, 1f);
|
||||||
|
var position = MidPointVector - dimensions / 2f;
|
||||||
|
handle.DrawString(_largerFont, position, Loc.GetString("shuttle-console-no-signal"), greyColor);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected override void Draw(DrawingHandleScreen handle)
|
||||||
|
{
|
||||||
|
base.Draw(handle);
|
||||||
|
if (!ActualRadarRange.Equals(WorldRange))
|
||||||
|
{
|
||||||
|
var diff = ActualRadarRange - WorldRange;
|
||||||
|
const float lerpRate = 10f;
|
||||||
|
|
||||||
|
WorldRange += (float) Math.Clamp(diff, -lerpRate * MathF.Abs(diff) * Timing.FrameTime.TotalSeconds, lerpRate * MathF.Abs(diff) * Timing.FrameTime.TotalSeconds);
|
||||||
|
WorldRangeChanged?.Invoke(WorldRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -10,6 +10,7 @@ using Content.Shared.Humanoid;
|
|||||||
using Content.Shared.Mobs.Components;
|
using Content.Shared.Mobs.Components;
|
||||||
using Content.Shared.Mobs.Systems;
|
using Content.Shared.Mobs.Systems;
|
||||||
using Content.Shared.Salvage.Expeditions;
|
using Content.Shared.Salvage.Expeditions;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Player;
|
using Robust.Shared.Player;
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
@@ -186,7 +187,7 @@ public sealed partial class SalvageSystem
|
|||||||
if (shuttleXform.MapUid != uid || HasComp<FTLComponent>(shuttleUid))
|
if (shuttleXform.MapUid != uid || HasComp<FTLComponent>(shuttleUid))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_shuttle.FTLTravel(shuttleUid, shuttle, member, ftlTime);
|
_shuttle.FTLToDock(shuttleUid, shuttle, member, ftlTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
break;
|
break;
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public sealed class DockCommand : IConsoleCommand
|
|||||||
}
|
}
|
||||||
|
|
||||||
var dockSystem = _entManager.System<DockingSystem>();
|
var dockSystem = _entManager.System<DockingSystem>();
|
||||||
dockSystem.Dock(airlock1.Value, dock1, airlock2.Value, dock2);
|
dockSystem.Dock((airlock1.Value, dock1), (airlock2.Value, dock2));
|
||||||
|
|
||||||
if (dock1.DockedWith == airlock2)
|
if (dock1.DockedWith == airlock2)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -1,14 +0,0 @@
|
|||||||
namespace Content.Server.Shuttles.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Added to entities when they are actively trying to dock with something else.
|
|
||||||
/// We track it because checking every dock constantly would be expensive.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class AutoDockComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Track who has requested autodocking so we can know when to be removed.
|
|
||||||
/// </summary>
|
|
||||||
public HashSet<EntityUid> Requesters = new();
|
|
||||||
}
|
|
||||||
10
Content.Server/Shuttles/Components/FTLBeaconComponent.cs
Normal file
10
Content.Server/Shuttles/Components/FTLBeaconComponent.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
namespace Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows up on a shuttle's map as an FTL target.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent]
|
||||||
|
public sealed partial class FTLBeaconComponent : Component
|
||||||
|
{
|
||||||
|
|
||||||
|
}
|
||||||
@@ -2,6 +2,7 @@ using Content.Shared.Shuttles.Systems;
|
|||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Prototypes;
|
||||||
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
using Robust.Shared.Serialization.TypeSerializers.Implementations.Custom.Prototype;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.Components;
|
namespace Content.Server.Shuttles.Components;
|
||||||
@@ -25,25 +26,19 @@ public sealed partial class FTLComponent : Component
|
|||||||
public float Accumulator = 0f;
|
public float Accumulator = 0f;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Target Uid to dock with at the end of FTL.
|
/// Coordinates to arrive it: May be relative to another grid (for docking) or map coordinates.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("targetUid")]
|
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||||
public EntityUid? TargetUid;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("targetCoordinates")]
|
|
||||||
public EntityCoordinates TargetCoordinates;
|
public EntityCoordinates TargetCoordinates;
|
||||||
|
|
||||||
/// <summary>
|
[DataField]
|
||||||
/// Should we dock with the target when arriving or show up nearby.
|
public Angle TargetAngle;
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("dock")]
|
|
||||||
public bool Dock;
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// If we're docking after FTL what is the prioritised dock tag (if applicable).
|
/// If we're docking after FTL what is the prioritised dock tag (if applicable).
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("priorityTag", customTypeSerializer:typeof(PrototypeIdSerializer<TagPrototype>))]
|
[ViewVariables(VVAccess.ReadWrite), DataField]
|
||||||
public string? PriorityTag;
|
public ProtoId<TagPrototype>? PriorityTag;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("soundTravel")]
|
[ViewVariables(VVAccess.ReadWrite), DataField("soundTravel")]
|
||||||
public SoundSpecifier? TravelSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_progress.ogg")
|
public SoundSpecifier? TravelSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_progress.ogg")
|
||||||
@@ -51,5 +46,6 @@ public sealed partial class FTLComponent : Component
|
|||||||
Params = AudioParams.Default.WithVolume(-3f).WithLoop(true)
|
Params = AudioParams.Default.WithVolume(-3f).WithLoop(true)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
[DataField]
|
||||||
public EntityUid? TravelStream;
|
public EntityUid? TravelStream;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
using Content.Shared.Whitelist;
|
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.Components;
|
|
||||||
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class FTLDestinationComponent : Component
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// Should this destination be restricted in some form from console visibility.
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("whitelist")]
|
|
||||||
public EntityWhitelist? Whitelist;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Is this destination visible but available to be warped to?
|
|
||||||
/// </summary>
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("enabled")]
|
|
||||||
public bool Enabled = true;
|
|
||||||
}
|
|
||||||
16
Content.Server/Shuttles/Components/FTLExclusionComponent.cs
Normal file
16
Content.Server/Shuttles/Components/FTLExclusionComponent.cs
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Prevents FTL from occuring around this entity.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, Access(typeof(SharedShuttleSystem))]
|
||||||
|
public sealed partial class FTLExclusionComponent : Component
|
||||||
|
{
|
||||||
|
[DataField]
|
||||||
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
[DataField(required: true)]
|
||||||
|
public float Range = 32f;
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
namespace Content.Server.Shuttles.Components;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Added to <see cref="DockingComponent"/> that have recently undocked.
|
|
||||||
/// This checks for whether they've left the specified radius before allowing them to automatically dock again.
|
|
||||||
/// </summary>
|
|
||||||
[RegisterComponent]
|
|
||||||
public sealed partial class RecentlyDockedComponent : Component
|
|
||||||
{
|
|
||||||
[DataField("lastDocked")]
|
|
||||||
public EntityUid LastDocked;
|
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField("radius")]
|
|
||||||
public float Radius = 1.5f;
|
|
||||||
}
|
|
||||||
@@ -437,7 +437,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||||||
if (xform.MapUid != arrivalsXform.MapUid)
|
if (xform.MapUid != arrivalsXform.MapUid)
|
||||||
{
|
{
|
||||||
if (arrivals.IsValid())
|
if (arrivals.IsValid())
|
||||||
_shuttles.FTLTravel(uid, shuttle, arrivals, dock: true);
|
_shuttles.FTLToDock(uid, shuttle, arrivals);
|
||||||
|
|
||||||
comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds(tripTime);
|
comp.NextArrivalsTime = _timing.CurTime + TimeSpan.FromSeconds(tripTime);
|
||||||
}
|
}
|
||||||
@@ -447,7 +447,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||||||
var targetGrid = _station.GetLargestGrid(data);
|
var targetGrid = _station.GetLargestGrid(data);
|
||||||
|
|
||||||
if (targetGrid != null)
|
if (targetGrid != null)
|
||||||
_shuttles.FTLTravel(uid, shuttle, targetGrid.Value, dock: true);
|
_shuttles.FTLToDock(uid, shuttle, targetGrid.Value);
|
||||||
|
|
||||||
// The ArrivalsCooldown includes the trip there, so we only need to add the time taken for
|
// The ArrivalsCooldown includes the trip there, so we only need to add the time taken for
|
||||||
// the trip back.
|
// the trip back.
|
||||||
@@ -567,7 +567,7 @@ public sealed class ArrivalsSystem : EntitySystem
|
|||||||
var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
|
var arrivalsComp = EnsureComp<ArrivalsShuttleComponent>(component.Shuttle);
|
||||||
arrivalsComp.Station = uid;
|
arrivalsComp.Station = uid;
|
||||||
EnsureComp<ProtectedGridComponent>(uid);
|
EnsureComp<ProtectedGridComponent>(uid);
|
||||||
_shuttles.FTLTravel(component.Shuttle, shuttleComp, arrivals, hyperspaceTime: RoundStartFTLDuration, dock: true);
|
_shuttles.FTLToDock(component.Shuttle, shuttleComp, arrivals, hyperspaceTime: RoundStartFTLDuration);
|
||||||
arrivalsComp.NextTransfer = _timing.CurTime + TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown));
|
arrivalsComp.NextTransfer = _timing.CurTime + TimeSpan.FromSeconds(_cfgManager.GetCVar(CCVars.ArrivalsCooldown));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,122 +0,0 @@
|
|||||||
using Content.Server.Shuttles.Components;
|
|
||||||
using Content.Shared.Shuttles.Components;
|
|
||||||
using Content.Shared.Shuttles.Events;
|
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.Systems;
|
|
||||||
|
|
||||||
public sealed partial class DockingSystem
|
|
||||||
{
|
|
||||||
private void UpdateAutodock()
|
|
||||||
{
|
|
||||||
// Work out what we can autodock with, what we shouldn't, and when we should stop tracking.
|
|
||||||
// Autodocking only stops when the client closes that dock viewport OR they lose pilotcomponent.
|
|
||||||
var dockingQuery = GetEntityQuery<DockingComponent>();
|
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
|
||||||
var recentQuery = GetEntityQuery<RecentlyDockedComponent>();
|
|
||||||
var query = EntityQueryEnumerator<AutoDockComponent>();
|
|
||||||
|
|
||||||
while (query.MoveNext(out var dockUid, out var comp))
|
|
||||||
{
|
|
||||||
if (comp.Requesters.Count == 0 || !dockingQuery.TryGetComponent(dockUid, out var dock))
|
|
||||||
{
|
|
||||||
RemComp<AutoDockComponent>(dockUid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Don't re-dock if we're already docked or recently were.
|
|
||||||
if (dock.Docked || recentQuery.HasComponent(dockUid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
var dockable = GetDockable(dockUid, xformQuery.GetComponent(dockUid));
|
|
||||||
|
|
||||||
if (dockable == null)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
TryDock(dockUid, dock, dockable.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
// Work out recent docks that have gone past their designated threshold.
|
|
||||||
var checkedRecent = new HashSet<EntityUid>();
|
|
||||||
var recentQueryEnumerator = EntityQueryEnumerator<RecentlyDockedComponent, TransformComponent>();
|
|
||||||
|
|
||||||
while (recentQueryEnumerator.MoveNext(out var uid, out var comp, out var xform))
|
|
||||||
{
|
|
||||||
if (!checkedRecent.Add(uid))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (!dockingQuery.HasComponent(uid))
|
|
||||||
{
|
|
||||||
RemCompDeferred<RecentlyDockedComponent>(uid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!xformQuery.TryGetComponent(comp.LastDocked, out var otherXform))
|
|
||||||
{
|
|
||||||
RemCompDeferred<RecentlyDockedComponent>(uid);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var worldPos = _transform.GetWorldPosition(xform, xformQuery);
|
|
||||||
var otherWorldPos = _transform.GetWorldPosition(otherXform, xformQuery);
|
|
||||||
|
|
||||||
if ((worldPos - otherWorldPos).Length() < comp.Radius)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
Log.Debug($"Removed RecentlyDocked from {ToPrettyString(uid)} and {ToPrettyString(comp.LastDocked)}");
|
|
||||||
RemComp<RecentlyDockedComponent>(uid);
|
|
||||||
RemComp<RecentlyDockedComponent>(comp.LastDocked);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRequestUndock(EntityUid uid, ShuttleConsoleComponent component, UndockRequestMessage args)
|
|
||||||
{
|
|
||||||
var dork = GetEntity(args.DockEntity);
|
|
||||||
|
|
||||||
Log.Debug($"Received undock request for {ToPrettyString(dork)}");
|
|
||||||
|
|
||||||
// TODO: Validation
|
|
||||||
if (!TryComp<DockingComponent>(dork, out var dock) ||
|
|
||||||
!dock.Docked ||
|
|
||||||
HasComp<PreventPilotComponent>(Transform(uid).GridUid))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
Undock(dork, dock);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRequestAutodock(EntityUid uid, ShuttleConsoleComponent component, AutodockRequestMessage args)
|
|
||||||
{
|
|
||||||
var dork = GetEntity(args.DockEntity);
|
|
||||||
Log.Debug($"Received autodock request for {ToPrettyString(dork)}");
|
|
||||||
var player = args.Session.AttachedEntity;
|
|
||||||
|
|
||||||
if (player == null ||
|
|
||||||
!HasComp<DockingComponent>(dork) ||
|
|
||||||
HasComp<PreventPilotComponent>(Transform(uid).GridUid))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Validation
|
|
||||||
var comp = EnsureComp<AutoDockComponent>(dork);
|
|
||||||
comp.Requesters.Add(player.Value);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnRequestStopAutodock(EntityUid uid, ShuttleConsoleComponent component, StopAutodockRequestMessage args)
|
|
||||||
{
|
|
||||||
var dork = GetEntity(args.DockEntity);
|
|
||||||
Log.Debug($"Received stop autodock request for {ToPrettyString(dork)}");
|
|
||||||
|
|
||||||
var player = args.Session.AttachedEntity;
|
|
||||||
|
|
||||||
// TODO: Validation
|
|
||||||
if (player == null || !TryComp<AutoDockComponent>(dork, out var comp))
|
|
||||||
return;
|
|
||||||
|
|
||||||
comp.Requesters.Remove(player.Value);
|
|
||||||
|
|
||||||
if (comp.Requesters.Count == 0)
|
|
||||||
RemComp<AutoDockComponent>(dork);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -19,13 +19,13 @@ public sealed partial class DockingSystem
|
|||||||
|
|
||||||
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, EntityQuery<TransformComponent> xformQuery)
|
||||||
{
|
{
|
||||||
var (shuttlePos, shuttleRot) = _transform.GetWorldPositionRotation(xform, xformQuery);
|
var (shuttlePos, shuttleRot) = _transform.GetWorldPositionRotation(xform);
|
||||||
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetXform, xformQuery);
|
var (targetPos, targetRot) = _transform.GetWorldPositionRotation(targetXform);
|
||||||
|
|
||||||
var shuttleCOM = Robust.Shared.Physics.Transform.Mul(new Transform(shuttlePos, shuttleRot),
|
var shuttleCOM = Robust.Shared.Physics.Transform.Mul(new Transform(shuttlePos, shuttleRot),
|
||||||
Comp<PhysicsComponent>(uid).LocalCenter);
|
_physicsQuery.GetComponent(uid).LocalCenter);
|
||||||
var targetCOM = Robust.Shared.Physics.Transform.Mul(new Transform(targetPos, targetRot),
|
var targetCOM = Robust.Shared.Physics.Transform.Mul(new Transform(targetPos, targetRot),
|
||||||
Comp<PhysicsComponent>(targetUid).LocalCenter);
|
_physicsQuery.GetComponent(targetUid).LocalCenter);
|
||||||
|
|
||||||
var mapDiff = shuttleCOM - targetCOM;
|
var mapDiff = shuttleCOM - targetCOM;
|
||||||
var angle = mapDiff.ToWorldAngle();
|
var angle = mapDiff.ToWorldAngle();
|
||||||
@@ -36,7 +36,7 @@ public sealed partial class DockingSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Checks if 2 docks can be connected by moving the shuttle directly onto docks.
|
/// Checks if 2 docks can be connected by moving the shuttle directly onto docks.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool CanDock(
|
private bool CanDock(
|
||||||
DockingComponent shuttleDock,
|
DockingComponent shuttleDock,
|
||||||
TransformComponent shuttleDockXform,
|
TransformComponent shuttleDockXform,
|
||||||
DockingComponent gridDock,
|
DockingComponent gridDock,
|
||||||
@@ -119,37 +119,63 @@ public sealed partial class DockingSystem
|
|||||||
return GetDockingConfigPrivate(shuttleUid, targetGrid, shuttleDocks, gridDocks, priorityTag);
|
return GetDockingConfigPrivate(shuttleUid, targetGrid, shuttleDocks, gridDocks, priorityTag);
|
||||||
}
|
}
|
||||||
|
|
||||||
private DockingConfig? GetDockingConfigPrivate(
|
/// <summary>
|
||||||
|
/// Tries to get a docking config at the specified coordinates and angle.
|
||||||
|
/// </summary>
|
||||||
|
public DockingConfig? GetDockingConfigAt(EntityUid shuttleUid,
|
||||||
|
EntityUid targetGrid,
|
||||||
|
EntityCoordinates coordinates,
|
||||||
|
Angle angle)
|
||||||
|
{
|
||||||
|
var gridDocks = GetDocks(targetGrid);
|
||||||
|
var shuttleDocks = GetDocks(shuttleUid);
|
||||||
|
|
||||||
|
var configs = GetDockingConfigs(shuttleUid, targetGrid, shuttleDocks, gridDocks);
|
||||||
|
|
||||||
|
foreach (var config in configs)
|
||||||
|
{
|
||||||
|
if (config.Coordinates.Equals(coordinates) && config.Angle.EqualsApprox(angle, 0.01))
|
||||||
|
{
|
||||||
|
return config;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets all docking configs between the 2 grids.
|
||||||
|
/// </summary>
|
||||||
|
private List<DockingConfig> GetDockingConfigs(
|
||||||
EntityUid shuttleUid,
|
EntityUid shuttleUid,
|
||||||
EntityUid targetGrid,
|
EntityUid targetGrid,
|
||||||
List<(EntityUid, DockingComponent)> shuttleDocks,
|
List<(EntityUid, DockingComponent)> shuttleDocks,
|
||||||
List<(EntityUid, DockingComponent)> gridDocks,
|
List<(EntityUid, DockingComponent)> gridDocks)
|
||||||
string? priorityTag = null)
|
{
|
||||||
{
|
var validDockConfigs = new List<DockingConfig>();
|
||||||
if (gridDocks.Count <= 0)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
if (gridDocks.Count <= 0)
|
||||||
var targetGridGrid = Comp<MapGridComponent>(targetGrid);
|
return validDockConfigs;
|
||||||
var targetGridXform = xformQuery.GetComponent(targetGrid);
|
|
||||||
|
var targetGridGrid = _gridQuery.GetComponent(targetGrid);
|
||||||
|
var targetGridXform = _xformQuery.GetComponent(targetGrid);
|
||||||
var targetGridAngle = _transform.GetWorldRotation(targetGridXform).Reduced();
|
var targetGridAngle = _transform.GetWorldRotation(targetGridXform).Reduced();
|
||||||
var shuttleFixturesComp = Comp<FixturesComponent>(shuttleUid);
|
var shuttleFixturesComp = Comp<FixturesComponent>(shuttleUid);
|
||||||
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
|
var shuttleAABB = _gridQuery.GetComponent(shuttleUid).LocalAABB;
|
||||||
|
|
||||||
var isMap = HasComp<MapComponent>(targetGrid);
|
var isMap = HasComp<MapComponent>(targetGrid);
|
||||||
|
|
||||||
var validDockConfigs = new List<DockingConfig>();
|
|
||||||
var grids = new List<Entity<MapGridComponent>>();
|
var grids = new List<Entity<MapGridComponent>>();
|
||||||
if (shuttleDocks.Count > 0)
|
if (shuttleDocks.Count > 0)
|
||||||
{
|
{
|
||||||
// We'll try all combinations of shuttle docks and see which one is most suitable
|
// We'll try all combinations of shuttle docks and see which one is most suitable
|
||||||
foreach (var (dockUid, shuttleDock) in shuttleDocks)
|
foreach (var (dockUid, shuttleDock) in shuttleDocks)
|
||||||
{
|
{
|
||||||
var shuttleDockXform = xformQuery.GetComponent(dockUid);
|
var shuttleDockXform = _xformQuery.GetComponent(dockUid);
|
||||||
|
|
||||||
foreach (var (gridDockUid, gridDock) in gridDocks)
|
foreach (var (gridDockUid, gridDock) in gridDocks)
|
||||||
{
|
{
|
||||||
var gridXform = xformQuery.GetComponent(gridDockUid);
|
var gridXform = _xformQuery.GetComponent(gridDockUid);
|
||||||
|
|
||||||
if (!CanDock(
|
if (!CanDock(
|
||||||
shuttleDock, shuttleDockXform,
|
shuttleDock, shuttleDockXform,
|
||||||
@@ -167,15 +193,15 @@ public sealed partial class DockingSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Can't just use the AABB as we want to get bounds as tight as possible.
|
// 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));
|
var gridPosition = new EntityCoordinates(targetGrid, matty.Transform(Vector2.Zero));
|
||||||
spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, spawnPosition.ToMapPos(EntityManager, _transform));
|
var spawnPosition = new EntityCoordinates(targetGridXform.MapUid!.Value, gridPosition.ToMapPos(EntityManager, _transform));
|
||||||
|
|
||||||
// TODO: use tight bounds
|
// TODO: use tight bounds
|
||||||
var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetAngle, spawnPosition.Position);
|
var dockedBounds = new Box2Rotated(shuttleAABB.Translated(spawnPosition.Position), targetAngle, spawnPosition.Position);
|
||||||
|
|
||||||
// Check if there's no intersecting grids (AKA oh god it's docking at cargo).
|
// Check if there's no intersecting grids (AKA oh god it's docking at cargo).
|
||||||
grids.Clear();
|
grids.Clear();
|
||||||
_mapManager.FindGridsIntersecting(targetGridXform.MapID, dockedBounds, ref grids);
|
_mapManager.FindGridsIntersecting(targetGridXform.MapID, dockedBounds, ref grids, includeMap: false);
|
||||||
if (grids.Any(o => o.Owner != targetGrid && o.Owner != targetGridXform.MapUid))
|
if (grids.Any(o => o.Owner != targetGrid && o.Owner != targetGridXform.MapUid))
|
||||||
{
|
{
|
||||||
continue;
|
continue;
|
||||||
@@ -204,9 +230,9 @@ public sealed partial class DockingSystem
|
|||||||
|
|
||||||
if (!CanDock(
|
if (!CanDock(
|
||||||
other,
|
other,
|
||||||
xformQuery.GetComponent(otherUid),
|
_xformQuery.GetComponent(otherUid),
|
||||||
otherGrid,
|
otherGrid,
|
||||||
xformQuery.GetComponent(otherGridUid),
|
_xformQuery.GetComponent(otherGridUid),
|
||||||
shuttleAABB,
|
shuttleAABB,
|
||||||
targetGridAngle,
|
targetGridAngle,
|
||||||
shuttleFixturesComp, targetGridGrid,
|
shuttleFixturesComp, targetGridGrid,
|
||||||
@@ -234,7 +260,7 @@ public sealed partial class DockingSystem
|
|||||||
validDockConfigs.Add(new DockingConfig()
|
validDockConfigs.Add(new DockingConfig()
|
||||||
{
|
{
|
||||||
Docks = dockedPorts,
|
Docks = dockedPorts,
|
||||||
Coordinates = spawnPosition,
|
Coordinates = gridPosition,
|
||||||
Area = dockedAABB,
|
Area = dockedAABB,
|
||||||
Angle = targetAngle,
|
Angle = targetAngle,
|
||||||
});
|
});
|
||||||
@@ -242,9 +268,23 @@ public sealed partial class DockingSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return validDockConfigs;
|
||||||
|
}
|
||||||
|
|
||||||
|
private DockingConfig? GetDockingConfigPrivate(
|
||||||
|
EntityUid shuttleUid,
|
||||||
|
EntityUid targetGrid,
|
||||||
|
List<(EntityUid, DockingComponent)> shuttleDocks,
|
||||||
|
List<(EntityUid, DockingComponent)> gridDocks,
|
||||||
|
string? priorityTag = null)
|
||||||
|
{
|
||||||
|
var validDockConfigs = GetDockingConfigs(shuttleUid, targetGrid, shuttleDocks, gridDocks);
|
||||||
|
|
||||||
if (validDockConfigs.Count <= 0)
|
if (validDockConfigs.Count <= 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
|
var targetGridAngle = _transform.GetWorldRotation(targetGrid).Reduced();
|
||||||
|
|
||||||
// Prioritise by priority docks, then by maximum connected ports, then by most similar angle.
|
// Prioritise by priority docks, then by maximum connected ports, then by most similar angle.
|
||||||
validDockConfigs = validDockConfigs
|
validDockConfigs = validDockConfigs
|
||||||
.OrderByDescending(x => x.Docks.Any(docks =>
|
.OrderByDescending(x => x.Docks.Any(docks =>
|
||||||
|
|||||||
@@ -5,7 +5,10 @@ using Content.Server.Shuttles.Components;
|
|||||||
using Content.Server.Shuttles.Events;
|
using Content.Server.Shuttles.Events;
|
||||||
using Content.Shared.Doors;
|
using Content.Shared.Doors;
|
||||||
using Content.Shared.Doors.Components;
|
using Content.Shared.Doors.Components;
|
||||||
|
using Content.Shared.Popups;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
using Content.Shared.Shuttles.Events;
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
using Robust.Shared.Map;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
@@ -17,27 +20,32 @@ using Robust.Shared.Utility;
|
|||||||
|
|
||||||
namespace Content.Server.Shuttles.Systems
|
namespace Content.Server.Shuttles.Systems
|
||||||
{
|
{
|
||||||
public sealed partial class DockingSystem : EntitySystem
|
public sealed partial class DockingSystem : SharedDockingSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IMapManager _mapManager = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
[Dependency] private readonly DoorSystem _doorSystem = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtureSystem = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly PathfindingSystem _pathfinding = default!;
|
[Dependency] private readonly PathfindingSystem _pathfinding = default!;
|
||||||
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
|
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
|
||||||
[Dependency] private readonly SharedJointSystem _jointSystem = default!;
|
[Dependency] private readonly SharedJointSystem _jointSystem = default!;
|
||||||
[Dependency] private readonly SharedPhysicsSystem _physics = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
|
|
||||||
private const string DockingFixture = "docking";
|
|
||||||
private const string DockingJoint = "docking";
|
private const string DockingJoint = "docking";
|
||||||
private const float DockingRadius = 0.20f;
|
|
||||||
|
|
||||||
|
private EntityQuery<MapGridComponent> _gridQuery;
|
||||||
private EntityQuery<PhysicsComponent> _physicsQuery;
|
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||||
|
private EntityQuery<TransformComponent> _xformQuery;
|
||||||
|
|
||||||
|
private readonly HashSet<Entity<DockingComponent>> _dockingSet = new();
|
||||||
|
private readonly HashSet<Entity<DockingComponent, DoorBoltComponent>> _dockingBoltSet = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
|
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<DockingComponent, ComponentStartup>(OnStartup);
|
SubscribeLocalEvent<DockingComponent, ComponentStartup>(OnStartup);
|
||||||
SubscribeLocalEvent<DockingComponent, ComponentShutdown>(OnShutdown);
|
SubscribeLocalEvent<DockingComponent, ComponentShutdown>(OnShutdown);
|
||||||
@@ -48,15 +56,35 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
|
|
||||||
// Yes this isn't in shuttle console; it may be used by other systems technically.
|
// Yes this isn't in shuttle console; it may be used by other systems technically.
|
||||||
// in which case I would also add their subs here.
|
// in which case I would also add their subs here.
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, AutodockRequestMessage>(OnRequestAutodock);
|
SubscribeLocalEvent<ShuttleConsoleComponent, DockRequestMessage>(OnRequestDock);
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, StopAutodockRequestMessage>(OnRequestStopAutodock);
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, UndockRequestMessage>(OnRequestUndock);
|
SubscribeLocalEvent<ShuttleConsoleComponent, UndockRequestMessage>(OnRequestUndock);
|
||||||
}
|
}
|
||||||
|
|
||||||
public override void Update(float frameTime)
|
/// <summary>
|
||||||
|
/// Sets the docks for the provided entity as enabled or disabled.
|
||||||
|
/// </summary>
|
||||||
|
public void SetDocks(EntityUid gridUid, bool enabled)
|
||||||
{
|
{
|
||||||
base.Update(frameTime);
|
_dockingSet.Clear();
|
||||||
UpdateAutodock();
|
_lookup.GetChildEntities(gridUid, _dockingSet);
|
||||||
|
|
||||||
|
foreach (var dock in _dockingSet)
|
||||||
|
{
|
||||||
|
Undock(dock);
|
||||||
|
dock.Comp.Enabled = enabled;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void SetDockBolts(EntityUid gridUid, bool enabled)
|
||||||
|
{
|
||||||
|
_dockingBoltSet.Clear();
|
||||||
|
_lookup.GetChildEntities(gridUid, _dockingBoltSet);
|
||||||
|
|
||||||
|
foreach (var entity in _dockingBoltSet)
|
||||||
|
{
|
||||||
|
_doorSystem.TryClose(entity);
|
||||||
|
_doorSystem.SetBoltsDown((entity.Owner, entity.Comp2), enabled);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAutoClose(EntityUid uid, DockingComponent component, BeforeDoorAutoCloseEvent args)
|
private void OnAutoClose(EntityUid uid, DockingComponent component, BeforeDoorAutoCloseEvent args)
|
||||||
@@ -66,79 +94,6 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
args.Cancel();
|
args.Cancel();
|
||||||
}
|
}
|
||||||
|
|
||||||
private Entity<DockingComponent>? GetDockable(EntityUid uid, TransformComponent dockingXform)
|
|
||||||
{
|
|
||||||
// Did you know Saltern is the most dockable station?
|
|
||||||
|
|
||||||
// Assume the docking port itself (and its body) is valid
|
|
||||||
|
|
||||||
if (!HasComp<ShuttleComponent>(dockingXform.GridUid))
|
|
||||||
{
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transform = _physics.GetPhysicsTransform(uid, dockingXform);
|
|
||||||
var dockingFixture = _fixtureSystem.GetFixtureOrNull(uid, DockingFixture);
|
|
||||||
|
|
||||||
if (dockingFixture == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
Box2? aabb = null;
|
|
||||||
|
|
||||||
for (var i = 0; i < dockingFixture.Shape.ChildCount; i++)
|
|
||||||
{
|
|
||||||
aabb = aabb?.Union(dockingFixture.Shape.ComputeAABB(transform, i)) ?? dockingFixture.Shape.ComputeAABB(transform, i);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (aabb == null)
|
|
||||||
return null;
|
|
||||||
|
|
||||||
var enlargedAABB = aabb.Value.Enlarged(DockingRadius * 1.5f);
|
|
||||||
|
|
||||||
// Get any docking ports in range on other grids.
|
|
||||||
var grids = new List<Entity<MapGridComponent>>();
|
|
||||||
_mapManager.FindGridsIntersecting(dockingXform.MapID, enlargedAABB, ref grids);
|
|
||||||
foreach (var otherGrid in grids)
|
|
||||||
{
|
|
||||||
if (otherGrid.Owner == dockingXform.GridUid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
foreach (var ent in otherGrid.Comp.GetAnchoredEntities(enlargedAABB))
|
|
||||||
{
|
|
||||||
if (!TryComp(ent, out DockingComponent? otherDocking) ||
|
|
||||||
!otherDocking.Enabled ||
|
|
||||||
!TryComp(ent, out FixturesComponent? otherBody))
|
|
||||||
{
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
var otherTransform = _physics.GetPhysicsTransform(ent);
|
|
||||||
var otherDockingFixture = _fixtureSystem.GetFixtureOrNull(ent, DockingFixture, manager: otherBody);
|
|
||||||
|
|
||||||
if (otherDockingFixture == null)
|
|
||||||
{
|
|
||||||
DebugTools.Assert(false);
|
|
||||||
Log.Error($"Found null docking fixture on {ent}");
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (var i = 0; i < otherDockingFixture.Shape.ChildCount; i++)
|
|
||||||
{
|
|
||||||
var otherAABB = otherDockingFixture.Shape.ComputeAABB(otherTransform, i);
|
|
||||||
|
|
||||||
if (!aabb.Value.Intersects(otherAABB))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO: Need CollisionManager's GJK for accurate bounds
|
|
||||||
// Realistically I want 2 fixtures anyway but I'll deal with that later.
|
|
||||||
return (ent, otherDocking);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnShutdown(EntityUid uid, DockingComponent component, ComponentShutdown args)
|
private void OnShutdown(EntityUid uid, DockingComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
if (component.DockedWith == null ||
|
if (component.DockedWith == null ||
|
||||||
@@ -147,6 +102,13 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var gridUid = Transform(uid).GridUid;
|
||||||
|
|
||||||
|
if (gridUid != null && !Terminating(gridUid.Value))
|
||||||
|
{
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
Cleanup(uid, component);
|
Cleanup(uid, component);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -166,12 +128,6 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
Log.Error($"Tried to cleanup {dockAUid} but not docked?");
|
Log.Error($"Tried to cleanup {dockAUid} but not docked?");
|
||||||
|
|
||||||
dockA.DockedWith = null;
|
dockA.DockedWith = null;
|
||||||
if (dockA.DockJoint != null)
|
|
||||||
{
|
|
||||||
// We'll still cleanup the dock joint on release at least
|
|
||||||
_jointSystem.RemoveJoint(dockA.DockJoint);
|
|
||||||
}
|
|
||||||
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -200,12 +156,16 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
RaiseLocalEvent(msg);
|
RaiseLocalEvent(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStartup(EntityUid uid, DockingComponent component, ComponentStartup args)
|
private void OnStartup(Entity<DockingComponent> entity, ref ComponentStartup args)
|
||||||
{
|
{
|
||||||
// Use startup so transform already initialized
|
var uid = entity.Owner;
|
||||||
if (!EntityManager.GetComponent<TransformComponent>(uid).Anchored) return;
|
var component = entity.Comp;
|
||||||
|
|
||||||
EnableDocking(uid, component);
|
// Use startup so transform already initialized
|
||||||
|
if (!EntityManager.GetComponent<TransformComponent>(uid).Anchored)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SetDockingEnabled((uid, component), true);
|
||||||
|
|
||||||
// This little gem is for docking deserialization
|
// This little gem is for docking deserialization
|
||||||
if (component.DockedWith != null)
|
if (component.DockedWith != null)
|
||||||
@@ -217,75 +177,62 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
var otherDock = EntityManager.GetComponent<DockingComponent>(component.DockedWith.Value);
|
var otherDock = EntityManager.GetComponent<DockingComponent>(component.DockedWith.Value);
|
||||||
DebugTools.Assert(otherDock.DockedWith != null);
|
DebugTools.Assert(otherDock.DockedWith != null);
|
||||||
|
|
||||||
Dock(uid, component, component.DockedWith.Value, otherDock);
|
Dock((uid, component), (component.DockedWith.Value, otherDock));
|
||||||
DebugTools.Assert(component.Docked && otherDock.Docked);
|
DebugTools.Assert(component.Docked && otherDock.Docked);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnAnchorChange(EntityUid uid, DockingComponent component, ref AnchorStateChangedEvent args)
|
private void OnAnchorChange(Entity<DockingComponent> entity, ref AnchorStateChangedEvent args)
|
||||||
{
|
{
|
||||||
if (args.Anchored)
|
if (args.Anchored)
|
||||||
{
|
{
|
||||||
EnableDocking(uid, component);
|
SetDockingEnabled(entity, true);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
DisableDocking(uid, component);
|
SetDockingEnabled(entity, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
_console.RefreshShuttleConsoles();
|
_console.RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDockingReAnchor(EntityUid uid, DockingComponent component, ref ReAnchorEvent args)
|
private void OnDockingReAnchor(Entity<DockingComponent> entity, ref ReAnchorEvent args)
|
||||||
{
|
{
|
||||||
|
var uid = entity.Owner;
|
||||||
|
var component = entity.Comp;
|
||||||
|
|
||||||
if (!component.Docked)
|
if (!component.Docked)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
var otherDock = component.DockedWith;
|
var otherDock = component.DockedWith;
|
||||||
var other = Comp<DockingComponent>(otherDock!.Value);
|
var other = Comp<DockingComponent>(otherDock!.Value);
|
||||||
|
|
||||||
Undock(uid, component);
|
Undock(entity);
|
||||||
Dock(uid, component, otherDock.Value, other);
|
Dock((uid, component), (otherDock.Value, other));
|
||||||
_console.RefreshShuttleConsoles();
|
_console.RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void DisableDocking(EntityUid uid, DockingComponent component)
|
public void SetDockingEnabled(Entity<DockingComponent> entity, bool value)
|
||||||
{
|
{
|
||||||
if (!component.Enabled)
|
if (entity.Comp.Enabled == value)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
component.Enabled = false;
|
entity.Comp.Enabled = value;
|
||||||
|
|
||||||
if (component.DockedWith != null)
|
if (!entity.Comp.Enabled && entity.Comp.DockedWith != null)
|
||||||
{
|
{
|
||||||
Undock(uid, component);
|
Undock(entity);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void EnableDocking(EntityUid uid, DockingComponent component)
|
|
||||||
{
|
|
||||||
if (component.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
if (!TryComp(uid, out PhysicsComponent? physicsComponent))
|
|
||||||
return;
|
|
||||||
|
|
||||||
component.Enabled = true;
|
|
||||||
|
|
||||||
var shape = new PhysShapeCircle(DockingRadius, new Vector2(0f, -0.5f));
|
|
||||||
|
|
||||||
// Listen it makes intersection tests easier; you can probably dump this but it requires a bunch more boilerplate
|
|
||||||
// TODO: I want this to ideally be 2 fixtures to force them to have some level of alignment buuuttt
|
|
||||||
// I also need collisionmanager for that yet again so they get dis.
|
|
||||||
// TODO: CollisionManager is fine so get to work sloth chop chop.
|
|
||||||
_fixtureSystem.TryCreateFixture(uid, shape, DockingFixture, hard: false, body: physicsComponent);
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Docks 2 ports together and assumes it is valid.
|
/// Docks 2 ports together and assumes it is valid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void Dock(EntityUid dockAUid, DockingComponent dockA, EntityUid dockBUid, DockingComponent dockB)
|
public void Dock(Entity<DockingComponent> dockA, Entity<DockingComponent> dockB)
|
||||||
{
|
{
|
||||||
|
var dockAUid = dockA.Owner;
|
||||||
|
var dockBUid = dockB.Owner;
|
||||||
|
|
||||||
if (dockBUid.GetHashCode() < dockAUid.GetHashCode())
|
if (dockBUid.GetHashCode() < dockAUid.GetHashCode())
|
||||||
{
|
{
|
||||||
(dockA, dockB) = (dockB, dockA);
|
(dockA, dockB) = (dockB, dockA);
|
||||||
@@ -322,10 +269,10 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
WeldJoint joint;
|
WeldJoint joint;
|
||||||
|
|
||||||
// Pre-existing joint so use that.
|
// Pre-existing joint so use that.
|
||||||
if (dockA.DockJointId != null)
|
if (dockA.Comp.DockJointId != null)
|
||||||
{
|
{
|
||||||
DebugTools.Assert(dockB.DockJointId == dockA.DockJointId);
|
DebugTools.Assert(dockB.Comp.DockJointId == dockA.Comp.DockJointId);
|
||||||
joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, dockA.DockJointId);
|
joint = _jointSystem.GetOrCreateWeldJoint(gridA, gridB, dockA.Comp.DockJointId);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -345,15 +292,15 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
joint.Stiffness = stiffness;
|
joint.Stiffness = stiffness;
|
||||||
joint.Damping = damping;
|
joint.Damping = damping;
|
||||||
|
|
||||||
dockA.DockJoint = joint;
|
dockA.Comp.DockJoint = joint;
|
||||||
dockA.DockJointId = joint.ID;
|
dockA.Comp.DockJointId = joint.ID;
|
||||||
|
|
||||||
dockB.DockJoint = joint;
|
dockB.Comp.DockJoint = joint;
|
||||||
dockB.DockJointId = joint.ID;
|
dockB.Comp.DockJointId = joint.ID;
|
||||||
}
|
}
|
||||||
|
|
||||||
dockA.DockedWith = dockBUid;
|
dockA.Comp.DockedWith = dockBUid;
|
||||||
dockB.DockedWith = dockAUid;
|
dockB.Comp.DockedWith = dockAUid;
|
||||||
|
|
||||||
if (TryComp(dockAUid, out DoorComponent? doorA))
|
if (TryComp(dockAUid, out DoorComponent? doorA))
|
||||||
{
|
{
|
||||||
@@ -381,8 +328,8 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
|
|
||||||
if (_pathfinding.TryCreatePortal(dockAXform.Coordinates, dockBXform.Coordinates, out var handle))
|
if (_pathfinding.TryCreatePortal(dockAXform.Coordinates, dockBXform.Coordinates, out var handle))
|
||||||
{
|
{
|
||||||
dockA.PathfindHandle = handle;
|
dockA.Comp.PathfindHandle = handle;
|
||||||
dockB.PathfindHandle = handle;
|
dockB.Comp.PathfindHandle = handle;
|
||||||
}
|
}
|
||||||
|
|
||||||
var msg = new DockEvent
|
var msg = new DockEvent
|
||||||
@@ -393,77 +340,35 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
GridBUid = gridB,
|
GridBUid = gridB,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
RaiseLocalEvent(dockAUid, msg);
|
RaiseLocalEvent(dockAUid, msg);
|
||||||
RaiseLocalEvent(dockBUid, msg);
|
RaiseLocalEvent(dockBUid, msg);
|
||||||
RaiseLocalEvent(msg);
|
RaiseLocalEvent(msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool CanDock(EntityUid dockAUid, EntityUid dockBUid, DockingComponent dockA, DockingComponent dockB)
|
|
||||||
{
|
|
||||||
if (!dockA.Enabled ||
|
|
||||||
!dockB.Enabled ||
|
|
||||||
dockA.DockedWith != null ||
|
|
||||||
dockB.DockedWith != null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var fixtureA = _fixtureSystem.GetFixtureOrNull(dockAUid, DockingFixture);
|
|
||||||
var fixtureB = _fixtureSystem.GetFixtureOrNull(dockBUid, DockingFixture);
|
|
||||||
|
|
||||||
if (fixtureA == null || fixtureB == null)
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
var transformA = _physics.GetPhysicsTransform(dockAUid);
|
|
||||||
var transformB = _physics.GetPhysicsTransform(dockBUid);
|
|
||||||
var intersect = false;
|
|
||||||
|
|
||||||
for (var i = 0; i < fixtureA.Shape.ChildCount; i++)
|
|
||||||
{
|
|
||||||
var aabb = fixtureA.Shape.ComputeAABB(transformA, i);
|
|
||||||
|
|
||||||
for (var j = 0; j < fixtureB.Shape.ChildCount; j++)
|
|
||||||
{
|
|
||||||
var otherAABB = fixtureB.Shape.ComputeAABB(transformB, j);
|
|
||||||
if (!aabb.Intersects(otherAABB))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
// TODO: Need collisionmanager's GJK for accurate checks don't @ me son
|
|
||||||
intersect = true;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (intersect)
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return intersect;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Attempts to dock 2 ports together and will return early if it's not possible.
|
/// Attempts to dock 2 ports together and will return early if it's not possible.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private void TryDock(EntityUid dockAUid, DockingComponent dockA, Entity<DockingComponent> dockB)
|
private void TryDock(Entity<DockingComponent> dockA, Entity<DockingComponent> dockB)
|
||||||
{
|
{
|
||||||
if (!CanDock(dockAUid, dockB, dockA, dockB))
|
if (!CanDock(dockA, dockB))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
Dock(dockAUid, dockA, dockB, dockB);
|
Dock(dockA, dockB);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void Undock(EntityUid dockUid, DockingComponent dock)
|
public void Undock(Entity<DockingComponent> dock)
|
||||||
{
|
{
|
||||||
if (dock.DockedWith == null)
|
if (dock.Comp.DockedWith == null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
OnUndock(dockUid, dock.DockedWith.Value);
|
OnUndock(dock.Owner);
|
||||||
OnUndock(dock.DockedWith.Value, dockUid);
|
OnUndock(dock.Comp.DockedWith.Value);
|
||||||
Cleanup(dockUid, dock);
|
Cleanup(dock.Owner, dock);
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnUndock(EntityUid dockUid, EntityUid other)
|
private void OnUndock(EntityUid dockUid)
|
||||||
{
|
{
|
||||||
if (TerminatingOrDeleted(dockUid))
|
if (TerminatingOrDeleted(dockUid))
|
||||||
return;
|
return;
|
||||||
@@ -473,9 +378,97 @@ namespace Content.Server.Shuttles.Systems
|
|||||||
|
|
||||||
if (TryComp(dockUid, out DoorComponent? door) && _doorSystem.TryClose(dockUid, door))
|
if (TryComp(dockUid, out DoorComponent? door) && _doorSystem.TryClose(dockUid, door))
|
||||||
door.ChangeAirtight = true;
|
door.ChangeAirtight = true;
|
||||||
|
}
|
||||||
|
|
||||||
var recentlyDocked = EnsureComp<RecentlyDockedComponent>(dockUid);
|
private void OnRequestUndock(EntityUid uid, ShuttleConsoleComponent component, UndockRequestMessage args)
|
||||||
recentlyDocked.LastDocked = other;
|
{
|
||||||
|
if (!TryGetEntity(args.DockEntity, out var dockEnt) ||
|
||||||
|
!TryComp(dockEnt, out DockingComponent? dockComp))
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-undock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var dock = (dockEnt.Value, dockComp);
|
||||||
|
|
||||||
|
if (!CanUndock(dock))
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-undock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Undock(dock);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnRequestDock(EntityUid uid, ShuttleConsoleComponent component, DockRequestMessage args)
|
||||||
|
{
|
||||||
|
var shuttleUid = Transform(uid).GridUid;
|
||||||
|
|
||||||
|
if (!CanShuttleDock(shuttleUid))
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-dock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryGetEntity(args.DockEntity, out var ourDock) ||
|
||||||
|
!TryGetEntity(args.TargetDockEntity, out var targetDock) ||
|
||||||
|
!TryComp(ourDock, out DockingComponent? ourDockComp) ||
|
||||||
|
!TryComp(targetDock, out DockingComponent? targetDockComp))
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-dock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Cheating?
|
||||||
|
if (!TryComp(ourDock, out TransformComponent? xformA) ||
|
||||||
|
xformA.GridUid != shuttleUid)
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-dock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: Move the CanDock stuff to the port state and also validate that stuff
|
||||||
|
// Also need to check preventpilot + enabled / dockedwith
|
||||||
|
if (!CanDock((ourDock.Value, ourDockComp), (targetDock.Value, targetDockComp)))
|
||||||
|
{
|
||||||
|
_popup.PopupCursor(Loc.GetString("shuttle-console-dock-fail"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
Dock((ourDock.Value, ourDockComp), (targetDock.Value, targetDockComp));
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanUndock(Entity<DockingComponent?> dock)
|
||||||
|
{
|
||||||
|
if (!Resolve(dock, ref dock.Comp) ||
|
||||||
|
!dock.Comp.Docked)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if both docks can connect. Does not consider whether the shuttle allows it.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanDock(Entity<DockingComponent> dockA, Entity<DockingComponent> dockB)
|
||||||
|
{
|
||||||
|
if (!dockA.Comp.Enabled ||
|
||||||
|
!dockB.Comp.Enabled ||
|
||||||
|
dockA.Comp.DockedWith != null ||
|
||||||
|
dockB.Comp.DockedWith != null)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var xformA = Transform(dockA);
|
||||||
|
var xformB = Transform(dockB);
|
||||||
|
var (worldPosA, worldRotA) = XformSystem.GetWorldPositionRotation(xformA);
|
||||||
|
var (worldPosB, worldRotB) = XformSystem.GetWorldPositionRotation(xformB);
|
||||||
|
|
||||||
|
return CanDock(new MapCoordinates(worldPosA, xformA.MapID), worldRotA,
|
||||||
|
new MapCoordinates(worldPosB, xformB.MapID), worldRotB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -163,15 +163,15 @@ public sealed partial class EmergencyShuttleSystem
|
|||||||
|
|
||||||
if (!Deleted(centcomm.Entity))
|
if (!Deleted(centcomm.Entity))
|
||||||
{
|
{
|
||||||
_shuttle.FTLTravel(comp.EmergencyShuttle.Value, shuttle,
|
_shuttle.FTLToDock(comp.EmergencyShuttle.Value, shuttle,
|
||||||
centcomm.Entity.Value, _consoleAccumulator, TransitTime, true);
|
centcomm.Entity.Value, _consoleAccumulator, TransitTime);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!Deleted(centcomm.MapEntity))
|
if (!Deleted(centcomm.MapEntity))
|
||||||
{
|
{
|
||||||
// TODO: Need to get non-overlapping positions.
|
// TODO: Need to get non-overlapping positions.
|
||||||
_shuttle.FTLTravel(comp.EmergencyShuttle.Value, shuttle,
|
_shuttle.FTLToCoordinates(comp.EmergencyShuttle.Value, shuttle,
|
||||||
new EntityCoordinates(centcomm.MapEntity.Value,
|
new EntityCoordinates(centcomm.MapEntity.Value,
|
||||||
_random.NextVector2(1000f)), _consoleAccumulator, TransitTime);
|
_random.NextVector2(1000f)), _consoleAccumulator, TransitTime);
|
||||||
}
|
}
|
||||||
@@ -201,7 +201,7 @@ public sealed partial class EmergencyShuttleSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Don't dock them. If you do end up doing this then stagger launch.
|
// Don't dock them. If you do end up doing this then stagger launch.
|
||||||
_shuttle.FTLTravel(uid, shuttle, centcomm.Entity.Value, hyperspaceTime: TransitTime);
|
_shuttle.FTLToDock(uid, shuttle, centcomm.Entity.Value, hyperspaceTime: TransitTime);
|
||||||
RemCompDeferred<EscapePodComponent>(uid);
|
RemCompDeferred<EscapePodComponent>(uid);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,15 +217,18 @@ public sealed partial class EmergencyShuttleSystem
|
|||||||
// All the others.
|
// All the others.
|
||||||
if (_consoleAccumulator < minTime)
|
if (_consoleAccumulator < minTime)
|
||||||
{
|
{
|
||||||
var query = AllEntityQuery<StationCentcommComponent>();
|
var query = AllEntityQuery<StationCentcommComponent, TransformComponent>();
|
||||||
|
|
||||||
// Guarantees that emergency shuttle arrives first before anyone else can FTL.
|
// Guarantees that emergency shuttle arrives first before anyone else can FTL.
|
||||||
while (query.MoveNext(out var comp))
|
while (query.MoveNext(out var comp, out var centcommXform))
|
||||||
{
|
{
|
||||||
if (Deleted(comp.Entity))
|
if (Deleted(comp.Entity))
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
_shuttle.AddFTLDestination(comp.Entity.Value, true);
|
if (_shuttle.TryAddFTLDestination(centcommXform.MapID, true, out var ftlComp))
|
||||||
|
{
|
||||||
|
_shuttle.SetFTLWhitelist((centcommXform.MapUid!.Value, ftlComp), null);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -445,7 +445,7 @@ public sealed partial class EmergencyShuttleSystem : EntitySystem
|
|||||||
|
|
||||||
component.MapEntity = map;
|
component.MapEntity = map;
|
||||||
component.Entity = grid;
|
component.Entity = grid;
|
||||||
_shuttle.AddFTLDestination(grid.Value, false);
|
_shuttle.TryAddFTLDestination(mapId, false, out _);
|
||||||
}
|
}
|
||||||
|
|
||||||
public HashSet<EntityUid> GetCentcommMaps()
|
public HashSet<EntityUid> GetCentcommMaps()
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ namespace Content.Server.Shuttles.Systems;
|
|||||||
|
|
||||||
public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
|
public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly ShuttleConsoleSystem _console = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
[Dependency] private readonly UserInterfaceSystem _uiSystem = default!;
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
@@ -39,11 +40,20 @@ public sealed class RadarConsoleSystem : SharedRadarConsoleSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (_uiSystem.TryGetUi(uid, RadarConsoleUiKey.Key, out var bui))
|
if (_uiSystem.TryGetUi(uid, RadarConsoleUiKey.Key, out var bui))
|
||||||
_uiSystem.SetUiState(bui, new RadarConsoleBoundInterfaceState(
|
{
|
||||||
component.MaxRange,
|
NavInterfaceState state;
|
||||||
GetNetCoordinates(coordinates),
|
var docks = _console.GetAllDocks();
|
||||||
angle,
|
|
||||||
new List<DockingInterfaceState>()
|
if (coordinates != null && angle != null)
|
||||||
));
|
{
|
||||||
|
state = _console.GetNavState(uid, docks, coordinates.Value, angle.Value);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
state = _console.GetNavState(uid, docks);
|
||||||
|
}
|
||||||
|
|
||||||
|
_uiSystem.SetUiState(bui, new NavBoundUserInterfaceState(state));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -7,6 +7,20 @@ namespace Content.Server.Shuttles.Systems;
|
|||||||
|
|
||||||
public sealed partial class ShuttleConsoleSystem
|
public sealed partial class ShuttleConsoleSystem
|
||||||
{
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the drone console target if applicable otherwise returns itself.
|
||||||
|
/// </summary>
|
||||||
|
private EntityUid? GetDroneConsole(EntityUid consoleUid)
|
||||||
|
{
|
||||||
|
var getShuttleEv = new ConsoleShuttleEvent
|
||||||
|
{
|
||||||
|
Console = consoleUid,
|
||||||
|
};
|
||||||
|
|
||||||
|
RaiseLocalEvent(consoleUid, ref getShuttleEv);
|
||||||
|
return getShuttleEv.Console;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Refreshes all drone console entities.
|
/// Refreshes all drone console entities.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
160
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs
Normal file
160
Content.Server/Shuttles/Systems/ShuttleConsoleSystem.FTL.cs
Normal file
@@ -0,0 +1,160 @@
|
|||||||
|
using Content.Server.Shuttles.Components;
|
||||||
|
using Content.Server.Shuttles.Events;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.Events;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
|
namespace Content.Server.Shuttles.Systems;
|
||||||
|
|
||||||
|
public sealed partial class ShuttleConsoleSystem
|
||||||
|
{
|
||||||
|
private void InitializeFTL()
|
||||||
|
{
|
||||||
|
SubscribeLocalEvent<FTLBeaconComponent, ComponentStartup>(OnBeaconStartup);
|
||||||
|
SubscribeLocalEvent<FTLBeaconComponent, AnchorStateChangedEvent>(OnBeaconAnchorChanged);
|
||||||
|
|
||||||
|
SubscribeLocalEvent<FTLExclusionComponent, ComponentStartup>(OnExclusionStartup);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnExclusionStartup(Entity<FTLExclusionComponent> ent, ref ComponentStartup args)
|
||||||
|
{
|
||||||
|
RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeaconStartup(Entity<FTLBeaconComponent> ent, ref ComponentStartup args)
|
||||||
|
{
|
||||||
|
RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeaconAnchorChanged(Entity<FTLBeaconComponent> ent, ref AnchorStateChangedEvent args)
|
||||||
|
{
|
||||||
|
RefreshShuttleConsoles();
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnBeaconFTLMessage(Entity<ShuttleConsoleComponent> ent, ref ShuttleConsoleFTLBeaconMessage args)
|
||||||
|
{
|
||||||
|
var beaconEnt = GetEntity(args.Beacon);
|
||||||
|
if (!_xformQuery.TryGetComponent(beaconEnt, out var targetXform))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var nCoordinates = new NetCoordinates(GetNetEntity(targetXform.ParentUid), targetXform.LocalPosition);
|
||||||
|
|
||||||
|
// Check target exists
|
||||||
|
if (!_shuttle.CanFTLBeacon(nCoordinates))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var angle = args.Angle.Reduced();
|
||||||
|
var targetCoordinates = new EntityCoordinates(targetXform.MapUid!.Value, _transform.GetWorldPosition(targetXform));
|
||||||
|
|
||||||
|
ConsoleFTL(ent, true, targetCoordinates, angle, targetXform.MapID);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void OnPositionFTLMessage(Entity<ShuttleConsoleComponent> entity, ref ShuttleConsoleFTLPositionMessage args)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(args.Coordinates.MapId);
|
||||||
|
|
||||||
|
// If it's beacons only block all position messages.
|
||||||
|
if (!Exists(mapUid) || _shuttle.IsBeaconMap(mapUid))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
var targetCoordinates = new EntityCoordinates(mapUid, args.Coordinates.Position);
|
||||||
|
var angle = args.Angle.Reduced();
|
||||||
|
ConsoleFTL(entity, false, targetCoordinates, angle, args.Coordinates.MapId);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetBeacons(ref List<ShuttleBeaconObject>? beacons)
|
||||||
|
{
|
||||||
|
var beaconQuery = AllEntityQuery<FTLBeaconComponent>();
|
||||||
|
|
||||||
|
while (beaconQuery.MoveNext(out var destUid, out _))
|
||||||
|
{
|
||||||
|
var meta = _metaQuery.GetComponent(destUid);
|
||||||
|
var name = meta.EntityName;
|
||||||
|
|
||||||
|
if (string.IsNullOrEmpty(name))
|
||||||
|
name = Loc.GetString("shuttle-console-unknown");
|
||||||
|
|
||||||
|
// Can't travel to same map (yet)
|
||||||
|
var destXform = _xformQuery.GetComponent(destUid);
|
||||||
|
beacons ??= new List<ShuttleBeaconObject>();
|
||||||
|
beacons.Add(new ShuttleBeaconObject(GetNetEntity(destUid), GetNetCoordinates(destXform.Coordinates), name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void GetExclusions(ref List<ShuttleExclusionObject>? exclusions)
|
||||||
|
{
|
||||||
|
var query = AllEntityQuery<FTLExclusionComponent, TransformComponent>();
|
||||||
|
|
||||||
|
while (query.MoveNext(out var uid, out var comp, out var xform))
|
||||||
|
{
|
||||||
|
if (!comp.Enabled)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
exclusions ??= new List<ShuttleExclusionObject>();
|
||||||
|
exclusions.Add(new ShuttleExclusionObject(GetNetCoordinates(xform.Coordinates), comp.Range, Loc.GetString("shuttle-console-exclusion")));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles shuttle console FTLs.
|
||||||
|
/// </summary>
|
||||||
|
private void ConsoleFTL(Entity<ShuttleConsoleComponent> ent, bool beacon, EntityCoordinates targetCoordinates, Angle targetAngle, MapId targetMap)
|
||||||
|
{
|
||||||
|
var consoleUid = GetDroneConsole(ent.Owner);
|
||||||
|
|
||||||
|
if (consoleUid == null)
|
||||||
|
return;
|
||||||
|
|
||||||
|
var shuttleUid = _xformQuery.GetComponent(consoleUid.Value).GridUid;
|
||||||
|
|
||||||
|
if (!TryComp(shuttleUid, out ShuttleComponent? shuttleComp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
// Check shuttle can even FTL
|
||||||
|
if (!_shuttle.CanFTL(shuttleUid.Value, out var reason))
|
||||||
|
{
|
||||||
|
// TODO: Session popup
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check shuttle can FTL to this target.
|
||||||
|
if (!_shuttle.CanFTLTo(shuttleUid.Value, targetMap))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShuttleExclusionObject>? exclusions = null;
|
||||||
|
GetExclusions(ref exclusions);
|
||||||
|
|
||||||
|
if (!beacon && !_shuttle.FTLFree(shuttleUid.Value, targetCoordinates, targetAngle, exclusions))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryComp(shuttleUid.Value, out PhysicsComponent? shuttlePhysics))
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Client sends the "adjusted" coordinates and we adjust it back to get the actual transform coordinates.
|
||||||
|
var adjustedCoordinates = targetCoordinates.Offset(targetAngle.RotateVec(-shuttlePhysics.LocalCenter));
|
||||||
|
|
||||||
|
var tagEv = new FTLTagEvent();
|
||||||
|
RaiseLocalEvent(shuttleUid.Value, ref tagEv);
|
||||||
|
|
||||||
|
var ev = new ShuttleConsoleFTLTravelStartEvent(ent.Owner);
|
||||||
|
RaiseLocalEvent(ref ev);
|
||||||
|
|
||||||
|
_shuttle.FTLToCoordinates(shuttleUid.Value, shuttleComp, adjustedCoordinates, targetAngle);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -3,7 +3,6 @@ using Content.Server.Power.EntitySystems;
|
|||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Server.Shuttles.Events;
|
using Content.Server.Shuttles.Events;
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Systems;
|
||||||
using Content.Server.UserInterface;
|
|
||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Alert;
|
using Content.Shared.Alert;
|
||||||
using Content.Shared.Popups;
|
using Content.Shared.Popups;
|
||||||
@@ -13,12 +12,11 @@ using Content.Shared.Shuttles.Events;
|
|||||||
using Content.Shared.Shuttles.Systems;
|
using Content.Shared.Shuttles.Systems;
|
||||||
using Content.Shared.Tag;
|
using Content.Shared.Tag;
|
||||||
using Content.Shared.Movement.Systems;
|
using Content.Shared.Movement.Systems;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
using Robust.Server.GameObjects;
|
using Robust.Server.GameObjects;
|
||||||
using Robust.Shared.Collections;
|
using Robust.Shared.Collections;
|
||||||
using Robust.Shared.GameStates;
|
using Robust.Shared.GameStates;
|
||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Physics.Components;
|
|
||||||
using Robust.Shared.Timing;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
using Content.Shared.UserInterface;
|
using Content.Shared.UserInterface;
|
||||||
|
|
||||||
@@ -26,27 +24,38 @@ namespace Content.Server.Shuttles.Systems;
|
|||||||
|
|
||||||
public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
||||||
{
|
{
|
||||||
[Dependency] private readonly IGameTiming _timing = default!;
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
[Dependency] private readonly ActionBlockerSystem _blocker = default!;
|
||||||
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
[Dependency] private readonly AlertsSystem _alertsSystem = default!;
|
||||||
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||||
|
[Dependency] private readonly SharedTransformSystem _transform = default!;
|
||||||
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
[Dependency] private readonly ShuttleSystem _shuttle = default!;
|
||||||
[Dependency] private readonly StationSystem _station = default!;
|
[Dependency] private readonly StationSystem _station = default!;
|
||||||
[Dependency] private readonly TagSystem _tags = default!;
|
[Dependency] private readonly TagSystem _tags = default!;
|
||||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||||
[Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!;
|
[Dependency] private readonly SharedContentEyeSystem _eyeSystem = default!;
|
||||||
|
|
||||||
|
private EntityQuery<MetaDataComponent> _metaQuery;
|
||||||
|
private EntityQuery<TransformComponent> _xformQuery;
|
||||||
|
|
||||||
|
private readonly HashSet<Entity<ShuttleConsoleComponent>> _consoles = new();
|
||||||
|
|
||||||
public override void Initialize()
|
public override void Initialize()
|
||||||
{
|
{
|
||||||
base.Initialize();
|
base.Initialize();
|
||||||
|
|
||||||
|
_metaQuery = GetEntityQuery<MetaDataComponent>();
|
||||||
|
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(OnConsoleShutdown);
|
SubscribeLocalEvent<ShuttleConsoleComponent, ComponentShutdown>(OnConsoleShutdown);
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(OnConsolePowerChange);
|
SubscribeLocalEvent<ShuttleConsoleComponent, PowerChangedEvent>(OnConsolePowerChange);
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChange);
|
SubscribeLocalEvent<ShuttleConsoleComponent, AnchorStateChangedEvent>(OnConsoleAnchorChange);
|
||||||
SubscribeLocalEvent<ShuttleConsoleComponent, ActivatableUIOpenAttemptEvent>(OnConsoleUIOpenAttempt);
|
SubscribeLocalEvent<ShuttleConsoleComponent, ActivatableUIOpenAttemptEvent>(OnConsoleUIOpenAttempt);
|
||||||
Subs.BuiEvents<ShuttleConsoleComponent>(ShuttleConsoleUiKey.Key, subs =>
|
Subs.BuiEvents<ShuttleConsoleComponent>(ShuttleConsoleUiKey.Key, subs =>
|
||||||
{
|
{
|
||||||
subs.Event<ShuttleConsoleFTLRequestMessage>(OnDestinationMessage);
|
subs.Event<ShuttleConsoleFTLBeaconMessage>(OnBeaconFTLMessage);
|
||||||
|
subs.Event<ShuttleConsoleFTLPositionMessage>(OnPositionFTLMessage);
|
||||||
subs.Event<BoundUIClosedEvent>(OnConsoleUIClose);
|
subs.Event<BoundUIClosedEvent>(OnConsoleUIClose);
|
||||||
});
|
});
|
||||||
|
|
||||||
@@ -60,11 +69,12 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
SubscribeLocalEvent<DockEvent>(OnDock);
|
SubscribeLocalEvent<DockEvent>(OnDock);
|
||||||
SubscribeLocalEvent<UndockEvent>(OnUndock);
|
SubscribeLocalEvent<UndockEvent>(OnUndock);
|
||||||
|
|
||||||
SubscribeLocalEvent<PilotComponent, MoveEvent>(HandlePilotMove);
|
|
||||||
SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
|
SubscribeLocalEvent<PilotComponent, ComponentGetState>(OnGetState);
|
||||||
|
|
||||||
SubscribeLocalEvent<FTLDestinationComponent, ComponentStartup>(OnFtlDestStartup);
|
SubscribeLocalEvent<FTLDestinationComponent, ComponentStartup>(OnFtlDestStartup);
|
||||||
SubscribeLocalEvent<FTLDestinationComponent, ComponentShutdown>(OnFtlDestShutdown);
|
SubscribeLocalEvent<FTLDestinationComponent, ComponentShutdown>(OnFtlDestShutdown);
|
||||||
|
|
||||||
|
InitializeFTL();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnFtlDestStartup(EntityUid uid, FTLDestinationComponent component, ComponentStartup args)
|
private void OnFtlDestStartup(EntityUid uid, FTLDestinationComponent component, ComponentStartup args)
|
||||||
@@ -77,65 +87,6 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
RefreshShuttleConsoles();
|
RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnDestinationMessage(EntityUid uid, ShuttleConsoleComponent component,
|
|
||||||
ShuttleConsoleFTLRequestMessage args)
|
|
||||||
{
|
|
||||||
var destination = GetEntity(args.Destination);
|
|
||||||
|
|
||||||
if (!TryComp<FTLDestinationComponent>(destination, out var dest))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!dest.Enabled)
|
|
||||||
return;
|
|
||||||
|
|
||||||
EntityUid? entity = uid;
|
|
||||||
|
|
||||||
var getShuttleEv = new ConsoleShuttleEvent
|
|
||||||
{
|
|
||||||
Console = uid,
|
|
||||||
};
|
|
||||||
|
|
||||||
RaiseLocalEvent(entity.Value, ref getShuttleEv);
|
|
||||||
entity = getShuttleEv.Console;
|
|
||||||
|
|
||||||
if (!TryComp<TransformComponent>(entity, out var xform) ||
|
|
||||||
!TryComp<ShuttleComponent>(xform.GridUid, out var shuttle))
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dest.Whitelist?.IsValid(entity.Value, EntityManager) == false &&
|
|
||||||
dest.Whitelist?.IsValid(xform.GridUid.Value, EntityManager) == false)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var shuttleUid = xform.GridUid.Value;
|
|
||||||
|
|
||||||
if (HasComp<FTLComponent>(shuttleUid))
|
|
||||||
{
|
|
||||||
_popup.PopupCursor(Loc.GetString("shuttle-console-in-ftl"), args.Session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_shuttle.CanFTL(xform.GridUid, out var reason))
|
|
||||||
{
|
|
||||||
_popup.PopupCursor(reason, args.Session);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
var dock = HasComp<MapComponent>(destination) && HasComp<MapGridComponent>(destination);
|
|
||||||
var tagEv = new FTLTagEvent();
|
|
||||||
RaiseLocalEvent(xform.GridUid.Value, ref tagEv);
|
|
||||||
|
|
||||||
var ev = new ShuttleConsoleFTLTravelStartEvent(uid);
|
|
||||||
RaiseLocalEvent(ref ev);
|
|
||||||
|
|
||||||
_shuttle.FTLTravel(xform.GridUid.Value, shuttle, destination, dock: dock, priorityTag: tagEv.Tag);
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnDock(DockEvent ev)
|
private void OnDock(DockEvent ev)
|
||||||
{
|
{
|
||||||
RefreshShuttleConsoles();
|
RefreshShuttleConsoles();
|
||||||
@@ -146,10 +97,21 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
RefreshShuttleConsoles();
|
RefreshShuttleConsoles();
|
||||||
}
|
}
|
||||||
|
|
||||||
public void RefreshShuttleConsoles(EntityUid _)
|
/// <summary>
|
||||||
|
/// Refreshes all the shuttle console data for a particular grid.
|
||||||
|
/// </summary>
|
||||||
|
public void RefreshShuttleConsoles(EntityUid gridUid)
|
||||||
{
|
{
|
||||||
// TODO: Should really call this per shuttle in some instances.
|
var exclusions = new List<ShuttleExclusionObject>();
|
||||||
RefreshShuttleConsoles();
|
GetExclusions(ref exclusions);
|
||||||
|
_consoles.Clear();
|
||||||
|
_lookup.GetChildEntities(gridUid, _consoles);
|
||||||
|
DockingInterfaceState? dockState = null;
|
||||||
|
|
||||||
|
foreach (var entity in _consoles)
|
||||||
|
{
|
||||||
|
UpdateState(entity, ref dockState);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -157,12 +119,14 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public void RefreshShuttleConsoles()
|
public void RefreshShuttleConsoles()
|
||||||
{
|
{
|
||||||
var docks = GetAllDocks();
|
var exclusions = new List<ShuttleExclusionObject>();
|
||||||
|
GetExclusions(ref exclusions);
|
||||||
var query = AllEntityQuery<ShuttleConsoleComponent>();
|
var query = AllEntityQuery<ShuttleConsoleComponent>();
|
||||||
|
DockingInterfaceState? dockState = null;
|
||||||
|
|
||||||
while (query.MoveNext(out var uid, out var _))
|
while (query.MoveNext(out var uid, out _))
|
||||||
{
|
{
|
||||||
UpdateState(uid, docks);
|
UpdateState(uid,ref dockState);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -177,12 +141,6 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// In case they D/C should still clean them up.
|
|
||||||
foreach (var comp in EntityQuery<AutoDockComponent>(true))
|
|
||||||
{
|
|
||||||
comp.Requesters.Remove(user);
|
|
||||||
}
|
|
||||||
|
|
||||||
RemovePilot(user);
|
RemovePilot(user);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -196,12 +154,14 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
private void OnConsoleAnchorChange(EntityUid uid, ShuttleConsoleComponent component,
|
private void OnConsoleAnchorChange(EntityUid uid, ShuttleConsoleComponent component,
|
||||||
ref AnchorStateChangedEvent args)
|
ref AnchorStateChangedEvent args)
|
||||||
{
|
{
|
||||||
UpdateState(uid);
|
DockingInterfaceState? dockState = null;
|
||||||
|
UpdateState(uid, ref dockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnConsolePowerChange(EntityUid uid, ShuttleConsoleComponent component, ref PowerChangedEvent args)
|
private void OnConsolePowerChange(EntityUid uid, ShuttleConsoleComponent component, ref PowerChangedEvent args)
|
||||||
{
|
{
|
||||||
UpdateState(uid);
|
DockingInterfaceState? dockState = null;
|
||||||
|
UpdateState(uid, ref dockState);
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TryPilot(EntityUid user, EntityUid uid)
|
private bool TryPilot(EntityUid user, EntityUid uid)
|
||||||
@@ -239,33 +199,38 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
/// <summary>
|
/// <summary>
|
||||||
/// Returns the position and angle of all dockingcomponents.
|
/// Returns the position and angle of all dockingcomponents.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
private List<DockingInterfaceState> GetAllDocks()
|
public Dictionary<NetEntity, List<DockingPortState>> GetAllDocks()
|
||||||
{
|
{
|
||||||
// TODO: NEED TO MAKE SURE THIS UPDATES ON ANCHORING CHANGES!
|
// TODO: NEED TO MAKE SURE THIS UPDATES ON ANCHORING CHANGES!
|
||||||
var result = new List<DockingInterfaceState>();
|
var result = new Dictionary<NetEntity, List<DockingPortState>>();
|
||||||
var query = AllEntityQuery<DockingComponent, TransformComponent>();
|
var query = AllEntityQuery<DockingComponent, TransformComponent, MetaDataComponent>();
|
||||||
|
|
||||||
while (query.MoveNext(out var uid, out var comp, out var xform))
|
while (query.MoveNext(out var uid, out var comp, out var xform, out var metadata))
|
||||||
{
|
{
|
||||||
if (xform.ParentUid != xform.GridUid)
|
if (xform.ParentUid != xform.GridUid)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var state = new DockingInterfaceState()
|
var gridDocks = result.GetOrNew(GetNetEntity(xform.GridUid.Value));
|
||||||
|
|
||||||
|
var state = new DockingPortState()
|
||||||
{
|
{
|
||||||
|
Name = metadata.EntityName,
|
||||||
Coordinates = GetNetCoordinates(xform.Coordinates),
|
Coordinates = GetNetCoordinates(xform.Coordinates),
|
||||||
Angle = xform.LocalRotation,
|
Angle = xform.LocalRotation,
|
||||||
Entity = GetNetEntity(uid),
|
Entity = GetNetEntity(uid),
|
||||||
Connected = comp.Docked,
|
GridDockedWith =
|
||||||
Color = comp.RadarColor,
|
_xformQuery.TryGetComponent(comp.DockedWith, out var otherDockXform) ?
|
||||||
HighlightedColor = comp.HighlightedRadarColor,
|
GetNetEntity(otherDockXform.GridUid) :
|
||||||
|
null,
|
||||||
};
|
};
|
||||||
result.Add(state);
|
|
||||||
|
gridDocks.Add(state);
|
||||||
}
|
}
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void UpdateState(EntityUid consoleUid, List<DockingInterfaceState>? docks = null)
|
private void UpdateState(EntityUid consoleUid, ref DockingInterfaceState? dockState)
|
||||||
{
|
{
|
||||||
EntityUid? entity = consoleUid;
|
EntityUid? entity = consoleUid;
|
||||||
|
|
||||||
@@ -278,77 +243,26 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
entity = getShuttleEv.Console;
|
entity = getShuttleEv.Console;
|
||||||
|
|
||||||
TryComp<TransformComponent>(entity, out var consoleXform);
|
TryComp<TransformComponent>(entity, out var consoleXform);
|
||||||
TryComp<RadarConsoleComponent>(entity, out var radar);
|
|
||||||
var range = radar?.MaxRange ?? SharedRadarConsoleSystem.DefaultMaxRange;
|
|
||||||
|
|
||||||
var shuttleGridUid = consoleXform?.GridUid;
|
var shuttleGridUid = consoleXform?.GridUid;
|
||||||
|
|
||||||
var destinations = new List<(NetEntity, string, bool)>();
|
NavInterfaceState navState;
|
||||||
var ftlState = FTLState.Available;
|
ShuttleMapInterfaceState mapState;
|
||||||
var ftlTime = TimeSpan.Zero;
|
dockState ??= GetDockState();
|
||||||
|
|
||||||
if (TryComp<FTLComponent>(shuttleGridUid, out var shuttleFtl))
|
if (shuttleGridUid != null && entity != null)
|
||||||
{
|
{
|
||||||
ftlState = shuttleFtl.State;
|
navState = GetNavState(entity.Value, dockState.Docks);
|
||||||
ftlTime = _timing.CurTime + TimeSpan.FromSeconds(shuttleFtl.Accumulator);
|
mapState = GetMapState(shuttleGridUid.Value);
|
||||||
}
|
}
|
||||||
|
else
|
||||||
// Mass too large
|
|
||||||
if (entity != null && shuttleGridUid != null &&
|
|
||||||
(!TryComp<PhysicsComponent>(shuttleGridUid, out var shuttleBody) || shuttleBody.Mass < 1000f))
|
|
||||||
{
|
{
|
||||||
var metaQuery = GetEntityQuery<MetaDataComponent>();
|
navState = new NavInterfaceState(0f, null, null, new Dictionary<NetEntity, List<DockingPortState>>());
|
||||||
|
mapState = new ShuttleMapInterfaceState(FTLState.Invalid, 0f, new List<ShuttleBeaconObject>(), new List<ShuttleExclusionObject>());
|
||||||
// Can't go anywhere when in FTL.
|
|
||||||
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.
|
|
||||||
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 (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(destUid);
|
|
||||||
var name = meta.EntityName;
|
|
||||||
|
|
||||||
if (string.IsNullOrEmpty(name))
|
|
||||||
name = Loc.GetString("shuttle-console-unknown");
|
|
||||||
|
|
||||||
var canTravel = !locked &&
|
|
||||||
comp.Enabled &&
|
|
||||||
(!TryComp<FTLComponent>(destUid, out var ftl) || ftl.State == FTLState.Cooldown);
|
|
||||||
|
|
||||||
// Can't travel to same map (yet)
|
|
||||||
if (canTravel && consoleXform?.MapUid == Transform(destUid).MapUid)
|
|
||||||
{
|
|
||||||
canTravel = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
destinations.Add((GetNetEntity(destUid), name, canTravel));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
docks ??= GetAllDocks();
|
|
||||||
|
|
||||||
if (_ui.TryGetUi(consoleUid, ShuttleConsoleUiKey.Key, out var bui))
|
if (_ui.TryGetUi(consoleUid, ShuttleConsoleUiKey.Key, out var bui))
|
||||||
{
|
{
|
||||||
_ui.SetUiState(bui, new ShuttleConsoleBoundInterfaceState(
|
_ui.SetUiState(bui, new ShuttleBoundUserInterfaceState(navState, mapState, dockState));
|
||||||
ftlState,
|
|
||||||
ftlTime,
|
|
||||||
destinations,
|
|
||||||
range,
|
|
||||||
GetNetCoordinates(consoleXform?.Coordinates),
|
|
||||||
consoleXform?.LocalRotation,
|
|
||||||
docks
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -376,27 +290,6 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// If pilot is moved then we'll stop them from piloting.
|
|
||||||
/// </summary>
|
|
||||||
private void HandlePilotMove(EntityUid uid, PilotComponent component, ref MoveEvent args)
|
|
||||||
{
|
|
||||||
if (component.Console == null || component.Position == null)
|
|
||||||
{
|
|
||||||
DebugTools.Assert(component.Position == null && component.Console == null);
|
|
||||||
EntityManager.RemoveComponent<PilotComponent>(uid);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (args.NewPosition.TryDistance(EntityManager, component.Position.Value, out var distance) &&
|
|
||||||
distance < PilotComponent.BreakDistance)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
RemovePilot(uid, component);
|
|
||||||
}
|
|
||||||
|
|
||||||
protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
|
protected override void HandlePilotShutdown(EntityUid uid, PilotComponent component, ComponentShutdown args)
|
||||||
{
|
{
|
||||||
base.HandlePilotShutdown(uid, component, args);
|
base.HandlePilotShutdown(uid, component, args);
|
||||||
@@ -467,4 +360,70 @@ public sealed partial class ShuttleConsoleSystem : SharedShuttleConsoleSystem
|
|||||||
RemovePilot(pilot, pilotComponent);
|
RemovePilot(pilot, pilotComponent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specific for a particular shuttle.
|
||||||
|
/// </summary>
|
||||||
|
public NavInterfaceState GetNavState(Entity<RadarConsoleComponent?, TransformComponent?> entity, Dictionary<NetEntity, List<DockingPortState>> docks)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp1, ref entity.Comp2))
|
||||||
|
return new NavInterfaceState(SharedRadarConsoleSystem.DefaultMaxRange, null, null, docks);
|
||||||
|
|
||||||
|
return GetNavState(
|
||||||
|
entity,
|
||||||
|
docks,
|
||||||
|
entity.Comp2.Coordinates,
|
||||||
|
entity.Comp2.LocalRotation);
|
||||||
|
}
|
||||||
|
|
||||||
|
public NavInterfaceState GetNavState(
|
||||||
|
Entity<RadarConsoleComponent?, TransformComponent?> entity,
|
||||||
|
Dictionary<NetEntity, List<DockingPortState>> docks,
|
||||||
|
EntityCoordinates coordinates,
|
||||||
|
Angle angle)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp1, ref entity.Comp2))
|
||||||
|
return new NavInterfaceState(SharedRadarConsoleSystem.DefaultMaxRange, GetNetCoordinates(coordinates), angle, docks);
|
||||||
|
|
||||||
|
return new NavInterfaceState(
|
||||||
|
entity.Comp1.MaxRange,
|
||||||
|
GetNetCoordinates(coordinates),
|
||||||
|
angle,
|
||||||
|
docks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Global for all shuttles.
|
||||||
|
/// </summary>
|
||||||
|
/// <returns></returns>
|
||||||
|
public DockingInterfaceState GetDockState()
|
||||||
|
{
|
||||||
|
var docks = GetAllDocks();
|
||||||
|
return new DockingInterfaceState(docks);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Specific to a particular shuttle.
|
||||||
|
/// </summary>
|
||||||
|
public ShuttleMapInterfaceState GetMapState(Entity<FTLComponent?> shuttle)
|
||||||
|
{
|
||||||
|
FTLState ftlState = FTLState.Available;
|
||||||
|
float stateDuration = 0f;
|
||||||
|
|
||||||
|
if (Resolve(shuttle, ref shuttle.Comp, false) && shuttle.Comp.LifeStage < ComponentLifeStage.Stopped)
|
||||||
|
{
|
||||||
|
ftlState = shuttle.Comp.State;
|
||||||
|
stateDuration = _shuttle.GetStateDuration(shuttle.Comp);
|
||||||
|
}
|
||||||
|
|
||||||
|
List<ShuttleBeaconObject>? beacons = null;
|
||||||
|
List<ShuttleExclusionObject>? exclusions = null;
|
||||||
|
GetBeacons(ref beacons);
|
||||||
|
GetExclusions(ref exclusions);
|
||||||
|
|
||||||
|
return new ShuttleMapInterfaceState(
|
||||||
|
ftlState, stateDuration,
|
||||||
|
beacons ?? new List<ShuttleBeaconObject>(),
|
||||||
|
exclusions ?? new List<ShuttleExclusionObject>());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -3,16 +3,16 @@ using System.Linq;
|
|||||||
using System.Numerics;
|
using System.Numerics;
|
||||||
using Content.Server.Shuttles.Components;
|
using Content.Server.Shuttles.Components;
|
||||||
using Content.Server.Shuttles.Events;
|
using Content.Server.Shuttles.Events;
|
||||||
using Content.Server.Station.Systems;
|
using Content.Server.Station.Events;
|
||||||
using Content.Shared.Body.Components;
|
using Content.Shared.Body.Components;
|
||||||
using Content.Shared.Buckle.Components;
|
using Content.Shared.Buckle.Components;
|
||||||
using Content.Shared.Doors.Components;
|
|
||||||
using Content.Shared.Ghost;
|
using Content.Shared.Ghost;
|
||||||
using Content.Shared.Maps;
|
using Content.Shared.Maps;
|
||||||
using Content.Shared.Parallax;
|
using Content.Shared.Parallax;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Content.Shared.Shuttles.Systems;
|
using Content.Shared.Shuttles.Systems;
|
||||||
using Content.Shared.StatusEffect;
|
using Content.Shared.StatusEffect;
|
||||||
|
using Content.Shared.Whitelist;
|
||||||
using JetBrains.Annotations;
|
using JetBrains.Annotations;
|
||||||
using Robust.Shared.Audio;
|
using Robust.Shared.Audio;
|
||||||
using Robust.Shared.Audio.Components;
|
using Robust.Shared.Audio.Components;
|
||||||
@@ -21,8 +21,8 @@ using Robust.Shared.Map;
|
|||||||
using Robust.Shared.Map.Components;
|
using Robust.Shared.Map.Components;
|
||||||
using Robust.Shared.Physics;
|
using Robust.Shared.Physics;
|
||||||
using Robust.Shared.Physics.Components;
|
using Robust.Shared.Physics.Components;
|
||||||
using Robust.Shared.Player;
|
|
||||||
using Robust.Shared.Utility;
|
using Robust.Shared.Utility;
|
||||||
|
using FTLMapComponent = Content.Shared.Shuttles.Components.FTLMapComponent;
|
||||||
|
|
||||||
namespace Content.Server.Shuttles.Systems;
|
namespace Content.Server.Shuttles.Systems;
|
||||||
|
|
||||||
@@ -32,18 +32,11 @@ public sealed partial class ShuttleSystem
|
|||||||
* This is a way to move a shuttle from one location to another, via an intermediate map for fanciness.
|
* This is a way to move a shuttle from one location to another, via an intermediate map for fanciness.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
private MapId? _hyperSpaceMap;
|
|
||||||
|
|
||||||
public const float DefaultStartupTime = 5.5f;
|
public const float DefaultStartupTime = 5.5f;
|
||||||
public const float DefaultTravelTime = 20f;
|
public const float DefaultTravelTime = 20f;
|
||||||
public const float DefaultArrivalTime = 5f;
|
public const float DefaultArrivalTime = 5f;
|
||||||
private const float FTLCooldown = 10f;
|
private const float FTLCooldown = 10f;
|
||||||
private const float ShuttleFTLRange = 100f;
|
public const float FTLMassLimit = 100000f;
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Minimum mass a grid needs to be to block a shuttle recall.
|
|
||||||
/// </summary>
|
|
||||||
public const float ShuttleFTLMassThreshold = 300f;
|
|
||||||
|
|
||||||
// I'm too lazy to make CVars.
|
// I'm too lazy to make CVars.
|
||||||
|
|
||||||
@@ -51,7 +44,7 @@ public sealed partial class ShuttleSystem
|
|||||||
{
|
{
|
||||||
Params = AudioParams.Default.WithVolume(-5f),
|
Params = AudioParams.Default.WithVolume(-5f),
|
||||||
};
|
};
|
||||||
// private SoundSpecifier _travelSound = new SoundPathSpecifier();
|
|
||||||
private readonly SoundSpecifier _arrivalSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_end.ogg")
|
private readonly SoundSpecifier _arrivalSound = new SoundPathSpecifier("/Audio/Effects/Shuttle/hyperspace_end.ogg")
|
||||||
{
|
{
|
||||||
Params = AudioParams.Default.WithVolume(-5f),
|
Params = AudioParams.Default.WithVolume(-5f),
|
||||||
@@ -59,7 +52,9 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
private readonly TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5);
|
private readonly TimeSpan _hyperspaceKnockdownTime = TimeSpan.FromSeconds(5);
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
/// Left-side of the station we're allowed to use
|
/// Left-side of the station we're allowed to use
|
||||||
|
/// </summary>
|
||||||
private float _index;
|
private float _index;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
@@ -72,12 +67,7 @@ public sealed partial class ShuttleSystem
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
private const int FTLProximityIterations = 3;
|
private const int FTLProximityIterations = 3;
|
||||||
|
|
||||||
/// <summary>
|
private readonly HashSet<EntityUid> _lookupEnts = new();
|
||||||
/// Minimum mass for an FTL destination
|
|
||||||
/// </summary>
|
|
||||||
public const float FTLDestinationMass = 500f;
|
|
||||||
|
|
||||||
private HashSet<EntityUid> _lookupEnts = new();
|
|
||||||
|
|
||||||
private EntityQuery<BodyComponent> _bodyQuery;
|
private EntityQuery<BodyComponent> _bodyQuery;
|
||||||
private EntityQuery<BuckleComponent> _buckleQuery;
|
private EntityQuery<BuckleComponent> _buckleQuery;
|
||||||
@@ -88,68 +78,112 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
private void InitializeFTL()
|
private void InitializeFTL()
|
||||||
{
|
{
|
||||||
|
SubscribeLocalEvent<StationPostInitEvent>(OnStationPostInit);
|
||||||
_bodyQuery = GetEntityQuery<BodyComponent>();
|
_bodyQuery = GetEntityQuery<BodyComponent>();
|
||||||
_buckleQuery = GetEntityQuery<BuckleComponent>();
|
_buckleQuery = GetEntityQuery<BuckleComponent>();
|
||||||
_ghostQuery = GetEntityQuery<GhostComponent>();
|
_ghostQuery = GetEntityQuery<GhostComponent>();
|
||||||
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
_statusQuery = GetEntityQuery<StatusEffectsComponent>();
|
_statusQuery = GetEntityQuery<StatusEffectsComponent>();
|
||||||
_xformQuery = GetEntityQuery<TransformComponent>();
|
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
|
||||||
SubscribeLocalEvent<StationGridAddedEvent>(OnStationGridAdd);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnStationGridAdd(StationGridAddedEvent ev)
|
private void OnStationPostInit(ref StationPostInitEvent ev)
|
||||||
{
|
{
|
||||||
if (HasComp<MapComponent>(ev.GridId) ||
|
// Add all grid maps as ftl destinations that anyone can FTL to.
|
||||||
TryComp<PhysicsComponent>(ev.GridId, out var body) &&
|
foreach (var gridUid in ev.Station.Comp.Grids)
|
||||||
body.Mass > FTLDestinationMass)
|
|
||||||
{
|
{
|
||||||
AddFTLDestination(ev.GridId, true);
|
var gridXform = _xformQuery.GetComponent(gridUid);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public bool CanFTL(EntityUid? uid, [NotNullWhen(false)] out string? reason)
|
if (gridXform.MapUid == null)
|
||||||
{
|
|
||||||
if (HasComp<PreventPilotComponent>(uid))
|
|
||||||
{
|
|
||||||
reason = Loc.GetString("shuttle-console-prevent");
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (uid != null)
|
|
||||||
{
|
|
||||||
var ev = new ConsoleFTLAttemptEvent(uid.Value, false, string.Empty);
|
|
||||||
RaiseLocalEvent(uid.Value, ref ev, true);
|
|
||||||
|
|
||||||
if (ev.Cancelled)
|
|
||||||
{
|
{
|
||||||
reason = ev.Reason;
|
continue;
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
reason = null;
|
TryAddFTLDestination(gridXform.MapID, true, out _);
|
||||||
return true;
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a target for hyperspace to every shuttle console.
|
/// Ensures the FTL map exists and returns it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public FTLDestinationComponent AddFTLDestination(EntityUid uid, bool enabled)
|
private EntityUid EnsureFTLMap()
|
||||||
{
|
{
|
||||||
if (TryComp<FTLDestinationComponent>(uid, out var destination) && destination.Enabled == enabled)
|
var query = AllEntityQuery<FTLMapComponent>();
|
||||||
return destination;
|
|
||||||
|
|
||||||
destination = EnsureComp<FTLDestinationComponent>(uid);
|
while (query.MoveNext(out var uid, out _))
|
||||||
|
|
||||||
if (HasComp<FTLComponent>(uid))
|
|
||||||
{
|
{
|
||||||
enabled = false;
|
return uid;
|
||||||
}
|
}
|
||||||
|
|
||||||
destination.Enabled = enabled;
|
var mapId = _mapManager.CreateMap();
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||||
|
var ftlMap = AddComp<FTLMapComponent>(mapUid);
|
||||||
|
|
||||||
|
_metadata.SetEntityName(mapUid, "FTL");
|
||||||
|
Log.Debug($"Setup hyperspace map at {mapUid}");
|
||||||
|
DebugTools.Assert(!_mapManager.IsMapPaused(mapId));
|
||||||
|
var parallax = EnsureComp<ParallaxComponent>(mapUid);
|
||||||
|
parallax.Parallax = ftlMap.Parallax;
|
||||||
|
|
||||||
|
return mapUid;
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetStateDuration(FTLComponent component)
|
||||||
|
{
|
||||||
|
var state = component.State;
|
||||||
|
|
||||||
|
switch (state)
|
||||||
|
{
|
||||||
|
case FTLState.Starting:
|
||||||
|
case FTLState.Travelling:
|
||||||
|
case FTLState.Arriving:
|
||||||
|
case FTLState.Cooldown:
|
||||||
|
return component.Accumulator;
|
||||||
|
case FTLState.Available:
|
||||||
|
return 0f;
|
||||||
|
default:
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Updates the whitelist for this FTL destination.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="entity"></param>
|
||||||
|
/// <param name="whitelist"></param>
|
||||||
|
public void SetFTLWhitelist(Entity<FTLDestinationComponent?> entity, EntityWhitelist? whitelist)
|
||||||
|
{
|
||||||
|
if (!Resolve(entity, ref entity.Comp))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (entity.Comp.Whitelist == whitelist)
|
||||||
|
return;
|
||||||
|
|
||||||
|
entity.Comp.Whitelist = whitelist;
|
||||||
_console.RefreshShuttleConsoles();
|
_console.RefreshShuttleConsoles();
|
||||||
return destination;
|
Dirty(entity);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Adds the target map as available for FTL.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryAddFTLDestination(MapId mapId, bool enabled, [NotNullWhen(true)] out FTLDestinationComponent? component)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||||
|
component = null;
|
||||||
|
|
||||||
|
if (!Exists(mapUid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
component = EnsureComp<FTLDestinationComponent>(mapUid);
|
||||||
|
|
||||||
|
if (component.Enabled == enabled)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
component.Enabled = enabled;
|
||||||
|
_console.RefreshShuttleConsoles();
|
||||||
|
Dirty(mapUid, component);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
[PublicAPI]
|
[PublicAPI]
|
||||||
@@ -162,40 +196,51 @@ public sealed partial class ShuttleSystem
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves a shuttle from its current position to the target one. Goes through the hyperspace map while the timer is running.
|
/// Returns true if the grid can FTL. Used to block protected shuttles like the emergency shuttle.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void FTLTravel(
|
public bool CanFTL(EntityUid shuttleUid, [NotNullWhen(false)] out string? reason)
|
||||||
EntityUid shuttleUid,
|
|
||||||
ShuttleComponent component,
|
|
||||||
EntityCoordinates coordinates,
|
|
||||||
float startupTime = DefaultStartupTime,
|
|
||||||
float hyperspaceTime = DefaultTravelTime,
|
|
||||||
string? priorityTag = null)
|
|
||||||
{
|
{
|
||||||
if (!TrySetupFTL(shuttleUid, component, out var hyperspace))
|
if (HasComp<FTLComponent>(shuttleUid))
|
||||||
return;
|
{
|
||||||
|
reason = Loc.GetString("shuttle-console-in-ftl");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
hyperspace.StartupTime = startupTime;
|
if (TryComp(shuttleUid, out PhysicsComponent? shuttlePhysics) && shuttlePhysics.Mass > 300f)
|
||||||
hyperspace.TravelTime = hyperspaceTime;
|
{
|
||||||
hyperspace.Accumulator = hyperspace.StartupTime;
|
reason = Loc.GetString("shuttle-console-mass");
|
||||||
hyperspace.TargetCoordinates = coordinates;
|
return false;
|
||||||
hyperspace.Dock = false;
|
}
|
||||||
hyperspace.PriorityTag = priorityTag;
|
|
||||||
_console.RefreshShuttleConsoles();
|
if (HasComp<PreventPilotComponent>(shuttleUid))
|
||||||
var ev = new FTLRequestEvent(_mapManager.GetMapEntityId(coordinates.ToMap(EntityManager, _transform).MapId));
|
{
|
||||||
|
reason = Loc.GetString("shuttle-console-prevent");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
var ev = new ConsoleFTLAttemptEvent(shuttleUid, false, string.Empty);
|
||||||
RaiseLocalEvent(shuttleUid, ref ev, true);
|
RaiseLocalEvent(shuttleUid, ref ev, true);
|
||||||
|
|
||||||
|
if (ev.Cancelled)
|
||||||
|
{
|
||||||
|
reason = ev.Reason;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
reason = null;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Moves a shuttle from its current position to docked on the target one. Goes through the hyperspace map while the timer is running.
|
/// Moves a shuttle from its current position to the target one without any checks. Goes through the hyperspace map while the timer is running.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void FTLTravel(
|
public void FTLToCoordinates(
|
||||||
EntityUid shuttleUid,
|
EntityUid shuttleUid,
|
||||||
ShuttleComponent component,
|
ShuttleComponent component,
|
||||||
EntityUid target,
|
EntityCoordinates coordinates,
|
||||||
|
Angle angle,
|
||||||
float startupTime = DefaultStartupTime,
|
float startupTime = DefaultStartupTime,
|
||||||
float hyperspaceTime = DefaultTravelTime,
|
float hyperspaceTime = DefaultTravelTime,
|
||||||
bool dock = false,
|
|
||||||
string? priorityTag = null)
|
string? priorityTag = null)
|
||||||
{
|
{
|
||||||
if (!TrySetupFTL(shuttleUid, component, out var hyperspace))
|
if (!TrySetupFTL(shuttleUid, component, out var hyperspace))
|
||||||
@@ -204,10 +249,58 @@ public sealed partial class ShuttleSystem
|
|||||||
hyperspace.StartupTime = startupTime;
|
hyperspace.StartupTime = startupTime;
|
||||||
hyperspace.TravelTime = hyperspaceTime;
|
hyperspace.TravelTime = hyperspaceTime;
|
||||||
hyperspace.Accumulator = hyperspace.StartupTime;
|
hyperspace.Accumulator = hyperspace.StartupTime;
|
||||||
hyperspace.TargetUid = target;
|
hyperspace.TargetCoordinates = coordinates;
|
||||||
hyperspace.Dock = dock;
|
hyperspace.TargetAngle = angle;
|
||||||
hyperspace.PriorityTag = priorityTag;
|
hyperspace.PriorityTag = priorityTag;
|
||||||
_console.RefreshShuttleConsoles();
|
|
||||||
|
_console.RefreshShuttleConsoles(shuttleUid);
|
||||||
|
|
||||||
|
var mapId = coordinates.GetMapId(EntityManager);
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(mapId);
|
||||||
|
var ev = new FTLRequestEvent(mapUid);
|
||||||
|
RaiseLocalEvent(shuttleUid, ref ev, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Moves a shuttle from its current position to docked on the target one.
|
||||||
|
/// If no docks are free when FTLing it will arrive in proximity
|
||||||
|
/// </summary>
|
||||||
|
public void FTLToDock(
|
||||||
|
EntityUid shuttleUid,
|
||||||
|
ShuttleComponent component,
|
||||||
|
EntityUid target,
|
||||||
|
float startupTime = DefaultStartupTime,
|
||||||
|
float hyperspaceTime = DefaultTravelTime,
|
||||||
|
string? priorityTag = null)
|
||||||
|
{
|
||||||
|
if (!TrySetupFTL(shuttleUid, component, out var hyperspace))
|
||||||
|
return;
|
||||||
|
|
||||||
|
var config = _dockSystem.GetDockingConfig(shuttleUid, target, priorityTag);
|
||||||
|
hyperspace.StartupTime = startupTime;
|
||||||
|
hyperspace.TravelTime = hyperspaceTime;
|
||||||
|
hyperspace.Accumulator = hyperspace.StartupTime;
|
||||||
|
hyperspace.PriorityTag = priorityTag;
|
||||||
|
|
||||||
|
_console.RefreshShuttleConsoles(shuttleUid);
|
||||||
|
|
||||||
|
// Valid dock for now time so just use that as the target.
|
||||||
|
if (config != null)
|
||||||
|
{
|
||||||
|
hyperspace.TargetCoordinates = config.Coordinates;
|
||||||
|
hyperspace.TargetAngle = config.Angle;
|
||||||
|
}
|
||||||
|
else if (TryGetFTLProximity(shuttleUid, target, out var coords, out var targAngle))
|
||||||
|
{
|
||||||
|
hyperspace.TargetCoordinates = coords;
|
||||||
|
hyperspace.TargetAngle = targAngle;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
// FTL back to its own position.
|
||||||
|
hyperspace.TargetCoordinates = Transform(shuttleUid).Coordinates;
|
||||||
|
Log.Error($"Unable to FTL grid {ToPrettyString(shuttleUid)} to target properly?");
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private bool TrySetupFTL(EntityUid uid, ShuttleComponent shuttle, [NotNullWhen(true)] out FTLComponent? component)
|
private bool TrySetupFTL(EntityUid uid, ShuttleComponent shuttle, [NotNullWhen(true)] out FTLComponent? component)
|
||||||
@@ -216,20 +309,14 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
if (HasComp<FTLComponent>(uid))
|
if (HasComp<FTLComponent>(uid))
|
||||||
{
|
{
|
||||||
Log.Warning($"Tried queuing {ToPrettyString(uid)} which already has HyperspaceComponent?");
|
Log.Warning($"Tried queuing {ToPrettyString(uid)} which already has {nameof(FTLComponent)}?");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (TryComp<FTLDestinationComponent>(uid, out var dest))
|
|
||||||
{
|
|
||||||
dest.Enabled = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
_thruster.DisableLinearThrusters(shuttle);
|
_thruster.DisableLinearThrusters(shuttle);
|
||||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
|
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.North);
|
||||||
_thruster.SetAngularThrust(shuttle, false);
|
_thruster.SetAngularThrust(shuttle, false);
|
||||||
// TODO: Maybe move this to docking instead?
|
_dockSystem.SetDocks(uid, false);
|
||||||
SetDocks(uid, false);
|
|
||||||
|
|
||||||
component = AddComp<FTLComponent>(uid);
|
component = AddComp<FTLComponent>(uid);
|
||||||
component.State = FTLState.Starting;
|
component.State = FTLState.Starting;
|
||||||
@@ -241,228 +328,226 @@ public sealed partial class ShuttleSystem
|
|||||||
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
|
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Play previs here for docking arrival.
|
||||||
|
|
||||||
// Make sure the map is setup before we leave to avoid pop-in (e.g. parallax).
|
// Make sure the map is setup before we leave to avoid pop-in (e.g. parallax).
|
||||||
SetupHyperspace();
|
EnsureFTLMap();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Transitions shuttle to FTL map.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateFTLStarting(Entity<FTLComponent, ShuttleComponent> entity)
|
||||||
|
{
|
||||||
|
var uid = entity.Owner;
|
||||||
|
var comp = entity.Comp1;
|
||||||
|
var xform = _xformQuery.GetComponent(entity);
|
||||||
|
DoTheDinosaur(xform);
|
||||||
|
|
||||||
|
comp.State = FTLState.Travelling;
|
||||||
|
var fromMapUid = xform.MapUid;
|
||||||
|
var fromMatrix = _transform.GetWorldMatrix(xform);
|
||||||
|
var fromRotation = _transform.GetWorldRotation(xform);
|
||||||
|
|
||||||
|
var width = Comp<MapGridComponent>(uid).LocalAABB.Width;
|
||||||
|
var ftlMap = EnsureFTLMap();
|
||||||
|
var body = _physicsQuery.GetComponent(entity);
|
||||||
|
var shuttleCenter = body.LocalCenter;
|
||||||
|
|
||||||
|
// Offset the start by buffer range just to avoid overlap.
|
||||||
|
var ftlStart = new EntityCoordinates(ftlMap, new Vector2(_index + width / 2f, 0f) - shuttleCenter);
|
||||||
|
|
||||||
|
_transform.SetCoordinates(entity.Owner, ftlStart);
|
||||||
|
|
||||||
|
// Reset rotation so they always face the same direction.
|
||||||
|
xform.LocalRotation = Angle.Zero;
|
||||||
|
_index += width + Buffer;
|
||||||
|
comp.Accumulator += comp.TravelTime - DefaultArrivalTime;
|
||||||
|
|
||||||
|
Enable(uid, component: body);
|
||||||
|
_physics.SetLinearVelocity(uid, new Vector2(0f, 20f), body: body);
|
||||||
|
_physics.SetAngularVelocity(uid, 0f, body: body);
|
||||||
|
_physics.SetLinearDamping(body, 0f);
|
||||||
|
_physics.SetAngularDamping(body, 0f);
|
||||||
|
|
||||||
|
_dockSystem.SetDockBolts(uid, true);
|
||||||
|
_console.RefreshShuttleConsoles(uid);
|
||||||
|
|
||||||
|
var ev = new FTLStartedEvent(uid, comp.TargetCoordinates, fromMapUid, fromMatrix, fromRotation);
|
||||||
|
RaiseLocalEvent(uid, ref ev, true);
|
||||||
|
|
||||||
|
// Audio
|
||||||
|
var wowdio = _audio.PlayPvs(comp.TravelSound, uid);
|
||||||
|
comp.TravelStream = wowdio?.Entity;
|
||||||
|
if (wowdio?.Component != null)
|
||||||
|
{
|
||||||
|
wowdio.Value.Component.Flags |= AudioFlags.GridAudio;
|
||||||
|
|
||||||
|
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
|
||||||
|
{
|
||||||
|
_transform.SetLocalPosition(wowdio.Value.Entity, gridPhysics.LocalCenter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shuttle arriving.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateFTLTravelling(Entity<FTLComponent, ShuttleComponent> entity)
|
||||||
|
{
|
||||||
|
var shuttle = entity.Comp2;
|
||||||
|
var comp = entity.Comp1;
|
||||||
|
comp.Accumulator += DefaultArrivalTime;
|
||||||
|
comp.State = FTLState.Arriving;
|
||||||
|
// TODO: Arrival effects
|
||||||
|
// For now we'll just use the ss13 bubbles but we can do fancier.
|
||||||
|
|
||||||
|
_thruster.DisableLinearThrusters(shuttle);
|
||||||
|
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
|
||||||
|
|
||||||
|
_console.RefreshShuttleConsoles(entity.Owner);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shuttle arrived.
|
||||||
|
/// </summary>
|
||||||
|
private void UpdateFTLArriving(Entity<FTLComponent, ShuttleComponent> entity)
|
||||||
|
{
|
||||||
|
var uid = entity.Owner;
|
||||||
|
var xform = _xformQuery.GetComponent(uid);
|
||||||
|
var body = _physicsQuery.GetComponent(uid);
|
||||||
|
var comp = entity.Comp1;
|
||||||
|
DoTheDinosaur(xform);
|
||||||
|
_dockSystem.SetDockBolts(entity, false);
|
||||||
|
_dockSystem.SetDocks(entity, true);
|
||||||
|
|
||||||
|
_physics.SetLinearVelocity(uid, Vector2.Zero, body: body);
|
||||||
|
_physics.SetAngularVelocity(uid, 0f, body: body);
|
||||||
|
_physics.SetLinearDamping(body, entity.Comp2.LinearDamping);
|
||||||
|
_physics.SetAngularDamping(body, entity.Comp2.AngularDamping);
|
||||||
|
|
||||||
|
var target = entity.Comp1.TargetCoordinates;
|
||||||
|
|
||||||
|
MapId mapId;
|
||||||
|
|
||||||
|
if (!Exists(entity.Comp1.TargetCoordinates.EntityId))
|
||||||
|
{
|
||||||
|
// Uhh good luck
|
||||||
|
// Pick earliest map?
|
||||||
|
var maps = EntityQuery<MapComponent>().Select(o => o.MapId).ToList();
|
||||||
|
var map = maps.Min(o => o.GetHashCode());
|
||||||
|
|
||||||
|
mapId = new MapId(map);
|
||||||
|
TryFTLProximity(uid, _mapManager.GetMapEntityId(mapId));
|
||||||
|
}
|
||||||
|
// Docking FTL
|
||||||
|
else if (HasComp<MapGridComponent>(target.EntityId) &&
|
||||||
|
!HasComp<MapComponent>(target.EntityId))
|
||||||
|
{
|
||||||
|
var config = _dockSystem.GetDockingConfigAt(uid, target.EntityId, target, entity.Comp1.TargetAngle);
|
||||||
|
|
||||||
|
// Couldn't dock somehow so just fallback to regular position FTL.
|
||||||
|
if (config == null)
|
||||||
|
{
|
||||||
|
_transform.SetCoordinates(uid, xform, target, rotation: entity.Comp1.TargetAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
mapId = target.GetMapId(EntityManager);
|
||||||
|
}
|
||||||
|
// Position ftl
|
||||||
|
else
|
||||||
|
{
|
||||||
|
mapId = target.GetMapId(EntityManager);
|
||||||
|
_transform.SetCoordinates(uid, xform, target, rotation: entity.Comp1.TargetAngle);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (_physicsQuery.TryGetComponent(uid, out body))
|
||||||
|
{
|
||||||
|
_physics.SetLinearVelocity(uid, Vector2.Zero, body: body);
|
||||||
|
_physics.SetAngularVelocity(uid, 0f, body: body);
|
||||||
|
|
||||||
|
// Disable shuttle if it's on a planet; unfortunately can't do this in parent change messages due
|
||||||
|
// to event ordering and awake body shenanigans (at least for now).
|
||||||
|
if (HasComp<MapGridComponent>(xform.MapUid))
|
||||||
|
{
|
||||||
|
Disable(uid, component: body);
|
||||||
|
}
|
||||||
|
|
||||||
|
Enable(uid, component: body, shuttle: entity.Comp2);
|
||||||
|
}
|
||||||
|
|
||||||
|
_thruster.DisableLinearThrusters(entity.Comp2);
|
||||||
|
|
||||||
|
comp.TravelStream = _audio.Stop(comp.TravelStream);
|
||||||
|
var audio = _audio.PlayPvs(_arrivalSound, uid);
|
||||||
|
audio.Value.Component.Flags |= AudioFlags.GridAudio;
|
||||||
|
// TODO: Shitcode til engine fix
|
||||||
|
|
||||||
|
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
|
||||||
|
{
|
||||||
|
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (TryComp<FTLDestinationComponent>(uid, out var dest))
|
||||||
|
{
|
||||||
|
dest.Enabled = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
comp.State = FTLState.Cooldown;
|
||||||
|
comp.Accumulator += FTLCooldown;
|
||||||
|
_console.RefreshShuttleConsoles(uid);
|
||||||
|
_mapManager.SetMapPaused(mapId, false);
|
||||||
|
Smimsh(uid, xform: xform);
|
||||||
|
|
||||||
|
var ftlEvent = new FTLCompletedEvent(uid, _mapManager.GetMapEntityId(mapId));
|
||||||
|
RaiseLocalEvent(uid, ref ftlEvent, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void UpdateFTLCooldown(Entity<FTLComponent, ShuttleComponent> entity)
|
||||||
|
{
|
||||||
|
RemCompDeferred<FTLComponent>(entity);
|
||||||
|
_console.RefreshShuttleConsoles(entity);
|
||||||
|
}
|
||||||
|
|
||||||
private void UpdateHyperspace(float frameTime)
|
private void UpdateHyperspace(float frameTime)
|
||||||
{
|
{
|
||||||
var query = EntityQueryEnumerator<FTLComponent>();
|
var query = EntityQueryEnumerator<FTLComponent, ShuttleComponent>();
|
||||||
|
|
||||||
while (query.MoveNext(out var uid, out var comp))
|
while (query.MoveNext(out var uid, out var comp, out var shuttle))
|
||||||
{
|
{
|
||||||
comp.Accumulator -= frameTime;
|
comp.Accumulator -= frameTime;
|
||||||
|
|
||||||
if (comp.Accumulator > 0f)
|
if (comp.Accumulator > 0f)
|
||||||
continue;
|
continue;
|
||||||
|
|
||||||
var xform = Transform(uid);
|
var entity = (uid, comp, shuttle);
|
||||||
PhysicsComponent? body;
|
|
||||||
ShuttleComponent? shuttle;
|
|
||||||
TryComp(uid, out shuttle);
|
|
||||||
|
|
||||||
switch (comp.State)
|
switch (comp.State)
|
||||||
{
|
{
|
||||||
// Startup time has elapsed and in hyperspace.
|
// Startup time has elapsed and in hyperspace.
|
||||||
case FTLState.Starting:
|
case FTLState.Starting:
|
||||||
{
|
UpdateFTLStarting(entity);
|
||||||
DoTheDinosaur(xform);
|
|
||||||
|
|
||||||
comp.State = FTLState.Travelling;
|
|
||||||
var fromMapUid = xform.MapUid;
|
|
||||||
var fromMatrix = _transform.GetWorldMatrix(xform);
|
|
||||||
var fromRotation = _transform.GetWorldRotation(xform);
|
|
||||||
|
|
||||||
var width = Comp<MapGridComponent>(uid).LocalAABB.Width;
|
|
||||||
xform.Coordinates = new EntityCoordinates(_mapManager.GetMapEntityId(_hyperSpaceMap!.Value),
|
|
||||||
new Vector2(_index + width / 2f, 0f));
|
|
||||||
xform.LocalRotation = Angle.Zero;
|
|
||||||
_index += width + Buffer;
|
|
||||||
comp.Accumulator += comp.TravelTime - DefaultArrivalTime;
|
|
||||||
|
|
||||||
if (TryComp(uid, out body))
|
|
||||||
{
|
|
||||||
if (shuttle != null)
|
|
||||||
Enable(uid, component: body, shuttle: shuttle);
|
|
||||||
_physics.SetLinearVelocity(uid, new Vector2(0f, 20f), body: body);
|
|
||||||
_physics.SetAngularVelocity(uid, 0f, body: body);
|
|
||||||
_physics.SetLinearDamping(body, 0f);
|
|
||||||
_physics.SetAngularDamping(body, 0f);
|
|
||||||
}
|
|
||||||
|
|
||||||
SetDockBolts(uid, true);
|
|
||||||
_console.RefreshShuttleConsoles(uid);
|
|
||||||
var target = comp.TargetUid != null
|
|
||||||
? new EntityCoordinates(comp.TargetUid.Value, Vector2.Zero)
|
|
||||||
: comp.TargetCoordinates;
|
|
||||||
|
|
||||||
var ev = new FTLStartedEvent(uid, target, fromMapUid, fromMatrix, fromRotation);
|
|
||||||
RaiseLocalEvent(uid, ref ev, true);
|
|
||||||
|
|
||||||
var wowdio = _audio.PlayPvs(comp.TravelSound, uid);
|
|
||||||
comp.TravelStream = wowdio?.Entity;
|
|
||||||
if (wowdio?.Component != null)
|
|
||||||
{
|
|
||||||
wowdio.Value.Component.Flags |= AudioFlags.GridAudio;
|
|
||||||
|
|
||||||
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
|
|
||||||
{
|
|
||||||
_transform.SetLocalPosition(wowdio.Value.Entity, gridPhysics.LocalCenter);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
// Arriving, play effects
|
// Arriving, play effects
|
||||||
case FTLState.Travelling:
|
case FTLState.Travelling:
|
||||||
comp.Accumulator += DefaultArrivalTime;
|
UpdateFTLTravelling(entity);
|
||||||
comp.State = FTLState.Arriving;
|
|
||||||
// TODO: Arrival effects
|
|
||||||
// For now we'll just use the ss13 bubbles but we can do fancier.
|
|
||||||
|
|
||||||
if (shuttle != null)
|
|
||||||
{
|
|
||||||
_thruster.DisableLinearThrusters(shuttle);
|
|
||||||
_thruster.EnableLinearThrustDirection(shuttle, DirectionFlag.South);
|
|
||||||
}
|
|
||||||
|
|
||||||
_console.RefreshShuttleConsoles(uid);
|
|
||||||
break;
|
break;
|
||||||
// Arrived
|
// Arrived
|
||||||
case FTLState.Arriving:
|
case FTLState.Arriving:
|
||||||
{
|
UpdateFTLArriving(entity);
|
||||||
DoTheDinosaur(xform);
|
|
||||||
SetDockBolts(uid, false);
|
|
||||||
SetDocks(uid, true);
|
|
||||||
|
|
||||||
if (TryComp(uid, out body))
|
|
||||||
{
|
|
||||||
_physics.SetLinearVelocity(uid, Vector2.Zero, body: body);
|
|
||||||
_physics.SetAngularVelocity(uid, 0f, body: body);
|
|
||||||
if (shuttle != null)
|
|
||||||
{
|
|
||||||
_physics.SetLinearDamping(body, shuttle.LinearDamping);
|
|
||||||
_physics.SetAngularDamping(body, shuttle.AngularDamping);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
MapId mapId;
|
|
||||||
|
|
||||||
if (comp.TargetUid != null && shuttle != null)
|
|
||||||
{
|
|
||||||
if (!Deleted(comp.TargetUid))
|
|
||||||
{
|
|
||||||
if (comp.Dock)
|
|
||||||
TryFTLDock(uid, shuttle, comp.TargetUid.Value, comp.PriorityTag);
|
|
||||||
else
|
|
||||||
TryFTLProximity(uid, shuttle, comp.TargetUid.Value);
|
|
||||||
|
|
||||||
mapId = Transform(comp.TargetUid.Value).MapID;
|
|
||||||
}
|
|
||||||
// oh boy, fallback time
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// Pick earliest map?
|
|
||||||
var maps = EntityQuery<MapComponent>().Select(o => o.MapId).ToList();
|
|
||||||
var map = maps.Min(o => o.GetHashCode());
|
|
||||||
|
|
||||||
mapId = new MapId(map);
|
|
||||||
TryFTLProximity(uid, shuttle, _mapManager.GetMapEntityId(mapId));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
xform.Coordinates = comp.TargetCoordinates;
|
|
||||||
mapId = comp.TargetCoordinates.GetMapId(EntityManager);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryComp(uid, out body))
|
|
||||||
{
|
|
||||||
_physics.SetLinearVelocity(uid, Vector2.Zero, body: body);
|
|
||||||
_physics.SetAngularVelocity(uid, 0f, body: body);
|
|
||||||
|
|
||||||
// Disable shuttle if it's on a planet; unfortunately can't do this in parent change messages due
|
|
||||||
// to event ordering and awake body shenanigans (at least for now).
|
|
||||||
if (HasComp<MapGridComponent>(xform.MapUid))
|
|
||||||
{
|
|
||||||
Disable(uid, component: body);
|
|
||||||
}
|
|
||||||
else if (shuttle != null)
|
|
||||||
{
|
|
||||||
Enable(uid, component: body, shuttle: shuttle);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (shuttle != null)
|
|
||||||
{
|
|
||||||
_thruster.DisableLinearThrusters(shuttle);
|
|
||||||
}
|
|
||||||
|
|
||||||
comp.TravelStream = _audio.Stop(comp.TravelStream);
|
|
||||||
var audio = _audio.PlayPvs(_arrivalSound, uid);
|
|
||||||
audio.Value.Component.Flags |= AudioFlags.GridAudio;
|
|
||||||
// TODO: Shitcode til engine fix
|
|
||||||
|
|
||||||
if (_physicsQuery.TryGetComponent(uid, out var gridPhysics))
|
|
||||||
{
|
|
||||||
_transform.SetLocalPosition(audio.Value.Entity, gridPhysics.LocalCenter);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (TryComp<FTLDestinationComponent>(uid, out var dest))
|
|
||||||
{
|
|
||||||
dest.Enabled = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
comp.State = FTLState.Cooldown;
|
|
||||||
comp.Accumulator += FTLCooldown;
|
|
||||||
_console.RefreshShuttleConsoles(uid);
|
|
||||||
_mapManager.SetMapPaused(mapId, false);
|
|
||||||
Smimsh(uid, xform: xform);
|
|
||||||
|
|
||||||
var ftlEvent = new FTLCompletedEvent(uid, _mapManager.GetMapEntityId(mapId));
|
|
||||||
RaiseLocalEvent(uid, ref ftlEvent, true);
|
|
||||||
}
|
|
||||||
break;
|
break;
|
||||||
case FTLState.Cooldown:
|
case FTLState.Cooldown:
|
||||||
RemComp<FTLComponent>(uid);
|
UpdateFTLCooldown(entity);
|
||||||
_console.RefreshShuttleConsoles(uid);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
Log.Error($"Found invalid FTL state {comp.State} for {uid}");
|
Log.Error($"Found invalid FTL state {comp.State} for {uid}");
|
||||||
RemComp<FTLComponent>(uid);
|
RemCompDeferred<FTLComponent>(uid);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetDocks(EntityUid uid, bool enabled)
|
|
||||||
{
|
|
||||||
var query = AllEntityQuery<DockingComponent, TransformComponent>();
|
|
||||||
|
|
||||||
while (query.MoveNext(out var dockUid, out var dock, out var xform))
|
|
||||||
{
|
|
||||||
if (xform.ParentUid != uid || dock.Enabled == enabled)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_dockSystem.Undock(dockUid, dock);
|
|
||||||
dock.Enabled = enabled;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void SetDockBolts(EntityUid uid, bool enabled)
|
|
||||||
{
|
|
||||||
var query = AllEntityQuery<DockingComponent, DoorBoltComponent, TransformComponent>();
|
|
||||||
|
|
||||||
while (query.MoveNext(out var doorUid, out _, out var door, out var xform))
|
|
||||||
{
|
|
||||||
if (xform.ParentUid != uid)
|
|
||||||
continue;
|
|
||||||
|
|
||||||
_doors.TryClose(doorUid);
|
|
||||||
_doors.SetBoltsDown((doorUid, door), enabled);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private float GetSoundRange(EntityUid uid)
|
private float GetSoundRange(EntityUid uid)
|
||||||
{
|
{
|
||||||
if (!_mapManager.TryGetGrid(uid, out var grid))
|
if (!_mapManager.TryGetGrid(uid, out var grid))
|
||||||
@@ -471,31 +556,6 @@ public sealed partial class ShuttleSystem
|
|||||||
return MathF.Max(grid.LocalAABB.Width, grid.LocalAABB.Height) + 12.5f;
|
return MathF.Max(grid.LocalAABB.Width, grid.LocalAABB.Height) + 12.5f;
|
||||||
}
|
}
|
||||||
|
|
||||||
private void SetupHyperspace()
|
|
||||||
{
|
|
||||||
if (_hyperSpaceMap != null)
|
|
||||||
return;
|
|
||||||
|
|
||||||
_hyperSpaceMap = _mapManager.CreateMap();
|
|
||||||
_metadata.SetEntityName(_mapManager.GetMapEntityId(_hyperSpaceMap.Value), "FTL");
|
|
||||||
Log.Debug($"Setup hyperspace map at {_hyperSpaceMap.Value}");
|
|
||||||
DebugTools.Assert(!_mapManager.IsMapPaused(_hyperSpaceMap.Value));
|
|
||||||
var parallax = EnsureComp<ParallaxComponent>(_mapManager.GetMapEntityId(_hyperSpaceMap.Value));
|
|
||||||
parallax.Parallax = "FastSpace";
|
|
||||||
}
|
|
||||||
|
|
||||||
private void CleanupHyperspace()
|
|
||||||
{
|
|
||||||
_index = 0f;
|
|
||||||
if (_hyperSpaceMap == null || !_mapManager.MapExists(_hyperSpaceMap.Value))
|
|
||||||
{
|
|
||||||
_hyperSpaceMap = null;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_mapManager.DeleteMap(_hyperSpaceMap.Value);
|
|
||||||
_hyperSpaceMap = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Puts everyone unbuckled on the floor, paralyzed.
|
/// Puts everyone unbuckled on the floor, paralyzed.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@@ -559,11 +619,12 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to dock with the target grid, otherwise falls back to proximity.
|
/// Tries to dock with the target grid, otherwise falls back to proximity.
|
||||||
|
/// This bypasses FTL travel time.
|
||||||
/// </summary>
|
/// </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)
|
||||||
{
|
{
|
||||||
if (!TryComp<TransformComponent>(shuttleUid, out var shuttleXform) ||
|
if (!_xformQuery.TryGetComponent(shuttleUid, out var shuttleXform) ||
|
||||||
!TryComp<TransformComponent>(targetUid, out var targetXform) ||
|
!_xformQuery.TryGetComponent(targetUid, out var targetXform) ||
|
||||||
targetXform.MapUid == null ||
|
targetXform.MapUid == null ||
|
||||||
!targetXform.MapUid.Value.IsValid())
|
!targetXform.MapUid.Value.IsValid())
|
||||||
{
|
{
|
||||||
@@ -574,35 +635,41 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
FTLDock(config, shuttleXform);
|
FTLDock((shuttleUid, shuttleXform), config);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
TryFTLProximity(shuttleUid, component, targetUid, shuttleXform, targetXform);
|
TryFTLProximity(shuttleUid, targetUid, shuttleXform, targetXform);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Forces an FTL dock.
|
/// Forces an FTL dock.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public void FTLDock(DockingConfig config, TransformComponent shuttleXform)
|
public void FTLDock(Entity<TransformComponent> shuttle, DockingConfig config)
|
||||||
{
|
{
|
||||||
// Set position
|
// Set position
|
||||||
shuttleXform.Coordinates = config.Coordinates;
|
var mapCoordinates = _transform.ToMapCoordinates(config.Coordinates);
|
||||||
_transform.SetWorldRotation(shuttleXform, config.Angle);
|
var mapUid = _mapManager.GetMapEntityId(mapCoordinates.MapId);
|
||||||
|
_transform.SetCoordinates(shuttle.Owner, shuttle.Comp, new EntityCoordinates(mapUid, mapCoordinates.Position), rotation: config.Angle);
|
||||||
|
|
||||||
// Connect everything
|
// Connect everything
|
||||||
foreach (var (dockAUid, dockBUid, dockA, dockB) in config.Docks)
|
foreach (var (dockAUid, dockBUid, dockA, dockB) in config.Docks)
|
||||||
{
|
{
|
||||||
_dockSystem.Dock(dockAUid, dockA, dockBUid, dockB);
|
_dockSystem.Dock((dockAUid, dockA), (dockBUid, dockB));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Tries to arrive nearby without overlapping with other grids.
|
/// Tries to get the target position to FTL near to another grid.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public bool TryFTLProximity(EntityUid shuttleUid, ShuttleComponent component, EntityUid targetUid, TransformComponent? xform = null, TransformComponent? targetXform = null)
|
private bool TryGetFTLProximity(EntityUid shuttleUid, EntityUid targetUid,
|
||||||
|
out EntityCoordinates coordinates, out Angle angle,
|
||||||
|
TransformComponent? xform = null, TransformComponent? targetXform = null)
|
||||||
{
|
{
|
||||||
|
coordinates = EntityCoordinates.Invalid;
|
||||||
|
angle = Angle.Zero;
|
||||||
|
|
||||||
if (!Resolve(targetUid, ref targetXform) ||
|
if (!Resolve(targetUid, ref targetXform) ||
|
||||||
targetXform.MapUid == null ||
|
targetXform.MapUid == null ||
|
||||||
!targetXform.MapUid.Value.IsValid() ||
|
!targetXform.MapUid.Value.IsValid() ||
|
||||||
@@ -611,6 +678,7 @@ public sealed partial class ShuttleSystem
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
var xformQuery = GetEntityQuery<TransformComponent>();
|
var xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
|
var shuttleAABB = Comp<MapGridComponent>(shuttleUid).LocalAABB;
|
||||||
Box2 targetLocalAABB;
|
Box2 targetLocalAABB;
|
||||||
@@ -702,17 +770,36 @@ public sealed partial class ShuttleSystem
|
|||||||
spawnPos = _transform.GetWorldPosition(targetXform, xformQuery);
|
spawnPos = _transform.GetWorldPosition(targetXform, xformQuery);
|
||||||
}
|
}
|
||||||
|
|
||||||
xform.Coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos);
|
|
||||||
|
|
||||||
if (!HasComp<MapComponent>(targetXform.GridUid))
|
if (!HasComp<MapComponent>(targetXform.GridUid))
|
||||||
{
|
{
|
||||||
_transform.SetLocalRotation(xform, _random.NextAngle());
|
angle = _random.NextAngle();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
_transform.SetLocalRotation(xform, Angle.Zero);
|
angle = Angle.Zero;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
coordinates = new EntityCoordinates(targetXform.MapUid.Value, spawnPos);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Tries to arrive nearby without overlapping with other grids.
|
||||||
|
/// </summary>
|
||||||
|
public bool TryFTLProximity(EntityUid shuttleUid, EntityUid targetUid, TransformComponent? xform = null, TransformComponent? targetXform = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(targetUid, ref targetXform) ||
|
||||||
|
targetXform.MapUid == null ||
|
||||||
|
!targetXform.MapUid.Value.IsValid() ||
|
||||||
|
!Resolve(shuttleUid, ref xform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!TryGetFTLProximity(shuttleUid, targetUid, out var coords, out var angle, xform, targetXform))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
_transform.SetCoordinates(shuttleUid, xform, coords, rotation: angle);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -768,6 +855,9 @@ public sealed partial class ShuttleSystem
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (HasComp<FTLBeaconComponent>(ent))
|
||||||
|
continue;
|
||||||
|
|
||||||
QueueDel(ent);
|
QueueDel(ent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public sealed partial class ShuttleSystem
|
|||||||
{
|
{
|
||||||
if (TryComp<ShuttleComponent>(ent[0], out var shuttle))
|
if (TryComp<ShuttleComponent>(ent[0], out var shuttle))
|
||||||
{
|
{
|
||||||
TryFTLProximity(ent[0], shuttle, targetGrid.Value);
|
TryFTLProximity(ent[0], targetGrid.Value);
|
||||||
}
|
}
|
||||||
|
|
||||||
_station.AddGridToStation(uid, ent[0]);
|
_station.AddGridToStation(uid, ent[0]);
|
||||||
@@ -127,7 +127,7 @@ public sealed partial class ShuttleSystem
|
|||||||
{
|
{
|
||||||
if (TryComp<ShuttleComponent>(ent[0], out var shuttle))
|
if (TryComp<ShuttleComponent>(ent[0], out var shuttle))
|
||||||
{
|
{
|
||||||
TryFTLProximity(ent[0], shuttle, targetGrid.Value);
|
TryFTLProximity(ent[0], targetGrid.Value);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
@@ -206,7 +206,7 @@ public sealed partial class ShuttleSystem
|
|||||||
|
|
||||||
if (config != null)
|
if (config != null)
|
||||||
{
|
{
|
||||||
FTLDock(config, shuttleXform);
|
FTLDock((ent[0], shuttleXform), config);
|
||||||
|
|
||||||
if (TryComp<StationMemberComponent>(xform.GridUid, out var stationMember))
|
if (TryComp<StationMemberComponent>(xform.GridUid, out var stationMember))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -33,7 +33,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
[Dependency] private readonly BiomeSystem _biomes = default!;
|
[Dependency] private readonly BiomeSystem _biomes = default!;
|
||||||
[Dependency] private readonly BodySystem _bobby = default!;
|
[Dependency] private readonly BodySystem _bobby = default!;
|
||||||
[Dependency] private readonly DockingSystem _dockSystem = default!;
|
[Dependency] private readonly DockingSystem _dockSystem = default!;
|
||||||
[Dependency] private readonly DoorSystem _doors = default!;
|
|
||||||
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
[Dependency] private readonly EntityLookupSystem _lookup = default!;
|
||||||
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
[Dependency] private readonly FixtureSystem _fixtures = default!;
|
||||||
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
[Dependency] private readonly MapLoaderSystem _loader = default!;
|
||||||
@@ -62,8 +61,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
SubscribeLocalEvent<ShuttleComponent, ComponentStartup>(OnShuttleStartup);
|
SubscribeLocalEvent<ShuttleComponent, ComponentStartup>(OnShuttleStartup);
|
||||||
SubscribeLocalEvent<ShuttleComponent, ComponentShutdown>(OnShuttleShutdown);
|
SubscribeLocalEvent<ShuttleComponent, ComponentShutdown>(OnShuttleShutdown);
|
||||||
|
|
||||||
SubscribeLocalEvent<RoundRestartCleanupEvent>(OnRoundRestart);
|
|
||||||
|
|
||||||
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
|
SubscribeLocalEvent<GridInitializeEvent>(OnGridInit);
|
||||||
SubscribeLocalEvent<FixturesComponent, GridFixtureChangeEvent>(OnGridFixtureChange);
|
SubscribeLocalEvent<FixturesComponent, GridFixtureChangeEvent>(OnGridFixtureChange);
|
||||||
}
|
}
|
||||||
@@ -74,11 +71,6 @@ public sealed partial class ShuttleSystem : SharedShuttleSystem
|
|||||||
UpdateHyperspace(frameTime);
|
UpdateHyperspace(frameTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void OnRoundRestart(RoundRestartCleanupEvent ev)
|
|
||||||
{
|
|
||||||
CleanupHyperspace();
|
|
||||||
}
|
|
||||||
|
|
||||||
private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args)
|
private void OnGridFixtureChange(EntityUid uid, FixturesComponent manager, GridFixtureChangeEvent args)
|
||||||
{
|
{
|
||||||
foreach (var fixture in args.NewFixtures)
|
foreach (var fixture in args.NewFixtures)
|
||||||
|
|||||||
@@ -10,7 +10,7 @@ namespace Content.Shared.Parallax;
|
|||||||
public sealed partial class ParallaxComponent : Component
|
public sealed partial class ParallaxComponent : Component
|
||||||
{
|
{
|
||||||
// I wish I could use a typeserializer here but parallax is extremely client-dependent.
|
// I wish I could use a typeserializer here but parallax is extremely client-dependent.
|
||||||
[DataField("parallax"), AutoNetworkedField]
|
[DataField, AutoNetworkedField]
|
||||||
public string Parallax = "Default";
|
public string Parallax = "Default";
|
||||||
|
|
||||||
[UsedImplicitly, ViewVariables(VVAccess.ReadWrite)]
|
[UsedImplicitly, ViewVariables(VVAccess.ReadWrite)]
|
||||||
|
|||||||
14
Content.Shared/Shuttles/BUIStates/DockingInterfaceState.cs
Normal file
14
Content.Shared/Shuttles/BUIStates/DockingInterfaceState.cs
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DockingInterfaceState
|
||||||
|
{
|
||||||
|
public Dictionary<NetEntity, List<DockingPortState>> Docks;
|
||||||
|
|
||||||
|
public DockingInterfaceState(Dictionary<NetEntity, List<DockingPortState>> docks)
|
||||||
|
{
|
||||||
|
Docks = docks;
|
||||||
|
}
|
||||||
|
}
|
||||||
20
Content.Shared/Shuttles/BUIStates/DockingPortState.cs
Normal file
20
Content.Shared/Shuttles/BUIStates/DockingPortState.cs
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// State of each individual docking port for interface purposes
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class DockingPortState
|
||||||
|
{
|
||||||
|
public string Name = string.Empty;
|
||||||
|
|
||||||
|
public NetCoordinates Coordinates;
|
||||||
|
public Angle Angle;
|
||||||
|
public NetEntity Entity;
|
||||||
|
public bool Connected => GridDockedWith != null;
|
||||||
|
|
||||||
|
public NetEntity? GridDockedWith;
|
||||||
|
}
|
||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Wrapper around <see cref="NavInterfaceState"/>
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class NavBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public NavInterfaceState State;
|
||||||
|
|
||||||
|
public NavBoundUserInterfaceState(NavInterfaceState state)
|
||||||
|
{
|
||||||
|
State = state;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,10 +4,9 @@ using Robust.Shared.Serialization;
|
|||||||
namespace Content.Shared.Shuttles.BUIStates;
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
[Virtual]
|
public sealed class NavInterfaceState
|
||||||
public class RadarConsoleBoundInterfaceState : BoundUserInterfaceState
|
|
||||||
{
|
{
|
||||||
public readonly float MaxRange;
|
public float MaxRange;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// The relevant coordinates to base the radar around.
|
/// The relevant coordinates to base the radar around.
|
||||||
@@ -19,13 +18,13 @@ public class RadarConsoleBoundInterfaceState : BoundUserInterfaceState
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public Angle? Angle;
|
public Angle? Angle;
|
||||||
|
|
||||||
public readonly List<DockingInterfaceState> Docks;
|
public Dictionary<NetEntity, List<DockingPortState>> Docks;
|
||||||
|
|
||||||
public RadarConsoleBoundInterfaceState(
|
public NavInterfaceState(
|
||||||
float maxRange,
|
float maxRange,
|
||||||
NetCoordinates? coordinates,
|
NetCoordinates? coordinates,
|
||||||
Angle? angle,
|
Angle? angle,
|
||||||
List<DockingInterfaceState> docks)
|
Dictionary<NetEntity, List<DockingPortState>> docks)
|
||||||
{
|
{
|
||||||
MaxRange = maxRange;
|
MaxRange = maxRange;
|
||||||
Coordinates = coordinates;
|
Coordinates = coordinates;
|
||||||
@@ -34,20 +33,6 @@ public class RadarConsoleBoundInterfaceState : BoundUserInterfaceState
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// State of each individual docking port for interface purposes
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class DockingInterfaceState
|
|
||||||
{
|
|
||||||
public NetCoordinates Coordinates;
|
|
||||||
public Angle Angle;
|
|
||||||
public NetEntity Entity;
|
|
||||||
public bool Connected;
|
|
||||||
public Color Color;
|
|
||||||
public Color HighlightedColor;
|
|
||||||
}
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public enum RadarConsoleUiKey : byte
|
public enum RadarConsoleUiKey : byte
|
||||||
{
|
{
|
||||||
@@ -0,0 +1,19 @@
|
|||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ShuttleBoundUserInterfaceState : BoundUserInterfaceState
|
||||||
|
{
|
||||||
|
public NavInterfaceState NavState;
|
||||||
|
public ShuttleMapInterfaceState MapState;
|
||||||
|
public DockingInterfaceState DockState;
|
||||||
|
|
||||||
|
public ShuttleBoundUserInterfaceState(NavInterfaceState navState, ShuttleMapInterfaceState mapState, DockingInterfaceState dockState)
|
||||||
|
{
|
||||||
|
NavState = navState;
|
||||||
|
MapState = mapState;
|
||||||
|
DockState = dockState;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,36 +0,0 @@
|
|||||||
using Content.Shared.Shuttles.Components;
|
|
||||||
using Content.Shared.Shuttles.Systems;
|
|
||||||
using Robust.Shared.Map;
|
|
||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles.BUIStates;
|
|
||||||
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class ShuttleConsoleBoundInterfaceState : RadarConsoleBoundInterfaceState
|
|
||||||
{
|
|
||||||
/// <summary>
|
|
||||||
/// The current FTL state.
|
|
||||||
/// </summary>
|
|
||||||
public readonly FTLState FTLState;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// When the next FTL state change happens.
|
|
||||||
/// </summary>
|
|
||||||
public readonly TimeSpan FTLTime;
|
|
||||||
|
|
||||||
public List<(NetEntity Entity, string Destination, bool Enabled)> Destinations;
|
|
||||||
|
|
||||||
public ShuttleConsoleBoundInterfaceState(
|
|
||||||
FTLState ftlState,
|
|
||||||
TimeSpan ftlTime,
|
|
||||||
List<(NetEntity Entity, string Destination, bool Enabled)> destinations,
|
|
||||||
float maxRange,
|
|
||||||
NetCoordinates? coordinates,
|
|
||||||
Angle? angle,
|
|
||||||
List<DockingInterfaceState> docks) : base(maxRange, coordinates, angle, docks)
|
|
||||||
{
|
|
||||||
FTLState = ftlState;
|
|
||||||
FTLTime = ftlTime;
|
|
||||||
Destinations = destinations;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,38 @@
|
|||||||
|
using Content.Shared.Shuttles.Systems;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.BUIStates;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Handles BUI data for Map screen.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ShuttleMapInterfaceState
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// The current FTL state.
|
||||||
|
/// </summary>
|
||||||
|
public readonly FTLState FTLState;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// How long the FTL state takes.
|
||||||
|
/// </summary>
|
||||||
|
public float FTLDuration;
|
||||||
|
|
||||||
|
public List<ShuttleBeaconObject> Destinations;
|
||||||
|
|
||||||
|
public List<ShuttleExclusionObject> Exclusions;
|
||||||
|
|
||||||
|
public ShuttleMapInterfaceState(
|
||||||
|
FTLState ftlState,
|
||||||
|
float ftlDuration,
|
||||||
|
List<ShuttleBeaconObject> destinations,
|
||||||
|
List<ShuttleExclusionObject> exclusions)
|
||||||
|
{
|
||||||
|
FTLState = ftlState;
|
||||||
|
FTLDuration = ftlDuration;
|
||||||
|
Destinations = destinations;
|
||||||
|
Exclusions = exclusions;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,26 @@
|
|||||||
|
using Content.Shared.Whitelist;
|
||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Components;
|
||||||
|
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class FTLDestinationComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Should this destination be restricted in some form from console visibility.
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||||
|
public EntityWhitelist? Whitelist;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Is this destination visible but available to be warped to?
|
||||||
|
/// </summary>
|
||||||
|
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||||
|
public bool Enabled = true;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can we only FTL to beacons on this map.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool BeaconsOnly;
|
||||||
|
}
|
||||||
28
Content.Shared/Shuttles/Components/FTLMapComponent.cs
Normal file
28
Content.Shared/Shuttles/Components/FTLMapComponent.cs
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Marker that specifies a map as being for FTLing entities.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class FTLMapComponent : Component
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Offset for FTLing shuttles so they don't overlap each other.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public int Index;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// What parallax to use for the background, immediately gets deffered to ParallaxComponent.
|
||||||
|
/// </summary>
|
||||||
|
[DataField]
|
||||||
|
public string Parallax = "FastSpace";
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Can FTL on this map only be done to beacons.
|
||||||
|
/// </summary>
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public bool Beacons;
|
||||||
|
}
|
||||||
@@ -15,10 +15,12 @@ public sealed partial class IFFComponent : Component
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public const bool ShowIFFDefault = true;
|
public const bool ShowIFFDefault = true;
|
||||||
|
|
||||||
|
public static readonly Color SelfColor = Color.MediumSpringGreen;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Default color to use for IFF if no component is found.
|
/// Default color to use for IFF if no component is found.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public static readonly Color IFFColor = Color.Aquamarine;
|
public static readonly Color IFFColor = Color.Gold;
|
||||||
|
|
||||||
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
[ViewVariables(VVAccess.ReadWrite), DataField, AutoNetworkedField]
|
||||||
public IFFFlags Flags = IFFFlags.None;
|
public IFFFlags Flags = IFFFlags.None;
|
||||||
|
|||||||
@@ -22,8 +22,6 @@ namespace Content.Shared.Shuttles.Components
|
|||||||
[ViewVariables]
|
[ViewVariables]
|
||||||
public EntityCoordinates? Position { get; set; }
|
public EntityCoordinates? Position { get; set; }
|
||||||
|
|
||||||
public const float BreakDistance = 0.25f;
|
|
||||||
|
|
||||||
public Vector2 CurTickStrafeMovement = Vector2.Zero;
|
public Vector2 CurTickStrafeMovement = Vector2.Zero;
|
||||||
public float CurTickRotationMovement;
|
public float CurTickRotationMovement;
|
||||||
public float CurTickBraking;
|
public float CurTickBraking;
|
||||||
|
|||||||
@@ -0,0 +1,17 @@
|
|||||||
|
using Robust.Shared.GameStates;
|
||||||
|
using Robust.Shared.Utility;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Components;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Shows a parallax background on the shuttle map console.
|
||||||
|
/// </summary>
|
||||||
|
[RegisterComponent, NetworkedComponent, AutoGenerateComponentState]
|
||||||
|
public sealed partial class ShuttleMapParallaxComponent : Component
|
||||||
|
{
|
||||||
|
public static readonly ResPath FallbackTexture = new ResPath("/Textures/Parallaxes/space_map2.png");
|
||||||
|
|
||||||
|
// TODO: This should ideally be shared with parallax stuff to avoid duplication, for now it's just a texture
|
||||||
|
[DataField, AutoNetworkedField]
|
||||||
|
public ResPath TexturePath;
|
||||||
|
}
|
||||||
@@ -3,10 +3,12 @@ using Robust.Shared.Serialization;
|
|||||||
namespace Content.Shared.Shuttles.Events;
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Raised on the client when it's viewing a particular docking port to try and dock it automatically.
|
/// Raised on the client when it's viewing a particular docking port to try and dock it.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class AutodockRequestMessage : BoundUserInterfaceMessage
|
public sealed class DockRequestMessage : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
public NetEntity DockEntity;
|
public NetEntity DockEntity;
|
||||||
|
|
||||||
|
public NetEntity TargetDockEntity;
|
||||||
}
|
}
|
||||||
@@ -0,0 +1,13 @@
|
|||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Events;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Raised on a client when it wishes to FTL to a beacon.
|
||||||
|
/// </summary>
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public sealed class ShuttleConsoleFTLBeaconMessage : BoundUserInterfaceMessage
|
||||||
|
{
|
||||||
|
public NetEntity Beacon;
|
||||||
|
public Angle Angle;
|
||||||
|
}
|
||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles.Events;
|
namespace Content.Shared.Shuttles.Events;
|
||||||
@@ -6,7 +7,8 @@ namespace Content.Shared.Shuttles.Events;
|
|||||||
/// Raised on the client when it wishes to travel somewhere.
|
/// Raised on the client when it wishes to travel somewhere.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[Serializable, NetSerializable]
|
[Serializable, NetSerializable]
|
||||||
public sealed class ShuttleConsoleFTLRequestMessage : BoundUserInterfaceMessage
|
public sealed class ShuttleConsoleFTLPositionMessage : BoundUserInterfaceMessage
|
||||||
{
|
{
|
||||||
public NetEntity Destination;
|
public MapCoordinates Coordinates;
|
||||||
|
public Angle Angle;
|
||||||
}
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
using Robust.Shared.Serialization;
|
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles.Events;
|
|
||||||
|
|
||||||
/// <summary>
|
|
||||||
/// Raised on a client when it is no longer viewing a dock.
|
|
||||||
/// </summary>
|
|
||||||
[Serializable, NetSerializable]
|
|
||||||
public sealed class StopAutodockRequestMessage : BoundUserInterfaceMessage
|
|
||||||
{
|
|
||||||
public NetEntity DockEntity;
|
|
||||||
}
|
|
||||||
78
Content.Shared/Shuttles/Systems/SharedDockingSystem.cs
Normal file
78
Content.Shared/Shuttles/Systems/SharedDockingSystem.cs
Normal file
@@ -0,0 +1,78 @@
|
|||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
|
public abstract class SharedDockingSystem : EntitySystem
|
||||||
|
{
|
||||||
|
[Dependency] protected readonly SharedTransformSystem XformSystem = default!;
|
||||||
|
|
||||||
|
public const float DockingHiglightRange = 4f;
|
||||||
|
public const float DockRange = 1f + 0.2f;
|
||||||
|
public static readonly double AlignmentTolerance = Angle.FromDegrees(15).Theta;
|
||||||
|
|
||||||
|
public bool CanShuttleDock(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
if (shuttle == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !HasComp<PreventPilotComponent>(shuttle.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanShuttleUndock(EntityUid? shuttle)
|
||||||
|
{
|
||||||
|
if (shuttle == null)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return !HasComp<PreventPilotComponent>(shuttle.Value);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanDock(MapCoordinates mapPosA, Angle worldRotA,
|
||||||
|
MapCoordinates mapPosB, Angle worldRotB)
|
||||||
|
{
|
||||||
|
// Uh oh
|
||||||
|
if (mapPosA.MapId != mapPosB.MapId)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return InRange(mapPosA, mapPosB) && InAlignment(mapPosA, worldRotA, mapPosB, worldRotB);
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool InRange(MapCoordinates mapPosA, MapCoordinates mapPosB)
|
||||||
|
{
|
||||||
|
return (mapPosA.Position - mapPosB.Position).Length() <= DockRange;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool InAlignment(MapCoordinates mapPosA, Angle worldRotA, MapCoordinates mapPosB, Angle worldRotB)
|
||||||
|
{
|
||||||
|
// Check if the nubs are in line with the two docks.
|
||||||
|
var worldRotToB = (mapPosB.Position - mapPosA.Position).ToWorldAngle();
|
||||||
|
var worldRotToA = (mapPosA.Position - mapPosB.Position).ToWorldAngle();
|
||||||
|
|
||||||
|
var aDiff = Angle.ShortestDistance((worldRotA - worldRotToB).Reduced(), Angle.Zero);
|
||||||
|
var bDiff = Angle.ShortestDistance((worldRotB - worldRotToA).Reduced(), Angle.Zero);
|
||||||
|
|
||||||
|
if (Math.Abs(aDiff.Theta) > AlignmentTolerance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (Math.Abs(bDiff.Theta) > AlignmentTolerance)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanDock(NetCoordinates coordinatesOne, Angle angleOne,
|
||||||
|
NetCoordinates coordinatesTwo, Angle angleTwo)
|
||||||
|
{
|
||||||
|
// TODO: Dump the dock fixtures
|
||||||
|
var coordsA = GetCoordinates(coordinatesOne);
|
||||||
|
var coordsB = GetCoordinates(coordinatesTwo);
|
||||||
|
|
||||||
|
var mapPosA = XformSystem.ToMapCoordinates(coordsA);
|
||||||
|
var mapPosB = XformSystem.ToMapCoordinates(coordsB);
|
||||||
|
|
||||||
|
var worldRotA = XformSystem.GetWorldRotation(coordsA.EntityId) + angleOne;
|
||||||
|
var worldRotB = XformSystem.GetWorldRotation(coordsB.EntityId) + angleTwo;
|
||||||
|
|
||||||
|
return CanDock(mapPosA, worldRotA, mapPosB, worldRotB);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -4,7 +4,6 @@ namespace Content.Shared.Shuttles.Systems;
|
|||||||
|
|
||||||
public abstract class SharedRadarConsoleSystem : EntitySystem
|
public abstract class SharedRadarConsoleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
public const float DefaultMinRange = 64f;
|
|
||||||
public const float DefaultMaxRange = 256f;
|
public const float DefaultMaxRange = 256f;
|
||||||
|
|
||||||
protected virtual void UpdateState(EntityUid uid, RadarConsoleComponent component)
|
protected virtual void UpdateState(EntityUid uid, RadarConsoleComponent component)
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
using Content.Shared.ActionBlocker;
|
using Content.Shared.ActionBlocker;
|
||||||
using Content.Shared.Movement.Events;
|
using Content.Shared.Movement.Events;
|
||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
using Content.Shared.Shuttles.Components;
|
using Content.Shared.Shuttles.Components;
|
||||||
using Robust.Shared.Serialization;
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
|||||||
@@ -11,6 +11,43 @@ public abstract partial class SharedShuttleSystem
|
|||||||
|
|
||||||
protected virtual void UpdateIFFInterfaces(EntityUid gridUid, IFFComponent component) {}
|
protected virtual void UpdateIFFInterfaces(EntityUid gridUid, IFFComponent component) {}
|
||||||
|
|
||||||
|
public Color GetIFFColor(EntityUid gridUid, bool self = false, IFFComponent? component = null)
|
||||||
|
{
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
return IFFComponent.SelfColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Resolve(gridUid, ref component, false))
|
||||||
|
{
|
||||||
|
return IFFComponent.IFFColor;
|
||||||
|
}
|
||||||
|
|
||||||
|
return component.Color;
|
||||||
|
}
|
||||||
|
|
||||||
|
public string? GetIFFLabel(EntityUid gridUid, bool self = false, IFFComponent? component = null)
|
||||||
|
{
|
||||||
|
if (!IFFComponent.ShowIFFDefault)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
var entName = MetaData(gridUid).EntityName;
|
||||||
|
|
||||||
|
if (self)
|
||||||
|
{
|
||||||
|
return entName;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (Resolve(gridUid, ref component, false) && (component.Flags & (IFFFlags.HideLabel | IFFFlags.Hide)) != 0x0)
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return string.IsNullOrEmpty(entName) ? Loc.GetString("shuttle-console-unknown") : entName;
|
||||||
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Sets the color for this grid to appear as on radar.
|
/// Sets the color for this grid to appear as on radar.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
|
|||||||
@@ -1,7 +1,198 @@
|
|||||||
|
using Content.Shared.Shuttles.BUIStates;
|
||||||
|
using Content.Shared.Shuttles.Components;
|
||||||
|
using Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Map.Components;
|
||||||
|
using Robust.Shared.Physics.Collision.Shapes;
|
||||||
|
using Robust.Shared.Physics.Components;
|
||||||
|
|
||||||
namespace Content.Shared.Shuttles.Systems;
|
namespace Content.Shared.Shuttles.Systems;
|
||||||
|
|
||||||
public abstract partial class SharedShuttleSystem : EntitySystem
|
public abstract partial class SharedShuttleSystem : EntitySystem
|
||||||
{
|
{
|
||||||
|
[Dependency] private readonly IMapManager _mapManager = default!;
|
||||||
|
[Dependency] protected readonly SharedMapSystem Maps = default!;
|
||||||
|
[Dependency] protected readonly SharedTransformSystem XformSystem = default!;
|
||||||
|
|
||||||
|
public const float FTLRange = 512f;
|
||||||
|
public const float FTLBufferRange = 8f;
|
||||||
|
|
||||||
|
private EntityQuery<MapGridComponent> _gridQuery;
|
||||||
|
private EntityQuery<PhysicsComponent> _physicsQuery;
|
||||||
|
private EntityQuery<TransformComponent> _xformQuery;
|
||||||
|
|
||||||
|
private List<Entity<MapGridComponent>> _grids = new();
|
||||||
|
|
||||||
|
public override void Initialize()
|
||||||
|
{
|
||||||
|
base.Initialize();
|
||||||
|
_gridQuery = GetEntityQuery<MapGridComponent>();
|
||||||
|
_physicsQuery = GetEntityQuery<PhysicsComponent>();
|
||||||
|
_xformQuery = GetEntityQuery<TransformComponent>();
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns whether an entity can FTL to the specified map.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanFTLTo(EntityUid shuttleUid, MapId targetMap)
|
||||||
|
{
|
||||||
|
var mapUid = _mapManager.GetMapEntityId(targetMap);
|
||||||
|
var shuttleMap = _xformQuery.GetComponent(shuttleUid).MapID;
|
||||||
|
|
||||||
|
if (shuttleMap == targetMap)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (!TryComp<FTLDestinationComponent>(mapUid, out var destination) ||
|
||||||
|
!destination.Enabled)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (HasComp<FTLMapComponent>(mapUid))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
return destination.Whitelist?.IsValid(shuttleUid, EntityManager) != false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of map objects relevant for the specified map.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<(ShuttleExclusionObject Exclusion, MapCoordinates Coordinates)> GetExclusions(MapId mapId, List<ShuttleExclusionObject> exclusions)
|
||||||
|
{
|
||||||
|
foreach (var exc in exclusions)
|
||||||
|
{
|
||||||
|
var beaconCoords = XformSystem.ToMapCoordinates(GetCoordinates(exc.Coordinates));
|
||||||
|
|
||||||
|
if (beaconCoords.MapId != mapId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return (exc, beaconCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Gets the list of map objects relevant for the specified map.
|
||||||
|
/// </summary>
|
||||||
|
public IEnumerable<(ShuttleBeaconObject Beacon, MapCoordinates Coordinates)> GetBeacons(MapId mapId, List<ShuttleBeaconObject> beacons)
|
||||||
|
{
|
||||||
|
foreach (var beacon in beacons)
|
||||||
|
{
|
||||||
|
var beaconCoords = XformSystem.ToMapCoordinates(GetCoordinates(beacon.Coordinates));
|
||||||
|
|
||||||
|
if (beaconCoords.MapId != mapId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
yield return (beacon, beaconCoords);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool CanDraw(EntityUid gridUid, PhysicsComponent? physics = null, IFFComponent? iffComp = null)
|
||||||
|
{
|
||||||
|
if (!Resolve(gridUid, ref physics))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if (physics.Mass < 10f)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Resolve(gridUid, ref iffComp, false))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hide it entirely.
|
||||||
|
return (iffComp.Flags & IFFFlags.Hide) == 0x0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public bool IsBeaconMap(EntityUid mapUid)
|
||||||
|
{
|
||||||
|
return TryComp(mapUid, out FTLDestinationComponent? ftlDest) && ftlDest.BeaconsOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if a beacon can be FTLd to.
|
||||||
|
/// </summary>
|
||||||
|
public bool CanFTLBeacon(NetCoordinates nCoordinates)
|
||||||
|
{
|
||||||
|
// Only beacons parented to map supported.
|
||||||
|
var coordinates = GetCoordinates(nCoordinates);
|
||||||
|
return HasComp<MapComponent>(coordinates.EntityId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public float GetFTLRange(EntityUid shuttleUid) => FTLRange;
|
||||||
|
|
||||||
|
public float GetFTLBufferRange(EntityUid shuttleUid, MapGridComponent? grid = null)
|
||||||
|
{
|
||||||
|
if (!_gridQuery.Resolve(shuttleUid, ref grid))
|
||||||
|
return 0f;
|
||||||
|
|
||||||
|
var localAABB = grid.LocalAABB;
|
||||||
|
var maxExtent = localAABB.MaxDimension / 2f;
|
||||||
|
var range = maxExtent + FTLBufferRange;
|
||||||
|
return range;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Returns true if the spot is free to be FTLd to (not close to any objects and in range).
|
||||||
|
/// </summary>
|
||||||
|
public bool FTLFree(EntityUid shuttleUid, EntityCoordinates coordinates, Angle angle, List<ShuttleExclusionObject>? exclusionZones)
|
||||||
|
{
|
||||||
|
if (!_physicsQuery.TryGetComponent(shuttleUid, out var shuttlePhysics) ||
|
||||||
|
!_xformQuery.TryGetComponent(shuttleUid, out var shuttleXform))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just checks if any grids inside of a buffer range at the target position.
|
||||||
|
_grids.Clear();
|
||||||
|
var ftlRange = FTLRange;
|
||||||
|
var mapCoordinates = coordinates.ToMap(EntityManager, XformSystem);
|
||||||
|
|
||||||
|
var ourPos = Maps.GetGridPosition((shuttleUid, shuttlePhysics, shuttleXform));
|
||||||
|
|
||||||
|
// This is the already adjusted position
|
||||||
|
var targetPosition = mapCoordinates.Position;
|
||||||
|
|
||||||
|
// Check range even if it's cross-map.
|
||||||
|
if ((targetPosition - ourPos).Length() > FTLRange)
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check exclusion zones.
|
||||||
|
// This needs to be passed in manually due to PVS.
|
||||||
|
if (exclusionZones != null)
|
||||||
|
{
|
||||||
|
foreach (var exclusion in exclusionZones)
|
||||||
|
{
|
||||||
|
var exclusionCoords = XformSystem.ToMapCoordinates(GetCoordinates(exclusion.Coordinates));
|
||||||
|
|
||||||
|
if (exclusionCoords.MapId != mapCoordinates.MapId)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if ((mapCoordinates.Position - exclusionCoords.Position).Length() <= exclusion.Range)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var ourFTLBuffer = GetFTLBufferRange(shuttleUid);
|
||||||
|
var circle = new PhysShapeCircle(ourFTLBuffer + FTLBufferRange, targetPosition);
|
||||||
|
|
||||||
|
_mapManager.FindGridsIntersecting(mapCoordinates.MapId, circle, Robust.Shared.Physics.Transform.Empty,
|
||||||
|
ref _grids, includeMap: false);
|
||||||
|
|
||||||
|
// If any grids in range that aren't us then can't FTL.
|
||||||
|
foreach (var grid in _grids)
|
||||||
|
{
|
||||||
|
if (grid.Owner == shuttleUid)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
[Flags]
|
[Flags]
|
||||||
|
|||||||
7
Content.Shared/Shuttles/UI/MapObjects/GridMapObject.cs
Normal file
7
Content.Shared/Shuttles/UI/MapObjects/GridMapObject.cs
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
namespace Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
|
||||||
|
public record struct GridMapObject : IMapObject
|
||||||
|
{
|
||||||
|
public string Name { get; set; }
|
||||||
|
public EntityUid Entity;
|
||||||
|
}
|
||||||
9
Content.Shared/Shuttles/UI/MapObjects/IMapObject.cs
Normal file
9
Content.Shared/Shuttles/UI/MapObjects/IMapObject.cs
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
namespace Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Abstract map object representing a grid, beacon etc for use on the map screen.
|
||||||
|
/// </summary>
|
||||||
|
public interface IMapObject
|
||||||
|
{
|
||||||
|
string Name { get; }
|
||||||
|
}
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public readonly record struct ShuttleBeaconObject(NetEntity Entity, NetCoordinates Coordinates, string Name) : IMapObject;
|
||||||
@@ -0,0 +1,7 @@
|
|||||||
|
using Robust.Shared.Map;
|
||||||
|
using Robust.Shared.Serialization;
|
||||||
|
|
||||||
|
namespace Content.Shared.Shuttles.UI.MapObjects;
|
||||||
|
|
||||||
|
[Serializable, NetSerializable]
|
||||||
|
public record struct ShuttleExclusionObject(NetCoordinates Coordinates, float Range, string Name = "") : IMapObject;
|
||||||
@@ -9,3 +9,9 @@
|
|||||||
copyright: '"hyperspace_progress.ogg" by /tg/station. Modified for looping by metalgearsloth.'
|
copyright: '"hyperspace_progress.ogg" by /tg/station. Modified for looping by metalgearsloth.'
|
||||||
license: CC-BY-SA-3.0
|
license: CC-BY-SA-3.0
|
||||||
source: https://github.com/tgstation/tgstation/tree/71a79f1f75902d2ccab27cbffb971b12b7ab042d/sound/runtime/hyperspace
|
source: https://github.com/tgstation/tgstation/tree/71a79f1f75902d2ccab27cbffb971b12b7ab042d/sound/runtime/hyperspace
|
||||||
|
|
||||||
|
- files:
|
||||||
|
- radar_ping.ogg
|
||||||
|
copyright: User unfa from freesound.org Remixed to mono and .ogg by metalgearsloth.
|
||||||
|
license: CC0-1.0
|
||||||
|
source: https://freesound.org/people/unfa/sounds/215415/
|
||||||
BIN
Resources/Audio/Effects/Shuttle/radar_ping.ogg
Normal file
BIN
Resources/Audio/Effects/Shuttle/radar_ping.ogg
Normal file
Binary file not shown.
@@ -1,41 +1,52 @@
|
|||||||
shuttle-pilot-start = Piloting ship
|
shuttle-pilot-start = Piloting ship
|
||||||
shuttle-pilot-end = Stopped piloting
|
shuttle-pilot-end = Stopped piloting
|
||||||
|
|
||||||
shuttle-console-in-ftl = Can't FTL while in FTL!
|
shuttle-console-in-ftl = Currently in FTL
|
||||||
shuttle-console-proximity = Too close to nearby objects
|
shuttle-console-mass = Too large to FTL
|
||||||
shuttle-console-prevent = You are unable to pilot this ship.
|
shuttle-console-prevent = You are unable to pilot this ship
|
||||||
|
|
||||||
|
# NAV
|
||||||
|
|
||||||
# Display
|
|
||||||
shuttle-console-display-label = Display
|
shuttle-console-display-label = Display
|
||||||
|
|
||||||
shuttle-console-ftl-state = FTL State
|
|
||||||
shuttle-console-ftl-available = Available
|
|
||||||
shuttle-console-ftl-starting = Starting
|
|
||||||
shuttle-console-ftl-travelling = Travelling
|
|
||||||
shuttle-console-ftl-arriving = Arriving
|
|
||||||
shuttle-console-ftl-cooldown = Cooldown
|
|
||||||
|
|
||||||
shuttle-console-ftl-timer = FTL Time
|
|
||||||
|
|
||||||
shuttle-console-max-radar = Max radar range:
|
|
||||||
shuttle-console-radar = Radar range:
|
|
||||||
shuttle-console-position = Position:
|
shuttle-console-position = Position:
|
||||||
shuttle-console-orientation = Orientation:
|
shuttle-console-orientation = Orientation:
|
||||||
shuttle-console-linear-velocity = Linear velocity:
|
shuttle-console-linear-velocity = Linear velocity:
|
||||||
shuttle-console-angular-velocity = Angular velocity:
|
shuttle-console-angular-velocity = Angular velocity:
|
||||||
|
|
||||||
shuttle-console-dock-label = Docking ports
|
|
||||||
shuttle-console-docked = {$index} (Docked)
|
|
||||||
shuttle-console-dock-button = Dock {$suffix}
|
|
||||||
|
|
||||||
shuttle-console-hyperspace-label = FTL destinations
|
|
||||||
shuttle-console-hyperspace-none = No destinations found
|
|
||||||
|
|
||||||
shuttle-console-unknown = Unknown
|
shuttle-console-unknown = Unknown
|
||||||
shuttle-console-iff-label = {$name} ({$distance}m)
|
shuttle-console-iff-label = {$name} ({$distance}m)
|
||||||
|
shuttle-console-exclusion = Exclusion area
|
||||||
|
|
||||||
# Buttons
|
shuttle-console-nav-settings = Settings
|
||||||
shuttle-console-strafing = Strafing mode
|
|
||||||
shuttle-console-iff-toggle = Show IFF
|
shuttle-console-iff-toggle = Show IFF
|
||||||
shuttle-console-dock-toggle = Show docks
|
shuttle-console-dock-toggle = Show docks
|
||||||
|
|
||||||
|
# MAP
|
||||||
|
|
||||||
|
shuttle-console-ftl-label = FTL Status
|
||||||
|
shuttle-console-ftl-state-Available = Available
|
||||||
|
shuttle-console-ftl-state-Starting = Starting
|
||||||
|
shuttle-console-ftl-state-Travelling = Travelling
|
||||||
|
shuttle-console-ftl-state-Arriving = Arriving
|
||||||
|
shuttle-console-ftl-state-Cooldown = Cooldown
|
||||||
|
|
||||||
|
shuttle-console-map-settings = Settings
|
||||||
|
shuttle-console-ftl-button = FTL
|
||||||
|
shuttle-console-map-rebuild = Scan for objects
|
||||||
|
shuttle-console-map-beacons = Show beacons
|
||||||
|
|
||||||
|
shuttle-console-no-signal = No signal
|
||||||
|
|
||||||
|
shuttle-console-map-objects = Sector objects
|
||||||
|
|
||||||
|
# DOCK
|
||||||
|
shuttle-console-docked = Docked objects
|
||||||
|
|
||||||
|
shuttle-console-view = View
|
||||||
shuttle-console-undock = Undock
|
shuttle-console-undock = Undock
|
||||||
|
shuttle-console-dock = Dock
|
||||||
|
shuttle-console-docks-label = Docks
|
||||||
|
|
||||||
|
shuttle-console-undock-fail = Undocking failed
|
||||||
|
shuttle-console-dock-fail = Docking failed
|
||||||
|
|||||||
@@ -29,10 +29,6 @@ entities:
|
|||||||
name: Syndicate Outpost
|
name: Syndicate Outpost
|
||||||
- type: Transform
|
- type: Transform
|
||||||
parent: 1295
|
parent: 1295
|
||||||
- type: FTLDestination
|
|
||||||
whitelist:
|
|
||||||
tags:
|
|
||||||
- Syndicate
|
|
||||||
- type: MapGrid
|
- type: MapGrid
|
||||||
chunks:
|
chunks:
|
||||||
-1,-1:
|
-1,-1:
|
||||||
@@ -1636,6 +1632,10 @@ entities:
|
|||||||
- type: MetaData
|
- type: MetaData
|
||||||
- type: Transform
|
- type: Transform
|
||||||
- type: Map
|
- type: Map
|
||||||
|
- type: FTLDestination
|
||||||
|
whitelist:
|
||||||
|
tags:
|
||||||
|
- Syndicate
|
||||||
- type: PhysicsMap
|
- type: PhysicsMap
|
||||||
- type: Broadphase
|
- type: Broadphase
|
||||||
- type: OccluderTree
|
- type: OccluderTree
|
||||||
|
|||||||
@@ -24,6 +24,9 @@ entities:
|
|||||||
components:
|
components:
|
||||||
- type: MetaData
|
- type: MetaData
|
||||||
name: GX-13 Infiltrator
|
name: GX-13 Infiltrator
|
||||||
|
- type: Tag
|
||||||
|
tags:
|
||||||
|
- Syndicate
|
||||||
- type: Transform
|
- type: Transform
|
||||||
pos: 0.64252126,4.1776605
|
pos: 0.64252126,4.1776605
|
||||||
parent: invalid
|
parent: invalid
|
||||||
|
|||||||
@@ -3,6 +3,16 @@
|
|||||||
parent: MarkerBase
|
parent: MarkerBase
|
||||||
name: FTL point
|
name: FTL point
|
||||||
components:
|
components:
|
||||||
- type: FTLDestination
|
- type: FTLBeacon
|
||||||
|
- type: Sprite
|
||||||
|
state: pink
|
||||||
|
|
||||||
|
- type: entity
|
||||||
|
id: FTLExclusion
|
||||||
|
parent: MarkerBase
|
||||||
|
name: FTL exclusion point
|
||||||
|
components:
|
||||||
|
- type: FTLExclusion
|
||||||
|
range: 32
|
||||||
- type: Sprite
|
- type: Sprite
|
||||||
state: pink
|
state: pink
|
||||||
|
|||||||
@@ -103,7 +103,7 @@
|
|||||||
tags:
|
tags:
|
||||||
- Syndicate
|
- Syndicate
|
||||||
- type: RadarConsole
|
- type: RadarConsole
|
||||||
maxRange: 1536
|
maxRange: 384
|
||||||
- type: WorldLoader
|
- type: WorldLoader
|
||||||
radius: 1536
|
radius: 1536
|
||||||
- type: PointLight
|
- type: PointLight
|
||||||
|
|||||||
@@ -1,6 +1,7 @@
|
|||||||
# Markers
|
# Markers
|
||||||
- type: entity
|
- type: entity
|
||||||
id: SalvageShuttleMarker
|
id: SalvageShuttleMarker
|
||||||
|
name: Salvage shuttle marker
|
||||||
parent: FTLPoint
|
parent: FTLPoint
|
||||||
|
|
||||||
# Biome mods -> at least 1 required
|
# Biome mods -> at least 1 required
|
||||||
|
|||||||
BIN
Resources/Textures/Parallaxes/space_map2.png
Normal file
BIN
Resources/Textures/Parallaxes/space_map2.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 5.9 MiB |
1
Resources/Textures/Parallaxes/weh.txt
Normal file
1
Resources/Textures/Parallaxes/weh.txt
Normal file
@@ -0,0 +1 @@
|
|||||||
|
spacescape
|
||||||
Reference in New Issue
Block a user