Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 32 additions & 2 deletions Content.Client/Shuttles/UI/MapScreen.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -52,12 +52,13 @@ public sealed partial class MapScreen : BoxContainer
private TimeSpan _nextMapDequeue;

private float _minMapDequeue = 0.05f;
private float _maxMapDequeue = 0.25f;
private float _maxMapDequeue = 0.10f; // Frontier: 0.25<0.10

private StyleBoxFlat _ftlStyle;

public event Action<MapCoordinates, Angle>? RequestFTL;
public event Action<NetEntity, Angle>? RequestBeaconFTL;
public event Action<NetEntity?, NetEntity>? RequestTrackEntity; // Frontier

private readonly Dictionary<MapId, BoxContainer> _mapHeadings = new();
private readonly Dictionary<MapId, List<IMapObject>> _mapObjects = new();
Expand Down Expand Up @@ -463,6 +464,16 @@ private void OnMapObjectPress(IMapObject mapObject)
// If it's our map then scroll, otherwise just set position there.
MapRadar.SetMap(coordinates.MapId, coordinates.Position, recentering: true);
}

// Frontier: entity tracking
private void OnMapObjectTrackPress(IMapObject mapObject)
{
if (mapObject is not GridMapObject gridObj)
return;

RequestTrackEntity?.Invoke(_shuttleEntity is null ? null : _entManager.GetNetEntity(_shuttleEntity), _entManager.GetNetEntity(gridObj.Entity));
}
// End Frontier: entity tracking

public void SetMap(MapId mapId, Vector2 position)
{
Expand All @@ -485,14 +496,16 @@ private void AddMapObject(MapId mapId, IMapObject mapObj)
Text = mapObj.Name,
HorizontalExpand = true,
};

gridButton.Label.ClipText = true; // Frontier

var gridContainer = new BoxContainer()
{
Children =
{
new Control()
{
MinWidth = 32f,
MinWidth = 16f, // Frontier: 32<16
},
gridButton
}
Expand All @@ -505,6 +518,23 @@ private void AddMapObject(MapId mapId, IMapObject mapObj)
{
OnMapObjectPress(mapObj);
};

// Frontier: tracking button handler
if (mapObj is GridMapObject gridObj)
{
var trackButton = new Button()
{
Text = Loc.GetString("shuttle-console-map-track"),
MinWidth = 32,
MaxWidth = 32
};
trackButton.OnPressed += args =>
{
OnMapObjectTrackPress(mapObj);
};
gridContainer.Children.Add(trackButton);
}
// End Frontier: tracking button handler

if (gridContents.ChildCount > 1)
{
Expand Down
29 changes: 29 additions & 0 deletions Content.Client/Shuttles/UI/NavScreen.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -141,6 +141,35 @@
</controls:BoxContainer>
</controls:PanelContainer>
<!-- End Frontier - Inertia dampener controls-->

<!-- Frontier - settable target -->
<controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="TargetCoordsBox">
<controls:Label Text="{controls:Loc 'shuttle-console-target'}"/>
<controls:BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
Align="Center"
Margin="0 0 0 3">
<controls:LineEdit Name="TargetX" Access="Public" HorizontalExpand="True"/>
<controls:LineEdit Name="TargetY" Access="Public" HorizontalExpand="True"/>
<controls:Button Name="TargetSet"
Text="{controls:Loc 'shuttle-console-set-target'}"
TextAlign="Center"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-set-target-description'}"
StyleClasses="OpenRight"
Margin="0"/>
<controls:Button Name="TargetShow"
Text="{controls:Loc 'shuttle-console-hide-target'}"
TextAlign="Center"
ToggleMode="True"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-hide-target-description'}"
StyleClasses="OpenLeft"
Margin="0"/>
</controls:BoxContainer>
</controls:BoxContainer>
Comment on lines +146 to +170
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Missing panel styling wrapper — visual inconsistency with adjacent sections.

The "Radar Target" section is a bare BoxContainer without the styled PanelContainer wrapper (with border and background) that all other sections in this sidebar use (IFF Search at line 174, Maximum IFF Distance at line 186, dampener controls at line 113, etc.). This will make the target section look visually different from the rest of the nav panel.

Consider wrapping the TargetCoordsBox in the same styled panel pattern used by adjacent controls:

🎨 Proposed: wrap in styled PanelContainer
                     <!-- Frontier - settable target -->
+                    <controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
+                        <controls:PanelContainer.PanelOverride>
+                            <graphics:StyleBoxFlat BorderThickness="2" BorderColor="#333333" BackgroundColor="#181818"/>
+                        </controls:PanelContainer.PanelOverride>
                     <controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="TargetCoordsBox">
                         <controls:Label Text="{controls:Loc 'shuttle-console-target'}"/>
                         ...
                     </controls:BoxContainer>
+                    </controls:PanelContainer>
                     <!-- End Frontier - settable target -->
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
<controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="TargetCoordsBox">
<controls:Label Text="{controls:Loc 'shuttle-console-target'}"/>
<controls:BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
Align="Center"
Margin="0 0 0 3">
<controls:LineEdit Name="TargetX" Access="Public" HorizontalExpand="True"/>
<controls:LineEdit Name="TargetY" Access="Public" HorizontalExpand="True"/>
<controls:Button Name="TargetSet"
Text="{controls:Loc 'shuttle-console-set-target'}"
TextAlign="Center"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-set-target-description'}"
StyleClasses="OpenRight"
Margin="0"/>
<controls:Button Name="TargetShow"
Text="{controls:Loc 'shuttle-console-hide-target'}"
TextAlign="Center"
ToggleMode="True"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-hide-target-description'}"
StyleClasses="OpenLeft"
Margin="0"/>
</controls:BoxContainer>
</controls:BoxContainer>
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
<controls:PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BorderThickness="2" BorderColor="#333333" BackgroundColor="#181818"/>
</controls:PanelContainer.PanelOverride>
<controls:BoxContainer Orientation="Vertical" HorizontalExpand="True" Name="TargetCoordsBox">
<controls:Label Text="{controls:Loc 'shuttle-console-target'}"/>
<controls:BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
Align="Center"
Margin="0 0 0 3">
<controls:LineEdit Name="TargetX" Access="Public" HorizontalExpand="True"/>
<controls:LineEdit Name="TargetY" Access="Public" HorizontalExpand="True"/>
<controls:Button Name="TargetSet"
Text="{controls:Loc 'shuttle-console-set-target'}"
TextAlign="Center"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-set-target-description'}"
StyleClasses="OpenRight"
Margin="0"/>
<controls:Button Name="TargetShow"
Text="{controls:Loc 'shuttle-console-hide-target'}"
TextAlign="Center"
ToggleMode="True"
MinWidth="20"
ToolTip="{controls:Loc 'shuttle-console-hide-target-description'}"
StyleClasses="OpenLeft"
Margin="0"/>
</controls:BoxContainer>
</controls:BoxContainer>
</controls:PanelContainer>
🤖 Prompt for AI Agents
In `@Content.Client/Shuttles/UI/NavScreen.xaml` around lines 146 - 170, The
TargetCoordsBox section is missing the styled PanelContainer wrapper used
elsewhere; wrap the existing controls (the controls:BoxContainer named
TargetCoordsBox containing TargetX, TargetY, TargetSet, TargetShow) inside the
same PanelContainer/PanelStyle pattern used by neighboring sections so it gains
the consistent border/background and margins—place the PanelContainer outer
element around TargetCoordsBox, move the Label and inner Horizontal BoxContainer
into the PanelContainer content, and apply the same style classes/margins used
by the IFF Search/Maximum IFF Distance panels to ensure visual consistency.

<!-- End Frontier - settable target -->

<!-- Frontier - IFF search -->
<controls:PanelContainer VerticalExpand="True" HorizontalExpand="True" Margin="4 4 4 4">
<controls:PanelContainer.PanelOverride>
Expand Down
2 changes: 1 addition & 1 deletion Content.Client/Shuttles/UI/NavScreen.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ public void UpdateState(NavInterfaceState scc)
// Update port names if custom names are available
UpdateNetworkPortButtonNames(scc.NetworkPortNames);

NfUpdateState(); // Frontier Update State
NfUpdateState(scc); // Frontier Update State
}

/// <summary>
Expand Down
8 changes: 8 additions & 0 deletions Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ public sealed partial class ShuttleConsoleWindow : FancyWindow,
public event Action<NetEntity>? UndockRequest;
public event Action<List<NetEntity>>? UndockAllRequest;
public event Action<List<NetEntity>, bool>? ToggleFTLLockRequest;
public event Action<NetEntity?, NetEntity>? RequestTrackEntity; // Frontier

public ShuttleConsoleWindow()
{
Expand Down Expand Up @@ -54,6 +55,13 @@ public ShuttleConsoleWindow()
{
RequestBeaconFTL?.Invoke(ent, angle);
};

// Frontier: entity tracking
MapContainer.RequestTrackEntity += (ent, trackEntity) =>
{
RequestTrackEntity?.Invoke(ent, trackEntity);
};
// End Frontie
Comment on lines +58 to +64
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Typo in comment: "End Frontie" → "End Frontier".

✏️ Fix
-        // End Frontie
+        // End Frontier
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// Frontier: entity tracking
MapContainer.RequestTrackEntity += (ent, trackEntity) =>
{
RequestTrackEntity?.Invoke(ent, trackEntity);
};
// End Frontie
// Frontier: entity tracking
MapContainer.RequestTrackEntity += (ent, trackEntity) =>
{
RequestTrackEntity?.Invoke(ent, trackEntity);
};
// End Frontier
🤖 Prompt for AI Agents
In `@Content.Client/Shuttles/UI/ShuttleConsoleWindow.xaml.cs` around lines 58 -
64, Fix the typo in the trailing comment in ShuttleConsoleWindow.xaml.cs: change
the comment text "End Frontie" to "End Frontier" near the
MapContainer.RequestTrackEntity event hookup (the block that invokes
RequestTrackEntity). No code logic changes required—only update the comment
string so it reads "End Frontier".


DockContainer.DockRequest += (entity, netEntity) =>
{
Expand Down
99 changes: 90 additions & 9 deletions Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs
Original file line number Diff line number Diff line change
Expand Up @@ -276,12 +276,6 @@ public void UpdateState(NavInterfaceState state)

RotateWithEntity = state.RotateWithEntity;

// Frontier
if (state.MaxIffRange != null)
MaximumIFFDistance = state.MaxIffRange.Value;
HideCoords = state.HideCoords;
// End Frontier

_docks = state.Docks;

NfUpdateState(state); // Frontier Update State
Expand Down Expand Up @@ -560,9 +554,6 @@ protected override void Draw(DrawingHandleScreen handle)
// End Frontier: IFF drawing functions
}

// Frontier Don't skip drawing blips if they're out of range.
NfDrawBlips(handle, blipDataList);

// Detailed view
var gridAABB = curGridToWorld.TransformBox(grid.Comp.LocalAABB);

Expand All @@ -577,6 +568,96 @@ protected override void Draw(DrawingHandleScreen handle)
DrawDocks(handle, gUid, curGridToView);
}
}

// Frontier: draw target
if (!HideTarget && Target is { } target)
{
var targetEntity = EntManager.GetEntity(TargetEntity);

string targetName;
if (EntManager.TryGetComponent<MetaDataComponent>(targetEntity, out var targetMeta))
targetName = targetMeta.EntityName;
else
targetName = Loc.GetString("shuttle-console-target-name");

var curGridToView = Matrix3Helpers.CreateTranslation(target) * worldToShuttle * shuttleToView;

var labelColor = TargetColor;
var coordColor = new Color(TargetColor.R * 0.8f, TargetColor.G * 0.8f, TargetColor.B * 0.8f, 0.5f);

//var gridCentre = Vector2.Transform(gridBody.LocalCenter, curGridToView);
//gridCentre.Y = -gridCentre.Y;

// Frontier: IFF drawing functions
// 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;
var uiPosition = Vector2.Transform(Vector2.Zero, curGridToView) / UIScale;

// Confines the UI position within the viewport.
var uiXCentre = (int) Width / 2;
var uiYCentre = (int) Height / 2;
var uiXOffset = uiPosition.X - uiXCentre;
var uiYOffset = uiPosition.Y - uiYCentre;
var uiDistance = (int) Math.Sqrt(Math.Pow(uiXOffset, 2) + Math.Pow(uiYOffset, 2));
var uiX = uiXCentre * uiXOffset / uiDistance;
var uiY = uiYCentre * uiYOffset / uiDistance;

var isOutsideRadarCircle = uiDistance > Math.Abs(uiX) && uiDistance > Math.Abs(uiY);
if (isOutsideRadarCircle)
{
// 0.95f for offsetting the icons slightly away from edge of radar so it doesnt clip.
uiX = uiXCentre * uiXOffset / uiDistance * 0.95f;
uiY = uiYCentre * uiYOffset / uiDistance * 0.95f;
uiPosition = new Vector2(
x: uiX + uiXCentre,
y: uiY + uiYCentre
);
}
Comment on lines +598 to +616
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

Division by zero when target is at radar center.

When uiDistance is 0 (target position coincides with radar center), lines 603–604 divide by zero: uiXCentre * uiXOffset / uiDistance. This will throw a DivideByZeroException since uiDistance is an int.

Add a guard to skip the confinement logic when uiDistance is 0:

Proposed fix
             var uiDistance = (int) Math.Sqrt(Math.Pow(uiXOffset, 2) + Math.Pow(uiYOffset, 2));
-            var uiX = uiXCentre * uiXOffset / uiDistance;
-            var uiY = uiYCentre * uiYOffset / uiDistance;
-
-            var isOutsideRadarCircle = uiDistance > Math.Abs(uiX) && uiDistance > Math.Abs(uiY);
+            var isOutsideRadarCircle = false;
+            if (uiDistance > 0)
+            {
+                var uiX = uiXCentre * uiXOffset / uiDistance;
+                var uiY = uiYCentre * uiYOffset / uiDistance;
+                isOutsideRadarCircle = uiDistance > Math.Abs(uiX) && uiDistance > Math.Abs(uiY);
+                if (isOutsideRadarCircle)
+                {
+                    uiX = uiXCentre * uiXOffset / uiDistance * 0.95f;
+                    uiY = uiYCentre * uiYOffset / uiDistance * 0.95f;
+                    uiPosition = new Vector2(
+                        x: uiX + uiXCentre,
+                        y: uiY + uiYCentre
+                    );
+                }
+            }

Note: the same pre-existing bug exists in the grid IFF drawing code around line 433, but it should be fixed here at minimum since this is new code.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var uiXCentre = (int) Width / 2;
var uiYCentre = (int) Height / 2;
var uiXOffset = uiPosition.X - uiXCentre;
var uiYOffset = uiPosition.Y - uiYCentre;
var uiDistance = (int) Math.Sqrt(Math.Pow(uiXOffset, 2) + Math.Pow(uiYOffset, 2));
var uiX = uiXCentre * uiXOffset / uiDistance;
var uiY = uiYCentre * uiYOffset / uiDistance;
var isOutsideRadarCircle = uiDistance > Math.Abs(uiX) && uiDistance > Math.Abs(uiY);
if (isOutsideRadarCircle)
{
// 0.95f for offsetting the icons slightly away from edge of radar so it doesnt clip.
uiX = uiXCentre * uiXOffset / uiDistance * 0.95f;
uiY = uiYCentre * uiYOffset / uiDistance * 0.95f;
uiPosition = new Vector2(
x: uiX + uiXCentre,
y: uiY + uiYCentre
);
}
var uiXCentre = (int) Width / 2;
var uiYCentre = (int) Height / 2;
var uiXOffset = uiPosition.X - uiXCentre;
var uiYOffset = uiPosition.Y - uiYCentre;
var uiDistance = (int) Math.Sqrt(Math.Pow(uiXOffset, 2) + Math.Pow(uiYOffset, 2));
var isOutsideRadarCircle = false;
if (uiDistance > 0)
{
var uiX = uiXCentre * uiXOffset / uiDistance;
var uiY = uiYCentre * uiYOffset / uiDistance;
isOutsideRadarCircle = uiDistance > Math.Abs(uiX) && uiDistance > Math.Abs(uiY);
if (isOutsideRadarCircle)
{
// 0.95f for offsetting the icons slightly away from edge of radar so it doesnt clip.
uiX = uiXCentre * uiXOffset / uiDistance * 0.95f;
uiY = uiYCentre * uiYOffset / uiDistance * 0.95f;
uiPosition = new Vector2(
x: uiX + uiXCentre,
y: uiY + uiYCentre
);
}
}
🤖 Prompt for AI Agents
In `@Content.Client/Shuttles/UI/ShuttleNavControl.xaml.cs` around lines 598 - 616,
The code divides by uiDistance (an int) when computing uiX/uiY which will throw
when uiDistance == 0; add a guard before the confinement logic that checks if
uiDistance == 0 and in that case skip the division (e.g., leave uiPosition at
center or return early) so the expressions using uiXCentre * uiXOffset /
uiDistance and uiYCentre * uiYOffset / uiDistance are never evaluated; modify
the block using uiDistance, uiX, uiY, uiXCentre, uiXOffset, uiYCentre,
uiYOffset, isOutsideRadarCircle and uiPosition in ShuttleNavControl.xaml.cs to
return or bypass the confinement math when uiDistance == 0.


var scaledMousePosition = GetMouseCoordinatesFromCenter().Position * UIScale;
var isMouseOver = Vector2.Distance(scaledMousePosition, uiPosition * UIScale) < 30f;

var distance = Vector2.Distance(target, mapPos.Position);

// Shows decimal when distance is < 50m, otherwise pointless to show it.
var displayedDistance = distance < 50f ? $"{distance:0.0}" : distance < 1000 ? $"{distance:0}" : $"{distance / 1000:0.0}k";
var labelText = Loc.GetString("shuttle-console-iff-label", ("name", targetName)!, ("distance", displayedDistance));

var coordsText = $"({target.X:0.0}, {target.Y:0.0})";

// Calculate unscaled offsets.
var labelDimensions = handle.GetDimensions(Font, labelText, 1f);
var blipSize = RadarBlipSize * 0.7f;
var labelOffset = new Vector2()
{
X = uiPosition.X > Width / 2f
? -labelDimensions.X - blipSize // right align the text to left of the blip
: blipSize, // left align the text to the right of the blip
Y = -labelDimensions.Y / 2f
};

handle.DrawString(Font, (uiPosition + labelOffset) * UIScale, labelText, UIScale, labelColor);
if (isMouseOver && !HideCoords)
{
var coordDimensions = handle.GetDimensions(Font, coordsText, 0.7f);
var coordOffset = new Vector2()
{
X = uiPosition.X > Width / 2f
? -coordDimensions.X - blipSize / 0.7f // right align the text to left of the blip (0.7 needed for scale)
: blipSize, // left align the text to the right of the blip
Y = coordDimensions.Y / 2
};
handle.DrawString(Font, (uiPosition + coordOffset) * UIScale, coordsText, 0.7f * UIScale, coordColor);
}

NfAddBlipToList(blipDataList, isOutsideRadarCircle, uiPosition, uiXCentre, uiYCentre, labelColor); // Frontier code
// End Frontier: IFF drawing functions
}

// Draw all blips on the map at this point.
NfDrawBlips(handle, blipDataList);
// End Frontier: draw target

// If we've set the controlling console, and it's on a different grid
// to the shuttle itself, then draw an additional marker to help the
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// New Frontiers - This file is licensed under AGPLv3
// Copyright (c) 2024 New Frontiers Contributors
// See AGPLv3.txt for details.
using System.Numerics;
using Content.Client.Shuttles.UI;
using Content.Shared._NF.Shuttles.Events;

Expand All @@ -14,6 +15,9 @@ private void NfOpen()
_window.OnInertiaDampeningModeChanged += OnInertiaDampeningModeChanged;
_window.OnMaxShuttleSpeedChanged += OnMaxShuttleSpeedChanged;
_window.OnNetworkPortButtonPressed += OnNetworkPortButtonPressed;
_window.OnSetTargetCoordinates += OnSetTargetCoordinates;
_window.OnSetHideTarget += OnSetHideTarget;
_window.RequestTrackEntity += OnTrackEntity;
}
private void OnInertiaDampeningModeChanged(NetEntity? entityUid, InertiaDampeningMode mode)
{
Expand Down Expand Up @@ -41,5 +45,33 @@ private void OnNetworkPortButtonPressed(string sourcePort, string targetPort)
TargetPort = targetPort
});
}

private void OnSetTargetCoordinates(NetEntity? entityUid, Vector2 position)
{
SendMessage(new SetTargetCoordinatesRequest
{
ShuttleEntityUid = entityUid,
TrackedPosition = position,
TrackedEntity = NetEntity.Invalid
});
}

private void OnSetHideTarget(NetEntity? entityUid, bool hide)
{
SendMessage(new SetHideTargetRequest
{
Hidden = hide
});
}

private void OnTrackEntity(NetEntity? entityUid, NetEntity trackEntity)
{
SendMessage(new SetTargetCoordinatesRequest
{
ShuttleEntityUid = entityUid,
TrackedPosition = Vector2.Zero, // don't care
TrackedEntity = trackEntity
});
}
}
}
Loading
Loading