Compare commits

..

4 Commits

Author SHA1 Message Date
Ed
60a87f1c35 more params in component 2024-07-03 17:06:08 +03:00
Ed
786c0ed97c Update CP14CloudShadowsComponent.cs 2024-07-03 15:17:46 +03:00
Ed
d3895e8288 fix 2024-07-03 15:17:05 +03:00
Ed
d184f795c4 not worked 2024-07-03 14:38:18 +03:00
8622 changed files with 714002 additions and 857352 deletions

View File

@@ -129,7 +129,7 @@ csharp_indent_braces = false
csharp_indent_switch_labels = true csharp_indent_switch_labels = true
# Space preferences # Space preferences
csharp_space_after_cast = false csharp_space_after_cast = true
csharp_space_after_colon_in_inheritance_clause = true csharp_space_after_colon_in_inheritance_clause = true
csharp_space_after_comma = true csharp_space_after_comma = true
csharp_space_after_dot = false csharp_space_after_dot = false

2
.github/CODEOWNERS vendored
View File

@@ -19,7 +19,7 @@
/Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer /Resources/engineCommandPerms.yml @moonheart08 @Chief-Engineer
/Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer /Resources/clientCommandPerms.yml @moonheart08 @Chief-Engineer
/Resources/Prototypes/Maps/** @Emisse /Resources/Prototypes/Maps/ @Emisse
/Resources/Prototypes/Body/ @DrSmugleaf # suffering /Resources/Prototypes/Body/ @DrSmugleaf # suffering
/Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf /Resources/Prototypes/Entities/Mobs/Player/ @DrSmugleaf

14
.github/FUNDING.yml vendored
View File

@@ -1,14 +0,0 @@
# These are supported funding model platforms
github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
polar: # Replace with a single Polar username
buy_me_a_coffee: # Replace with a single Buy Me a Coffee username
custom: ['https://boosty.to/theshued']

View File

@@ -7,11 +7,11 @@
<!-- Зачем нужно это изменение? Прикрепите любые обсуждения или проблемы здесь. Опишите, как это повлияет на текущий баланс игры. --> <!-- Зачем нужно это изменение? Прикрепите любые обсуждения или проблемы здесь. Опишите, как это повлияет на текущий баланс игры. -->
## Media ## Media
<!-- <!--
PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes. PRs which make ingame changes (adding clothing, items, new features, etc) are required to have media attached that showcase the changes.
Small fixes/refactors are exempt. Small fixes/refactors are exempt.
--> -->
<!-- <!--
Пулл реквесты, которые несут за собой игровые изменения (добавления одежды, предметов и так далее) требуют чтобы вы прикрепили скриншоты или видеоролики, демонстрирующие эти изменения. Пулл реквесты, которые несут за собой игровые изменения (добавления одежды, предметов и так далее) требуют чтобы вы прикрепили скриншоты или видеоролики, демонстрирующие эти изменения.
Небольшие исправления не считаются. Небольшие исправления не считаются.
--> -->

8
.github/labeler.yml vendored
View File

@@ -5,17 +5,13 @@
"Changes: Map": "Changes: Map":
- changed-files: - changed-files:
- any-glob-to-any-file: - any-glob-to-any-file:
- 'Resources/Maps/**/*.yml' - 'Resources/Maps/*.yml'
- 'Resources/Prototypes/Maps/**/*.yml' - 'Resources/Prototypes/Maps/*.yml'
"Changes: UI": "Changes: UI":
- changed-files: - changed-files:
- any-glob-to-any-file: '**/*.xaml*' - any-glob-to-any-file: '**/*.xaml*'
"Changes: Shaders":
- changed-files:
- any-glob-to-any-file: '**/*.swsl'
"No C#": "No C#":
- changed-files: - changed-files:
# Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label. # Equiv to any-glob-to-all as long as this has one matcher. If ALL changed files are not C# files, then apply label.

View File

@@ -85,7 +85,7 @@
"CC-BY-NC-SA-3.0", "CC-BY-NC-SA-3.0",
"CC-BY-NC-SA-4.0", "CC-BY-NC-SA-4.0",
"CC0-1.0", "CC0-1.0",
"CLA" "All rights reserved for the CrystallPunk14 project only"
], ],
"examples":[ "examples":[
"CC-BY-SA-3.0" "CC-BY-SA-3.0"

View File

@@ -1,20 +0,0 @@
name: Publish
concurrency:
group: publish
on:
workflow_dispatch:
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Send POST-request
uses: appleboy/ssh-action@master
with:
host: ${{ secrets.BUILD_HOST }}
username: ${{ secrets.BUILD_USER }}
password: ${{ secrets.BUILD_PASS }}
port: 22
script: sh update.sh &> /dev/null

View File

@@ -5,8 +5,8 @@ concurrency:
on: on:
workflow_dispatch: workflow_dispatch:
# schedule: schedule:
# - cron: '0 10 * * *' - cron: '0 10 * * *'
jobs: jobs:
build: build:
@@ -41,11 +41,31 @@ jobs:
- name: Package client - name: Package client
run: dotnet run --project Content.Packaging client --no-wipe-release run: dotnet run --project Content.Packaging client --no-wipe-release
- name: Publish version - name: Update Build Info
run: Tools/publish_multi_request.py run: Tools/gen_build_info.py
env:
PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }} - name: Shuffle files around
GITHUB_REPOSITORY: ${{ vars.GITHUB_REPOSITORY }} run: |
mkdir "release/${{ github.sha }}"
mv release/*.zip "release/${{ github.sha }}"
- name: Upload files to centcomm
uses: appleboy/scp-action@master
with:
host: centcomm.spacestation14.io
username: wizards-build-push
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
source: "release/${{ github.sha }}"
target: "/home/wizards-build-push/builds_dir/builds/"
strip_components: 1
- name: Update manifest JSON
uses: appleboy/ssh-action@master
with:
host: centcomm.spacestation14.io
username: wizards-build-push
key: ${{ secrets.CENTCOMM_WIZARDS_BUILDS_PUSH_KEY }}
script: /home/wizards-build-push/push.ps1 ${{ github.sha }}
- name: Publish changelog (Discord) - name: Publish changelog (Discord)
run: Tools/actions_changelogs_since_last_run.py run: Tools/actions_changelogs_since_last_run.py

View File

@@ -64,3 +64,11 @@ jobs:
- name: Package client - name: Package client
run: dotnet run --project Content.Packaging client --no-wipe-release run: dotnet run --project Content.Packaging client --no-wipe-release
- name: Update Build Info
run: Tools/gen_build_info.py
- name: Shuffle files around
run: |
mkdir "release/${{ github.sha }}"
mv release/*.zip "release/${{ github.sha }}"

View File

@@ -19,8 +19,6 @@ jobs:
- name: Get this week's Contributors - name: Get this week's Contributors
shell: pwsh shell: pwsh
env:
GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}}
run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt run: Tools/dump_github_contributors.ps1 > Resources/Credits/GitHub.txt
# TODO # TODO

4
.vscode/settings.json vendored Normal file
View File

@@ -0,0 +1,4 @@
{
"omnisharp.analyzeOpenDocumentsOnly": true,
"dotnet.defaultSolution": "SpaceStation14.sln"
}

6
.vscode/tasks.json vendored
View File

@@ -10,7 +10,7 @@
"args": [ "args": [
"build", "build",
"/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names. "/property:GenerateFullPaths=true", // Ask dotnet build to generate full paths for file names.
"/consoleloggerparameters:'ForceNoAlign;NoSummary'" // Do not generate summary otherwise it leads to duplicate errors in Problems panel "/consoleloggerparameters:NoSummary" // Do not generate summary otherwise it leads to duplicate errors in Problems panel
], ],
"group": { "group": {
"kind": "build", "kind": "build",
@@ -29,9 +29,9 @@
"build", "build",
"${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj", "${workspaceFolder}/Content.YAMLLinter/Content.YAMLLinter.csproj",
"/property:GenerateFullPaths=true", "/property:GenerateFullPaths=true",
"/consoleloggerparameters:'ForceNoAlign;NoSummary'" "/consoleloggerparameters:NoSummary"
], ],
"problemMatcher": "$msCompile" "problemMatcher": "$msCompile"
} }
] ]
} }

View File

@@ -46,7 +46,7 @@ public class MapLoadBenchmark
PoolManager.Shutdown(); PoolManager.Shutdown();
} }
public static readonly string[] MapsSource = { "Empty", "Satlern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Cog" }; public static readonly string[] MapsSource = { "Empty", "Box", "Bagel", "Dev", "CentComm", "Atlas", "Core", "TestTeg", "Saltern", "Packed", "Omega", "Cluster", "Reach", "Origin", "Meta", "Marathon", "Europa", "MeteorArena", "Fland", "Barratry", "Oasis" };
[ParamsSource(nameof(MapsSource))] [ParamsSource(nameof(MapsSource))]
public string Map; public string Map;

View File

@@ -12,18 +12,11 @@ namespace Content.Client.Access.UI;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class AccessLevelControl : GridContainer public sealed partial class AccessLevelControl : GridContainer
{ {
[Dependency] private readonly ILogManager _logManager = default!;
private ISawmill _sawmill = default!;
public readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new(); public readonly Dictionary<ProtoId<AccessLevelPrototype>, Button> ButtonsList = new();
public AccessLevelControl() public AccessLevelControl()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_sawmill = _logManager.GetSawmill("accesslevelcontrol");
} }
public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager) public void Populate(List<ProtoId<AccessLevelPrototype>> accessLevels, IPrototypeManager prototypeManager)
@@ -32,7 +25,7 @@ public sealed partial class AccessLevelControl : GridContainer
{ {
if (!prototypeManager.TryIndex(access, out var accessLevel)) if (!prototypeManager.TryIndex(access, out var accessLevel))
{ {
_sawmill.Error($"Unable to find accesslevel for {access}"); Logger.Error($"Unable to find accesslevel for {access}");
continue; continue;
} }

View File

@@ -2,7 +2,6 @@ using Content.Shared.Access;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.AccessOverriderComponent; using static Content.Shared.Access.Components.AccessOverriderComponent;
@@ -24,28 +23,6 @@ namespace Content.Client.Access.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<AccessOverriderWindow>();
RefreshAccess();
_window.Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName;
_window.OnSubmit += SubmitData;
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
}
public override void OnProtoReload(PrototypesReloadedEventArgs args)
{
base.OnProtoReload(args);
if (!args.WasModified<AccessLevelPrototype>())
return;
RefreshAccess();
if (State != null)
_window?.UpdateState(_prototypeManager, (AccessOverriderBoundUserInterfaceState) State);
}
private void RefreshAccess()
{
List<ProtoId<AccessLevelPrototype>> accessLevels; List<ProtoId<AccessLevelPrototype>> accessLevels;
if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider)) if (EntMan.TryGetComponent<AccessOverriderComponent>(Owner, out var accessOverrider))
@@ -53,20 +30,38 @@ namespace Content.Client.Access.UI
accessLevels = accessOverrider.AccessLevels; accessLevels = accessOverrider.AccessLevels;
accessLevels.Sort(); accessLevels.Sort();
} }
else else
{ {
accessLevels = new List<ProtoId<AccessLevelPrototype>>(); accessLevels = new List<ProtoId<AccessLevelPrototype>>();
_accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!"); _accessOverriderSystem.Log.Error($"No AccessOverrider component found for {EntMan.ToPrettyString(Owner)}!");
} }
_window?.SetAccessLevels(_prototypeManager, accessLevels); _window = new AccessOverriderWindow(this, _prototypeManager, accessLevels)
{
Title = EntMan.GetComponent<MetaDataComponent>(Owner).EntityName
};
_window.PrivilegedIdButton.OnPressed += _ => SendMessage(new ItemSlotButtonPressedEvent(PrivilegedIdCardSlotId));
_window.OnClose += Close;
_window.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_window?.Dispose();
} }
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
{ {
base.UpdateState(state); base.UpdateState(state);
var castState = (AccessOverriderBoundUserInterfaceState) state; var castState = (AccessOverriderBoundUserInterfaceState) state;
_window?.UpdateState(_prototypeManager, castState); _window?.UpdateState(castState);
} }
public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList) public void SubmitData(List<ProtoId<AccessLevelPrototype>> newAccessList)

View File

@@ -13,24 +13,26 @@ namespace Content.Client.Access.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class AccessOverriderWindow : DefaultWindow public sealed partial class AccessOverriderWindow : DefaultWindow
{ {
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly AccessOverriderBoundUserInterface _owner;
private readonly Dictionary<string, Button> _accessButtons = new(); private readonly Dictionary<string, Button> _accessButtons = new();
public event Action<List<ProtoId<AccessLevelPrototype>>>? OnSubmit; public AccessOverriderWindow(AccessOverriderBoundUserInterface owner, IPrototypeManager prototypeManager,
List<ProtoId<AccessLevelPrototype>> accessLevels)
public AccessOverriderWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
} IoCManager.InjectDependencies(this);
var logMill = _logManager.GetSawmill(SharedAccessOverriderSystem.Sawmill);
public void SetAccessLevels(IPrototypeManager protoManager, List<ProtoId<AccessLevelPrototype>> accessLevels) _owner = owner;
{
_accessButtons.Clear();
AccessLevelGrid.DisposeAllChildren();
foreach (var access in accessLevels) foreach (var access in accessLevels)
{ {
if (!protoManager.TryIndex(access, out var accessLevel)) if (!prototypeManager.TryIndex(access, out var accessLevel))
{ {
logMill.Error($"Unable to find accesslevel for {access}");
continue; continue;
} }
@@ -42,16 +44,11 @@ namespace Content.Client.Access.UI
AccessLevelGrid.AddChild(newButton); AccessLevelGrid.AddChild(newButton);
_accessButtons.Add(accessLevel.ID, newButton); _accessButtons.Add(accessLevel.ID, newButton);
newButton.OnPressed += _ => newButton.OnPressed += _ => SubmitData();
{
OnSubmit?.Invoke(
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
};
} }
} }
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state) public void UpdateState(AccessOverriderBoundUserInterfaceState state)
{ {
PrivilegedIdLabel.Text = state.PrivilegedIdName; PrivilegedIdLabel.Text = state.PrivilegedIdName;
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
@@ -69,11 +66,11 @@ namespace Content.Client.Access.UI
if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any()) if (state.MissingPrivilegesList != null && state.MissingPrivilegesList.Any())
{ {
var missingPrivileges = new List<string>(); List<string> missingPrivileges = new List<string>();
foreach (string tag in state.MissingPrivilegesList) foreach (string tag in state.MissingPrivilegesList)
{ {
var privilege = Loc.GetString(protoManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown"); string privilege = Loc.GetString(_prototypeManager.Index<AccessLevelPrototype>(tag)?.Name ?? "generic-unknown");
missingPrivileges.Add(privilege); missingPrivileges.Add(privilege);
} }
@@ -93,5 +90,13 @@ namespace Content.Client.Access.UI
} }
} }
} }
private void SubmitData()
{
_owner.SubmitData(
// Iterate over the buttons dictionary, filter by `Pressed`, only get key from the key/value pair
_accessButtons.Where(x => x.Value.Pressed).Select(x => new ProtoId<AccessLevelPrototype>(x.Key)).ToList());
}
} }
} }

View File

@@ -1,7 +1,6 @@
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.StatusIcon; using Content.Shared.StatusIcon;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Access.UI namespace Content.Client.Access.UI
@@ -21,11 +20,16 @@ namespace Content.Client.Access.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<AgentIDCardWindow>(); _window?.Dispose();
_window = new AgentIDCardWindow(this);
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.OnNameChanged += OnNameChanged; _window.OnNameChanged += OnNameChanged;
_window.OnJobChanged += OnJobChanged; _window.OnJobChanged += OnJobChanged;
_window.OnJobIconChanged += OnJobIconChanged;
} }
private void OnNameChanged(string newName) private void OnNameChanged(string newName)
@@ -38,7 +42,7 @@ namespace Content.Client.Access.UI
SendMessage(new AgentIDCardJobChangedMessage(newJob)); SendMessage(new AgentIDCardJobChangedMessage(newJob));
} }
public void OnJobIconChanged(ProtoId<JobIconPrototype> newJobIconId) public void OnJobIconChanged(ProtoId<StatusIconPrototype> newJobIconId)
{ {
SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId)); SendMessage(new AgentIDCardJobIconChangedMessage(newJobIconId));
} }
@@ -55,7 +59,16 @@ namespace Content.Client.Access.UI
_window.SetCurrentName(cast.CurrentName); _window.SetCurrentName(cast.CurrentName);
_window.SetCurrentJob(cast.CurrentJob); _window.SetCurrentJob(cast.CurrentJob);
_window.SetAllowedIcons(cast.CurrentJobIconId); _window.SetAllowedIcons(cast.Icons, cast.CurrentJobIconId);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_window?.Dispose();
} }
} }
} }

View File

@@ -6,9 +6,12 @@
<LineEdit Name="NameLineEdit" /> <LineEdit Name="NameLineEdit" />
<Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" /> <Label Name="CurrentJob" Text="{Loc 'agent-id-card-current-job'}" />
<LineEdit Name="JobLineEdit" /> <LineEdit Name="JobLineEdit" />
<Label Text="{Loc 'agent-id-card-job-icon-label'}"/> <BoxContainer Orientation="Horizontal">
<GridContainer Name="IconGrid" Columns="10"> <Label Text="{Loc 'agent-id-card-job-icon-label'}"/>
<!-- Job icon buttons are generated in the code --> <Control HorizontalExpand="True" MinSize="50 0"/>
</GridContainer> <GridContainer Name="IconGrid" Columns="10">
<!-- Job icon buttons are generated in the code -->
</GridContainer>
</BoxContainer>
</BoxContainer> </BoxContainer>
</DefaultWindow> </DefaultWindow>

View File

@@ -8,7 +8,6 @@ using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using System.Numerics; using System.Numerics;
using System.Linq;
namespace Content.Client.Access.UI namespace Content.Client.Access.UI
{ {
@@ -18,19 +17,19 @@ namespace Content.Client.Access.UI
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntitySystemManager _entitySystem = default!; [Dependency] private readonly IEntitySystemManager _entitySystem = default!;
private readonly SpriteSystem _spriteSystem; private readonly SpriteSystem _spriteSystem;
private readonly AgentIDCardBoundUserInterface _bui;
private const int JobIconColumnCount = 10; private const int JobIconColumnCount = 10;
public event Action<string>? OnNameChanged; public event Action<string>? OnNameChanged;
public event Action<string>? OnJobChanged; public event Action<string>? OnJobChanged;
public event Action<ProtoId<JobIconPrototype>>? OnJobIconChanged; public AgentIDCardWindow(AgentIDCardBoundUserInterface bui)
public AgentIDCardWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>(); _spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
_bui = bui;
NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text); NameLineEdit.OnTextEntered += e => OnNameChanged?.Invoke(e.Text);
NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text); NameLineEdit.OnFocusExit += e => OnNameChanged?.Invoke(e.Text);
@@ -39,16 +38,17 @@ namespace Content.Client.Access.UI
JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text); JobLineEdit.OnFocusExit += e => OnJobChanged?.Invoke(e.Text);
} }
public void SetAllowedIcons(string currentJobIconId) public void SetAllowedIcons(HashSet<ProtoId<StatusIconPrototype>> icons, string currentJobIconId)
{ {
IconGrid.DisposeAllChildren(); IconGrid.DisposeAllChildren();
var jobIconButtonGroup = new ButtonGroup(); var jobIconGroup = new ButtonGroup();
var i = 0; var i = 0;
var icons = _prototypeManager.EnumeratePrototypes<JobIconPrototype>().Where(icon => icon.AllowSelection).ToList(); foreach (var jobIconId in icons)
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
foreach (var jobIcon in icons)
{ {
if (!_prototypeManager.TryIndex(jobIconId, out var jobIcon))
continue;
String styleBase = StyleBase.ButtonOpenBoth; String styleBase = StyleBase.ButtonOpenBoth;
var modulo = i % JobIconColumnCount; var modulo = i % JobIconColumnCount;
if (modulo == 0) if (modulo == 0)
@@ -62,13 +62,12 @@ namespace Content.Client.Access.UI
Access = AccessLevel.Public, Access = AccessLevel.Public,
StyleClasses = { styleBase }, StyleClasses = { styleBase },
MaxSize = new Vector2(42, 28), MaxSize = new Vector2(42, 28),
Group = jobIconButtonGroup, Group = jobIconGroup,
Pressed = currentJobIconId == jobIcon.ID, Pressed = i == 0,
ToolTip = jobIcon.LocalizedJobName
}; };
// Generate buttons textures // Generate buttons textures
var jobIconTexture = new TextureRect TextureRect jobIconTexture = new TextureRect
{ {
Texture = _spriteSystem.Frame0(jobIcon.Icon), Texture = _spriteSystem.Frame0(jobIcon.Icon),
TextureScale = new Vector2(2.5f, 2.5f), TextureScale = new Vector2(2.5f, 2.5f),
@@ -76,9 +75,12 @@ namespace Content.Client.Access.UI
}; };
jobIconButton.AddChild(jobIconTexture); jobIconButton.AddChild(jobIconTexture);
jobIconButton.OnPressed += _ => OnJobIconChanged?.Invoke(jobIcon.ID); jobIconButton.OnPressed += _ => _bui.OnJobIconChanged(jobIconId);
IconGrid.AddChild(jobIconButton); IconGrid.AddChild(jobIconButton);
if (jobIconId.Equals(currentJobIconId))
jobIconButton.Pressed = true;
i++; i++;
} }
} }

View File

@@ -48,30 +48,6 @@ namespace Content.Client.Actions
SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState); SubscribeLocalEvent<InstantActionComponent, ComponentHandleState>(OnInstantHandleState);
SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState); SubscribeLocalEvent<EntityTargetActionComponent, ComponentHandleState>(OnEntityTargetHandleState);
SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState); SubscribeLocalEvent<WorldTargetActionComponent, ComponentHandleState>(OnWorldTargetHandleState);
SubscribeLocalEvent<EntityWorldTargetActionComponent, ComponentHandleState>(OnEntityWorldTargetHandleState);
}
public override void FrameUpdate(float frameTime)
{
base.FrameUpdate(frameTime);
var worldActionQuery = EntityQueryEnumerator<WorldTargetActionComponent>();
while (worldActionQuery.MoveNext(out var uid, out var action))
{
UpdateAction(uid, action);
}
var instantActionQuery = EntityQueryEnumerator<InstantActionComponent>();
while (instantActionQuery.MoveNext(out var uid, out var action))
{
UpdateAction(uid, action);
}
var entityActionQuery = EntityQueryEnumerator<EntityTargetActionComponent>();
while (entityActionQuery.MoveNext(out var uid, out var action))
{
UpdateAction(uid, action);
}
} }
private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args) private void OnInstantHandleState(EntityUid uid, InstantActionComponent component, ref ComponentHandleState args)
@@ -100,26 +76,12 @@ namespace Content.Client.Actions
BaseHandleState<WorldTargetActionComponent>(uid, component, state); BaseHandleState<WorldTargetActionComponent>(uid, component, state);
} }
private void OnEntityWorldTargetHandleState(EntityUid uid,
EntityWorldTargetActionComponent component,
ref ComponentHandleState args)
{
if (args.Current is not EntityWorldTargetActionComponentState state)
return;
component.Whitelist = state.Whitelist;
component.CanTargetSelf = state.CanTargetSelf;
BaseHandleState<EntityWorldTargetActionComponent>(uid, component, state);
}
private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent private void BaseHandleState<T>(EntityUid uid, BaseActionComponent component, BaseActionComponentState state) where T : BaseActionComponent
{ {
// TODO ACTIONS use auto comp states // TODO ACTIONS use auto comp states
component.Icon = state.Icon; component.Icon = state.Icon;
component.IconOn = state.IconOn; component.IconOn = state.IconOn;
component.IconColor = state.IconColor; component.IconColor = state.IconColor;
component.OriginalIconColor = state.OriginalIconColor;
component.DisabledIconColor = state.DisabledIconColor;
component.Keywords.Clear(); component.Keywords.Clear();
component.Keywords.UnionWith(state.Keywords); component.Keywords.UnionWith(state.Keywords);
component.Enabled = state.Enabled; component.Enabled = state.Enabled;
@@ -145,13 +107,11 @@ namespace Content.Client.Actions
UpdateAction(uid, component); UpdateAction(uid, component);
} }
public override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null) protected override void UpdateAction(EntityUid? actionId, BaseActionComponent? action = null)
{ {
if (!ResolveActionData(actionId, ref action)) if (!ResolveActionData(actionId, ref action))
return; return;
action.IconColor = action.Charges < 1 ? action.DisabledIconColor : action.OriginalIconColor;
base.UpdateAction(actionId, action); base.UpdateAction(actionId, action);
if (_playerManager.LocalEntity != action.AttachedEntity) if (_playerManager.LocalEntity != action.AttachedEntity)
return; return;
@@ -286,6 +246,12 @@ namespace Content.Client.Actions
if (action.ClientExclusive) if (action.ClientExclusive)
{ {
if (instantAction.Event != null)
{
instantAction.Event.Performer = user;
instantAction.Event.Action = actionId;
}
PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime); PerformAction(user, actions, actionId, instantAction, instantAction.Event, GameTiming.CurTime);
} }
else else
@@ -327,7 +293,7 @@ namespace Content.Client.Actions
continue; continue;
var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true); var action = _serialization.Read<BaseActionComponent>(actionNode, notNullableOverride: true);
var actionId = Spawn(); var actionId = Spawn(null);
AddComp(actionId, action); AddComp(actionId, action);
AddActionDirect(user, actionId); AddActionDirect(user, actionId);

View File

@@ -1,4 +1,4 @@
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing; using Robust.Shared.Timing;
using Robust.Shared.Utility; using Robust.Shared.Utility;
@@ -77,12 +77,9 @@ namespace Content.Client.Actions.UI
MaxWidth = TooltipTextMaxWidth, MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements} StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
}; };
requiresLabel.SetMessage(FormattedMessage.FromMarkup("[color=#635c5c]" +
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup)) requires +
return; "[/color]"));
requiresLabel.SetMessage(markup);
vbox.AddChild(requiresLabel); vbox.AddChild(requiresLabel);
} }
} }
@@ -100,11 +97,8 @@ namespace Content.Client.Actions.UI
if (timeLeft > TimeSpan.Zero) if (timeLeft > TimeSpan.Zero)
{ {
var duration = Cooldown.Value.End - Cooldown.Value.Start; var duration = Cooldown.Value.End - Cooldown.Value.Start;
_cooldownLabel.SetMessage(FormattedMessage.FromMarkup(
if (!FormattedMessage.TryFromMarkup(Loc.GetString("ui-actionslot-duration", ("duration", (int)duration.TotalSeconds), ("timeLeft", (int)timeLeft.TotalSeconds + 1)), out var markup)) $"[color=#a10505]{(int) duration.TotalSeconds} sec cooldown ({(int) timeLeft.TotalSeconds + 1} sec remaining)[/color]"));
return;
_cooldownLabel.SetMessage(markup);
_cooldownLabel.Visible = true; _cooldownLabel.Visible = true;
} }
else else

View File

@@ -2,75 +2,72 @@ using System.Numerics;
using Content.Client.Administration.Systems; using Content.Client.Administration.Systems;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Configuration; using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
namespace Content.Client.Administration; namespace Content.Client.Administration
internal sealed class AdminNameOverlay : Overlay
{ {
private readonly AdminSystem _system; internal sealed class AdminNameOverlay : Overlay
private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup;
private readonly IUserInterfaceManager _userInterfaceManager;
private readonly Font _font;
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
{ {
_system = system; private readonly AdminSystem _system;
_entityManager = entityManager; private readonly IEntityManager _entityManager;
_eyeManager = eyeManager; private readonly IEyeManager _eyeManager;
_entityLookup = entityLookup; private readonly EntityLookupSystem _entityLookup;
_userInterfaceManager = userInterfaceManager; private readonly Font _font;
ZIndex = 200;
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
public override OverlaySpace Space => OverlaySpace.ScreenSpace; public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup)
protected override void Draw(in OverlayDrawArgs args)
{
var viewport = args.WorldAABB;
foreach (var playerInfo in _system.PlayerList)
{ {
var entity = _entityManager.GetEntity(playerInfo.NetEntity); _system = system;
_entityManager = entityManager;
_eyeManager = eyeManager;
_entityLookup = entityLookup;
ZIndex = 200;
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
// Otherwise the entity can not exist yet public override OverlaySpace Space => OverlaySpace.ScreenSpace;
if (entity == null || !_entityManager.EntityExists(entity))
protected override void Draw(in OverlayDrawArgs args)
{
var viewport = args.WorldAABB;
foreach (var playerInfo in _system.PlayerList)
{ {
continue; var entity = _entityManager.GetEntity(playerInfo.NetEntity);
}
// if not on the same map, continue // Otherwise the entity can not exist yet
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId) if (entity == null || !_entityManager.EntityExists(entity))
{ {
continue; continue;
} }
var aabb = _entityLookup.GetWorldAABB(entity.Value); // if not on the same map, continue
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != _eyeManager.CurrentMap)
{
continue;
}
// if not on screen, continue var aabb = _entityLookup.GetWorldAABB(entity.Value);
if (!aabb.Intersects(in viewport))
{
continue;
}
var uiScale = _userInterfaceManager.RootControl.UIScale; // if not on screen, continue
var lineoffset = new Vector2(0f, 11f) * uiScale; if (!aabb.Intersects(in viewport))
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center + {
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec( continue;
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f); }
if (playerInfo.Antag)
{ var lineoffset = new Vector2(0f, 11f);
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", uiScale, Color.OrangeRed); var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
; new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
if (playerInfo.Antag)
{
args.ScreenHandle.DrawString(_font, screenCoordinates + (lineoffset * 2), "ANTAG", Color.OrangeRed);
}
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, playerInfo.Connected ? Color.Aquamarine : Color.White);
} }
args.ScreenHandle.DrawString(_font, screenCoordinates+lineoffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
args.ScreenHandle.DrawString(_font, screenCoordinates, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
} }
} }
} }

View File

@@ -1,8 +1,6 @@
using Content.Client.Administration.Managers; using Content.Client.Administration.Managers;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared.Configuration;
namespace Content.Client.Administration.Systems namespace Content.Client.Administration.Systems
{ {
@@ -13,7 +11,6 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IClientAdminManager _adminManager = default!; [Dependency] private readonly IClientAdminManager _adminManager = default!;
[Dependency] private readonly IEyeManager _eyeManager = default!; [Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!; [Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
private AdminNameOverlay _adminNameOverlay = default!; private AdminNameOverlay _adminNameOverlay = default!;
@@ -22,7 +19,7 @@ namespace Content.Client.Administration.Systems
private void InitializeOverlay() private void InitializeOverlay()
{ {
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager); _adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated; _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
} }

View File

@@ -1,4 +1,4 @@
using Content.Shared.Administration.Notes; using Content.Shared.Administration.Notes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
@@ -13,7 +13,7 @@ public sealed partial class AdminMessagePopupMessage : Control
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
Admin.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString( Admin.SetMessage(FormattedMessage.FromMarkup(Loc.GetString(
"admin-notes-message-admin", "admin-notes-message-admin",
("admin", message.AdminName), ("admin", message.AdminName),
("date", message.AddedOn.ToLocalTime())))); ("date", message.AddedOn.ToLocalTime()))));

View File

@@ -49,7 +49,7 @@ public sealed partial class AdminMessagePopupWindow : Control
MessageContainer.AddChild(new AdminMessagePopupMessage(message)); MessageContainer.AddChild(new AdminMessagePopupMessage(message));
} }
Description.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length)))); Description.SetMessage(FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
} }
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj) private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)

View File

@@ -1,5 +1,4 @@
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;

View File

@@ -11,8 +11,9 @@ 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.Network; using Robust.Shared.Network;
using Robust.Shared.Configuration;
using Robust.Shared.Utility; using Robust.Shared.Utility;
using Robust.Shared.Timing;
using Robust.Shared.Configuration;
namespace Content.Client.Administration.UI.Bwoink namespace Content.Client.Administration.UI.Bwoink
{ {
@@ -87,51 +88,26 @@ namespace Content.Client.Administration.UI.Bwoink
var ach = AHelpHelper.EnsurePanel(a.SessionId); var ach = AHelpHelper.EnsurePanel(a.SessionId);
var bch = AHelpHelper.EnsurePanel(b.SessionId); var bch = AHelpHelper.EnsurePanel(b.SessionId);
// Pinned players first // First, sort by unread. Any chat with unread messages appears first. We just sort based on unread
if (a.IsPinned != b.IsPinned) // status, not number of unread messages, so that more recent unread messages take priority.
return a.IsPinned ? -1 : 1;
// First, sort by unread. Any chat with unread messages appears first.
var aUnread = ach.Unread > 0; var aUnread = ach.Unread > 0;
var bUnread = bch.Unread > 0; var bUnread = bch.Unread > 0;
if (aUnread != bUnread) if (aUnread != bUnread)
return aUnread ? -1 : 1; return aUnread ? -1 : 1;
// Sort by recent messages during the current round.
var aRecent = a.ActiveThisRound && ach.LastMessage != DateTime.MinValue;
var bRecent = b.ActiveThisRound && bch.LastMessage != DateTime.MinValue;
if (aRecent != bRecent)
return aRecent ? -1 : 1;
// Next, sort by connection status. Any disconnected players are grouped towards the end. // Next, sort by connection status. Any disconnected players are grouped towards the end.
if (a.Connected != b.Connected) if (a.Connected != b.Connected)
return a.Connected ? -1 : 1; return a.Connected ? -1 : 1;
// Sort connected players by New Player status, then by Antag status // Next, group by whether or not the players have participated in this round.
if (a.Connected && b.Connected) // The ahelp window shows all players that have connected since server restart, this groups them all towards the bottom.
{ if (a.ActiveThisRound != b.ActiveThisRound)
var aNewPlayer = a.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold)); return a.ActiveThisRound ? -1 : 1;
var bNewPlayer = b.OverallPlaytime <= TimeSpan.FromMinutes(_cfg.GetCVar(CCVars.NewPlayerThreshold));
if (aNewPlayer != bNewPlayer)
return aNewPlayer ? -1 : 1;
if (a.Antag != b.Antag)
return a.Antag ? -1 : 1;
}
// Sort disconnected players by participation in the round
if (!a.Connected && !b.Connected)
{
if (a.ActiveThisRound != b.ActiveThisRound)
return a.ActiveThisRound ? -1 : 1;
}
// Finally, sort by the most recent message. // Finally, sort by the most recent message.
return bch.LastMessage.CompareTo(ach.LastMessage); return bch.LastMessage.CompareTo(ach.LastMessage);
}; };
Bans.OnPressed += _ => Bans.OnPressed += _ =>
{ {
if (_currentPlayer is not null) if (_currentPlayer is not null)
@@ -277,20 +253,7 @@ namespace Content.Client.Administration.UI.Bwoink
public void PopulateList() public void PopulateList()
{ {
// Maintain existing pin statuses
var pinnedPlayers = ChannelSelector.PlayerInfo.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
ChannelSelector.PopulateList(); ChannelSelector.PopulateList();
// Restore pin statuses
foreach (var player in ChannelSelector.PlayerInfo)
{
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
{
player.IsPinned = pinnedPlayer.IsPinned;
}
}
UpdateButtons(); UpdateButtons();
} }
} }

View File

@@ -59,7 +59,7 @@ namespace Content.Client.Administration.UI.Bwoink
Unread++; Unread++;
var formatted = new FormattedMessage(1); var formatted = new FormattedMessage(1);
formatted.AddMarkupOrThrow($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}"); formatted.AddMarkup($"[color=gray]{message.SentAt.ToShortTimeString()}[/color] {message.Text}");
TextOutput.AddMessage(formatted); TextOutput.AddMessage(formatted);
LastMessage = message.SentAt; LastMessage = message.SentAt;
} }

View File

@@ -30,11 +30,7 @@ namespace Content.Client.Administration.UI.Bwoink
} }
}; };
OnOpen += () => OnOpen += () => Bwoink.PopulateList();
{
Bwoink.ChannelSelector.StopFiltering();
Bwoink.PopulateList();
};
} }
} }
} }

View File

@@ -4,166 +4,154 @@ using Content.Client.UserInterface.Controls;
using Content.Client.Verbs.UI; using Content.Client.Verbs.UI;
using Content.Shared.Administration; using Content.Shared.Administration;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; 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.Input; using Robust.Shared.Input;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls; namespace Content.Client.Administration.UI.CustomControls
[GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
{ {
private readonly AdminSystem _adminSystem; [GenerateTypedNameReferences]
public sealed partial class PlayerListControl : BoxContainer
private readonly IEntityManager _entManager;
private readonly IUserInterfaceManager _uiManager;
private PlayerInfo? _selectedPlayer;
private List<PlayerInfo> _playerList = new();
private List<PlayerInfo> _sortedPlayerList = new();
public Comparison<PlayerInfo>? Comparison;
public Func<PlayerInfo, string, string>? OverrideText;
public PlayerListControl()
{ {
_entManager = IoCManager.Resolve<IEntityManager>(); private readonly AdminSystem _adminSystem;
_uiManager = IoCManager.Resolve<IUserInterfaceManager>();
_adminSystem = _entManager.System<AdminSystem>();
RobustXamlLoader.Load(this);
// Fill the Option data
PlayerListContainer.ItemPressed += PlayerListItemPressed;
PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = new Color(32, 32, 40) };
}
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList; private List<PlayerInfo> _playerList = new();
private readonly List<PlayerInfo> _sortedPlayerList = new();
public event Action<PlayerInfo?>? OnSelectionChanged; public event Action<PlayerInfo?>? OnSelectionChanged;
public IReadOnlyList<PlayerInfo> PlayerInfo => _playerList;
private void PlayerListNoItemSelected() public Func<PlayerInfo, string, string>? OverrideText;
{ public Comparison<PlayerInfo>? Comparison;
_selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data) private IEntityManager _entManager;
{ private IUserInterfaceManager _uiManager;
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (selectedPlayer == _selectedPlayer) private PlayerInfo? _selectedPlayer;
return;
if (args.Event.Function != EngineKeyFunctions.UIClick) public PlayerListControl()
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{ {
var displayName = $"{info.CharacterName} ({info.Username})"; _entManager = IoCManager.Resolve<IEntityManager>();
if (info.IdentityName != info.CharacterName) _uiManager = IoCManager.Resolve<IUserInterfaceManager>();
displayName += $" [{info.IdentityName}]"; _adminSystem = _entManager.System<AdminSystem>();
if (!string.IsNullOrEmpty(FilterLineEdit.Text) RobustXamlLoader.Load(this);
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant())) // Fill the Option data
continue; PlayerListContainer.ItemPressed += PlayerListItemPressed;
_sortedPlayerList.Add(info); PlayerListContainer.ItemKeyBindDown += PlayerListItemKeyBindDown;
PlayerListContainer.GenerateItem += GenerateButton;
PlayerListContainer.NoItemSelected += PlayerListNoItemSelected;
PopulateList(_adminSystem.PlayerList);
FilterLineEdit.OnTextChanged += _ => FilterList();
_adminSystem.PlayerListChanged += PopulateList;
BackgroundPanel.PanelOverride = new StyleBoxFlat {BackgroundColor = new Color(32, 32, 40)};
} }
if (Comparison != null) private void PlayerListNoItemSelected()
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
// Maintain existing pin statuses
var pinnedPlayers = _playerList.Where(p => p.IsPinned).ToDictionary(p => p.SessionId);
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
// Restore pin statuses
foreach (var player in _playerList)
{ {
if (pinnedPlayers.TryGetValue(player.SessionId, out var pinnedPlayer))
{
player.IsPinned = pinnedPlayer.IsPinned;
}
}
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null; _selectedPlayer = null;
OnSelectionChanged?.Invoke(null);
}
FilterList(); private void PlayerListItemPressed(BaseButton.ButtonEventArgs? args, ListData? data)
}
private string GetText(PlayerInfo info)
{
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
var entry = new PlayerListEntry();
entry.Setup(info, OverrideText);
entry.OnPinStatusChanged += _ =>
{ {
if (args == null || data is not PlayerListData {Info: var selectedPlayer})
return;
if (selectedPlayer == _selectedPlayer)
return;
if (args.Event.Function != EngineKeyFunctions.UIClick)
return;
OnSelectionChanged?.Invoke(selectedPlayer);
_selectedPlayer = selectedPlayer;
// update label text. Only required if there is some override (e.g. unread bwoink count).
if (OverrideText != null && args.Button.Children.FirstOrDefault()?.Children?.FirstOrDefault() is Label label)
label.Text = GetText(selectedPlayer);
}
private void PlayerListItemKeyBindDown(GUIBoundKeyEventArgs? args, ListData? data)
{
if (args == null || data is not PlayerListData { Info: var selectedPlayer })
return;
if (args.Function != EngineKeyFunctions.UIRightClick || selectedPlayer.NetEntity == null)
return;
_uiManager.GetUIController<VerbMenuUIController>().OpenVerbMenu(selectedPlayer.NetEntity.Value, true);
args.Handle();
}
public void StopFiltering()
{
FilterLineEdit.Text = string.Empty;
}
private void FilterList()
{
_sortedPlayerList.Clear();
foreach (var info in _playerList)
{
var displayName = $"{info.CharacterName} ({info.Username})";
if (info.IdentityName != info.CharacterName)
displayName += $" [{info.IdentityName}]";
if (!string.IsNullOrEmpty(FilterLineEdit.Text)
&& !displayName.ToLowerInvariant().Contains(FilterLineEdit.Text.Trim().ToLowerInvariant()))
continue;
_sortedPlayerList.Add(info);
}
if (Comparison != null)
_sortedPlayerList.Sort((a, b) => Comparison(a, b));
PlayerListContainer.PopulateList(_sortedPlayerList.Select(info => new PlayerListData(info)).ToList());
if (_selectedPlayer != null)
PlayerListContainer.Select(new PlayerListData(_selectedPlayer));
}
public void PopulateList(IReadOnlyList<PlayerInfo>? players = null)
{
players ??= _adminSystem.PlayerList;
_playerList = players.ToList();
if (_selectedPlayer != null && !_playerList.Contains(_selectedPlayer))
_selectedPlayer = null;
FilterList(); FilterList();
}; }
button.AddChild(entry); private string GetText(PlayerInfo info)
button.AddStyleClass(ListContainer.StyleClassListContainerButton); {
var text = $"{info.CharacterName} ({info.Username})";
if (OverrideText != null)
text = OverrideText.Invoke(info, text);
return text;
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var info })
return;
button.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
{
new Label
{
ClipText = true,
Text = GetText(info)
}
}
});
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
}
} }
}
public record PlayerListData(PlayerInfo Info) : ListData; public record PlayerListData(PlayerInfo Info) : ListData;
}

View File

@@ -1,6 +0,0 @@
<BoxContainer xmlns="https://spacestation14.io"
Orientation="Horizontal" HorizontalExpand="true">
<Label Name="PlayerEntryLabel" Text="" ClipText="True" HorizontalExpand="True" />
<TextureButton Name="PlayerEntryPinButton"
HorizontalAlignment="Right" />
</BoxContainer>

View File

@@ -1,58 +0,0 @@
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListEntry : BoxContainer
{
public PlayerListEntry()
{
RobustXamlLoader.Load(this);
}
public event Action<PlayerInfo>? OnPinStatusChanged;
public void Setup(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
Update(info, overrideText);
PlayerEntryPinButton.OnPressed += HandlePinButtonPressed(info);
}
private Action<BaseButton.ButtonEventArgs> HandlePinButtonPressed(PlayerInfo info)
{
return args =>
{
info.IsPinned = !info.IsPinned;
UpdatePinButtonTexture(info.IsPinned);
OnPinStatusChanged?.Invoke(info);
};
}
private void Update(PlayerInfo info, Func<PlayerInfo, string, string>? overrideText)
{
PlayerEntryLabel.Text = overrideText?.Invoke(info, $"{info.CharacterName} ({info.Username})") ??
$"{info.CharacterName} ({info.Username})";
UpdatePinButtonTexture(info.IsPinned);
}
private void UpdatePinButtonTexture(bool isPinned)
{
if (isPinned)
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
}
else
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
}
}
}

View File

@@ -1,10 +1,10 @@
<Popup xmlns="https://spacestation14.io" <Popup xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"> xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client">
<PanelContainer> <PanelContainer StyleClasses="BackgroundDark">
<PanelContainer.PanelOverride> <PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BorderThickness="2" BorderColor="#18181B" BackgroundColor="#25252a"/> <gfx:StyleBoxFlat BorderThickness="1" BorderColor="#18181B"/>
</PanelContainer.PanelOverride> </PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical" Margin="4 4 4 4"> <BoxContainer Orientation="Vertical">
<Label Name="PlayerNameLabel"/> <Label Name="PlayerNameLabel"/>
<Label Name="IdLabel"/> <Label Name="IdLabel"/>
<Label Name="TypeLabel"/> <Label Name="TypeLabel"/>

View File

@@ -1,36 +0,0 @@
<ui:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc ban-panel-title}" MinSize="300 300">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<Label Name="PlayerName"/>
<Button Name="UsernameCopyButton" Text="{Loc player-panel-copy-username}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Name="Whitelisted"/>
<controls:ConfirmButton Name="WhitelistToggle" Text="{Loc 'player-panel-false'}" Visible="False"></controls:ConfirmButton>
</BoxContainer>
<Label Name="Playtime"/>
<Label Name="Notes"/>
<Label Name="Bans"/>
<Label Name="RoleBans"/>
<Label Name="SharedConnections"/>
<BoxContainer Align="Center">
<GridContainer Rows="5">
<Button Name="NotesButton" Text="{Loc player-panel-show-notes}" SetWidth="136" Disabled="True"/>
<Button Name="AhelpButton" Text="{Loc player-panel-help}" Disabled="True"/>
<Button Name="FreezeButton" Text = "{Loc player-panel-freeze}" Disabled="True"/>
<controls:ConfirmButton Name="KickButton" Text="{Loc player-panel-kick}" Disabled="True"/>
<controls:ConfirmButton Name="DeleteButton" Text="{Loc player-panel-delete}" Disabled="True"/>
<Button Name="ShowBansButton" Text="{Loc player-panel-show-bans}" SetWidth="136" Disabled="True"/>
<Button Name="LogsButton" Text="{Loc player-panel-logs}" Disabled="True"/>
<Button Name="FreezeAndMuteToggleButton" Text="{Loc player-panel-freeze-and-mute}" Disabled="True"/>
<Button Name="BanButton" Text="{Loc player-panel-ban}" Disabled="True"/>
<controls:ConfirmButton Name="RejuvenateButton" Text="{Loc player-panel-rejuvenate}" Disabled="True"/>
</GridContainer>
</BoxContainer>
</BoxContainer>
</ui:FancyWindow>

View File

@@ -1,132 +0,0 @@
using Content.Client.Administration.Managers;
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Network;
using Robust.Shared.Utility;
namespace Content.Client.Administration.UI.PlayerPanel;
[GenerateTypedNameReferences]
public sealed partial class PlayerPanel : FancyWindow
{
private readonly IClientAdminManager _adminManager;
public event Action<string>? OnUsernameCopy;
public event Action<NetUserId?>? OnOpenNotes;
public event Action<NetUserId?>? OnOpenBans;
public event Action<NetUserId?>? OnAhelp;
public event Action<string?>? OnKick;
public event Action<NetUserId?>? OnOpenBanPanel;
public event Action<NetUserId?, bool>? OnWhitelistToggle;
public event Action? OnFreezeAndMuteToggle;
public event Action? OnFreeze;
public event Action? OnLogs;
public event Action? OnDelete;
public event Action? OnRejuvenate;
public NetUserId? TargetPlayer;
public string? TargetUsername;
private bool _isWhitelisted;
public PlayerPanel(IClientAdminManager adminManager)
{
RobustXamlLoader.Load(this);
_adminManager = adminManager;
UsernameCopyButton.OnPressed += _ => OnUsernameCopy?.Invoke(PlayerName.Text ?? "");
BanButton.OnPressed += _ => OnOpenBanPanel?.Invoke(TargetPlayer);
KickButton.OnPressed += _ => OnKick?.Invoke(TargetUsername);
NotesButton.OnPressed += _ => OnOpenNotes?.Invoke(TargetPlayer);
ShowBansButton.OnPressed += _ => OnOpenBans?.Invoke(TargetPlayer);
AhelpButton.OnPressed += _ => OnAhelp?.Invoke(TargetPlayer);
WhitelistToggle.OnPressed += _ =>
{
OnWhitelistToggle?.Invoke(TargetPlayer, _isWhitelisted);
SetWhitelisted(!_isWhitelisted);
};
FreezeButton.OnPressed += _ => OnFreeze?.Invoke();
FreezeAndMuteToggleButton.OnPressed += _ => OnFreezeAndMuteToggle?.Invoke();
LogsButton.OnPressed += _ => OnLogs?.Invoke();
DeleteButton.OnPressed += _ => OnDelete?.Invoke();
RejuvenateButton.OnPressed += _ => OnRejuvenate?.Invoke();
}
public void SetUsername(string player)
{
Title = Loc.GetString("player-panel-title", ("player", player));
PlayerName.Text = Loc.GetString("player-panel-username", ("player", player));
}
public void SetWhitelisted(bool? whitelisted)
{
if (whitelisted == null)
{
Whitelisted.Text = null;
WhitelistToggle.Visible = false;
}
else
{
Whitelisted.Text = Loc.GetString("player-panel-whitelisted");
WhitelistToggle.Text = whitelisted.Value ? Loc.GetString("player-panel-true") : Loc.GetString("player-panel-false");
WhitelistToggle.Visible = true;
_isWhitelisted = whitelisted.Value;
}
}
public void SetBans(int? totalBans, int? totalRoleBans)
{
// If one value exists then so should the other.
DebugTools.Assert(totalBans.HasValue && totalRoleBans.HasValue || totalBans == null && totalRoleBans == null);
Bans.Text = totalBans != null ? Loc.GetString("player-panel-bans", ("totalBans", totalBans)) : null;
RoleBans.Text = totalRoleBans != null ? Loc.GetString("player-panel-rolebans", ("totalRoleBans", totalRoleBans)) : null;
}
public void SetNotes(int? totalNotes)
{
Notes.Text = totalNotes != null ? Loc.GetString("player-panel-notes", ("totalNotes", totalNotes)) : null;
}
public void SetSharedConnections(int sharedConnections)
{
SharedConnections.Text = Loc.GetString("player-panel-shared-connections", ("sharedConnections", sharedConnections));
}
public void SetPlaytime(TimeSpan playtime)
{
Playtime.Text = Loc.GetString("player-panel-playtime",
("days", playtime.Days),
("hours", playtime.Hours % 24),
("minutes", playtime.Minutes % (24 * 60)));
}
public void SetFrozen(bool canFreeze, bool frozen)
{
FreezeAndMuteToggleButton.Disabled = !canFreeze;
FreezeButton.Disabled = !canFreeze || frozen;
FreezeAndMuteToggleButton.Text = Loc.GetString(!frozen ? "player-panel-freeze-and-mute" : "player-panel-unfreeze");
}
public void SetAhelp(bool canAhelp)
{
AhelpButton.Disabled = !canAhelp;
}
public void SetButtons()
{
BanButton.Disabled = !_adminManager.CanCommand("banpanel");
KickButton.Disabled = !_adminManager.CanCommand("kick");
NotesButton.Disabled = !_adminManager.CanCommand("adminnotes");
ShowBansButton.Disabled = !_adminManager.CanCommand("banlist");
WhitelistToggle.Disabled =
!(_adminManager.CanCommand("whitelistadd") && _adminManager.CanCommand("whitelistremove"));
LogsButton.Disabled = !_adminManager.CanCommand("adminlogs");
RejuvenateButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
DeleteButton.Disabled = !_adminManager.HasFlag(AdminFlags.Debug);
}
}

View File

@@ -1,72 +0,0 @@
using Content.Client.Administration.Managers;
using Content.Client.Eui;
using Content.Shared.Administration;
using Content.Shared.Eui;
using JetBrains.Annotations;
using Robust.Client.Console;
using Robust.Client.UserInterface;
namespace Content.Client.Administration.UI.PlayerPanel;
[UsedImplicitly]
public sealed class PlayerPanelEui : BaseEui
{
[Dependency] private readonly IClientConsoleHost _console = default!;
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IClipboardManager _clipboard = default!;
private PlayerPanel PlayerPanel { get; }
public PlayerPanelEui()
{
PlayerPanel = new PlayerPanel(_admin);
PlayerPanel.OnUsernameCopy += username => _clipboard.SetText(username);
PlayerPanel.OnOpenNotes += id => _console.ExecuteCommand($"adminnotes \"{id}\"");
// Kick command does not support GUIDs
PlayerPanel.OnKick += username => _console.ExecuteCommand($"kick \"{username}\"");
PlayerPanel.OnOpenBanPanel += id => _console.ExecuteCommand($"banpanel \"{id}\"");
PlayerPanel.OnOpenBans += id => _console.ExecuteCommand($"banlist \"{id}\"");
PlayerPanel.OnAhelp += id => _console.ExecuteCommand($"openahelp \"{id}\"");
PlayerPanel.OnWhitelistToggle += (id, whitelisted) =>
{
_console.ExecuteCommand(whitelisted ? $"whitelistremove \"{id}\"" : $"whitelistadd \"{id}\"");
};
PlayerPanel.OnFreezeAndMuteToggle += () => SendMessage(new PlayerPanelFreezeMessage(true));
PlayerPanel.OnFreeze += () => SendMessage(new PlayerPanelFreezeMessage());
PlayerPanel.OnLogs += () => SendMessage(new PlayerPanelLogsMessage());
PlayerPanel.OnRejuvenate += () => SendMessage(new PlayerPanelRejuvenationMessage());
PlayerPanel.OnDelete+= () => SendMessage(new PlayerPanelDeleteMessage());
PlayerPanel.OnClose += () => SendMessage(new CloseEuiMessage());
}
public override void Opened()
{
PlayerPanel.OpenCentered();
}
public override void Closed()
{
PlayerPanel.Close();
}
public override void HandleState(EuiStateBase state)
{
if (state is not PlayerPanelEuiState s)
return;
PlayerPanel.TargetPlayer = s.Guid;
PlayerPanel.TargetUsername = s.Username;
PlayerPanel.SetUsername(s.Username);
PlayerPanel.SetPlaytime(s.Playtime);
PlayerPanel.SetBans(s.TotalBans, s.TotalRoleBans);
PlayerPanel.SetNotes(s.TotalNotes);
PlayerPanel.SetWhitelisted(s.Whitelisted);
PlayerPanel.SetSharedConnections(s.SharedConnections);
PlayerPanel.SetFrozen(s.CanFreeze, s.Frozen);
PlayerPanel.SetAhelp(s.CanAhelp);
PlayerPanel.SetButtons();
}
}

View File

@@ -1,13 +1,13 @@
using System.Linq;
using System.Numerics; using System.Numerics;
using Content.Client.UserInterface.Controls;
using Content.Shared.Preferences.Loadouts;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Localization;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Administration.UI.SetOutfit namespace Content.Client.Administration.UI.SetOutfit
@@ -64,18 +64,9 @@ namespace Content.Client.Administration.UI.SetOutfit
PopulateByFilter(SearchBar.Text); PopulateByFilter(SearchBar.Text);
} }
private IEnumerable<StartingGearPrototype> GetPrototypes()
{
// Filter out any StartingGearPrototypes that belong to loadouts
var loadouts = _prototypeManager.EnumeratePrototypes<LoadoutPrototype>();
var loadoutGears = loadouts.Select(l => l.StartingGear);
return _prototypeManager.EnumeratePrototypes<StartingGearPrototype>()
.Where(p => !loadoutGears.Contains(p.ID));
}
private void PopulateList() private void PopulateList()
{ {
foreach (var gear in GetPrototypes()) foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
{ {
OutfitList.Add(GetItem(gear, OutfitList)); OutfitList.Add(GetItem(gear, OutfitList));
} }
@@ -84,7 +75,7 @@ namespace Content.Client.Administration.UI.SetOutfit
private void PopulateByFilter(string filter) private void PopulateByFilter(string filter)
{ {
OutfitList.Clear(); OutfitList.Clear();
foreach (var gear in GetPrototypes()) foreach (var gear in _prototypeManager.EnumeratePrototypes<StartingGearPrototype>())
{ {
if (!string.IsNullOrEmpty(filter) && if (!string.IsNullOrEmpty(filter) &&
gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant())) gear.ID.ToLowerInvariant().Contains(filter.Trim().ToLowerInvariant()))

View File

@@ -1,21 +1,20 @@
using Content.Client.Administration.Managers;
using Content.Client.Station; using Content.Client.Station;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.Console;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map.Components; using Robust.Shared.Map.Components;
using Robust.Shared.Timing;
namespace Content.Client.Administration.UI.Tabs.ObjectsTab; namespace Content.Client.Administration.UI.Tabs.ObjectsTab;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class ObjectsTab : Control public sealed partial class ObjectsTab : Control
{ {
[Dependency] private readonly IClientAdminManager _admin = default!;
[Dependency] private readonly IEntityManager _entityManager = default!; [Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IClientConsoleHost _console = default!; [Dependency] private readonly IGameTiming _timing = default!;
private readonly Color _altColor = Color.FromHex("#292B38"); private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B"); private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
@@ -51,20 +50,10 @@ public sealed partial class ObjectsTab : Control
RefreshListButton.OnPressed += _ => RefreshObjectList(); RefreshListButton.OnPressed += _ => RefreshObjectList();
var defaultSelection = ObjectsTabSelection.Grids; var defaultSelection = ObjectsTabSelection.Grids;
ObjectTypeOptions.SelectId((int)defaultSelection); ObjectTypeOptions.SelectId((int) defaultSelection);
RefreshObjectList(defaultSelection); RefreshObjectList(defaultSelection);
} }
private void TeleportTo(NetEntity nent)
{
_console.ExecuteCommand($"tpto {nent}");
}
private void Delete(NetEntity nent)
{
_console.ExecuteCommand($"delete {nent}");
}
public void RefreshObjectList() public void RefreshObjectList()
{ {
RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]); RefreshObjectList(_selections[ObjectTypeOptions.SelectedId]);
@@ -128,9 +117,9 @@ public sealed partial class ObjectsTab : Control
if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor }) if (data is not ObjectsListData { Info: var info, BackgroundColor: var backgroundColor })
return; return;
var entry = new ObjectsTabEntry(_admin, info.Name, info.Entity, new StyleBoxFlat { BackgroundColor = backgroundColor }); var entry = new ObjectsTabEntry(info.Name,
entry.OnTeleport += TeleportTo; info.Entity,
entry.OnDelete += Delete; new StyleBoxFlat { BackgroundColor = backgroundColor });
button.ToolTip = $"{info.Name}, {info.Entity}"; button.ToolTip = $"{info.Name}, {info.Entity}";
button.AddChild(entry); button.AddChild(entry);

View File

@@ -5,25 +5,13 @@
HorizontalExpand="True" HorizontalExpand="True"
SeparationOverride="4"> SeparationOverride="4">
<Label Name="NameLabel" <Label Name="NameLabel"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="EIDLabel"
SizeFlagsStretchRatio="5"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Button Name="TeleportButton"
Text="{Loc object-tab-entity-teleport}"
SizeFlagsStretchRatio="3" SizeFlagsStretchRatio="3"
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True"/> ClipText="True"/>
<customControls:VSeparator/> <customControls:VSeparator/>
<Button Name="DeleteButton" <Label Name="EIDLabel"
Text="{Loc object-tab-entity-delete}" SizeFlagsStretchRatio="3"
SizeFlagsStretchRatio="3" HorizontalExpand="True"
HorizontalExpand="True" ClipText="True"/>
ClipText="True"/>
</BoxContainer> </BoxContainer>
</PanelContainer> </PanelContainer>

View File

@@ -1,5 +1,4 @@
using Content.Client.Administration.Managers; using Robust.Client.AutoGenerated;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
@@ -11,30 +10,12 @@ public sealed partial class ObjectsTabEntry : PanelContainer
{ {
public NetEntity AssocEntity; public NetEntity AssocEntity;
public Action<NetEntity>? OnTeleport; public ObjectsTabEntry(string name, NetEntity nent, StyleBox styleBox)
public Action<NetEntity>? OnDelete;
private readonly Dictionary<Button, ConfirmationData> _confirmations = new();
public ObjectsTabEntry(IClientAdminManager manager, string name, NetEntity nent, StyleBox styleBox)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
AssocEntity = nent; AssocEntity = nent;
EIDLabel.Text = nent.ToString(); EIDLabel.Text = nent.ToString();
NameLabel.Text = name; NameLabel.Text = name;
BackgroundColorPanel.PanelOverride = styleBox; BackgroundColorPanel.PanelOverride = styleBox;
TeleportButton.Disabled = !manager.CanCommand("tpto");
DeleteButton.Disabled = !manager.CanCommand("delete");
TeleportButton.OnPressed += _ => OnTeleport?.Invoke(nent);
DeleteButton.OnPressed += _ =>
{
if (!AdminUIHelpers.TryConfirm(DeleteButton, _confirmations))
{
return;
}
OnDelete?.Invoke(nent);
};
} }
} }

View File

@@ -5,23 +5,17 @@
HorizontalExpand="True" HorizontalExpand="True"
SeparationOverride="4"> SeparationOverride="4">
<Label Name="ObjectNameLabel" <Label Name="ObjectNameLabel"
SizeFlagsStretchRatio="5" SizeFlagsStretchRatio="3"
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True" ClipText="True"
Text="{Loc object-tab-object-name}" Text="{Loc object-tab-object-name}"
MouseFilter="Pass"/> MouseFilter="Pass"/>
<cc:VSeparator/> <cc:VSeparator/>
<Label Name="EntityIDLabel" <Label Name="EntityIDLabel"
SizeFlagsStretchRatio="5" SizeFlagsStretchRatio="3"
HorizontalExpand="True" HorizontalExpand="True"
ClipText="True" ClipText="True"
Text="{Loc object-tab-entity-id}" Text="{Loc object-tab-entity-id}"
MouseFilter="Pass"/> MouseFilter="Pass"/>
<Label Name="EntityTeleportLabel"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"/>
<Label Name="EntityDeleteLabel"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"/>
</BoxContainer> </BoxContainer>
</Control> </Control>

View File

@@ -10,219 +10,220 @@ using Robust.Client.UserInterface.XAML;
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader; using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
using static Robust.Client.UserInterface.Controls.BaseButton; using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Administration.UI.Tabs.PlayerTab; namespace Content.Client.Administration.UI.Tabs.PlayerTab
[GenerateTypedNameReferences]
public sealed partial class PlayerTab : Control
{ {
[Dependency] private readonly IEntityManager _entManager = default!; [GenerateTypedNameReferences]
[Dependency] private readonly IPlayerManager _playerMan = default!; public sealed partial class PlayerTab : Control
private const string ArrowUp = "↑";
private const string ArrowDown = "↓";
private readonly Color _altColor = Color.FromHex("#292B38");
private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
private readonly AdminSystem _adminSystem;
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
private Header _headerClicked = Header.Username;
private bool _ascending = true;
private bool _showDisconnected;
public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
public PlayerTab()
{ {
IoCManager.InjectDependencies(this); [Dependency] private readonly IEntityManager _entManager = default!;
RobustXamlLoader.Load(this); [Dependency] private readonly IPlayerManager _playerMan = default!;
_adminSystem = _entManager.System<AdminSystem>(); private const string ArrowUp = "↑";
_adminSystem.PlayerListChanged += RefreshPlayerList; private const string ArrowDown = "↓";
_adminSystem.OverlayEnabled += OverlayEnabled; private readonly Color _altColor = Color.FromHex("#292B38");
_adminSystem.OverlayDisabled += OverlayDisabled; private readonly Color _defaultColor = Color.FromHex("#2F2F3B");
private readonly AdminSystem _adminSystem;
private IReadOnlyList<PlayerInfo> _players = new List<PlayerInfo>();
OverlayButton.OnPressed += OverlayButtonPressed; private Header _headerClicked = Header.Username;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed; private bool _ascending = true;
private bool _showDisconnected;
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor); public event Action<GUIBoundKeyEventArgs, ListData>? OnEntryKeyBindDown;
ListHeader.OnHeaderClicked += HeaderClicked;
SearchList.SearchBar = SearchLineEdit; public PlayerTab()
SearchList.GenerateItem += GenerateButton;
SearchList.DataFilterCondition += DataFilterCondition;
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
RefreshPlayerList(_adminSystem.PlayerList);
}
#region Antag Overlay
private void OverlayEnabled()
{
OverlayButton.Pressed = true;
}
private void OverlayDisabled()
{
OverlayButton.Pressed = false;
}
private void OverlayButtonPressed(ButtonEventArgs args)
{
if (args.Button.Pressed)
{ {
_adminSystem.AdminOverlayOn(); IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_adminSystem = _entManager.System<AdminSystem>();
_adminSystem.PlayerListChanged += RefreshPlayerList;
_adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled;
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
ListHeader.BackgroundColorPanel.PanelOverride = new StyleBoxFlat(_altColor);
ListHeader.OnHeaderClicked += HeaderClicked;
SearchList.SearchBar = SearchLineEdit;
SearchList.GenerateItem += GenerateButton;
SearchList.DataFilterCondition += DataFilterCondition;
SearchList.ItemKeyBindDown += (args, data) => OnEntryKeyBindDown?.Invoke(args, data);
RefreshPlayerList(_adminSystem.PlayerList);
} }
else
#region Antag Overlay
private void OverlayEnabled()
{ {
_adminSystem.AdminOverlayOff(); OverlayButton.Pressed = true;
} }
}
#endregion private void OverlayDisabled()
private void ShowDisconnectedPressed(ButtonEventArgs args)
{
_showDisconnected = args.Button.Pressed;
RefreshPlayerList(_players);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{ {
_adminSystem.PlayerListChanged -= RefreshPlayerList; OverlayButton.Pressed = false;
_adminSystem.OverlayEnabled -= OverlayEnabled;
_adminSystem.OverlayDisabled -= OverlayDisabled;
OverlayButton.OnPressed -= OverlayButtonPressed;
ListHeader.OnHeaderClicked -= HeaderClicked;
} }
}
#region ListContainer private void OverlayButtonPressed(ButtonEventArgs args)
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{
_players = players;
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
sortedPlayers.Sort(Compare);
UpdateHeaderSymbols();
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
.ToList());
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var player})
return;
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
button.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
}
/// <summary>
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
/// If all characters are lowercase, the comparison ignores case.
/// If there is an uppercase character, the comparison is case sensitive.
/// </summary>
/// <param name="filter"></param>
/// <param name="listData"></param>
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
private bool DataFilterCondition(string filter, ListData listData)
{
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
return false;
if (!_showDisconnected && !info.Connected)
return false;
if (IsAllLower(filter))
{ {
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase)) if (args.Button.Pressed)
{
_adminSystem.AdminOverlayOn();
}
else
{
_adminSystem.AdminOverlayOff();
}
}
#endregion
private void ShowDisconnectedPressed(ButtonEventArgs args)
{
_showDisconnected = args.Button.Pressed;
RefreshPlayerList(_players);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_adminSystem.PlayerListChanged -= RefreshPlayerList;
_adminSystem.OverlayEnabled -= OverlayEnabled;
_adminSystem.OverlayDisabled -= OverlayDisabled;
OverlayButton.OnPressed -= OverlayButtonPressed;
ListHeader.OnHeaderClicked -= HeaderClicked;
}
}
#region ListContainer
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{
_players = players;
PlayerCount.Text = Loc.GetString("player-tab-player-count", ("count", _playerMan.PlayerCount));
var filteredPlayers = players.Where(info => _showDisconnected || info.Connected).ToList();
var sortedPlayers = new List<PlayerInfo>(filteredPlayers);
sortedPlayers.Sort(Compare);
UpdateHeaderSymbols();
SearchList.PopulateList(sortedPlayers.Select(info => new PlayerListData(info,
$"{info.Username} {info.CharacterName} {info.IdentityName} {info.StartingJob}"))
.ToList());
}
private void GenerateButton(ListData data, ListContainerButton button)
{
if (data is not PlayerListData { Info: var player})
return;
var entry = new PlayerTabEntry(player, new StyleBoxFlat(button.Index % 2 == 0 ? _altColor : _defaultColor));
button.AddChild(entry);
button.ToolTip = $"{player.Username}, {player.CharacterName}, {player.IdentityName}, {player.StartingJob}";
}
/// <summary>
/// Determines whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.
/// If all characters are lowercase, the comparison ignores case.
/// If there is an uppercase character, the comparison is case sensitive.
/// </summary>
/// <param name="filter"></param>
/// <param name="listData"></param>
/// <returns>Whether <paramref name="filter"/> is contained in <paramref name="listData"/>.FilteringString.</returns>
private bool DataFilterCondition(string filter, ListData listData)
{
if (listData is not PlayerListData {Info: var info, FilteringString: var playerString})
return false; return false;
}
else if (!_showDisconnected && !info.Connected)
{
if (!playerString.Contains(filter))
return false; return false;
if (IsAllLower(filter))
{
if (!playerString.Contains(filter, StringComparison.CurrentCultureIgnoreCase))
return false;
}
else
{
if (!playerString.Contains(filter))
return false;
}
return true;
} }
return true; private bool IsAllLower(string input)
}
private bool IsAllLower(string input)
{
foreach (var c in input)
{ {
if (char.IsLetter(c) && !char.IsLower(c)) foreach (var c in input)
return false; {
if (char.IsLetter(c) && !char.IsLower(c))
return false;
}
return true;
} }
return true; #endregion
}
#endregion #region Header
#region Header private void UpdateHeaderSymbols()
private void UpdateHeaderSymbols()
{
ListHeader.ResetHeaderText();
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
}
private int Compare(PlayerInfo x, PlayerInfo y)
{
if (!_ascending)
{ {
(x, y) = (y, x); ListHeader.ResetHeaderText();
ListHeader.GetHeader(_headerClicked).Text += $" {(_ascending ? ArrowUp : ArrowDown)}";
} }
return _headerClicked switch private int Compare(PlayerInfo x, PlayerInfo y)
{ {
Header.Username => Compare(x.Username, y.Username), if (!_ascending)
Header.Character => Compare(x.CharacterName, y.CharacterName), {
Header.Job => Compare(x.StartingJob, y.StartingJob), (x, y) = (y, x);
Header.Antagonist => x.Antag.CompareTo(y.Antag), }
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1
};
}
private int Compare(string x, string y) return _headerClicked switch
{ {
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase); Header.Username => Compare(x.Username, y.Username),
} Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob),
private void HeaderClicked(Header header) Header.Antagonist => x.Antag.CompareTo(y.Antag),
{ Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
if (_headerClicked == header) _ => 1
{ };
_ascending = !_ascending;
}
else
{
_headerClicked = header;
_ascending = true;
} }
RefreshPlayerList(_adminSystem.PlayerList); private int Compare(string x, string y)
{
return string.Compare(x, y, StringComparison.OrdinalIgnoreCase);
}
private void HeaderClicked(Header header)
{
if (_headerClicked == header)
{
_ascending = !_ascending;
}
else
{
_headerClicked = header;
_ascending = true;
}
RefreshPlayerList(_adminSystem.PlayerList);
}
#endregion
} }
#endregion public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;
} }
public record PlayerListData(PlayerInfo Info, string FilteringString) : ListData;

View File

@@ -93,6 +93,6 @@ public sealed class ClientAlertsSystem : AlertsSystem
public void AlertClicked(ProtoId<AlertPrototype> alertType) public void AlertClicked(ProtoId<AlertPrototype> alertType)
{ {
RaisePredictiveEvent(new ClickAlertEvent(alertType)); RaiseNetworkEvent(new ClickAlertEvent(alertType));
} }
} }

View File

@@ -1,6 +1,5 @@
using Content.Shared.Ame.Components; using Content.Shared.Ame.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Ame.UI namespace Content.Client.Ame.UI
{ {
@@ -17,8 +16,9 @@ namespace Content.Client.Ame.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<AmeWindow>(); _window = new AmeWindow(this);
_window.OnAmeButton += ButtonPressed; _window.OnClose += Close;
_window.OpenCentered();
} }
/// <summary> /// <summary>
@@ -40,5 +40,15 @@ namespace Content.Client.Ame.UI
{ {
SendMessage(new UiButtonPressedMessage(button)); SendMessage(new UiButtonPressedMessage(button));
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_window?.Dispose();
}
}
} }
} }

View File

@@ -1,4 +1,3 @@
using System.Linq;
using Content.Client.UserInterface; using Content.Client.UserInterface;
using Content.Shared.Ame.Components; using Content.Shared.Ame.Components;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
@@ -10,17 +9,15 @@ namespace Content.Client.Ame.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class AmeWindow : DefaultWindow public sealed partial class AmeWindow : DefaultWindow
{ {
public event Action<UiButton>? OnAmeButton; public AmeWindow(AmeControllerBoundUserInterface ui)
public AmeWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
EjectButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.Eject); EjectButton.OnPressed += _ => ui.ButtonPressed(UiButton.Eject);
ToggleInjection.OnPressed += _ => OnAmeButton?.Invoke(UiButton.ToggleInjection); ToggleInjection.OnPressed += _ => ui.ButtonPressed(UiButton.ToggleInjection);
IncreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.IncreaseFuel); IncreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.IncreaseFuel);
DecreaseFuelButton.OnPressed += _ => OnAmeButton?.Invoke(UiButton.DecreaseFuel); DecreaseFuelButton.OnPressed += _ => ui.ButtonPressed(UiButton.DecreaseFuel);
} }
/// <summary> /// <summary>
@@ -32,7 +29,7 @@ namespace Content.Client.Ame.UI
var castState = (AmeControllerBoundUserInterfaceState) state; var castState = (AmeControllerBoundUserInterfaceState) state;
// Disable all buttons if not powered // Disable all buttons if not powered
if (Contents.Children.Any()) if (Contents.Children != null)
{ {
ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower); ButtonHelpers.SetButtonDisabledRecursive(Contents, !castState.HasPower);
EjectButton.Disabled = false; EjectButton.Disabled = false;
@@ -68,8 +65,8 @@ namespace Content.Client.Ame.UI
CoreCount.Text = $"{castState.CoreCount}"; CoreCount.Text = $"{castState.CoreCount}";
InjectionAmount.Text = $"{castState.InjectionAmount}"; InjectionAmount.Text = $"{castState.InjectionAmount}";
// format power statistics to pretty numbers // format power statistics to pretty numbers
CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply:N1}"; CurrentPowerSupply.Text = $"{castState.CurrentPowerSupply.ToString("N1")}";
TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply:N1}"; TargetedPowerSupply.Text = $"{castState.TargetedPowerSupply.ToString("N1")}";
} }
} }
} }

View File

@@ -20,9 +20,8 @@ public sealed class AnomalySystem : SharedAnomalySystem
SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged); SubscribeLocalEvent<AnomalyComponent, AppearanceChangeEvent>(OnAppearanceChanged);
SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup); SubscribeLocalEvent<AnomalyComponent, ComponentStartup>(OnStartup);
SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete); SubscribeLocalEvent<AnomalyComponent, AnimationCompletedEvent>(OnAnimationComplete);
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentShutdown>(OnShutdown);
} }
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args) private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
{ {
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime); _floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);
@@ -76,13 +75,4 @@ public sealed class AnomalySystem : SharedAnomalySystem
} }
} }
} }
private void OnShutdown(Entity<AnomalySupercriticalComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
sprite.Scale = Vector2.One;
sprite.Color = sprite.Color.WithAlpha(1f);
}
} }

View File

@@ -1,50 +0,0 @@
using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects;
using Content.Shared.Body.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects;
public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
{
public override void Initialize()
{
SubscribeLocalEvent<InnerBodyAnomalyComponent, AfterAutoHandleStateEvent>(OnAfterHandleState);
SubscribeLocalEvent<InnerBodyAnomalyComponent, ComponentShutdown>(OnCompShutdown);
}
private void OnAfterHandleState(Entity<InnerBodyAnomalyComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
if (ent.Comp.FallbackSprite is null)
return;
if (!sprite.LayerMapTryGet(ent.Comp.LayerMap, out var index))
index = sprite.LayerMapReserveBlank(ent.Comp.LayerMap);
if (TryComp<BodyComponent>(ent, out var body) &&
body.Prototype is not null &&
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
{
sprite.LayerSetSprite(index, speciesSprite);
}
else
{
sprite.LayerSetSprite(index, ent.Comp.FallbackSprite);
}
sprite.LayerSetVisible(index, true);
sprite.LayerSetShader(index, "unshaded");
}
private void OnCompShutdown(Entity<InnerBodyAnomalyComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
var index = sprite.LayerMapGet(ent.Comp.LayerMap);
sprite.LayerSetVisible(index, false);
}
}

View File

@@ -1,6 +1,7 @@
using Content.Shared.Anomaly; using Content.Shared.Anomaly;
using Content.Shared.Gravity;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface; using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Ui; namespace Content.Client.Anomaly.Ui;
@@ -17,8 +18,10 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_window = this.CreateWindow<AnomalyGeneratorWindow>(); _window = new(Owner);
_window.SetEntity(Owner);
_window.OpenCentered();
_window.OnClose += Close;
_window.OnGenerateButtonPressed += () => _window.OnGenerateButtonPressed += () =>
{ {
@@ -34,5 +37,18 @@ public sealed class AnomalyGeneratorBoundUserInterface : BoundUserInterface
return; return;
_window?.UpdateState(msg); _window?.UpdateState(msg);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
public void SetPowerSwitch(bool on)
{
SendMessage(new SharedGravityGeneratorComponent.SwitchGeneratorMessage(on));
}
} }

View File

@@ -18,21 +18,17 @@ public sealed partial class AnomalyGeneratorWindow : FancyWindow
public Action? OnGenerateButtonPressed; public Action? OnGenerateButtonPressed;
public AnomalyGeneratorWindow() public AnomalyGeneratorWindow(EntityUid gen)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
EntityView.SetEntity(gen);
EntityView.SpriteOffset = false; EntityView.SpriteOffset = false;
GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke(); GenerateButton.OnPressed += _ => OnGenerateButtonPressed?.Invoke();
} }
public void SetEntity(EntityUid uid)
{
EntityView.SetEntity(uid);
}
public void UpdateState(AnomalyGeneratorUserInterfaceState state) public void UpdateState(AnomalyGeneratorUserInterfaceState state)
{ {
_cooldownEnd = state.CooldownEndTime; _cooldownEnd = state.CooldownEndTime;

View File

@@ -31,7 +31,7 @@ public sealed partial class AnomalyScannerMenu : FancyWindow
msg.PushNewline(); msg.PushNewline();
var time = NextPulseTime.Value - _timing.CurTime; var time = NextPulseTime.Value - _timing.CurTime;
var timestring = $"{time.Minutes:00}:{time.Seconds:00}"; var timestring = $"{time.Minutes:00}:{time.Seconds:00}";
msg.AddMarkupOrThrow(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring))); msg.AddMarkup(Loc.GetString("anomaly-scanner-pulse-timer", ("time", timestring)));
} }
TextDisplay.SetMarkup(msg.ToMarkup()); TextDisplay.SetMarkup(msg.ToMarkup());

View File

@@ -28,6 +28,8 @@ namespace Content.Client.Arcade
private static readonly Vector2 BlockSize = new(15, 15); private static readonly Vector2 BlockSize = new(15, 15);
private readonly BlockGameBoundUserInterface _owner;
private readonly PanelContainer _mainPanel; private readonly PanelContainer _mainPanel;
private readonly BoxContainer _gameRootContainer; private readonly BoxContainer _gameRootContainer;
@@ -56,11 +58,10 @@ namespace Content.Client.Arcade
private bool _isPlayer = false; private bool _isPlayer = false;
private bool _gameOver = false; private bool _gameOver = false;
public event Action<BlockGamePlayerAction>? OnAction; public BlockGameMenu(BlockGameBoundUserInterface owner)
public BlockGameMenu()
{ {
Title = Loc.GetString("blockgame-menu-title"); Title = Loc.GetString("blockgame-menu-title");
_owner = owner;
MinSize = SetSize = new Vector2(410, 490); MinSize = SetSize = new Vector2(410, 490);
@@ -175,7 +176,7 @@ namespace Content.Client.Arcade
}; };
_newGameButton.OnPressed += (e) => _newGameButton.OnPressed += (e) =>
{ {
OnAction?.Invoke(BlockGamePlayerAction.NewGame); _owner.SendAction(BlockGamePlayerAction.NewGame);
}; };
pauseMenuContainer.AddChild(_newGameButton); pauseMenuContainer.AddChild(_newGameButton);
pauseMenuContainer.AddChild(new Control { MinSize = new Vector2(1, 10) }); pauseMenuContainer.AddChild(new Control { MinSize = new Vector2(1, 10) });
@@ -185,10 +186,7 @@ namespace Content.Client.Arcade
Text = Loc.GetString("blockgame-menu-button-scoreboard"), Text = Loc.GetString("blockgame-menu-button-scoreboard"),
TextAlign = Label.AlignMode.Center TextAlign = Label.AlignMode.Center
}; };
_scoreBoardButton.OnPressed += (e) => _scoreBoardButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.ShowHighscores);
{
OnAction?.Invoke(BlockGamePlayerAction.ShowHighscores);
};
pauseMenuContainer.AddChild(_scoreBoardButton); pauseMenuContainer.AddChild(_scoreBoardButton);
_unpauseButtonMargin = new Control { MinSize = new Vector2(1, 10), Visible = false }; _unpauseButtonMargin = new Control { MinSize = new Vector2(1, 10), Visible = false };
pauseMenuContainer.AddChild(_unpauseButtonMargin); pauseMenuContainer.AddChild(_unpauseButtonMargin);
@@ -201,7 +199,7 @@ namespace Content.Client.Arcade
}; };
_unpauseButton.OnPressed += (e) => _unpauseButton.OnPressed += (e) =>
{ {
OnAction?.Invoke(BlockGamePlayerAction.Unpause); _owner.SendAction(BlockGamePlayerAction.Unpause);
}; };
pauseMenuContainer.AddChild(_unpauseButton); pauseMenuContainer.AddChild(_unpauseButton);
@@ -259,7 +257,7 @@ namespace Content.Client.Arcade
}; };
_finalNewGameButton.OnPressed += (e) => _finalNewGameButton.OnPressed += (e) =>
{ {
OnAction?.Invoke(BlockGamePlayerAction.NewGame); _owner.SendAction(BlockGamePlayerAction.NewGame);
}; };
gameOverMenuContainer.AddChild(_finalNewGameButton); gameOverMenuContainer.AddChild(_finalNewGameButton);
@@ -329,10 +327,7 @@ namespace Content.Client.Arcade
Text = Loc.GetString("blockgame-menu-button-back"), Text = Loc.GetString("blockgame-menu-button-back"),
TextAlign = Label.AlignMode.Center TextAlign = Label.AlignMode.Center
}; };
_highscoreBackButton.OnPressed += (e) => _highscoreBackButton.OnPressed += (e) => _owner.SendAction(BlockGamePlayerAction.Pause);
{
OnAction?.Invoke(BlockGamePlayerAction.Pause);
};
menuContainer.AddChild(_highscoreBackButton); menuContainer.AddChild(_highscoreBackButton);
menuInnerPanel.AddChild(menuContainer); menuInnerPanel.AddChild(menuContainer);
@@ -380,7 +375,7 @@ namespace Content.Client.Arcade
{ {
PanelOverride = back, PanelOverride = back,
HorizontalExpand = true, HorizontalExpand = true,
SizeFlagsStretchRatio = 34.25f SizeFlagsStretchRatio = 60
}; };
var backgroundPanel = new PanelContainer var backgroundPanel = new PanelContainer
{ {
@@ -478,7 +473,7 @@ namespace Content.Client.Arcade
private void TryPause() private void TryPause()
{ {
OnAction?.Invoke(BlockGamePlayerAction.Pause); _owner.SendAction(BlockGamePlayerAction.Pause);
} }
public void SetStarted() public void SetStarted()
@@ -581,19 +576,19 @@ namespace Content.Client.Arcade
return; return;
else if (args.Function == ContentKeyFunctions.ArcadeLeft) else if (args.Function == ContentKeyFunctions.ArcadeLeft)
OnAction?.Invoke(BlockGamePlayerAction.StartLeft); _owner.SendAction(BlockGamePlayerAction.StartLeft);
else if (args.Function == ContentKeyFunctions.ArcadeRight) else if (args.Function == ContentKeyFunctions.ArcadeRight)
OnAction?.Invoke(BlockGamePlayerAction.StartRight); _owner.SendAction(BlockGamePlayerAction.StartRight);
else if (args.Function == ContentKeyFunctions.ArcadeUp) else if (args.Function == ContentKeyFunctions.ArcadeUp)
OnAction?.Invoke(BlockGamePlayerAction.Rotate); _owner.SendAction(BlockGamePlayerAction.Rotate);
else if (args.Function == ContentKeyFunctions.Arcade3) else if (args.Function == ContentKeyFunctions.Arcade3)
OnAction?.Invoke(BlockGamePlayerAction.CounterRotate); _owner.SendAction(BlockGamePlayerAction.CounterRotate);
else if (args.Function == ContentKeyFunctions.ArcadeDown) else if (args.Function == ContentKeyFunctions.ArcadeDown)
OnAction?.Invoke(BlockGamePlayerAction.SoftdropStart); _owner.SendAction(BlockGamePlayerAction.SoftdropStart);
else if (args.Function == ContentKeyFunctions.Arcade2) else if (args.Function == ContentKeyFunctions.Arcade2)
OnAction?.Invoke(BlockGamePlayerAction.Hold); _owner.SendAction(BlockGamePlayerAction.Hold);
else if (args.Function == ContentKeyFunctions.Arcade1) else if (args.Function == ContentKeyFunctions.Arcade1)
OnAction?.Invoke(BlockGamePlayerAction.Harddrop); _owner.SendAction(BlockGamePlayerAction.Harddrop);
} }
protected override void KeyBindUp(GUIBoundKeyEventArgs args) protected override void KeyBindUp(GUIBoundKeyEventArgs args)
@@ -604,11 +599,11 @@ namespace Content.Client.Arcade
return; return;
else if (args.Function == ContentKeyFunctions.ArcadeLeft) else if (args.Function == ContentKeyFunctions.ArcadeLeft)
OnAction?.Invoke(BlockGamePlayerAction.EndLeft); _owner.SendAction(BlockGamePlayerAction.EndLeft);
else if (args.Function == ContentKeyFunctions.ArcadeRight) else if (args.Function == ContentKeyFunctions.ArcadeRight)
OnAction?.Invoke(BlockGamePlayerAction.EndRight); _owner.SendAction(BlockGamePlayerAction.EndRight);
else if (args.Function == ContentKeyFunctions.ArcadeDown) else if (args.Function == ContentKeyFunctions.ArcadeDown)
OnAction?.Invoke(BlockGamePlayerAction.SoftdropEnd); _owner.SendAction(BlockGamePlayerAction.SoftdropEnd);
} }
public void UpdateNextBlock(BlockGameBlock[] blocks) public void UpdateNextBlock(BlockGameBlock[] blocks)

View File

@@ -8,6 +8,8 @@ namespace Content.Client.Arcade
{ {
public sealed class SpaceVillainArcadeMenu : DefaultWindow public sealed class SpaceVillainArcadeMenu : DefaultWindow
{ {
public SpaceVillainArcadeBoundUserInterface Owner { get; set; }
private readonly Label _enemyNameLabel; private readonly Label _enemyNameLabel;
private readonly Label _playerInfoLabel; private readonly Label _playerInfoLabel;
private readonly Label _enemyInfoLabel; private readonly Label _enemyInfoLabel;
@@ -15,13 +17,11 @@ namespace Content.Client.Arcade
private readonly Label _enemyActionLabel; private readonly Label _enemyActionLabel;
private readonly Button[] _gameButtons = new Button[3]; //used to disable/enable all game buttons private readonly Button[] _gameButtons = new Button[3]; //used to disable/enable all game buttons
public SpaceVillainArcadeMenu(SpaceVillainArcadeBoundUserInterface owner)
public event Action<SharedSpaceVillainArcadeComponent.PlayerAction>? OnPlayerAction;
public SpaceVillainArcadeMenu()
{ {
MinSize = SetSize = new Vector2(300, 225); MinSize = SetSize = new Vector2(300, 225);
Title = Loc.GetString("spacevillain-menu-title"); Title = Loc.GetString("spacevillain-menu-title");
Owner = owner;
var grid = new GridContainer { Columns = 1 }; var grid = new GridContainer { Columns = 1 };
@@ -47,43 +47,32 @@ namespace Content.Client.Arcade
grid.AddChild(_enemyActionLabel); grid.AddChild(_enemyActionLabel);
var buttonGrid = new GridContainer { Columns = 3 }; var buttonGrid = new GridContainer { Columns = 3 };
_gameButtons[0] = new Button() _gameButtons[0] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Attack)
{ {
Text = Loc.GetString("spacevillain-menu-button-attack") Text = Loc.GetString("spacevillain-menu-button-attack")
}; };
_gameButtons[0].OnPressed +=
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Attack);
buttonGrid.AddChild(_gameButtons[0]); buttonGrid.AddChild(_gameButtons[0]);
_gameButtons[1] = new Button() _gameButtons[1] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Heal)
{ {
Text = Loc.GetString("spacevillain-menu-button-heal") Text = Loc.GetString("spacevillain-menu-button-heal")
}; };
_gameButtons[1].OnPressed +=
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Heal);
buttonGrid.AddChild(_gameButtons[1]); buttonGrid.AddChild(_gameButtons[1]);
_gameButtons[2] = new Button() _gameButtons[2] = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.Recharge)
{ {
Text = Loc.GetString("spacevillain-menu-button-recharge") Text = Loc.GetString("spacevillain-menu-button-recharge")
}; };
_gameButtons[2].OnPressed +=
_ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.Recharge);
buttonGrid.AddChild(_gameButtons[2]); buttonGrid.AddChild(_gameButtons[2]);
centerContainer = new CenterContainer(); centerContainer = new CenterContainer();
centerContainer.AddChild(buttonGrid); centerContainer.AddChild(buttonGrid);
grid.AddChild(centerContainer); grid.AddChild(centerContainer);
var newGame = new Button() var newGame = new ActionButton(Owner, SharedSpaceVillainArcadeComponent.PlayerAction.NewGame)
{ {
Text = Loc.GetString("spacevillain-menu-button-new-game") Text = Loc.GetString("spacevillain-menu-button-new-game")
}; };
newGame.OnPressed += _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.NewGame);
grid.AddChild(newGame); grid.AddChild(newGame);
Contents.AddChild(grid); Contents.AddChild(grid);
@@ -110,5 +99,23 @@ namespace Content.Client.Arcade
_playerActionLabel.Text = message.PlayerActionMessage; _playerActionLabel.Text = message.PlayerActionMessage;
_enemyActionLabel.Text = message.EnemyActionMessage; _enemyActionLabel.Text = message.EnemyActionMessage;
} }
private sealed class ActionButton : Button
{
private readonly SpaceVillainArcadeBoundUserInterface _owner;
private readonly SharedSpaceVillainArcadeComponent.PlayerAction _playerAction;
public ActionButton(SpaceVillainArcadeBoundUserInterface owner, SharedSpaceVillainArcadeComponent.PlayerAction playerAction)
{
_owner = owner;
_playerAction = playerAction;
OnPressed += Clicked;
}
private void Clicked(ButtonEventArgs e)
{
_owner.SendAction(_playerAction);
}
}
} }
} }

View File

@@ -1,6 +1,5 @@
using Content.Shared.Arcade; using Content.Shared.Arcade;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Arcade.UI; namespace Content.Client.Arcade.UI;
@@ -16,8 +15,9 @@ public sealed class BlockGameBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<BlockGameMenu>(); _menu = new BlockGameMenu(this);
_menu.OnAction += SendAction; _menu.OnClose += Close;
_menu.OpenCentered();
} }
protected override void ReceiveMessage(BoundUserInterfaceMessage message) protected override void ReceiveMessage(BoundUserInterfaceMessage message)

View File

@@ -1,5 +1,4 @@
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.ViewVariables; using Robust.Shared.ViewVariables;
using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent; using static Content.Shared.Arcade.SharedSpaceVillainArcadeComponent;
@@ -10,6 +9,8 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
{ {
[ViewVariables] private SpaceVillainArcadeMenu? _menu; [ViewVariables] private SpaceVillainArcadeMenu? _menu;
//public SharedSpaceVillainArcadeComponent SpaceVillainArcade;
public SpaceVillainArcadeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) public SpaceVillainArcadeBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
SendAction(PlayerAction.RequestData); SendAction(PlayerAction.RequestData);
@@ -24,8 +25,10 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<SpaceVillainArcadeMenu>(); _menu = new SpaceVillainArcadeMenu(this);
_menu.OnPlayerAction += SendAction;
_menu.OnClose += Close;
_menu.OpenCentered();
} }
protected override void ReceiveMessage(BoundUserInterfaceMessage message) protected override void ReceiveMessage(BoundUserInterfaceMessage message)
@@ -33,4 +36,12 @@ public sealed class SpaceVillainArcadeBoundUserInterface : BoundUserInterface
if (message is SpaceVillainArcadeDataUpdateMessage msg) if (message is SpaceVillainArcadeDataUpdateMessage msg)
_menu?.UpdateInfo(msg); _menu?.UpdateInfo(msg);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_menu?.Dispose();
}
} }

View File

@@ -1,81 +0,0 @@
<BoxContainer xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:s="clr-namespace:Content.Client.Stylesheets"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical" HorizontalExpand ="True" Margin="0 0 0 3">
<!-- Device selection button -->
<Button Name="FocusButton" HorizontalExpand="True" SetHeight="32" Margin="12 0 0 0" StyleClasses="OpenBoth" Access="Public">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Horizontal">
<!-- Alarm state -->
<TextureRect Stretch="Keep" HorizontalAlignment="Left" Margin="-20 -2 0 0" ModulateSelfOverride="#25252a" TexturePath="/Textures/Interface/AtmosMonitoring/status_bg.png">
<BoxContainer VerticalExpand="True" HorizontalExpand="True" Orientation="Horizontal" Margin="8 0">
<TextureRect Name="ArrowTexture" VerticalAlignment="Center" SetSize="12 12" Stretch="KeepAspectCentered" Margin="3 0" TexturePath="/Textures/Interface/Nano/triangle_right.png"></TextureRect>
<Label Name="AlarmStateLabel" HorizontalExpand="True" HorizontalAlignment="Center" FontColorOverride="#5A5A5A" Text="{Loc 'atmos-alerts-window-invalid-state'}"></Label>
</BoxContainer>
</TextureRect>
<!-- Alarm name -->
<Label Name="AlarmNameLabel" Text="???" HorizontalExpand="True" HorizontalAlignment="Center" Margin="5 0"></Label>
</BoxContainer>
</Button>
<!-- Panel that appears on selecting the device -->
<PanelContainer Name="FocusContainer" HorizontalExpand="True" Margin="1 -1 1 0" ReservesSpace="False" Visible="False" Access="Public">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#25252a"/>
</PanelContainer.PanelOverride>
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical">
<!-- Atmosphere status -->
<Control>
<!-- Main container for displaying atmospheric data -->
<BoxContainer Name="MainDataContainer" HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" ReservesSpace="False" Visible="False">
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
<Label Name="TemperatureHeaderLabel" Text="{Loc 'atmos-alerts-window-temperature-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="PressureHeaderLabel" Text="{Loc 'atmos-alerts-window-pressure-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="OxygenationHeaderLabel" Text="{Loc 'atmos-alerts-window-oxygenation-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 2 0 0" SetHeight="24"></Label>
</BoxContainer>
<PanelContainer HorizontalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
</PanelContainer.PanelOverride>
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
<Label Name="TemperatureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="PressureLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
<Label Name="OxygenationLabel" Text="???" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#5A5A5A" Margin="0 2 0 0" SetHeight="24"></Label>
</BoxContainer>
</PanelContainer>
<BoxContainer HorizontalExpand="True" Orientation="Horizontal">
<Label Name="GasesHeaderLabel" Text="{Loc 'atmos-alerts-window-other-gases-label'}" HorizontalAlignment="Center" HorizontalExpand="True" FontColorOverride="#a9a9a9" Margin="0 4 0 0" SetHeight="24"></Label>
</BoxContainer>
<PanelContainer HorizontalExpand="True">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#202023"/>
</PanelContainer.PanelOverride>
<!-- Gas entries added via C# code -->
<GridContainer Name="GasGridContainer" HorizontalExpand="True" Columns = "4"></GridContainer>
</PanelContainer>
</BoxContainer>
<!-- If the alarm is inactive, this is label is diplayed instead -->
<Label Name="NoDataLabel" Text="{Loc 'atmos-alerts-window-no-data-available'}" HorizontalAlignment="Center" Margin="0 15" FontColorOverride="#a9a9a9" ReservesSpace="False" Visible="False"></Label>
<!-- Silencing progress bar -->
<controls:StripeBack Name="SilenceAlarmProgressBar" ReservesSpace="False" Visible="False" Access="Public">
<PanelContainer>
<Label Text="{Loc 'atmos-alerts-window-alerts-being-silenced'}" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="5 5 5 5"/>
</PanelContainer>
</controls:StripeBack>
</Control>
<!-- Check box for silencing this alarm -->
<CheckBox Name="SilenceCheckBox" Text="{Loc 'atmos-alerts-window-silence-alerts'}" HorizontalAlignment="Left" Margin="5 5 5 5" Access="Public"></CheckBox>
</BoxContainer>
</PanelContainer>
</BoxContainer>

View File

@@ -1,215 +0,0 @@
using Content.Client.Stylesheets;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Monitor;
using Content.Shared.FixedPoint;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using System.Linq;
namespace Content.Client.Atmos.Consoles;
[GenerateTypedNameReferences]
public sealed partial class AtmosAlarmEntryContainer : BoxContainer
{
public NetEntity NetEntity;
public EntityCoordinates? Coordinates;
private readonly IEntityManager _entManager;
private readonly IResourceCache _cache;
private Dictionary<AtmosAlarmType, string> _alarmStrings = new Dictionary<AtmosAlarmType, string>()
{
[AtmosAlarmType.Invalid] = "atmos-alerts-window-invalid-state",
[AtmosAlarmType.Normal] = "atmos-alerts-window-normal-state",
[AtmosAlarmType.Warning] = "atmos-alerts-window-warning-state",
[AtmosAlarmType.Danger] = "atmos-alerts-window-danger-state",
};
private Dictionary<Gas, string> _gasShorthands = new Dictionary<Gas, string>()
{
[Gas.Ammonia] = "NH₃",
[Gas.CarbonDioxide] = "CO₂",
[Gas.Frezon] = "F",
[Gas.Nitrogen] = "N₂",
[Gas.NitrousOxide] = "N₂O",
[Gas.Oxygen] = "O₂",
[Gas.Plasma] = "P",
[Gas.Tritium] = "T",
[Gas.WaterVapor] = "H₂O",
};
public AtmosAlarmEntryContainer(NetEntity uid, EntityCoordinates? coordinates)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>();
NetEntity = uid;
Coordinates = coordinates;
// Load fonts
var headerFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Bold.ttf"), 11);
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
var smallFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
// Set fonts
TemperatureHeaderLabel.FontOverride = headerFont;
PressureHeaderLabel.FontOverride = headerFont;
OxygenationHeaderLabel.FontOverride = headerFont;
GasesHeaderLabel.FontOverride = headerFont;
TemperatureLabel.FontOverride = normalFont;
PressureLabel.FontOverride = normalFont;
OxygenationLabel.FontOverride = normalFont;
NoDataLabel.FontOverride = headerFont;
SilenceCheckBox.Label.FontOverride = smallFont;
SilenceCheckBox.Label.FontColorOverride = Color.DarkGray;
}
public void UpdateEntry(AtmosAlertsComputerEntry entry, bool isFocus, AtmosAlertsFocusDeviceData? focusData = null)
{
NetEntity = entry.NetEntity;
Coordinates = _entManager.GetCoordinates(entry.Coordinates);
// Load fonts
var normalFont = new VectorFont(_cache.GetResource<FontResource>("/Fonts/NotoSansDisplay/NotoSansDisplay-Regular.ttf"), 11);
// Update alarm state
if (!_alarmStrings.TryGetValue(entry.AlarmState, out var alarmString))
alarmString = "atmos-alerts-window-invalid-state";
AlarmStateLabel.Text = Loc.GetString(alarmString);
AlarmStateLabel.FontColorOverride = GetAlarmStateColor(entry.AlarmState);
// Update alarm name
AlarmNameLabel.Text = Loc.GetString("atmos-alerts-window-alarm-label", ("name", entry.EntityName), ("address", entry.Address));
// Focus updates
FocusContainer.Visible = isFocus;
if (isFocus)
SetAsFocus();
else
RemoveAsFocus();
if (isFocus && entry.Group == AtmosAlertsComputerGroup.AirAlarm)
{
MainDataContainer.Visible = (entry.AlarmState != AtmosAlarmType.Invalid);
NoDataLabel.Visible = (entry.AlarmState == AtmosAlarmType.Invalid);
if (focusData != null)
{
// Update temperature
var tempK = (FixedPoint2)focusData.Value.TemperatureData.Item1;
var tempC = (FixedPoint2)TemperatureHelpers.KelvinToCelsius(tempK.Float());
TemperatureLabel.Text = Loc.GetString("atmos-alerts-window-temperature-value", ("valueInC", tempC), ("valueInK", tempK));
TemperatureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.TemperatureData.Item2);
// Update pressure
PressureLabel.Text = Loc.GetString("atmos-alerts-window-pressure-value", ("value", (FixedPoint2)focusData.Value.PressureData.Item1));
PressureLabel.FontColorOverride = GetAlarmStateColor(focusData.Value.PressureData.Item2);
// Update oxygenation
var oxygenPercent = (FixedPoint2)0f;
var oxygenAlert = AtmosAlarmType.Invalid;
if (focusData.Value.GasData.TryGetValue(Gas.Oxygen, out var oxygenData))
{
oxygenPercent = oxygenData.Item2 * 100f;
oxygenAlert = oxygenData.Item3;
}
OxygenationLabel.Text = Loc.GetString("atmos-alerts-window-oxygenation-value", ("value", oxygenPercent));
OxygenationLabel.FontColorOverride = GetAlarmStateColor(oxygenAlert);
// Update other present gases
GasGridContainer.RemoveAllChildren();
var gasData = focusData.Value.GasData.Where(g => g.Key != Gas.Oxygen);
if (gasData.Count() == 0)
{
// No other gases
var gasLabel = new Label()
{
Text = Loc.GetString("atmos-alerts-window-other-gases-value-nil"),
FontOverride = normalFont,
FontColorOverride = StyleNano.DisabledFore,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
HorizontalExpand = true,
Margin = new Thickness(0, 2, 0, 0),
SetHeight = 24f,
};
GasGridContainer.AddChild(gasLabel);
}
else
{
// Add an entry for each gas
foreach ((var gas, (var mol, var percent, var alert)) in gasData)
{
var gasPercent = (FixedPoint2)0f;
gasPercent = percent * 100f;
if (!_gasShorthands.TryGetValue(gas, out var gasShorthand))
gasShorthand = "X";
var gasLabel = new Label()
{
Text = Loc.GetString("atmos-alerts-window-other-gases-value", ("shorthand", gasShorthand), ("value", gasPercent)),
FontOverride = normalFont,
FontColorOverride = GetAlarmStateColor(alert),
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
HorizontalExpand = true,
Margin = new Thickness(0, 2, 0, 0),
SetHeight = 24f,
};
GasGridContainer.AddChild(gasLabel);
}
}
}
}
}
public void SetAsFocus()
{
FocusButton.AddStyleClass(StyleNano.StyleClassButtonColorGreen);
ArrowTexture.TexturePath = "/Textures/Interface/Nano/inverted_triangle.svg.png";
}
public void RemoveAsFocus()
{
FocusButton.RemoveStyleClass(StyleNano.StyleClassButtonColorGreen);
ArrowTexture.TexturePath = "/Textures/Interface/Nano/triangle_right.png";
FocusContainer.Visible = false;
}
private Color GetAlarmStateColor(AtmosAlarmType alarmType)
{
switch (alarmType)
{
case AtmosAlarmType.Normal:
return StyleNano.GoodGreenFore;
case AtmosAlarmType.Warning:
return StyleNano.ConcerningOrangeFore;
case AtmosAlarmType.Danger:
return StyleNano.DangerousRedFore;
}
return StyleNano.DisabledFore;
}
}

View File

@@ -1,52 +0,0 @@
using Content.Shared.Atmos.Components;
namespace Content.Client.Atmos.Consoles;
public sealed class AtmosAlertsComputerBoundUserInterface : BoundUserInterface
{
[ViewVariables]
private AtmosAlertsComputerWindow? _menu;
public AtmosAlertsComputerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
protected override void Open()
{
_menu = new AtmosAlertsComputerWindow(this, Owner);
_menu.OpenCentered();
_menu.OnClose += Close;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
var castState = (AtmosAlertsComputerBoundInterfaceState) state;
if (castState == null)
return;
EntMan.TryGetComponent<TransformComponent>(Owner, out var xform);
_menu?.UpdateUI(xform?.Coordinates, castState.AirAlarms, castState.FireAlarms, castState.FocusData);
}
public void SendFocusChangeMessage(NetEntity? netEntity)
{
SendMessage(new AtmosAlertsComputerFocusChangeMessage(netEntity));
}
public void SendDeviceSilencedMessage(NetEntity netEntity, bool silenceDevice)
{
SendMessage(new AtmosAlertsComputerDeviceSilencedMessage(netEntity, silenceDevice));
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
}

View File

@@ -1,108 +0,0 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:ui="clr-namespace:Content.Client.Pinpointer.UI"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
Title="{Loc 'atmos-alerts-window-title'}"
Resizable="False"
SetSize="1120 750"
MinSize="1120 750">
<BoxContainer Orientation="Vertical">
<!-- Main display -->
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
<!-- Nav map -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<ui:NavMapControl Name="NavMap" Margin="5 5" VerticalExpand="True" HorizontalExpand="True">
<!-- System warning -->
<PanelContainer Name="SystemWarningPanel"
HorizontalAlignment="Center"
VerticalAlignment="Top"
HorizontalExpand="True"
Margin="0 48 0 0"
Visible="False">
<RichTextLabel Name="SystemWarningLabel" Margin="12 8 12 8"/>
</PanelContainer>
</ui:NavMapControl>
<!-- Nav map legend -->
<BoxContainer Orientation="Horizontal" Margin="0 10 0 10">
<Label Text="{Loc 'atmos-alerts-window-label-alert-types'}"
Margin="20 0 5 0"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
Modulate="#5A5A5A"
SetSize="16 16"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-alerts-window-invalid-state'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_circle.png"
Modulate="#32cd32"
SetSize="16 16"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-alerts-window-normal-state'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_triangle.png"
SetSize="16 16"
Modulate="#ffb648"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-alerts-window-warning-state'}"/>
<TextureRect Stretch="KeepAspectCentered"
TexturePath="/Textures/Interface/NavMap/beveled_square.png"
SetSize="16 16"
Modulate="#ff4343"
Margin="20 0 5 0"/>
<Label Text="{Loc 'atmos-alerts-window-danger-state'}"/>
</BoxContainer>
</BoxContainer>
<!-- Atmosphere status -->
<BoxContainer Orientation="Vertical" VerticalExpand="True" SetWidth="440" Margin="0 0 10 10">
<!-- Station name -->
<controls:StripeBack>
<PanelContainer>
<RichTextLabel Name="StationName" HorizontalAlignment="Center" VerticalAlignment="Center" Margin="0 5 0 3"/>
</PanelContainer>
</controls:StripeBack>
<!-- Alarm status (entries added by C# code) -->
<TabContainer Name="MasterTabContainer" VerticalExpand="True" HorizontalExpand="True" Margin="0 10 0 0">
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
<BoxContainer Name="AlertsTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
</ScrollContainer>
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
<BoxContainer Name="AirAlarmsTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
</ScrollContainer>
<ScrollContainer HorizontalExpand="True" Margin="8, 8, 8, 8">
<BoxContainer Name="FireAlarmsTable" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True" Margin="0 0 0 10"/>
</ScrollContainer>
</TabContainer>
<!-- Overlay toggles -->
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
<Label Text="{Loc 'atmos-alerts-window-toggle-overlays'}" Margin="0 0 0 5"/>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<CheckBox Name="ShowInactiveAlarms" Text="{Loc 'atmos-alerts-window-invalid-state'}" Pressed="False" HorizontalExpand="True"/>
<CheckBox Name="ShowNormalAlarms" Text="{Loc 'atmos-alerts-window-normal-state'}" Pressed="False" HorizontalExpand="True"/>
<CheckBox Name="ShowWarningAlarms" Text="{Loc 'atmos-alerts-window-warning-state'}" Pressed="True" HorizontalExpand="True"/>
<CheckBox Name="ShowDangerAlarms" Text="{Loc 'atmos-alerts-window-danger-state'}" Pressed="True" HorizontalExpand="True"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Footer -->
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal" Margin="10 2 5 0" VerticalAlignment="Bottom">
<Label Text="{Loc 'atmos-alerts-window-flavor-left'}" StyleClasses="WindowFooterText" />
<Label Text="{Loc 'atmos-alerts-window-flavor-right'}" StyleClasses="WindowFooterText"
HorizontalAlignment="Right" HorizontalExpand="True" Margin="0 0 5 0" />
<TextureRect StyleClasses="NTLogoDark" Stretch="KeepAspectCentered"
VerticalAlignment="Center" HorizontalAlignment="Right" SetSize="19 19"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -1,611 +0,0 @@
using Content.Client.Message;
using Content.Client.Pinpointer.UI;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Pinpointer;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Map;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
namespace Content.Client.Atmos.Consoles;
[GenerateTypedNameReferences]
public sealed partial class AtmosAlertsComputerWindow : FancyWindow
{
private readonly IEntityManager _entManager;
private readonly SpriteSystem _spriteSystem;
private readonly SharedNavMapSystem _navMapSystem;
private EntityUid? _owner;
private NetEntity? _trackedEntity;
private AtmosAlertsComputerEntry[]? _airAlarms = null;
private AtmosAlertsComputerEntry[]? _fireAlarms = null;
private IEnumerable<AtmosAlertsComputerEntry>? _allAlarms = null;
private IEnumerable<AtmosAlertsComputerEntry>? _activeAlarms = null;
private Dictionary<NetEntity, float> _deviceSilencingProgress = new();
public event Action<NetEntity?>? SendFocusChangeMessageAction;
public event Action<NetEntity, bool>? SendDeviceSilencedMessageAction;
private bool _autoScrollActive = false;
private bool _autoScrollAwaitsUpdate = false;
private const float SilencingDuration = 2.5f;
// Colors
private Color _wallColor = new Color(64, 64, 64);
private Color _tileColor = new Color(28, 28, 28);
private Color _monitorBlipColor = Color.Cyan;
private Color _untrackedEntColor = Color.DimGray;
private Color _regionBaseColor = new Color(154, 154, 154);
private Color _inactiveColor = StyleNano.DisabledFore;
private Color _statusTextColor = StyleNano.GoodGreenFore;
private Color _goodColor = Color.LimeGreen;
private Color _warningColor = new Color(255, 182, 72);
private Color _dangerColor = new Color(255, 67, 67);
public AtmosAlertsComputerWindow(AtmosAlertsComputerBoundUserInterface userInterface, EntityUid? owner)
{
RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>();
_spriteSystem = _entManager.System<SpriteSystem>();
_navMapSystem = _entManager.System<SharedNavMapSystem>();
// Pass the owner to nav map
_owner = owner;
NavMap.Owner = _owner;
// Set nav map colors
NavMap.WallColor = _wallColor;
NavMap.TileColor = _tileColor;
// Set nav map grid uid
var stationName = Loc.GetString("atmos-alerts-window-unknown-location");
if (_entManager.TryGetComponent<TransformComponent>(owner, out var xform))
{
NavMap.MapUid = xform.GridUid;
// Assign station name
if (_entManager.TryGetComponent<MetaDataComponent>(xform.GridUid, out var stationMetaData))
stationName = stationMetaData.EntityName;
var msg = new FormattedMessage();
msg.TryAddMarkup(Loc.GetString("atmos-alerts-window-station-name", ("stationName", stationName)), out _);
StationName.SetMessage(msg);
}
else
{
StationName.SetMessage(stationName);
NavMap.Visible = false;
}
// Set trackable entity selected action
NavMap.TrackedEntitySelectedAction += SetTrackedEntityFromNavMap;
// Update nav map
NavMap.ForceNavMapUpdate();
// Set tab container headers
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-no-alerts"));
MasterTabContainer.SetTabTitle(1, Loc.GetString("atmos-alerts-window-tab-air-alarms"));
MasterTabContainer.SetTabTitle(2, Loc.GetString("atmos-alerts-window-tab-fire-alarms"));
// Set UI toggles
ShowInactiveAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowInactiveAlarms, AtmosAlarmType.Invalid);
ShowNormalAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowNormalAlarms, AtmosAlarmType.Normal);
ShowWarningAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowWarningAlarms, AtmosAlarmType.Warning);
ShowDangerAlarms.OnToggled += _ => OnShowAlarmsToggled(ShowDangerAlarms, AtmosAlarmType.Danger);
// Set atmos monitoring message action
SendFocusChangeMessageAction += userInterface.SendFocusChangeMessage;
SendDeviceSilencedMessageAction += userInterface.SendDeviceSilencedMessage;
}
#region Toggle handling
private void OnShowAlarmsToggled(CheckBox toggle, AtmosAlarmType toggledAlarmState)
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner.Value, out var console))
return;
foreach (var device in console.AtmosDevices)
{
var alarmState = GetAlarmState(device.NetEntity);
if (toggledAlarmState != alarmState)
continue;
if (toggle.Pressed)
AddTrackedEntityToNavMap(device, alarmState);
else
NavMap.TrackedEntities.Remove(device.NetEntity);
}
}
private void OnSilenceAlertsToggled(NetEntity netEntity, bool toggleState)
{
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner, out var console))
return;
if (toggleState)
_deviceSilencingProgress[netEntity] = SilencingDuration;
else
_deviceSilencingProgress.Remove(netEntity);
foreach (AtmosAlarmEntryContainer entryContainer in AlertsTable.Children)
{
if (entryContainer.NetEntity == netEntity)
entryContainer.SilenceAlarmProgressBar.Visible = toggleState;
}
SendDeviceSilencedMessageAction?.Invoke(netEntity, toggleState);
}
#endregion
public void UpdateUI(EntityCoordinates? consoleCoords, AtmosAlertsComputerEntry[] airAlarms, AtmosAlertsComputerEntry[] fireAlarms, AtmosAlertsFocusDeviceData? focusData)
{
if (_owner == null)
return;
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner.Value, out var console))
return;
if (_trackedEntity != focusData?.NetEntity)
{
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
focusData = null;
}
// Retain alarm data for use inbetween updates
_airAlarms = airAlarms;
_fireAlarms = fireAlarms;
_allAlarms = airAlarms.Concat(fireAlarms);
var silenced = console.SilencedDevices;
_activeAlarms = _allAlarms.Where(x => x.AlarmState > AtmosAlarmType.Normal &&
(!silenced.Contains(x.NetEntity) || _deviceSilencingProgress.ContainsKey(x.NetEntity)));
// Reset nav map data
NavMap.TrackedCoordinates.Clear();
NavMap.TrackedEntities.Clear();
// Add tracked entities to the nav map
foreach (var device in console.AtmosDevices)
{
if (!device.NetEntity.Valid)
continue;
if (!NavMap.Visible)
continue;
var alarmState = GetAlarmState(device.NetEntity);
if (_trackedEntity != device.NetEntity)
{
// Skip air alarms if the appropriate overlay is off
if (!ShowInactiveAlarms.Pressed && alarmState == AtmosAlarmType.Invalid)
continue;
if (!ShowNormalAlarms.Pressed && alarmState == AtmosAlarmType.Normal)
continue;
if (!ShowWarningAlarms.Pressed && alarmState == AtmosAlarmType.Warning)
continue;
if (!ShowDangerAlarms.Pressed && alarmState == AtmosAlarmType.Danger)
continue;
}
AddTrackedEntityToNavMap(device, alarmState);
}
// Show the monitor location
var consoleUid = _entManager.GetNetEntity(_owner);
if (consoleCoords != null && consoleUid != null)
{
var texture = _spriteSystem.Frame0(new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")));
var blip = new NavMapBlip(consoleCoords.Value, texture, _monitorBlipColor, true, false);
NavMap.TrackedEntities[consoleUid.Value] = blip;
}
// Update the nav map
NavMap.ForceNavMapUpdate();
// Clear excess children from the tables
var activeAlarmCount = _activeAlarms.Count();
while (AlertsTable.ChildCount > activeAlarmCount)
AlertsTable.RemoveChild(AlertsTable.GetChild(AlertsTable.ChildCount - 1));
while (AirAlarmsTable.ChildCount > airAlarms.Length)
AirAlarmsTable.RemoveChild(AirAlarmsTable.GetChild(AirAlarmsTable.ChildCount - 1));
while (FireAlarmsTable.ChildCount > fireAlarms.Length)
FireAlarmsTable.RemoveChild(FireAlarmsTable.GetChild(FireAlarmsTable.ChildCount - 1));
// Update all entries in each table
for (int index = 0; index < _activeAlarms.Count(); index++)
{
var entry = _activeAlarms.ElementAt(index);
UpdateUIEntry(entry, index, AlertsTable, console, focusData);
}
for (int index = 0; index < airAlarms.Count(); index++)
{
var entry = airAlarms.ElementAt(index);
UpdateUIEntry(entry, index, AirAlarmsTable, console, focusData);
}
for (int index = 0; index < fireAlarms.Count(); index++)
{
var entry = fireAlarms.ElementAt(index);
UpdateUIEntry(entry, index, FireAlarmsTable, console, focusData);
}
// If no alerts are active, display a message
if (MasterTabContainer.CurrentTab == 0 && activeAlarmCount == 0)
{
var label = new RichTextLabel()
{
HorizontalExpand = true,
VerticalExpand = true,
HorizontalAlignment = HAlignment.Center,
VerticalAlignment = VAlignment.Center,
};
label.SetMarkup(Loc.GetString("atmos-alerts-window-no-active-alerts", ("color", _statusTextColor.ToHexNoAlpha())));
AlertsTable.AddChild(label);
}
// Update the alerts tab with the number of active alerts
if (activeAlarmCount == 0)
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-no-alerts"));
else
MasterTabContainer.SetTabTitle(0, Loc.GetString("atmos-alerts-window-tab-alerts", ("value", activeAlarmCount)));
// Update sensor regions
NavMap.RegionOverlays.Clear();
var prioritizedRegionOverlays = new Dictionary<NavMapRegionOverlay, int>();
if (_owner != null &&
_entManager.TryGetComponent<TransformComponent>(_owner, out var xform) &&
_entManager.TryGetComponent<NavMapComponent>(xform.GridUid, out var navMap))
{
var regionOverlays = _navMapSystem.GetNavMapRegionOverlays(_owner.Value, navMap, AtmosAlertsComputerUiKey.Key);
foreach (var (regionOwner, regionOverlay) in regionOverlays)
{
var alarmState = GetAlarmState(regionOwner);
if (!TryGetSensorRegionColor(regionOwner, alarmState, out var regionColor))
continue;
regionOverlay.Color = regionColor;
var priority = (_trackedEntity == regionOwner) ? 999 : (int)alarmState;
prioritizedRegionOverlays.Add(regionOverlay, priority);
}
// Sort overlays according to their priority
var sortedOverlays = prioritizedRegionOverlays.OrderBy(x => x.Value).Select(x => x.Key).ToList();
NavMap.RegionOverlays = sortedOverlays;
}
// Auto-scroll re-enable
if (_autoScrollAwaitsUpdate)
{
_autoScrollActive = true;
_autoScrollAwaitsUpdate = false;
}
}
private void AddTrackedEntityToNavMap(AtmosAlertsDeviceNavMapData metaData, AtmosAlarmType alarmState)
{
var data = GetBlipTexture(alarmState);
if (data == null)
return;
var texture = data.Value.Item1;
var color = data.Value.Item2;
var coords = _entManager.GetCoordinates(metaData.NetCoordinates);
if (_trackedEntity != null && _trackedEntity != metaData.NetEntity)
color *= _untrackedEntColor;
var selectable = true;
var blip = new NavMapBlip(coords, _spriteSystem.Frame0(texture), color, _trackedEntity == metaData.NetEntity, selectable);
NavMap.TrackedEntities[metaData.NetEntity] = blip;
}
private bool TryGetSensorRegionColor(NetEntity regionOwner, AtmosAlarmType alarmState, out Color color)
{
color = Color.White;
var blip = GetBlipTexture(alarmState);
if (blip == null)
return false;
// Color the region based on alarm state and entity tracking
color = blip.Value.Item2 * _regionBaseColor;
if (_trackedEntity != null && _trackedEntity != regionOwner)
color *= _untrackedEntColor;
return true;
}
private void UpdateUIEntry(AtmosAlertsComputerEntry entry, int index, Control table, AtmosAlertsComputerComponent console, AtmosAlertsFocusDeviceData? focusData = null)
{
// Make new UI entry if required
if (index >= table.ChildCount)
{
var newEntryContainer = new AtmosAlarmEntryContainer(entry.NetEntity, _entManager.GetCoordinates(entry.Coordinates));
// On click
newEntryContainer.FocusButton.OnButtonUp += args =>
{
if (_trackedEntity == newEntryContainer.NetEntity)
{
_trackedEntity = null;
}
else
{
_trackedEntity = newEntryContainer.NetEntity;
if (newEntryContainer.Coordinates != null)
NavMap.CenterToCoordinates(newEntryContainer.Coordinates.Value);
}
// Send message to console that the focus has changed
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
// Update affected UI elements across all tables
UpdateConsoleTable(console, AlertsTable, _trackedEntity);
UpdateConsoleTable(console, AirAlarmsTable, _trackedEntity);
UpdateConsoleTable(console, FireAlarmsTable, _trackedEntity);
};
// On toggling the silence check box
newEntryContainer.SilenceCheckBox.OnToggled += _ => OnSilenceAlertsToggled(newEntryContainer.NetEntity, newEntryContainer.SilenceCheckBox.Pressed);
// Add the entry to the current table
table.AddChild(newEntryContainer);
}
// Update values and UI elements
var tableChild = table.GetChild(index);
if (tableChild is not AtmosAlarmEntryContainer)
{
table.RemoveChild(tableChild);
UpdateUIEntry(entry, index, table, console, focusData);
return;
}
var entryContainer = (AtmosAlarmEntryContainer)tableChild;
entryContainer.UpdateEntry(entry, entry.NetEntity == _trackedEntity, focusData);
if (_trackedEntity != entry.NetEntity)
{
var silenced = console.SilencedDevices;
entryContainer.SilenceCheckBox.Pressed = (silenced.Contains(entry.NetEntity) || _deviceSilencingProgress.ContainsKey(entry.NetEntity));
}
entryContainer.SilenceAlarmProgressBar.Visible = (table == AlertsTable && _deviceSilencingProgress.ContainsKey(entry.NetEntity));
}
private void UpdateConsoleTable(AtmosAlertsComputerComponent console, Control table, NetEntity? currTrackedEntity)
{
foreach (var tableChild in table.Children)
{
if (tableChild is not AtmosAlarmEntryContainer)
continue;
var entryContainer = (AtmosAlarmEntryContainer)tableChild;
if (entryContainer.NetEntity != currTrackedEntity)
entryContainer.RemoveAsFocus();
else if (entryContainer.NetEntity == currTrackedEntity)
entryContainer.SetAsFocus();
}
}
private void SetTrackedEntityFromNavMap(NetEntity? netEntity)
{
if (netEntity == null)
return;
if (!_entManager.TryGetComponent<AtmosAlertsComputerComponent>(_owner, out var console))
return;
_trackedEntity = netEntity;
if (netEntity != null)
{
// Tab switching
if (MasterTabContainer.CurrentTab != 0 || _activeAlarms?.Any(x => x.NetEntity == netEntity) == false)
{
var device = console.AtmosDevices.FirstOrNull(x => x.NetEntity == netEntity);
switch (device?.Group)
{
case AtmosAlertsComputerGroup.AirAlarm:
MasterTabContainer.CurrentTab = 1; break;
case AtmosAlertsComputerGroup.FireAlarm:
MasterTabContainer.CurrentTab = 2; break;
}
}
// Get the scroll position of the selected entity on the selected button the UI
ActivateAutoScrollToFocus();
}
// Send message to console that the focus has changed
SendFocusChangeMessageAction?.Invoke(_trackedEntity);
}
protected override void FrameUpdate(FrameEventArgs args)
{
AutoScrollToFocus();
// Device silencing update
foreach ((var device, var remainingTime) in _deviceSilencingProgress)
{
var t = remainingTime - args.DeltaSeconds;
if (t <= 0)
{
_deviceSilencingProgress.Remove(device);
if (device == _trackedEntity)
_trackedEntity = null;
}
else
_deviceSilencingProgress[device] = t;
}
}
private void ActivateAutoScrollToFocus()
{
_autoScrollActive = false;
_autoScrollAwaitsUpdate = true;
}
private void AutoScrollToFocus()
{
if (!_autoScrollActive)
return;
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer;
if (scroll == null)
return;
if (!TryGetVerticalScrollbar(scroll, out var vScrollbar))
return;
if (!TryGetNextScrollPosition(out float? nextScrollPosition))
return;
vScrollbar.ValueTarget = nextScrollPosition.Value;
if (MathHelper.CloseToPercent(vScrollbar.Value, vScrollbar.ValueTarget))
_autoScrollActive = false;
}
private bool TryGetVerticalScrollbar(ScrollContainer scroll, [NotNullWhen(true)] out VScrollBar? vScrollBar)
{
vScrollBar = null;
foreach (var child in scroll.Children)
{
if (child is not VScrollBar)
continue;
var castChild = child as VScrollBar;
if (castChild != null)
{
vScrollBar = castChild;
return true;
}
}
return false;
}
private bool TryGetNextScrollPosition([NotNullWhen(true)] out float? nextScrollPosition)
{
nextScrollPosition = null;
var scroll = MasterTabContainer.Children.ElementAt(MasterTabContainer.CurrentTab) as ScrollContainer;
if (scroll == null)
return false;
var container = scroll.Children.ElementAt(0) as BoxContainer;
if (container == null || container.Children.Count() == 0)
return false;
// Exit if the heights of the children haven't been initialized yet
if (!container.Children.Any(x => x.Height > 0))
return false;
nextScrollPosition = 0;
foreach (var control in container.Children)
{
if (control == null || control is not AtmosAlarmEntryContainer)
continue;
if (((AtmosAlarmEntryContainer)control).NetEntity == _trackedEntity)
return true;
nextScrollPosition += control.Height;
}
// Failed to find control
nextScrollPosition = null;
return false;
}
private AtmosAlarmType GetAlarmState(NetEntity netEntity)
{
var alarmState = _allAlarms?.FirstOrNull(x => x.NetEntity == netEntity)?.AlarmState;
if (alarmState == null)
return AtmosAlarmType.Invalid;
return alarmState.Value;
}
private (SpriteSpecifier.Texture, Color)? GetBlipTexture(AtmosAlarmType alarmState)
{
(SpriteSpecifier.Texture, Color)? output = null;
switch (alarmState)
{
case AtmosAlarmType.Invalid:
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), _inactiveColor); break;
case AtmosAlarmType.Normal:
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_circle.png")), _goodColor); break;
case AtmosAlarmType.Warning:
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_triangle.png")), _warningColor); break;
case AtmosAlarmType.Danger:
output = (new SpriteSpecifier.Texture(new ResPath("/Textures/Interface/NavMap/beveled_square.png")), _dangerColor); break;
}
return output;
}
}

View File

@@ -1,10 +0,0 @@
using Content.Shared.Atmos.EntitySystems;
using JetBrains.Annotations;
namespace Content.Client.Atmos.EntitySystems;
[UsedImplicitly]
public sealed class GasMinerSystem : SharedGasMinerSystem
{
}

View File

@@ -2,7 +2,6 @@ using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Monitor.Components; using Content.Shared.Atmos.Monitor.Components;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.GameObjects; using Robust.Shared.GameObjects;
using Robust.Shared.IoC; using Robust.Shared.IoC;
using Robust.Shared.Log; using Robust.Shared.Log;
@@ -21,15 +20,23 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_window = this.CreateWindow<AirAlarmWindow>(); _window = new AirAlarmWindow(this);
_window.SetEntity(Owner);
if (State != null)
{
UpdateState(State);
}
_window.OpenCentered();
_window.OnClose += Close;
_window.AtmosDeviceDataChanged += OnDeviceDataChanged; _window.AtmosDeviceDataChanged += OnDeviceDataChanged;
_window.AtmosDeviceDataCopied += OnDeviceDataCopied; _window.AtmosDeviceDataCopied += OnDeviceDataCopied;
_window.AtmosAlarmThresholdChanged += OnThresholdChanged; _window.AtmosAlarmThresholdChanged += OnThresholdChanged;
_window.AirAlarmModeChanged += OnAirAlarmModeChanged; _window.AirAlarmModeChanged += OnAirAlarmModeChanged;
_window.AutoModeChanged += OnAutoModeChanged; _window.AutoModeChanged += OnAutoModeChanged;
_window.ResyncAllRequested += ResyncAllDevices; _window.ResyncAllRequested += ResyncAllDevices;
_window.AirAlarmTabChange += OnTabChanged;
} }
private void ResyncAllDevices() private void ResyncAllDevices()
@@ -62,6 +69,11 @@ public sealed class AirAlarmBoundUserInterface : BoundUserInterface
SendMessage(new AirAlarmUpdateAlarmThresholdMessage(address, type, threshold, gas)); SendMessage(new AirAlarmUpdateAlarmThresholdMessage(address, type, threshold, gas));
} }
private void OnTabChanged(AirAlarmTab tab)
{
SendMessage(new AirAlarmTabSetMessage(tab));
}
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
{ {
base.UpdateState(state); base.UpdateState(state);

View File

@@ -23,6 +23,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
public event Action<AirAlarmMode>? AirAlarmModeChanged; public event Action<AirAlarmMode>? AirAlarmModeChanged;
public event Action<bool>? AutoModeChanged; public event Action<bool>? AutoModeChanged;
public event Action? ResyncAllRequested; public event Action? ResyncAllRequested;
public event Action<AirAlarmTab>? AirAlarmTabChange;
private RichTextLabel _address => CDeviceAddress; private RichTextLabel _address => CDeviceAddress;
private RichTextLabel _deviceTotal => CDeviceTotal; private RichTextLabel _deviceTotal => CDeviceTotal;
@@ -46,7 +47,7 @@ public sealed partial class AirAlarmWindow : FancyWindow
private CheckBox _autoMode => AutoModeCheckBox; private CheckBox _autoMode => AutoModeCheckBox;
public AirAlarmWindow() public AirAlarmWindow(BoundUserInterface owner)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -79,6 +80,11 @@ public sealed partial class AirAlarmWindow : FancyWindow
_tabContainer.SetTabTitle(1, Loc.GetString("air-alarm-ui-window-tab-scrubbers")); _tabContainer.SetTabTitle(1, Loc.GetString("air-alarm-ui-window-tab-scrubbers"));
_tabContainer.SetTabTitle(2, Loc.GetString("air-alarm-ui-window-tab-sensors")); _tabContainer.SetTabTitle(2, Loc.GetString("air-alarm-ui-window-tab-sensors"));
_tabContainer.OnTabChanged += idx =>
{
AirAlarmTabChange!((AirAlarmTab) idx);
};
_resyncDevices.OnPressed += _ => _resyncDevices.OnPressed += _ =>
{ {
_ventDevices.RemoveAllChildren(); _ventDevices.RemoveAllChildren();
@@ -89,11 +95,8 @@ public sealed partial class AirAlarmWindow : FancyWindow
_sensors.Clear(); _sensors.Clear();
ResyncAllRequested!.Invoke(); ResyncAllRequested!.Invoke();
}; };
}
public void SetEntity(EntityUid uid) EntityView.SetEntity(owner.Owner);
{
EntityView.SetEntity(uid);
} }
public void UpdateState(AirAlarmUIState state) public void UpdateState(AirAlarmUIState state)
@@ -111,6 +114,8 @@ public sealed partial class AirAlarmWindow : FancyWindow
{ {
UpdateDeviceData(addr, dev); UpdateDeviceData(addr, dev);
} }
_tabContainer.CurrentTab = (int) state.Tab;
} }
public void UpdateModeSelector(AirAlarmMode mode) public void UpdateModeSelector(AirAlarmMode mode)

View File

@@ -16,8 +16,6 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasAnalyzerWindow : DefaultWindow public sealed partial class GasAnalyzerWindow : DefaultWindow
{ {
private NetEntity _currentEntity = NetEntity.Invalid;
public GasAnalyzerWindow() public GasAnalyzerWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -57,13 +55,6 @@ namespace Content.Client.Atmos.UI
// Device Tab // Device Tab
if (msg.NodeGasMixes.Length > 1) if (msg.NodeGasMixes.Length > 1)
{ {
if (_currentEntity != msg.DeviceUid)
{
// when we get new device data switch to the device tab
CTabContainer.CurrentTab = 0;
_currentEntity = msg.DeviceUid;
}
CTabContainer.SetTabVisible(0, true); CTabContainer.SetTabVisible(0, true);
CTabContainer.SetTabTitle(0, Loc.GetString("gas-analyzer-window-tab-title-capitalized", ("title", msg.DeviceName))); CTabContainer.SetTabTitle(0, Loc.GetString("gas-analyzer-window-tab-title-capitalized", ("title", msg.DeviceName)));
// Set up Grid // Set up Grid
@@ -152,7 +143,6 @@ namespace Content.Client.Atmos.UI
CTabContainer.SetTabVisible(0, false); CTabContainer.SetTabVisible(0, false);
CTabContainer.CurrentTab = 1; CTabContainer.CurrentTab = 1;
minSize = new Vector2(CEnvironmentMix.DesiredSize.X + 40, MinSize.Y); minSize = new Vector2(CEnvironmentMix.DesiredSize.X + 40, MinSize.Y);
_currentEntity = NetEntity.Invalid;
} }
MinSize = minSize; MinSize = minSize;

View File

@@ -1,7 +1,6 @@
using Content.Shared.Atmos.Piping.Binary.Components; using Content.Shared.Atmos.Piping.Binary.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -22,8 +21,14 @@ namespace Content.Client.Atmos.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<GasCanisterWindow>(); _window = new GasCanisterWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ReleaseValveCloseButtonPressed += OnReleaseValveClosePressed; _window.ReleaseValveCloseButtonPressed += OnReleaseValveClosePressed;
_window.ReleaseValveOpenButtonPressed += OnReleaseValveOpenPressed; _window.ReleaseValveOpenButtonPressed += OnReleaseValveOpenPressed;
_window.ReleasePressureSet += OnReleasePressureSet; _window.ReleasePressureSet += OnReleasePressureSet;

View File

@@ -3,7 +3,6 @@ using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components; using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -29,8 +28,14 @@ namespace Content.Client.Atmos.UI
var atmosSystem = EntMan.System<AtmosphereSystem>(); var atmosSystem = EntMan.System<AtmosphereSystem>();
_window = this.CreateWindow<GasFilterWindow>(); _window = new GasFilterWindow(atmosSystem.Gases);
_window.PopulateGasList(atmosSystem.Gases);
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.FilterTransferRateChanged += OnFilterTransferRatePressed; _window.FilterTransferRateChanged += OnFilterTransferRatePressed;

View File

@@ -26,9 +26,10 @@ namespace Content.Client.Atmos.UI
public event Action<string>? FilterTransferRateChanged; public event Action<string>? FilterTransferRateChanged;
public event Action? SelectGasPressed; public event Action? SelectGasPressed;
public GasFilterWindow() public GasFilterWindow(IEnumerable<GasPrototype> gases)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
PopulateGasList(gases);
ToggleStatusButton.OnPressed += _ => SetFilterStatus(!FilterStatus); ToggleStatusButton.OnPressed += _ => SetFilterStatus(!FilterStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke(); ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
@@ -72,7 +73,7 @@ namespace Content.Client.Atmos.UI
SelectGasButton.Disabled = true; SelectGasButton.Disabled = true;
} }
public void PopulateGasList(IEnumerable<GasPrototype> gases) private void PopulateGasList(IEnumerable<GasPrototype> gases)
{ {
GasList.Add(new ItemList.Item(GasList) GasList.Add(new ItemList.Item(GasList)
{ {
@@ -80,7 +81,7 @@ namespace Content.Client.Atmos.UI
Text = Loc.GetString("comp-gas-filter-ui-filter-gas-none") Text = Loc.GetString("comp-gas-filter-ui-filter-gas-none")
}); });
foreach (var gas in gases) foreach (GasPrototype gas in gases)
{ {
var gasName = Loc.GetString(gas.Name); var gasName = Loc.GetString(gas.Name);
GasList.Add(GetGasItem(gas.ID, gasName, GasList)); GasList.Add(GetGasItem(gas.ID, gasName, GasList));

View File

@@ -2,7 +2,7 @@ using Content.Shared.Atmos;
using Content.Shared.Atmos.Piping.Trinary.Components; using Content.Shared.Atmos.Piping.Trinary.Components;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface; using Robust.Client.GameObjects;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -26,7 +26,14 @@ namespace Content.Client.Atmos.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<GasMixerWindow>(); _window = new GasMixerWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.MixerOutputPressureChanged += OnMixerOutputPressurePressed; _window.MixerOutputPressureChanged += OnMixerOutputPressurePressed;
@@ -76,5 +83,12 @@ namespace Content.Client.Atmos.UI
_window.SetOutputPressure(cast.OutputPressure); _window.SetOutputPressure(cast.OutputPressure);
_window.SetNodePercentages(cast.NodeOne); _window.SetNodePercentages(cast.NodeOne);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
} }
} }

View File

@@ -3,7 +3,6 @@ using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -27,7 +26,14 @@ namespace Content.Client.Atmos.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<GasPressurePumpWindow>(); _window = new GasPressurePumpWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.PumpOutputPressureChanged += OnPumpOutputPressurePressed; _window.PumpOutputPressureChanged += OnPumpOutputPressurePressed;
@@ -61,5 +67,12 @@ namespace Content.Client.Atmos.UI
_window.SetPumpStatus(cast.Enabled); _window.SetPumpStatus(cast.Enabled);
_window.SetOutputPressure(cast.OutputPressure); _window.SetOutputPressure(cast.OutputPressure);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
} }
} }

View File

@@ -2,7 +2,6 @@
using Content.Shared.Atmos.Piping.Unary.Components; using Content.Shared.Atmos.Piping.Unary.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -32,7 +31,14 @@ namespace Content.Client.Atmos.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<GasThermomachineWindow>(); _window = new GasThermomachineWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value); _window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value);
@@ -85,5 +91,12 @@ namespace Content.Client.Atmos.UI
true => Loc.GetString("comp-gas-thermomachine-ui-title-heater") true => Loc.GetString("comp-gas-thermomachine-ui-title-heater")
}; };
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
} }
} }

View File

@@ -3,7 +3,6 @@ using Content.Shared.Atmos.Piping.Binary.Components;
using Content.Shared.Localizations; using Content.Shared.Localizations;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
{ {
@@ -27,7 +26,14 @@ namespace Content.Client.Atmos.UI
{ {
base.Open(); base.Open();
_window = this.CreateWindow<GasVolumePumpWindow>(); _window = new GasVolumePumpWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed; _window.ToggleStatusButtonPressed += OnToggleStatusButtonPressed;
_window.PumpTransferRateChanged += OnPumpTransferRatePressed; _window.PumpTransferRateChanged += OnPumpTransferRatePressed;
@@ -58,9 +64,16 @@ namespace Content.Client.Atmos.UI
if (_window == null || state is not GasVolumePumpBoundUserInterfaceState cast) if (_window == null || state is not GasVolumePumpBoundUserInterfaceState cast)
return; return;
_window.Title = cast.PumpLabel; _window.Title = (cast.PumpLabel);
_window.SetPumpStatus(cast.Enabled); _window.SetPumpStatus(cast.Enabled);
_window.SetTransferRate(cast.TransferRate); _window.SetTransferRate(cast.TransferRate);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing) return;
_window?.Dispose();
}
} }
} }

View File

@@ -1,6 +1,5 @@
using Content.Shared.Atmos.Piping.Portable.Components; using Content.Shared.Atmos.Piping.Portable.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
namespace Content.Client.Atmos.UI; namespace Content.Client.Atmos.UI;
@@ -22,7 +21,14 @@ public sealed class SpaceHeaterBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_window = this.CreateWindow<SpaceHeaterWindow>(); _window = new SpaceHeaterWindow();
if (State != null)
UpdateState(State);
_window.OpenCentered();
_window.OnClose += Close;
_window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta); _window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta);

View File

@@ -57,7 +57,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
/// </summary> /// </summary>
private int MaxSingleSound => (int) (_maxAmbientCount / (16.0f / 6.0f)); private int MaxSingleSound => (int) (_maxAmbientCount / (16.0f / 6.0f));
private readonly Dictionary<Entity<AmbientSoundComponent>, (EntityUid? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new(); private readonly Dictionary<AmbientSoundComponent, (EntityUid? Stream, SoundSpecifier Sound, string Path)> _playingSounds = new();
private readonly Dictionary<string, int> _playingCount = new(); private readonly Dictionary<string, int> _playingCount = new();
public bool OverlayEnabled public bool OverlayEnabled
@@ -107,7 +107,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
private void OnShutdown(EntityUid uid, AmbientSoundComponent component, ComponentShutdown args) private void OnShutdown(EntityUid uid, AmbientSoundComponent component, ComponentShutdown args)
{ {
if (!_playingSounds.Remove((uid, component), out var sound)) if (!_playingSounds.Remove(component, out var sound))
return; return;
_audio.Stop(sound.Stream); _audio.Stop(sound.Stream);
@@ -120,13 +120,13 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
{ {
_ambienceVolume = SharedAudioSystem.GainToVolume(value); _ambienceVolume = SharedAudioSystem.GainToVolume(value);
foreach (var (ent, values) in _playingSounds) foreach (var (comp, values) in _playingSounds)
{ {
if (values.Stream == null) if (values.Stream == null)
continue; continue;
var stream = values.Stream; var stream = values.Stream;
_audio.SetVolume(stream, _params.Volume + ent.Comp.Volume + _ambienceVolume); _audio.SetVolume(stream, _params.Volume + comp.Volume + _ambienceVolume);
} }
} }
private void SetCooldown(float value) => _cooldown = value; private void SetCooldown(float value) => _cooldown = value;
@@ -165,7 +165,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (_gameTiming.CurTime < _targetTime) if (_gameTiming.CurTime < _targetTime)
return; return;
_targetTime = _gameTiming.CurTime + TimeSpan.FromSeconds(_cooldown); _targetTime = _gameTiming.CurTime+TimeSpan.FromSeconds(_cooldown);
var player = _playerManager.LocalEntity; var player = _playerManager.LocalEntity;
if (!EntityManager.TryGetComponent(player, out TransformComponent? xform)) if (!EntityManager.TryGetComponent(player, out TransformComponent? xform))
@@ -190,7 +190,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
private readonly struct QueryState private readonly struct QueryState
{ {
public readonly Dictionary<string, List<(float Importance, Entity<AmbientSoundComponent>)>> SourceDict = new(); public readonly Dictionary<string, List<(float Importance, AmbientSoundComponent)>> SourceDict = new();
public readonly Vector2 MapPos; public readonly Vector2 MapPos;
public readonly TransformComponent Player; public readonly TransformComponent Player;
public readonly SharedTransformSystem TransformSystem; public readonly SharedTransformSystem TransformSystem;
@@ -224,11 +224,11 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (ambientComp.Sound is SoundPathSpecifier path) if (ambientComp.Sound is SoundPathSpecifier path)
key = path.Path.ToString(); key = path.Path.ToString();
else else
key = ((SoundCollectionSpecifier)ambientComp.Sound).Collection ?? string.Empty; key = ((SoundCollectionSpecifier) ambientComp.Sound).Collection ?? string.Empty;
// Prioritize far away & loud sounds. // Prioritize far away & loud sounds.
var importance = range * (ambientComp.Volume + 32); var importance = range * (ambientComp.Volume + 32);
state.SourceDict.GetOrNew(key).Add((importance, (value.Uid, ambientComp))); state.SourceDict.GetOrNew(key).Add((importance, ambientComp));
return true; return true;
} }
@@ -242,18 +242,16 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
var mapPos = _xformSystem.GetMapCoordinates(playerXform); var mapPos = _xformSystem.GetMapCoordinates(playerXform);
// Remove out-of-range ambiences // Remove out-of-range ambiences
foreach (var (ent, sound) in _playingSounds) foreach (var (comp, sound) in _playingSounds)
{ {
//var entity = comp.Owner; var entity = comp.Owner;
var owner = ent.Owner;
var comp = ent.Comp;
if (comp.Enabled && if (comp.Enabled &&
// Don't keep playing sounds that have changed since. // Don't keep playing sounds that have changed since.
sound.Sound == comp.Sound && sound.Sound == comp.Sound &&
query.TryGetComponent(owner, out var xform) && query.TryGetComponent(entity, out var xform) &&
xform.MapID == playerXform.MapID && xform.MapID == playerXform.MapID &&
!metaQuery.GetComponent(owner).EntityPaused) !metaQuery.GetComponent(entity).EntityPaused)
{ {
// TODO: This is just trydistance for coordinates. // TODO: This is just trydistance for coordinates.
var distance = (xform.ParentUid == playerXform.ParentUid) var distance = (xform.ParentUid == playerXform.ParentUid)
@@ -265,7 +263,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
} }
_audio.Stop(sound.Stream); _audio.Stop(sound.Stream);
_playingSounds.Remove(ent); _playingSounds.Remove(comp);
_playingCount[sound.Path] -= 1; _playingCount[sound.Path] -= 1;
if (_playingCount[sound.Path] == 0) if (_playingCount[sound.Path] == 0)
_playingCount.Remove(sound.Path); _playingCount.Remove(sound.Path);
@@ -280,7 +278,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
_treeSys.QueryAabb(ref state, Callback, mapPos.MapId, worldAabb); _treeSys.QueryAabb(ref state, Callback, mapPos.MapId, worldAabb);
// Add in range ambiences // Add in range ambiences
foreach (var (key, sourceList) in state.SourceDict) foreach (var (key, sources) in state.SourceDict)
{ {
if (_playingSounds.Count >= _maxAmbientCount) if (_playingSounds.Count >= _maxAmbientCount)
break; break;
@@ -288,14 +286,13 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
if (_playingCount.TryGetValue(key, out var playingCount) && playingCount >= MaxSingleSound) if (_playingCount.TryGetValue(key, out var playingCount) && playingCount >= MaxSingleSound)
continue; continue;
sourceList.Sort(static (a, b) => b.Importance.CompareTo(a.Importance)); sources.Sort(static (a, b) => b.Importance.CompareTo(a.Importance));
foreach (var (_, sourceEntity) in sourceList) foreach (var (_, comp) in sources)
{ {
var uid = sourceEntity.Owner; var uid = comp.Owner;
var comp = sourceEntity.Comp;
if (_playingSounds.ContainsKey(sourceEntity) || if (_playingSounds.ContainsKey(comp) ||
metaQuery.GetComponent(uid).EntityPaused) metaQuery.GetComponent(uid).EntityPaused)
continue; continue;
@@ -306,10 +303,7 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
.WithMaxDistance(comp.Range); .WithMaxDistance(comp.Range);
var stream = _audio.PlayEntity(comp.Sound, Filter.Local(), uid, false, audioParams); var stream = _audio.PlayEntity(comp.Sound, Filter.Local(), uid, false, audioParams);
if (stream == null) _playingSounds[comp] = (stream.Value.Entity, comp.Sound, key);
continue;
_playingSounds[sourceEntity] = (stream.Value.Entity, comp.Sound, key);
playingCount++; playingCount++;
if (_playingSounds.Count >= _maxAmbientCount) if (_playingSounds.Count >= _maxAmbientCount)

View File

@@ -54,7 +54,7 @@ public sealed class AudioUIController : UIController
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
{ {
var resource = GetSoundOrFallback(value, CCVars.UIClickSound.DefaultValue); var resource = _cache.GetResource<AudioResource>(value);
var source = var source =
_audioManager.CreateAudioSource(resource); _audioManager.CreateAudioSource(resource);
@@ -77,7 +77,7 @@ public sealed class AudioUIController : UIController
{ {
if (!string.IsNullOrEmpty(value)) if (!string.IsNullOrEmpty(value))
{ {
var hoverResource = GetSoundOrFallback(value, CCVars.UIHoverSound.DefaultValue); var hoverResource = _cache.GetResource<AudioResource>(value);
var hoverSource = var hoverSource =
_audioManager.CreateAudioSource(hoverResource); _audioManager.CreateAudioSource(hoverResource);
@@ -95,12 +95,4 @@ public sealed class AudioUIController : UIController
UIManager.SetHoverSound(null); UIManager.SetHoverSound(null);
} }
} }
private AudioResource GetSoundOrFallback(string path, string fallback)
{
if (!_cache.TryGetResource(path, out AudioResource? resource))
return _cache.GetResource<AudioResource>(fallback);
return resource;
}
} }

View File

@@ -67,7 +67,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
if(!_adminAudioEnabled) return; if(!_adminAudioEnabled) return;
var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams); var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams);
_adminAudio.Add(stream?.Entity); _adminAudio.Add(stream.Value.Entity);
} }
private void PlayStationEventMusic(StationEventMusicEvent soundEvent) private void PlayStationEventMusic(StationEventMusicEvent soundEvent)
@@ -76,7 +76,7 @@ public sealed class ClientGlobalSoundSystem : SharedGlobalSoundSystem
if(!_eventAudioEnabled || _eventAudio.ContainsKey(soundEvent.Type)) return; if(!_eventAudioEnabled || _eventAudio.ContainsKey(soundEvent.Type)) return;
var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams); var stream = _audio.PlayGlobal(soundEvent.Filename, Filter.Local(), false, soundEvent.AudioParams);
_eventAudio.Add(soundEvent.Type, stream?.Entity); _eventAudio.Add(soundEvent.Type, stream.Value.Entity);
} }
private void PlayGameSound(GameGlobalSoundEvent soundEvent) private void PlayGameSound(GameGlobalSoundEvent soundEvent)

View File

@@ -4,7 +4,6 @@ using Content.Shared.Audio;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.GameTicking; using Content.Shared.GameTicking;
using Content.Shared.Random; using Content.Shared.Random;
using Content.Shared.Random.Rules;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Player; using Robust.Client.Player;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
@@ -117,7 +116,6 @@ public sealed partial class ContentAudioSystem
private void OnRoundEndMessage(RoundEndMessageEvent ev) private void OnRoundEndMessage(RoundEndMessageEvent ev)
{ {
OnRoundEndMessageAmbientLoop(); //CP14
// If scoreboard shows then just stop the music // If scoreboard shows then just stop the music
_ambientMusicStream = _audio.Stop(_ambientMusicStream); _ambientMusicStream = _audio.Stop(_ambientMusicStream);
_nextAudio = TimeSpan.FromMinutes(3); _nextAudio = TimeSpan.FromMinutes(3);
@@ -214,9 +212,9 @@ public sealed partial class ContentAudioSystem
false, false,
AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider)); AudioParams.Default.WithVolume(_musicProto.Sound.Params.Volume + _volumeSlider));
_ambientMusicStream = strim?.Entity; _ambientMusicStream = strim.Value.Entity;
if (_musicProto.FadeIn && strim != null) if (_musicProto.FadeIn)
{ {
FadeIn(_ambientMusicStream, strim.Value.Component, AmbientMusicFadeTime); FadeIn(_ambientMusicStream, strim.Value.Component, AmbientMusicFadeTime);
} }

View File

@@ -1,128 +0,0 @@
using System.Linq;
using Content.Client.Gameplay;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
namespace Content.Client.Audio;
public sealed partial class ContentAudioSystem
{
private const float AmbientLoopFadeInTime = 1f;
private const float AmbientLoopFadeOutTime = 4f;
private Dictionary<CP14AmbientLoopPrototype, EntityUid> _loopStreams = new();
private TimeSpan _nextUpdateTime = TimeSpan.Zero;
private readonly TimeSpan _updateFrequency = TimeSpan.FromSeconds(1f);
private void CP14InitializeAmbientLoops()
{
Subs.CVar(_configManager, CCVars.AmbientMusicVolume, AmbienceCVarChangedAmbientMusic, true);
}
private void AmbienceCVarChangedAmbientMusic(float obj)
{
_volumeSlider = SharedAudioSystem.GainToVolume(obj);
foreach (var loop in _loopStreams)
{
_audio.SetVolume(loop.Value, loop.Key.Sound.Params.Volume + _volumeSlider);
}
}
private void OnRoundEndMessageAmbientLoop()
{
foreach (var loop in _loopStreams)
{
StopAmbientLoop(loop.Key);
}
}
private void CP14UpdateAmbientLoops()
{
return; //DISABLED UNTIL CLIENT ERROR SPAM FIXED
if (_timing.CurTime <= _nextUpdateTime)
return;
_nextUpdateTime = _timing.CurTime + _updateFrequency;
if (_state.CurrentState is not GameplayState)
return;
var requiredLoops = GetAmbientLoops();
foreach (var loop in _loopStreams)
{
if (!requiredLoops.Contains(loop.Key)) //If ambient is playing and it shouldn't, stop it.
StopAmbientLoop(loop.Key);
}
foreach (var loop in requiredLoops)
{
if (!_loopStreams.ContainsKey(loop)) //If it's not playing, but should, run it
StartAmbientLoop(loop);
}
}
private void StartAmbientLoop(CP14AmbientLoopPrototype proto)
{
if (_loopStreams.ContainsKey(proto))
return;
var newLoop = _audio.PlayGlobal(
proto.Sound,
Filter.Local(),
false,
AudioParams.Default
.WithLoop(true)
.WithVolume(proto.Sound.Params.Volume + _volumeSlider)
.WithPlayOffset(_random.NextFloat(0f, 100f)));
if (newLoop is null)
return;
_loopStreams.Add(proto, newLoop.Value.Entity);
FadeIn(newLoop.Value.Entity, newLoop.Value.Component, AmbientLoopFadeInTime);
}
private void StopAmbientLoop(CP14AmbientLoopPrototype proto)
{
if (!_loopStreams.TryGetValue(proto, out var audioEntity))
return;
FadeOut(audioEntity, duration: AmbientLoopFadeOutTime);
_loopStreams.Remove(proto);
}
/// <summary>
/// Checks the player's environment, and returns a list of all ambients that should currently be playing around the player
/// </summary>
/// <returns></returns>
private List<CP14AmbientLoopPrototype> GetAmbientLoops()
{
List<CP14AmbientLoopPrototype> list = new();
var player = _player.LocalEntity;
if (player == null)
return list;
var ambientLoops = _proto.EnumeratePrototypes<CP14AmbientLoopPrototype>().ToList();
foreach (var loop in ambientLoops)
{
if (_rules.IsTrue(player.Value, _proto.Index(loop.Rules)))
{
list.Add(loop);
}
}
return list;
}
}

View File

@@ -20,6 +20,7 @@ public sealed partial class ContentAudioSystem
{ {
[Dependency] private readonly IBaseClient _client = default!; [Dependency] private readonly IBaseClient _client = default!;
[Dependency] private readonly ClientGameTicker _gameTicker = default!; [Dependency] private readonly ClientGameTicker _gameTicker = default!;
[Dependency] private readonly IStateManager _stateManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!; [Dependency] private readonly IResourceCache _resourceCache = default!;
private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f); private readonly AudioParams _lobbySoundtrackParams = new(-5f, 1, 0, 0, 0, false, 0f);
@@ -70,7 +71,7 @@ public sealed partial class ContentAudioSystem
Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged); Subs.CVar(_configManager, CCVars.LobbyMusicEnabled, LobbyMusicCVarChanged);
Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged); Subs.CVar(_configManager, CCVars.LobbyMusicVolume, LobbyMusicVolumeCVarChanged);
_state.OnStateChanged += StateManagerOnStateChanged; _stateManager.OnStateChanged += StateManagerOnStateChanged;
_client.PlayerLeaveServer += OnLeave; _client.PlayerLeaveServer += OnLeave;
@@ -114,7 +115,7 @@ public sealed partial class ContentAudioSystem
private void LobbyMusicCVarChanged(bool musicEnabled) private void LobbyMusicCVarChanged(bool musicEnabled)
{ {
if (musicEnabled && _state.CurrentState is LobbyState) if (musicEnabled && _stateManager.CurrentState is LobbyState)
{ {
StartLobbyMusic(); StartLobbyMusic();
} }
@@ -184,7 +185,7 @@ public sealed partial class ContentAudioSystem
false, false,
_lobbySoundtrackParams.WithVolume(_lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume))) _lobbySoundtrackParams.WithVolume(_lobbySoundtrackParams.Volume + SharedAudioSystem.GainToVolume(_configManager.GetCVar(CCVars.LobbyMusicVolume)))
); );
if (playResult == null) if (playResult.Value.Entity == default)
{ {
_sawmill.Warning( _sawmill.Warning(
$"Tried to play lobby soundtrack '{{Filename}}' using {nameof(SharedAudioSystem)}.{nameof(SharedAudioSystem.PlayGlobal)} but it returned default value of EntityUid!", $"Tried to play lobby soundtrack '{{Filename}}' using {nameof(SharedAudioSystem)}.{nameof(SharedAudioSystem.PlayGlobal)} but it returned default value of EntityUid!",
@@ -233,7 +234,7 @@ public sealed partial class ContentAudioSystem
private void ShutdownLobbyMusic() private void ShutdownLobbyMusic()
{ {
_state.OnStateChanged -= StateManagerOnStateChanged; _stateManager.OnStateChanged -= StateManagerOnStateChanged;
_client.PlayerLeaveServer -= OnLeave; _client.PlayerLeaveServer -= OnLeave;

View File

@@ -29,14 +29,13 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
public const float AmbientMusicMultiplier = 3f; public const float AmbientMusicMultiplier = 3f;
public const float LobbyMultiplier = 3f; public const float LobbyMultiplier = 3f;
public const float InterfaceMultiplier = 2f; public const float InterfaceMultiplier = 2f;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
UpdatesOutsidePrediction = true; UpdatesOutsidePrediction = true;
InitializeAmbientMusic(); InitializeAmbientMusic();
CP14InitializeAmbientLoops(); //CP14 ambient loops
InitializeLobbyMusic(); InitializeLobbyMusic();
SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundCleanup); SubscribeNetworkEvent<RoundRestartCleanupEvent>(OnRoundCleanup);
} }
@@ -83,7 +82,6 @@ public sealed partial class ContentAudioSystem : SharedContentAudioSystem
return; return;
UpdateAmbientMusic(); UpdateAmbientMusic();
CP14UpdateAmbientLoops(); //CP14
UpdateLobbyMusic(); UpdateLobbyMusic();
UpdateFades(frameTime); UpdateFades(frameTime);
} }

View File

@@ -1,7 +1,8 @@
using Content.Shared.Audio.Jukebox; using Content.Shared.Audio.Jukebox;
using Robust.Client.Audio; using Robust.Client.Audio;
using Robust.Client.UserInterface; using Robust.Client.Player;
using Robust.Shared.Audio.Components; using Robust.Shared.Audio.Components;
using Robust.Shared.Player;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Audio.Jukebox; namespace Content.Client.Audio.Jukebox;
@@ -22,7 +23,9 @@ public sealed class JukeboxBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<JukeboxMenu>(); _menu = new JukeboxMenu();
_menu.OnClose += Close;
_menu.OpenCentered();
_menu.OnPlayPressed += args => _menu.OnPlayPressed += args =>
{ {
@@ -97,5 +100,19 @@ public sealed class JukeboxBoundUserInterface : BoundUserInterface
SendMessage(new JukeboxSetTimeMessage(sentTime)); SendMessage(new JukeboxSetTimeMessage(sentTime));
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
if (_menu == null)
return;
_menu.OnClose -= Close;
_menu.Dispose();
_menu = null;
}
} }

View File

@@ -1,4 +1,3 @@
using Content.Client.BarSign.Ui;
using Content.Shared.BarSign; using Content.Shared.BarSign;
using Content.Shared.Power; using Content.Shared.Power;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
@@ -9,7 +8,6 @@ namespace Content.Client.BarSign;
public sealed class BarSignSystem : VisualizerSystem<BarSignComponent> public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly UserInterfaceSystem _ui = default!;
public override void Initialize() public override void Initialize()
{ {
@@ -19,9 +17,6 @@ public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
private void OnAfterAutoHandleState(EntityUid uid, BarSignComponent component, ref AfterAutoHandleStateEvent args) private void OnAfterAutoHandleState(EntityUid uid, BarSignComponent component, ref AfterAutoHandleStateEvent args)
{ {
if (_ui.TryGetOpenUi<BarSignBoundUserInterface>(uid, BarSignUiKey.Key, out var bui))
bui.Update(component.Current);
UpdateAppearance(uid, component); UpdateAppearance(uid, component);
} }
@@ -39,9 +34,9 @@ public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
if (powered if (powered
&& sign.Current != null && sign.Current != null
&& _prototypeManager.TryIndex(sign.Current, out var proto)) && _prototypeManager.TryIndex(sign.Current, out BarSignPrototype? proto))
{ {
sprite.LayerSetSprite(0, proto.Icon); sprite.LayerSetState(0, proto.Icon);
sprite.LayerSetShader(0, "unshaded"); sprite.LayerSetShader(0, "unshaded");
} }
else else

View File

@@ -1,50 +0,0 @@
using System.Linq;
using Content.Shared.BarSign;
using JetBrains.Annotations;
using Robust.Shared.Prototypes;
namespace Content.Client.BarSign.Ui;
[UsedImplicitly]
public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
[Dependency] private readonly IPrototypeManager _prototype = default!;
private BarSignMenu? _menu;
protected override void Open()
{
base.Open();
var sign = EntMan.GetComponentOrNull<BarSignComponent>(Owner)?.Current is { } current
? _prototype.Index(current)
: null;
var allSigns = Shared.BarSign.BarSignSystem.GetAllBarSigns(_prototype)
.OrderBy(p => Loc.GetString(p.Name))
.ToList();
_menu = new(sign, allSigns);
_menu.OnSignSelected += id =>
{
SendMessage(new SetBarSignMessage(id));
};
_menu.OnClose += Close;
_menu.OpenCentered();
}
public void Update(ProtoId<BarSignPrototype>? sign)
{
if (_prototype.TryIndex(sign, out var signPrototype))
_menu?.UpdateState(signPrototype);
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
}

View File

@@ -1,19 +0,0 @@
<controls:FancyWindow
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
Title="{Loc 'barsign-ui-menu'}"
MinSize="280 180"
SetSize="280 180">
<BoxContainer VerticalExpand="True" VerticalAlignment="Center" HorizontalAlignment="Center" Margin="10 5 10 10">
<BoxContainer Orientation="Vertical" VerticalAlignment="Center" HorizontalAlignment="Center">
<BoxContainer Orientation="Vertical" HorizontalAlignment="Center">
<Label Text="{Loc 'barsign-ui-set-label'}" HorizontalAlignment="Center" StyleClasses="LabelSubText" Margin="30 0"/>
<customControls:HSeparator Margin="0 0 0 5"/>
</BoxContainer>
<OptionButton Name="SignOptions" HorizontalAlignment="Center" VerticalAlignment="Center" MinSize="175 60" Margin="0 0 0 20"/>
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View File

@@ -1,50 +0,0 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.BarSign;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.BarSign.Ui;
[GenerateTypedNameReferences]
public sealed partial class BarSignMenu : FancyWindow
{
private string? _currentId;
private readonly List<BarSignPrototype> _cachedPrototypes = new();
public event Action<string>? OnSignSelected;
public BarSignMenu(BarSignPrototype? currentSign, List<BarSignPrototype> signs)
{
RobustXamlLoader.Load(this);
_currentId = currentSign?.ID;
_cachedPrototypes.Clear();
_cachedPrototypes = signs;
foreach (var proto in _cachedPrototypes)
{
SignOptions.AddItem(Loc.GetString(proto.Name));
}
SignOptions.OnItemSelected += idx =>
{
OnSignSelected?.Invoke(_cachedPrototypes[idx.Id].ID);
SignOptions.SelectId(idx.Id);
};
if (currentSign != null)
{
var idx = _cachedPrototypes.IndexOf(currentSign);
SignOptions.TrySelectId(idx);
}
}
public void UpdateState(BarSignPrototype newSign)
{
if (_currentId != null && newSign.ID == _currentId)
return;
_currentId = newSign.ID;
var idx = _cachedPrototypes.IndexOf(newSign);
SignOptions.TrySelectId(idx);
}
}

View File

@@ -1,6 +1,5 @@
using Content.Shared.Bed.Cryostorage; using Content.Shared.Bed.Cryostorage;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Bed.Cryostorage; namespace Content.Client.Bed.Cryostorage;
@@ -18,7 +17,9 @@ public sealed class CryostorageBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<CryostorageMenu>(); _menu = new();
_menu.OnClose += Close;
_menu.SlotRemoveButtonPressed += (ent, slot) => _menu.SlotRemoveButtonPressed += (ent, slot) =>
{ {
@@ -29,6 +30,8 @@ public sealed class CryostorageBoundUserInterface : BoundUserInterface
{ {
SendMessage(new CryostorageRemoveItemBuiMessage(ent, hand, CryostorageRemoveItemBuiMessage.RemovalType.Hand)); SendMessage(new CryostorageRemoveItemBuiMessage(ent, hand, CryostorageRemoveItemBuiMessage.RemovalType.Hand));
}; };
_menu.OpenCentered();
} }
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
@@ -42,4 +45,12 @@ public sealed class CryostorageBoundUserInterface : BoundUserInterface
break; break;
} }
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
} }

View File

@@ -15,6 +15,7 @@ internal sealed class BuckleSystem : SharedBuckleSystem
{ {
base.Initialize(); base.Initialize();
SubscribeLocalEvent<BuckleComponent, ComponentHandleState>(OnHandleState);
SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange); SubscribeLocalEvent<BuckleComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent); SubscribeLocalEvent<StrapComponent, MoveEvent>(OnStrapMoveEvent);
} }
@@ -56,6 +57,21 @@ internal sealed class BuckleSystem : SharedBuckleSystem
} }
} }
private void OnHandleState(Entity<BuckleComponent> ent, ref ComponentHandleState args)
{
if (args.Current is not BuckleState state)
return;
ent.Comp.DontCollide = state.DontCollide;
ent.Comp.BuckleTime = state.BuckleTime;
var strapUid = EnsureEntity<BuckleComponent>(state.BuckledTo, ent);
SetBuckledTo(ent, strapUid == null ? null : new (strapUid.Value, null));
var (uid, component) = ent;
}
private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args) private void OnAppearanceChange(EntityUid uid, BuckleComponent component, ref AppearanceChangeEvent args)
{ {
if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals)) if (!TryComp<RotationVisualsComponent>(uid, out var rotVisuals))

View File

@@ -1,7 +1,6 @@
using Content.Client.Cargo.UI; using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Cargo.BUI; namespace Content.Client.Cargo.BUI;
@@ -19,7 +18,9 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<CargoBountyMenu>(); _menu = new();
_menu.OnClose += Close;
_menu.OnLabelButtonPressed += id => _menu.OnLabelButtonPressed += id =>
{ {
@@ -30,6 +31,8 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
{ {
SendMessage(new BountySkipMessage(id)); SendMessage(new BountySkipMessage(id));
}; };
_menu.OpenCentered();
} }
protected override void UpdateState(BoundUserInterfaceState message) protected override void UpdateState(BoundUserInterfaceState message)
@@ -41,4 +44,14 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
_menu?.UpdateEntries(state.Bounties, state.UntilNextSkip); _menu?.UpdateEntries(state.Bounties, state.UntilNextSkip);
} }
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (!disposing)
return;
_menu?.Dispose();
}
} }

View File

@@ -2,7 +2,6 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.BUI;
using Content.Shared.Cargo.Events; using Content.Shared.Cargo.Events;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.Cargo.BUI; namespace Content.Client.Cargo.BUI;
@@ -19,9 +18,21 @@ public sealed class CargoPalletConsoleBoundUserInterface : BoundUserInterface
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<CargoPalletMenu>(); _menu = new CargoPalletMenu();
_menu.AppraiseRequested += OnAppraisal; _menu.AppraiseRequested += OnAppraisal;
_menu.SellRequested += OnSell; _menu.SellRequested += OnSell;
_menu.OnClose += Close;
_menu.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_menu?.Dispose();
}
} }
private void OnAppraisal() private void OnAppraisal()

View File

@@ -2,7 +2,6 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.BUI; using Content.Shared.Cargo.BUI;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.Cargo.BUI; namespace Content.Client.Cargo.BUI;
@@ -10,8 +9,6 @@ namespace Content.Client.Cargo.BUI;
[UsedImplicitly] [UsedImplicitly]
public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
{ {
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[ViewVariables] [ViewVariables]
private CargoShuttleMenu? _menu; private CargoShuttleMenu? _menu;
@@ -22,7 +19,24 @@ public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
protected override void Open() protected override void Open()
{ {
base.Open(); base.Open();
_menu = this.CreateWindow<CargoShuttleMenu>(); var collection = IoCManager.Instance;
if (collection == null)
return;
_menu = new CargoShuttleMenu(collection.Resolve<IPrototypeManager>(), collection.Resolve<IEntitySystemManager>().GetEntitySystem<SpriteSystem>());
_menu.OnClose += Close;
_menu.OpenCentered();
}
protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
{
_menu?.Dispose();
}
} }
protected override void UpdateState(BoundUserInterfaceState state) protected override void UpdateState(BoundUserInterfaceState state)
@@ -31,6 +45,6 @@ public sealed class CargoShuttleConsoleBoundUserInterface : BoundUserInterface
if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return; if (state is not CargoShuttleConsoleBoundUserInterfaceState cargoState) return;
_menu?.SetAccountName(cargoState.AccountName); _menu?.SetAccountName(cargoState.AccountName);
_menu?.SetShuttleName(cargoState.ShuttleName); _menu?.SetShuttleName(cargoState.ShuttleName);
_menu?.SetOrders(EntMan.System<SpriteSystem>(), _protoManager, cargoState.Orders); _menu?.SetOrders(cargoState.Orders);
} }
} }

View File

@@ -1,21 +0,0 @@
using Content.Shared.Timing;
using Content.Shared.Cargo.Systems;
namespace Content.Client.Cargo.Systems;
/// <summary>
/// This handles...
/// </summary>
public sealed class ClientPriceGunSystem : SharedPriceGunSystem
{
[Dependency] private readonly UseDelaySystem _useDelay = default!;
protected override bool GetPriceOrBounty(EntityUid priceGunUid, EntityUid target, EntityUid user)
{
if (!TryComp(priceGunUid, out UseDelayComponent? useDelay) || _useDelay.IsDelayed((priceGunUid, useDelay)))
return false;
// It feels worse if the cooldown is predicted but the popup isn't! So only do the cooldown reset on the server.
return true;
}
}

View File

@@ -12,9 +12,14 @@ namespace Content.Client.Cargo.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class CargoShuttleMenu : FancyWindow public sealed partial class CargoShuttleMenu : FancyWindow
{ {
public CargoShuttleMenu() private readonly IPrototypeManager _protoManager;
private readonly SpriteSystem _spriteSystem;
public CargoShuttleMenu(IPrototypeManager protoManager, SpriteSystem spriteSystem)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_protoManager = protoManager;
_spriteSystem = spriteSystem;
Title = Loc.GetString("cargo-shuttle-console-menu-title"); Title = Loc.GetString("cargo-shuttle-console-menu-title");
} }
@@ -28,19 +33,19 @@ namespace Content.Client.Cargo.UI
ShuttleNameLabel.Text = name; ShuttleNameLabel.Text = name;
} }
public void SetOrders(SpriteSystem sprites, IPrototypeManager protoManager, List<CargoOrderData> orders) public void SetOrders(List<CargoOrderData> orders)
{ {
Orders.DisposeAllChildren(); Orders.DisposeAllChildren();
foreach (var order in orders) foreach (var order in orders)
{ {
var product = protoManager.Index<EntityPrototype>(order.ProductId); var product = _protoManager.Index<EntityPrototype>(order.ProductId);
var productName = product.Name; var productName = product.Name;
var row = new CargoOrderRow var row = new CargoOrderRow
{ {
Order = order, Order = order,
Icon = { Texture = sprites.Frame0(product) }, Icon = { Texture = _spriteSystem.Frame0(product) },
ProductName = ProductName =
{ {
Text = Loc.GetString( Text = Loc.GetString(

View File

@@ -1,5 +1,4 @@
using Content.Client.CrewManifest.UI; using Content.Shared.CrewManifest;
using Content.Shared.CrewManifest;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;

View File

@@ -26,7 +26,7 @@
Text="{Loc 'news-read-ui-next-text'}" Text="{Loc 'news-read-ui-next-text'}"
ToolTip="{Loc 'news-read-ui-next-tooltip'}"/> ToolTip="{Loc 'news-read-ui-next-tooltip'}"/>
</BoxContainer> </BoxContainer>
<controls:StripeBack Name="ArticleNameContainer"> <controls:StripeBack Name="АrticleNameContainer">
<PanelContainer> <PanelContainer>
<Label Name="PageNum" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="4,0,0,0"/> <Label Name="PageNum" VerticalAlignment="Center" HorizontalAlignment="Left" Margin="4,0,0,0"/>
<Label Name="PageName" Align="Center"/> <Label Name="PageName" Align="Center"/>

View File

@@ -31,7 +31,7 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
Author.Visible = true; Author.Visible = true;
PageName.Text = article.Title; PageName.Text = article.Title;
PageText.SetMarkupPermissive(article.Content); PageText.SetMarkup(article.Content);
PageNum.Text = $"{targetNum}/{totalNum}"; PageNum.Text = $"{targetNum}/{totalNum}";

View File

@@ -1,30 +0,0 @@
using Content.Client.UserInterface.Fragments;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.UserInterface;
namespace Content.Client.CartridgeLoader.Cartridges;
public sealed partial class WantedListUi : UIFragment
{
private WantedListUiFragment? _fragment;
public override Control GetUIFragmentRoot()
{
return _fragment!;
}
public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new WantedListUiFragment();
}
public override void UpdateState(BoundUserInterfaceState state)
{
switch (state)
{
case WantedListUiState cast:
_fragment?.UpdateState(cast.Records);
break;
}
}
}

View File

@@ -1,240 +0,0 @@
using System.Linq;
using Content.Client.UserInterface.Controls;
using Content.Shared.CriminalRecords.Systems;
using Content.Shared.Security;
using Content.Shared.StatusIcon;
using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects;
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.Map;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.CartridgeLoader.Cartridges;
[GenerateTypedNameReferences]
public sealed partial class WantedListUiFragment : BoxContainer
{
[Dependency] private readonly IEntitySystemManager _entitySystem = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private readonly SpriteSystem _spriteSystem;
private string? _selectedTargetName;
private List<WantedRecord> _wantedRecords = new();
public WantedListUiFragment()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
_spriteSystem = _entitySystem.GetEntitySystem<SpriteSystem>();
SearchBar.OnTextChanged += OnSearchBarTextChanged;
}
private void OnSearchBarTextChanged(LineEdit.LineEditEventArgs args)
{
var found = !String.IsNullOrWhiteSpace(args.Text)
? _wantedRecords.FindAll(r =>
r.TargetInfo.Name.Contains(args.Text) ||
r.Status.ToString().Contains(args.Text, StringComparison.OrdinalIgnoreCase))
: _wantedRecords;
UpdateState(found, false);
}
public void UpdateState(List<WantedRecord> records, bool refresh = true)
{
if (records.Count == 0)
{
NoRecords.Visible = true;
RecordsList.Visible = false;
RecordUnselected.Visible = false;
PersonContainer.Visible = false;
_selectedTargetName = null;
if (refresh)
_wantedRecords.Clear();
RecordsList.PopulateList(new List<ListData>());
return;
}
NoRecords.Visible = false;
RecordsList.Visible = true;
RecordUnselected.Visible = true;
PersonContainer.Visible = false;
var dataList = records.Select(r => new StatusListData(r)).ToList();
RecordsList.GenerateItem = GenerateItem;
RecordsList.ItemPressed = OnItemSelected;
RecordsList.PopulateList(dataList);
if (refresh)
_wantedRecords = records;
}
private void OnItemSelected(BaseButton.ButtonEventArgs args, ListData data)
{
if (data is not StatusListData(var record))
return;
FormattedMessage GetLoc(string fluentId, params (string,object)[] args)
{
var msg = new FormattedMessage();
var fluent = Loc.GetString(fluentId, args);
msg.AddMarkupPermissive(fluent);
return msg;
}
// Set personal info
PersonName.Text = record.TargetInfo.Name;
TargetAge.SetMessage(GetLoc(
"wanted-list-age-label",
("age", record.TargetInfo.Age)
));
TargetJob.SetMessage(GetLoc(
"wanted-list-job-label",
("job", record.TargetInfo.JobTitle.ToLower())
));
TargetSpecies.SetMessage(GetLoc(
"wanted-list-species-label",
("species", record.TargetInfo.Species.ToLower())
));
TargetGender.SetMessage(GetLoc(
"wanted-list-gender-label",
("gender", record.TargetInfo.Gender)
));
// Set reason
WantedReason.SetMessage(GetLoc(
"wanted-list-reason-label",
("reason", record.Reason ?? Loc.GetString("wanted-list-unknown-reason-label"))
));
// Set status
PersonState.SetMessage(GetLoc(
"wanted-list-status-label",
("status", record.Status.ToString().ToLower())
));
// Set initiator
InitiatorName.SetMessage(GetLoc(
"wanted-list-initiator-label",
("initiator", record.Initiator ?? Loc.GetString("wanted-list-unknown-initiator-label"))
));
// History table
// Clear table if it exists
HistoryTable.RemoveAllChildren();
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-time-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
});
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-reason-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
HorizontalExpand = true,
});
HistoryTable.AddChild(new Label()
{
Text = Loc.GetString("wanted-list-history-table-initiator-col"),
StyleClasses = { "LabelSmall" },
HorizontalAlignment = HAlignment.Center,
});
if (record.History.Count > 0)
{
HistoryTable.Visible = true;
foreach (var history in record.History.OrderByDescending(h => h.AddTime))
{
HistoryTable.AddChild(new Label()
{
Text = $"{history.AddTime.Hours:00}:{history.AddTime.Minutes:00}:{history.AddTime.Seconds:00}",
StyleClasses = { "LabelSmall" },
VerticalAlignment = VAlignment.Top,
});
HistoryTable.AddChild(new RichTextLabel()
{
Text = $"[color=white]{history.Crime}[/color]",
HorizontalExpand = true,
VerticalAlignment = VAlignment.Top,
StyleClasses = { "LabelSubText" },
Margin = new(10f, 0f),
});
HistoryTable.AddChild(new RichTextLabel()
{
Text = $"[color=white]{history.InitiatorName}[/color]",
StyleClasses = { "LabelSubText" },
VerticalAlignment = VAlignment.Top,
});
}
}
RecordUnselected.Visible = false;
PersonContainer.Visible = true;
// Save selected item
_selectedTargetName = record.TargetInfo.Name;
}
private void GenerateItem(ListData data, ListContainerButton button)
{
if (data is not StatusListData(var record))
return;
var box = new BoxContainer() { Orientation = LayoutOrientation.Horizontal, HorizontalExpand = true };
var label = new Label() { Text = record.TargetInfo.Name };
var rect = new TextureRect()
{
TextureScale = new(2.2f),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Margin = new(0f, 0f, 6f, 0f),
};
if (record.Status is not SecurityStatus.None)
{
var proto = "SecurityIcon" + record.Status switch
{
SecurityStatus.Detained => "Incarcerated",
_ => record.Status.ToString(),
};
if (_prototypeManager.TryIndex<SecurityIconPrototype>(proto, out var prototype))
{
rect.Texture = _spriteSystem.Frame0(prototype.Icon);
}
}
box.AddChild(rect);
box.AddChild(label);
button.AddChild(box);
button.AddStyleClass(ListContainer.StyleClassListContainerButton);
if (record.TargetInfo.Name.Equals(_selectedTargetName))
{
button.Pressed = true;
// For some reason the event is not called when `Pressed` changed, call it manually.
OnItemSelected(
new(button, new(new(), BoundKeyState.Down, new(), false, new(), new())),
data);
}
}
}
internal record StatusListData(WantedRecord Record) : ListData;

View File

@@ -1,50 +0,0 @@
<cartridges:WantedListUiFragment xmlns:cartridges="clr-namespace:Content.Client.CartridgeLoader.Cartridges"
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True">
<LineEdit Name="SearchBar" PlaceHolder="{Loc 'wanted-list-search-placeholder'}"/>
<BoxContainer Name="MainContainer" Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
<Label Name="NoRecords" Text="{Loc 'wanted-list-label-no-records'}" Align="Center" VAlign="Center" HorizontalExpand="True" FontColorOverride="DarkGray"/>
<!-- Any attempts to set dimensions for ListContainer breaks the renderer, I have to roughly set sizes and margins in other controllers. -->
<controls:ListContainer
Name="RecordsList"
HorizontalAlignment="Left"
VerticalExpand="True"
Visible="False"
Toggle="True"
Group="True"
SetWidth="192" />
<Label Name="RecordUnselected"
Text="{Loc 'criminal-records-console-select-record-info'}"
Align="Center"
FontColorOverride="DarkGray"
Visible="False"
HorizontalExpand="True" />
<BoxContainer Name="PersonContainer" Orientation="Vertical" HorizontalExpand="True" SetWidth="334" Margin="5 0 77 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Name="PersonName" StyleClasses="LabelBig" />
<RichTextLabel Name="PersonState" HorizontalAlignment="Right" HorizontalExpand="True" />
</BoxContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Name="DataContainer" Orientation="Vertical">
<RichTextLabel Name="TargetAge" />
<RichTextLabel Name="TargetJob" />
<RichTextLabel Name="TargetSpecies" />
<RichTextLabel Name="TargetGender" />
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5"/>
<RichTextLabel Name="InitiatorName" VerticalAlignment="Stretch"/>
<RichTextLabel Name="WantedReason" VerticalAlignment="Stretch"/>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5" />
<controls:TableContainer Name="HistoryTable" Columns="3" Visible="False" HorizontalAlignment="Stretch" />
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</BoxContainer>
</cartridges:WantedListUiFragment>

View File

@@ -131,13 +131,13 @@ public sealed partial class ChangelogTab : Control
Margin = new Thickness(6, 0, 0, 0), Margin = new Thickness(6, 0, 0, 0),
}; };
authorLabel.SetMessage( authorLabel.SetMessage(
FormattedMessage.FromMarkupOrThrow(Loc.GetString("changelog-author-changed", ("author", author)))); FormattedMessage.FromMarkup(Loc.GetString("changelog-author-changed", ("author", author))));
ChangelogBody.AddChild(authorLabel); ChangelogBody.AddChild(authorLabel);
foreach (var change in groupedEntry.SelectMany(c => c.Changes)) foreach (var change in groupedEntry.SelectMany(c => c.Changes))
{ {
var text = new RichTextLabel(); var text = new RichTextLabel();
text.SetMessage(FormattedMessage.FromMarkupOrThrow(change.Message)); text.SetMessage(FormattedMessage.FromMarkup(change.Message));
ChangelogBody.AddChild(new BoxContainer ChangelogBody.AddChild(new BoxContainer
{ {
Orientation = LayoutOrientation.Horizontal, Orientation = LayoutOrientation.Horizontal,

View File

@@ -5,80 +5,71 @@ using Content.Shared.Chat;
using Robust.Client.Console; using Robust.Client.Console;
using Robust.Shared.Utility; using Robust.Shared.Utility;
namespace Content.Client.Chat.Managers; namespace Content.Client.Chat.Managers
internal sealed class ChatManager : IChatManager
{ {
[Dependency] private readonly IClientConsoleHost _consoleHost = default!; internal sealed class ChatManager : IChatManager
[Dependency] private readonly IClientAdminManager _adminMgr = default!;
[Dependency] private readonly IEntitySystemManager _systems = default!;
private ISawmill _sawmill = default!;
public void Initialize()
{ {
_sawmill = Logger.GetSawmill("chat"); [Dependency] private readonly IClientConsoleHost _consoleHost = default!;
_sawmill.Level = LogLevel.Info; [Dependency] private readonly IClientAdminManager _adminMgr = default!;
} [Dependency] private readonly IEntitySystemManager _systems = default!;
public void SendAdminAlert(string message) private ISawmill _sawmill = default!;
{
// See server-side manager. This just exists for shared code.
}
public void SendAdminAlert(EntityUid player, string message) public void Initialize()
{
// See server-side manager. This just exists for shared code.
}
public void SendMessage(string text, ChatSelectChannel channel)
{
var str = text.ToString();
switch (channel)
{ {
case ChatSelectChannel.Console: _sawmill = Logger.GetSawmill("chat");
// run locally _sawmill.Level = LogLevel.Info;
_consoleHost.ExecuteCommand(text); }
break;
case ChatSelectChannel.LOOC: public void SendMessage(string text, ChatSelectChannel channel)
_consoleHost.ExecuteCommand($"looc \"{CommandParsing.Escape(str)}\""); {
break; var str = text.ToString();
switch (channel)
{
case ChatSelectChannel.Console:
// run locally
_consoleHost.ExecuteCommand(text);
break;
case ChatSelectChannel.OOC: case ChatSelectChannel.LOOC:
_consoleHost.ExecuteCommand($"ooc \"{CommandParsing.Escape(str)}\""); _consoleHost.ExecuteCommand($"looc \"{CommandParsing.Escape(str)}\"");
break; break;
case ChatSelectChannel.Admin: case ChatSelectChannel.OOC:
_consoleHost.ExecuteCommand($"asay \"{CommandParsing.Escape(str)}\""); _consoleHost.ExecuteCommand($"ooc \"{CommandParsing.Escape(str)}\"");
break; break;
case ChatSelectChannel.Emotes: case ChatSelectChannel.Admin:
_consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\""); _consoleHost.ExecuteCommand($"asay \"{CommandParsing.Escape(str)}\"");
break; break;
case ChatSelectChannel.Dead: case ChatSelectChannel.Emotes:
if (_systems.GetEntitySystemOrNull<GhostSystem>() is {IsGhost: true}) _consoleHost.ExecuteCommand($"me \"{CommandParsing.Escape(str)}\"");
goto case ChatSelectChannel.Local; break;
if (_adminMgr.HasFlag(AdminFlags.Admin)) case ChatSelectChannel.Dead:
_consoleHost.ExecuteCommand($"dsay \"{CommandParsing.Escape(str)}\""); if (_systems.GetEntitySystemOrNull<GhostSystem>() is {IsGhost: true})
else goto case ChatSelectChannel.Local;
_sawmill.Warning("Tried to speak on deadchat without being ghost or admin.");
break;
// TODO sepearate radio and say into separate commands. if (_adminMgr.HasFlag(AdminFlags.Admin))
case ChatSelectChannel.Radio: _consoleHost.ExecuteCommand($"dsay \"{CommandParsing.Escape(str)}\"");
case ChatSelectChannel.Local: else
_consoleHost.ExecuteCommand($"say \"{CommandParsing.Escape(str)}\""); _sawmill.Warning("Tried to speak on deadchat without being ghost or admin.");
break; break;
case ChatSelectChannel.Whisper: // TODO sepearate radio and say into separate commands.
_consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\""); case ChatSelectChannel.Radio:
break; case ChatSelectChannel.Local:
_consoleHost.ExecuteCommand($"say \"{CommandParsing.Escape(str)}\"");
break;
default: case ChatSelectChannel.Whisper:
throw new ArgumentOutOfRangeException(nameof(channel), channel, null); _consoleHost.ExecuteCommand($"whisper \"{CommandParsing.Escape(str)}\"");
break;
default:
throw new ArgumentOutOfRangeException(nameof(channel), channel, null);
}
} }
} }
} }

View File

@@ -2,8 +2,10 @@ using Content.Shared.Chat;
namespace Content.Client.Chat.Managers namespace Content.Client.Chat.Managers
{ {
public interface IChatManager : ISharedChatManager public interface IChatManager
{ {
void Initialize();
public void SendMessage(string text, ChatSelectChannel channel); public void SendMessage(string text, ChatSelectChannel channel);
} }
} }

View File

@@ -2,36 +2,21 @@
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Content.Shared.Inventory;
namespace Content.Client.Chat.TypingIndicator; namespace Content.Client.Chat.TypingIndicator;
public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent> public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingIndicatorComponent>
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventory = default!;
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args) protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
{ {
if (args.Sprite == null) if (args.Sprite == null)
return; return;
var currentTypingIndicator = component.TypingIndicatorPrototype; if (!_prototypeManager.TryIndex<TypingIndicatorPrototype>(component.Prototype, out var proto))
var evt = new BeforeShowTypingIndicatorEvent();
if (TryComp<InventoryComponent>(uid, out var inventoryComp))
_inventory.RelayEvent((uid, inventoryComp), ref evt);
var overrideIndicator = evt.GetMostRecentIndicator();
if (overrideIndicator != null)
currentTypingIndicator = overrideIndicator.Value;
if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
{ {
Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}"); Log.Error($"Unknown typing indicator id: {component.Prototype}");
return; return;
} }

Some files were not shown because too many files have changed in this diff Show More