Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ThunderBear2006 <bearthunder06@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Content.Shared._Goobstation.Silicon.AiCameraWarping; // Goobstation

namespace Content.Client._Goobstation.Silicon.AiCameraWarping; // Goobstation

public sealed class StationAiWarpSystem : SharedStationAiWarpSystem { }
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ThunderBear2006 <bearthunder06@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later


using Content.Shared._Goobstation.Silicon.AiCameraWarping; // Goobstation
using Robust.Client.UserInterface;
using Serilog;

namespace Content.Client._Goobstation.Silicon.AiCameraWarping.Ui; // Goobstation

public sealed class AiCameraWarpBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private AiCameraWarpMenu? _menu;

protected override void Open()
{
base.Open();

_menu = this.CreateWindow<AiCameraWarpMenu>();
_menu.OnCamWarpAction += SendAction;
_menu.OnRefresh += SendRefresh;
}

protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);

if (state is not CameraWarpBuiState msg)
return;

_menu?.Update(msg);
}

public void SendAction(CameraWarpActionEvent action)
{
SendMessage(new CameraWarpActionMessage(action));
}

public void SendRefresh()
{
SendMessage(new CameraWarpRefreshActionMessage());
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="using:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc ai-cam-warp-ui-menu-title}"
MinSize="355 100"
SetSize="355 415">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<Button Name="RefreshButton" StyleClasses="chatSelectorOptionButton" Text="{Loc ai-camera-ui-menu-refresh}"/>
<LineEdit Name="SearchBar" PlaceHolder="Search" HorizontalExpand="True" Margin="0 4" />
<PanelContainer VerticalExpand="True" Margin="10 10 10 10">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#1B1B1E"/>
</PanelContainer.PanelOverride>
<ScrollContainer
HScrollEnabled="False"
HorizontalExpand="True"
VerticalExpand="True">
<BoxContainer
Name="CameraWarpsDisplayContainer"
Orientation="Vertical"
VerticalExpand="True"
Margin="10 10 10 0">
</BoxContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
</controls:FancyWindow>
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ThunderBear2006 <bearthunder06@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Shared._Goobstation.Silicon.AiCameraWarping; // Goobstation
using Content.Shared.Mobs;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;

namespace Content.Client._Goobstation.Silicon.AiCameraWarping.Ui; // Goobstation

[GenerateTypedNameReferences]
public sealed partial class AiCameraWarpMenu : FancyWindow
{
public event Action<CameraWarpActionEvent>? OnCamWarpAction;
public event Action? OnRefresh;

public AiCameraWarpMenu()
{
RobustXamlLoader.Load(this);

SearchBar.OnTextChanged += UpdateVisibleButtons;
RefreshButton.OnPressed += _ => OnRefresh?.Invoke();
}

public void Update(CameraWarpBuiState state)
{
CameraWarpsDisplayContainer.Children.Clear();

if (state.CameraList == null)
return;

// Sort the list alphabetically
state.CameraList.Sort((a, b) => a.DisplayName.CompareTo(b.DisplayName));

var previousName = "";
var i = 2;

state.CameraList.ForEach(data =>
{
// This is to make the menu more readable when
// there is many duplicate names.
if (data.DisplayName.Equals(previousName))
{
data.DisplayName = $"{previousName} {i++}";
}
else
{
previousName = data.DisplayName;
i = 2;
}

var button = new Button
{
Text = data.DisplayName
};

button.OnPressed += _ =>
{
OnCamWarpAction?.Invoke(new CameraWarpActionEvent(data.NetEntityUid));
};

CameraWarpsDisplayContainer.AddChild(button);
});
}

private void UpdateVisibleButtons(LineEdit.LineEditEventArgs args)
{
foreach (var child in CameraWarpsDisplayContainer.Children)
{
if (child is Button button)
button.Visible = button.Text != null && button.Text.ToLower().Contains(args.Text.ToLower());
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 ThunderBear2006 <bearthunder06@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Content.Shared._Goobstation.Silicon.AiCameraWarping; // Goobstation
using Content.Server.Station.Systems;
using Content.Server.SurveillanceCamera;
using Content.Shared.Silicons.StationAi;
using Microsoft.VisualBasic;
using Robust.Server.GameObjects;
using Robust.Shared.Player;

namespace Content.Server._Goobstation.Silicon.AiCameraWarping; // Goobstation

public sealed class StationAiWarpSystem : SharedStationAiWarpSystem
{
[Dependency] private readonly SharedStationAiSystem _stationAiSystem = default!;
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly UserInterfaceSystem _userInterface = default!;

public override void Initialize()
{
base.Initialize();

SubscribeLocalEvent<StationAiHeldComponent, CameraWarpActionMessage>(OnUiWarpAction);
SubscribeLocalEvent<StationAiHeldComponent, CameraWarpRefreshActionMessage>(OnUiRefreshRequested);
}

private void OnUiRefreshRequested(Entity<StationAiHeldComponent> ent, ref CameraWarpRefreshActionMessage args)
{
if (!_stationAiSystem.TryGetCore(ent.Owner, out var core))
return;

var cameras = GetCameras(core.Owner);

var state = new CameraWarpBuiState(cameras);
_userInterface.SetUiState(ent.Owner, CamWarpUiKey.Key, state);
}

private List<CameraWarpData> GetCameras(EntityUid coreUid)
{
List<CameraWarpData> cameras = new();

var query = EntityManager.EntityQueryEnumerator<SurveillanceCameraComponent>();

var aiGrid = _xformSystem.GetGrid(coreUid);

while (query.MoveNext(out var queryUid, out var comp))
{
if (_xformSystem.GetGrid(queryUid) != aiGrid || !comp.Active)
continue;

var data = new CameraWarpData
{
NetEntityUid = GetNetEntity(queryUid),
DisplayName = comp.CameraId
};

cameras.Add(data);
}

return cameras;
}

private void OnUiWarpAction(Entity<StationAiHeldComponent> ent, ref CameraWarpActionMessage args)
{
if (args.WarpAction == null)
return;

var target = GetEntity(args.WarpAction.Target);

// The UI doesn't get refreshed when a camera goes offline or whatever
// so we gotta check if it can still be jumped to.

if (!TryComp<SurveillanceCameraComponent>(target, out var camera) || !camera.Active)
return;

if (!_stationAiSystem.TryGetCore(ent.Owner, out var core) || core.Comp?.RemoteEntity == null)
return;

// The AI shouldn't be able to jump to cams on other stations/shuttles
if (_xformSystem.GetGrid(core.Owner) != _xformSystem.GetGrid(target))
return;

_xformSystem.SetWorldPosition(core.Comp.RemoteEntity.Value, _xformSystem.GetWorldPosition(target));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
// SPDX-FileCopyrightText: 2024 Aidenkrz <aiden@djkraz.com>
// SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
// SPDX-FileCopyrightText: 2024 VMSolidus <evilexecutive@gmail.com>
// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
// SPDX-FileCopyrightText: 2025 GoobBot <uristmchands@proton.me>
// SPDX-FileCopyrightText: 2025 Ilya246 <ilyukarno@gmail.com>
// SPDX-FileCopyrightText: 2025 Misandry <mary@thughunt.ing>
// SPDX-FileCopyrightText: 2025 gus <august.eymann@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

using Robust.Shared.Prototypes;

namespace Content.Shared._Goobstation.Clothing.Components // Goobstation
{
[RegisterComponent]
public sealed partial class ClothingGrantComponent : Component
{
[DataField("component", required: true)]
[AlwaysPushInheritance]
public ComponentRegistry Components { get; private set; } = new();

[ViewVariables(VVAccess.ReadWrite)]
public Dictionary<string, bool> Active = new(); // Goobstation
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-FileCopyrightText: 2024 Aidenkrz <aiden@djkraz.com>
// SPDX-FileCopyrightText: 2024 Piras314 <p1r4s@proton.me>
// SPDX-FileCopyrightText: 2024 VMSolidus <evilexecutive@gmail.com>
// SPDX-FileCopyrightText: 2025 Aiden <28298836+Aidenkrz@users.noreply.github.com>
// SPDX-FileCopyrightText: 2025 Misandry <mary@thughunt.ing>
// SPDX-FileCopyrightText: 2025 gus <august.eymann@gmail.com>
//
// SPDX-License-Identifier: AGPL-3.0-or-later

namespace Content.Shared._Goobstation.Clothing.Components // Goobstation
{
[RegisterComponent]
public sealed partial class ClothingGrantTagComponent : Component
{
[DataField("tag", required: true), ViewVariables(VVAccess.ReadWrite)]
public string Tag = "";

[ViewVariables(VVAccess.ReadWrite)]
public bool IsActive = false;
}
}
Loading
Loading