Skip to content

Commit d6144a8

Browse files
authored
Merge pull request #1407 from ss14Starlight/starlight-dev
Live push 8 20 2025
2 parents a853a4e + 3c261b2 commit d6144a8

749 files changed

Lines changed: 33107 additions & 4736 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.github/labeler.yml

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,3 +24,7 @@
2424
- changed-files:
2525
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.
2626
- all-globs-to-all-files: "!**/*.cs"
27+
28+
"Changes: C#":
29+
- changed-files:
30+
- any-glob-to-any-file: '**/*.cs'

.github/workflows/auto-assign.yml

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,10 @@ jobs:
1919
"Changes: Sprites": ["BORTBORTBORTBORTBORT"],
2020
"Changes: Map": ["PubliclyExecutedPig"],
2121
"Changes: UI": ["ss14-Starlight"],
22-
"Changes: Shaders": ["ss14-Starlight"],
23-
"Changes: Audio": ["ss14-Starlight"],
24-
"Changes: No C#": ["ss14-Starlight"],
22+
"Changes: Shaders": ["ss14-Starlight, Happyrobot33, FoxxoTrystan"],
23+
"Changes: Audio": ["ss14-Starlight, Happyrobot33, FoxxoTrystan"],
24+
"Changes: No C#": ["ss14-Starlight, Happyrobot33, FoxxoTrystan"],
25+
"Changes: C#": ["ss14-Starlight, Happyrobot33, FoxxoTrystan"],
2526
};
2627
2728
const labels = context.payload.pull_request.labels.map(label => label.name);

.github/workflows/publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,7 @@ jobs:
5151

5252
update-changelog:
5353
name: Publish Changelogs
54-
needs: build
54+
# needs: build
5555
runs-on: ubuntu-latest
5656
environment: prod
5757
steps:
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: Check PR Target Branch
2+
3+
on:
4+
pull_request:
5+
types: [opened, synchronize, reopened, edited]
6+
7+
jobs:
8+
check-target-branch:
9+
runs-on: ubuntu-latest
10+
11+
steps:
12+
- name: Check if PR targets starlight-dev
13+
run: |
14+
SOURCE_BRANCH="${{ github.event.pull_request.head.ref }}"
15+
TARGET_BRANCH="${{ github.event.pull_request.base.ref }}"
16+
echo "PR from: $SOURCE_BRANCH -> $TARGET_BRANCH"
17+
18+
# Allow starlight-dev to starlight PRs in both directions
19+
if [[ ("$SOURCE_BRANCH" == "starlight-dev" && "$TARGET_BRANCH" == "starlight") ||
20+
("$SOURCE_BRANCH" == "starlight" && "$TARGET_BRANCH" == "starlight-dev") ]]; then
21+
echo "Allowed: PR between starlight-dev and starlight branches"
22+
exit 0
23+
fi
24+
25+
# For all other PRs, require starlight-dev as target
26+
if [ "$TARGET_BRANCH" != "starlight-dev" ]; then
27+
echo "Error: PR must target 'starlight-dev' branch, but targets '$TARGET_BRANCH'"
28+
echo "Please change the target branch to 'starlight-dev'"
29+
exit 1
30+
else
31+
echo "Success: PR correctly targets 'starlight-dev' branch"
32+
fi

Content.Client/Actions/ActionsSystem.cs

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,12 @@ private void OnWorldTargetAttempt(Entity<WorldTargetActionComponent> ent, ref Ac
334334

335335
private void OnEntityTargetAttempt(Entity<EntityTargetActionComponent> ent, ref ActionTargetAttemptEvent args)
336336
{
337-
if (args.Handled || args.Input.EntityUid is not { Valid: true } entity)
337+
if (args.Handled)
338+
return;
339+
340+
args.Handled = true;
341+
342+
if (args.Input.EntityUid is not { Valid: true } entity)
338343
return;
339344

340345
// let world target component handle it
@@ -345,8 +350,6 @@ private void OnEntityTargetAttempt(Entity<EntityTargetActionComponent> ent, ref
345350
return;
346351
}
347352

348-
args.Handled = true;
349-
350353
var action = args.Action;
351354
var user = args.User;
352355

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
<Control
2+
xmlns="https://spacestation14.io"
3+
xmlns:viewport="clr-namespace:Content.Client.Viewport"
4+
MouseFilter="Stop">
5+
<PanelContainer StyleClasses="BackgroundDark" Name="AdminCameraWindowRoot" Access="Public">
6+
<BoxContainer Orientation="Vertical" Access="Public">
7+
<!-- Camera -->
8+
<Control VerticalExpand="True" Name="CameraViewBox">
9+
<viewport:ScalingViewport Name="CameraView"
10+
MinSize="100 100"
11+
MouseFilter="Ignore" />
12+
</Control>
13+
<!-- Controller buttons -->
14+
<BoxContainer Orientation="Horizontal" Margin="5 5 5 5">
15+
<Button StyleClasses="OpenRight" Name="FollowButton" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-follow'}" />
16+
<Button StyleClasses="OpenLeft" Name="PopControl" HorizontalExpand="True" Access="Public" Text="{Loc 'admin-camera-window-pop-out'}" />
17+
</BoxContainer>
18+
</BoxContainer>
19+
</PanelContainer>
20+
</Control>
Lines changed: 101 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,101 @@
1+
using System.Numerics;
2+
using Content.Client.Eye;
3+
using Content.Shared.Administration;
4+
using Robust.Client.AutoGenerated;
5+
using Robust.Client.Graphics;
6+
using Robust.Client.Timing;
7+
using Robust.Client.UserInterface;
8+
using Robust.Client.UserInterface.XAML;
9+
using Robust.Shared.Timing;
10+
11+
namespace Content.Client.Administration.UI.AdminCamera;
12+
13+
[GenerateTypedNameReferences]
14+
public sealed partial class AdminCameraControl : Control
15+
{
16+
[Dependency] private readonly IEntityManager _entManager = default!;
17+
[Dependency] private readonly IClientGameTiming _timing = default!;
18+
19+
public event Action? OnFollow;
20+
public event Action? OnPopoutControl;
21+
22+
private readonly EyeLerpingSystem _eyeLerpingSystem;
23+
private readonly FixedEye _defaultEye = new();
24+
private AdminCameraEuiState? _nextState;
25+
26+
private const float MinimumZoom = 0.1f;
27+
private const float MaximumZoom = 2.0f;
28+
29+
public EntityUid? CurrentCamera;
30+
public float Zoom = 1.0f;
31+
32+
public bool IsPoppedOut;
33+
34+
public AdminCameraControl()
35+
{
36+
RobustXamlLoader.Load(this);
37+
IoCManager.InjectDependencies(this);
38+
39+
_eyeLerpingSystem = _entManager.System<EyeLerpingSystem>();
40+
41+
CameraView.Eye = _defaultEye;
42+
43+
FollowButton.OnPressed += _ => OnFollow?.Invoke();
44+
PopControl.OnPressed += _ => OnPopoutControl?.Invoke();
45+
CameraView.OnResized += OnResized;
46+
}
47+
48+
private new void OnResized()
49+
{
50+
var width = Math.Max(CameraView.PixelWidth, (int)Math.Floor(CameraView.MinWidth));
51+
var height = Math.Max(CameraView.PixelHeight, (int)Math.Floor(CameraView.MinHeight));
52+
53+
CameraView.ViewportSize = new Vector2i(width, height);
54+
}
55+
56+
protected override void MouseWheel(GUIMouseWheelEventArgs args)
57+
{
58+
base.MouseWheel(args);
59+
60+
if (CameraView.Eye == null)
61+
return;
62+
63+
Zoom = Math.Clamp(Zoom - args.Delta.Y * 0.15f * Zoom, MinimumZoom, MaximumZoom);
64+
CameraView.Eye.Zoom = new Vector2(Zoom, Zoom);
65+
args.Handle();
66+
}
67+
68+
public void SetState(AdminCameraEuiState state)
69+
{
70+
_nextState = state;
71+
}
72+
73+
// I know that this is awful, but I copied this from the solution editor anyways.
74+
// This is needed because EUIs update before the gamestate is applied, which means it will fail to get the uid from the net entity.
75+
// The suggestion from the comment in the solution editor saying to use a BUI is not ideal either:
76+
// - We would need to bind the UI to an entity, but with how BUIs currently work we cannot open it in the same tick as we spawn that entity on the server.
77+
// - We want the UI opened by the user session, not by their currently attached entity. Otherwise it would close in cases where admins move from one entity to another, for example when ghosting.
78+
protected override void FrameUpdate(FrameEventArgs args)
79+
{
80+
if (_nextState == null || _timing.LastRealTick < _nextState.Tick) // make sure the last gamestate has been applied
81+
return;
82+
83+
if (!_entManager.TryGetEntity(_nextState.Camera, out var cameraUid))
84+
return;
85+
86+
if (CurrentCamera == null)
87+
{
88+
_eyeLerpingSystem.AddEye(cameraUid.Value);
89+
CurrentCamera = cameraUid;
90+
}
91+
else if (CurrentCamera != cameraUid)
92+
{
93+
_eyeLerpingSystem.RemoveEye(CurrentCamera.Value);
94+
_eyeLerpingSystem.AddEye(cameraUid.Value);
95+
CurrentCamera = cameraUid;
96+
}
97+
98+
if (_entManager.TryGetComponent<EyeComponent>(CurrentCamera, out var eye))
99+
CameraView.Eye = eye.Eye ?? _defaultEye;
100+
}
101+
}
Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,117 @@
1+
using System.Numerics;
2+
using Content.Client.Eui;
3+
using Content.Shared.Administration;
4+
using Content.Shared.Eui;
5+
using JetBrains.Annotations;
6+
using Robust.Client.UserInterface.Controls;
7+
8+
namespace Content.Client.Administration.UI.AdminCamera;
9+
10+
/// <summary>
11+
/// Admin Eui for opening a viewport window to observe entities.
12+
/// Use the "Open Camera" admin verb or the "camera" command to open.
13+
/// </summary>
14+
[UsedImplicitly]
15+
public sealed partial class AdminCameraEui : BaseEui
16+
{
17+
private readonly AdminCameraWindow _window;
18+
private readonly AdminCameraControl _control;
19+
20+
// If not null the camera is in "popped out" mode and is in an external window.
21+
private OSWindow? _OSWindow;
22+
23+
// The last location the window was located at in game.
24+
// Is used for getting knowing where to "pop in" external windows.
25+
private Vector2 _lastLocation;
26+
27+
public AdminCameraEui()
28+
{
29+
_window = new AdminCameraWindow();
30+
_control = new AdminCameraControl();
31+
32+
_window.Contents.AddChild(_control);
33+
34+
_control.OnFollow += () => SendMessage(new AdminCameraFollowMessage());
35+
_window.OnClose += () =>
36+
{
37+
if (!_control.IsPoppedOut)
38+
SendMessage(new CloseEuiMessage());
39+
};
40+
41+
_control.OnPopoutControl += () =>
42+
{
43+
if (_control.IsPoppedOut)
44+
PopIn();
45+
else
46+
PopOut();
47+
};
48+
}
49+
50+
// Pop the window out into an external OS window
51+
private void PopOut()
52+
{
53+
_lastLocation = _window.Position;
54+
55+
// TODO: When there is a way to have a minimum window size, enforce something!
56+
_OSWindow = new OSWindow
57+
{
58+
SetSize = _window.Size,
59+
Title = _window.Title ?? Loc.GetString("admin-camera-window-title-placeholder"),
60+
};
61+
62+
_OSWindow.Show();
63+
64+
if (_OSWindow.Root == null)
65+
return;
66+
67+
_control.Orphan();
68+
_OSWindow.Root.AddChild(_control);
69+
70+
_OSWindow.Closed += () =>
71+
{
72+
if (_control.IsPoppedOut)
73+
SendMessage(new CloseEuiMessage());
74+
};
75+
76+
_control.IsPoppedOut = true;
77+
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-in");
78+
79+
_window.Close();
80+
}
81+
82+
// Pop the window back into the in game window.
83+
private void PopIn()
84+
{
85+
_control.Orphan();
86+
_window.Contents.AddChild(_control);
87+
88+
_window.Open(_lastLocation);
89+
90+
_control.IsPoppedOut = false;
91+
_control.PopControl.Text = Loc.GetString("admin-camera-window-pop-out");
92+
93+
_OSWindow?.Close();
94+
_OSWindow = null;
95+
}
96+
97+
public override void Opened()
98+
{
99+
base.Opened();
100+
_window.OpenCentered();
101+
}
102+
103+
public override void Closed()
104+
{
105+
base.Closed();
106+
_window.Close();
107+
}
108+
109+
public override void HandleState(EuiStateBase baseState)
110+
{
111+
if (baseState is not AdminCameraEuiState state)
112+
return;
113+
114+
_window.SetState(state);
115+
_control.SetState(state);
116+
}
117+
}
Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
<DefaultWindow xmlns="https://spacestation14.io"
2+
Title="{Loc admin-camera-window-title-placeholder}"
3+
SetSize="425 550"
4+
MinSize="200 225"
5+
Name="Window">
6+
</DefaultWindow>
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using Content.Shared.Administration;
2+
using Robust.Client.AutoGenerated;
3+
using Robust.Client.UserInterface.CustomControls;
4+
using Robust.Client.UserInterface.XAML;
5+
6+
namespace Content.Client.Administration.UI.AdminCamera;
7+
8+
[GenerateTypedNameReferences]
9+
public sealed partial class AdminCameraWindow : DefaultWindow
10+
{
11+
public AdminCameraWindow()
12+
{
13+
RobustXamlLoader.Load(this);
14+
IoCManager.InjectDependencies(this);
15+
16+
ContentsContainer.Margin = new Thickness(5, 0, 5, 0);
17+
}
18+
19+
public void SetState(AdminCameraEuiState state)
20+
{
21+
Title = Loc.GetString("admin-camera-window-title", ("name", state.Name));
22+
}
23+
}

0 commit comments

Comments
 (0)