Compare commits

..

1 Commits

Author SHA1 Message Date
Ed
8c24bb1535 Revert "baff (#995)"
This reverts commit a0337843ac.
2025-03-18 11:08:30 +03:00
2056 changed files with 198435 additions and 110267 deletions

View File

@@ -11,7 +11,7 @@ jobs:
name: Run Benchmarks
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
with:
submodules: 'recursive'
- name: Get Engine version

View File

@@ -8,7 +8,7 @@ jobs:
docfx:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Setup submodule
run: |
git submodule update --init --recursive
@@ -19,7 +19,7 @@ jobs:
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x

View File

@@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v4.2.2
uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: |
@@ -34,7 +34,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x

View File

@@ -19,7 +19,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v4.2.2
uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: |
@@ -34,7 +34,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x

View File

@@ -10,6 +10,6 @@ jobs:
if: github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Check for CRLF
run: Tools/check_crlf.py

View File

@@ -16,11 +16,11 @@ jobs:
- name: Install dependencies
run: sudo apt-get install -y python3-paramiko python3-lxml
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
with:
submodules: 'recursive'
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x

View File

@@ -11,14 +11,14 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v4.2.2
uses: actions/checkout@v3.6.0
- name: Get changed files
id: files
uses: Ana06/get-changed-files@v2.3.0
with:
format: 'space-delimited'
filter: |
filter: |
**.rsi
**.png

View File

@@ -34,7 +34,7 @@ jobs:
steps:
- name: Checkout Master
uses: actions/checkout@v4.2.2
uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: |
@@ -49,7 +49,7 @@ jobs:
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x

View File

@@ -4,19 +4,19 @@ on:
workflow_dispatch:
schedule:
- cron: 0 0 * * 0
jobs:
get_credits:
runs-on: ubuntu-latest
# Hey there fork dev! If you like to include your own contributors in this then you can probably just change this to your own repo
# Do this in dump_github_contributors.ps1 too into your own repo
if: github.repository == 'space-wizards/space-station-14'
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
with:
ref: master
- name: Get this week's Contributors
shell: pwsh
env:
@@ -25,25 +25,25 @@ jobs:
# TODO
#- name: Get this week's Patreons
# run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml
# run: Tools/script2dumppatreons > Resources/Credits/Patrons.yml
# MAKE SURE YOU ENABLED "Allow GitHub Actions to create and approve pull requests" IN YOUR ACTIONS, OTHERWISE IT WILL MOST LIKELY FAIL
# For this you can use a pat token of an account with direct push access to the repo if you have protected branches.
# For this you can use a pat token of an account with direct push access to the repo if you have protected branches.
# Uncomment this and comment the other line if you do this.
# https://github.com/stefanzweifel/git-auto-commit-action#push-to-protected-branches
#- name: Commit new credit files
# uses: stefanzweifel/git-auto-commit-action@v4
# with:
# commit_message: Update Credits
# commit_author: PJBot <pieterjan.briers+bot@gmail.com>
# This will make a PR
- name: Set current date as env variable
run: echo "NOW=$(date +'%Y-%m-%dT%H-%M-%S')" >> $GITHUB_ENV
- name: Create Pull Request
uses: peter-evans/create-pull-request@v5
with:

View File

@@ -12,7 +12,7 @@ jobs:
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
- name: Pull engine updates

View File

@@ -13,7 +13,7 @@ jobs:
name: Validate RSIs
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
- name: Pull engine updates

View File

@@ -12,7 +12,7 @@ jobs:
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Setup Submodule
run: git submodule update --init
- name: Pull engine updates

View File

@@ -13,7 +13,7 @@ jobs:
if: github.actor != 'PJBot' && github.event.pull_request.draft == false
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4.2.2
- uses: actions/checkout@v3.6.0
- name: Setup submodule
run: |
git submodule update --init --recursive
@@ -24,7 +24,7 @@ jobs:
cd RobustToolbox/
git submodule update --init --recursive
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
uses: actions/setup-dotnet@v3.2.0
with:
dotnet-version: 9.0.x
- name: Install dependencies

View File

@@ -1,10 +1,7 @@
using System.Linq;
using System.Numerics;
using Content.Client.Administration.Systems;
using Content.Client.Stylesheets;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Content.Shared.Ghost;
using Content.Shared.Mind;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
@@ -17,54 +14,32 @@ namespace Content.Client.Administration;
internal sealed class AdminNameOverlay : Overlay
{
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly AdminSystem _system;
private readonly IEntityManager _entityManager;
private readonly IEyeManager _eyeManager;
private readonly EntityLookupSystem _entityLookup;
private readonly IUserInterfaceManager _userInterfaceManager;
private readonly Font _font;
private readonly Font _fontBold;
private bool _overlayClassic;
private bool _overlaySymbols;
private bool _overlayPlaytime;
private bool _overlayStartingJob;
private float _ghostFadeDistance;
private float _ghostHideDistance;
private int _overlayStackMax;
private float _overlayMergeDistance;
//TODO make this adjustable via GUI
private readonly ProtoId<RoleTypePrototype>[] _filter =
["SoloAntagonist", "TeamAntagonist", "SiliconAntagonist", "FreeAgent"];
private readonly string _antagLabelClassic = Loc.GetString("admin-overlay-antag-classic");
private readonly Color _antagColorClassic = Color.OrangeRed;
public AdminNameOverlay(
AdminSystem system,
IEntityManager entityManager,
IEyeManager eyeManager,
IResourceCache resourceCache,
EntityLookupSystem entityLookup,
IUserInterfaceManager userInterfaceManager,
IConfigurationManager config)
public AdminNameOverlay(AdminSystem system, IEntityManager entityManager, IEyeManager eyeManager, IResourceCache resourceCache, EntityLookupSystem entityLookup, IUserInterfaceManager userInterfaceManager)
{
IoCManager.InjectDependencies(this);
_system = system;
_entityManager = entityManager;
_eyeManager = eyeManager;
_entityLookup = entityLookup;
_userInterfaceManager = userInterfaceManager;
ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack();
_fontBold = resourceCache.NotoStack(variation: "Bold");
config.OnValueChanged(CCVars.AdminOverlayClassic, (show) => { _overlayClassic = show; }, true);
config.OnValueChanged(CCVars.AdminOverlaySymbols, (show) => { _overlaySymbols = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayPlaytime, (show) => { _overlayPlaytime = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayStartingJob, (show) => { _overlayStartingJob = show; }, true);
config.OnValueChanged(CCVars.AdminOverlayGhostHideDistance, (f) => { _ghostHideDistance = f; }, true);
config.OnValueChanged(CCVars.AdminOverlayGhostFadeDistance, (f) => { _ghostFadeDistance = f; }, true);
config.OnValueChanged(CCVars.AdminOverlayStackMax, (i) => { _overlayStackMax = i; }, true);
config.OnValueChanged(CCVars.AdminOverlayMergeDistance, (f) => { _overlayMergeDistance = f; }, true);
_font = new VectorFont(resourceCache.GetResource<FontResource>("/Fonts/NotoSans/NotoSans-Regular.ttf"), 10);
}
public override OverlaySpace Space => OverlaySpace.ScreenSpace;
@@ -72,147 +47,75 @@ internal sealed class AdminNameOverlay : Overlay
protected override void Draw(in OverlayDrawArgs args)
{
var viewport = args.WorldAABB;
var colorDisconnected = Color.White;
var uiScale = _userInterfaceManager.RootControl.UIScale;
var lineoffset = new Vector2(0f, 14f) * uiScale;
var drawnOverlays = new List<(Vector2,Vector2)>() ; // A saved list of the overlays already drawn
// Get all player positions before drawing overlays, so they can be sorted before iteration
var sortable = new List<(PlayerInfo, Box2, EntityUid, Vector2)>();
foreach (var info in _system.PlayerList)
//TODO make this adjustable via GUI
var classic = _config.GetCVar(CCVars.AdminOverlayClassic);
var playTime = _config.GetCVar(CCVars.AdminOverlayPlaytime);
var startingJob = _config.GetCVar(CCVars.AdminOverlayStartingJob);
foreach (var playerInfo in _system.PlayerList)
{
var entity = _entityManager.GetEntity(info.NetEntity);
var entity = _entityManager.GetEntity(playerInfo.NetEntity);
// If entity does not exist or is on a different map, skip
if (entity == null
|| !_entityManager.EntityExists(entity)
|| _entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
// Otherwise the entity can not exist yet
if (entity == null || !_entityManager.EntityExists(entity))
{
continue;
}
// if not on the same map, continue
if (_entityManager.GetComponent<TransformComponent>(entity.Value).MapID != args.MapId)
{
continue;
}
var aabb = _entityLookup.GetWorldAABB(entity.Value);
// if not on screen, skip
// if not on screen, continue
if (!aabb.Intersects(in viewport))
{
continue;
}
// Get on-screen coordinates of player
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center).Rounded();
var uiScale = _userInterfaceManager.RootControl.UIScale;
var lineoffset = new Vector2(0f, 14f) * uiScale;
var screenCoordinates = _eyeManager.WorldToScreen(aabb.Center +
new Angle(-_eyeManager.CurrentEye.Rotation).RotateVec(
aabb.TopRight - aabb.Center)) + new Vector2(1f, 7f);
sortable.Add((info, aabb, entity.Value, screenCoordinates));
}
// Draw overlays for visible players, starting from the top of the screen
foreach (var info in sortable.OrderBy(s => s.Item4.Y).ToList())
{
var playerInfo = info.Item1;
var aabb = info.Item2;
var entity = info.Item3;
var screenCoordinatesCenter = info.Item4;
//the center position is kept separately, for simpler position comparison later
var centerOffset = new Vector2(28f, -18f) * uiScale;
var screenCoordinates = screenCoordinatesCenter + centerOffset;
var alpha = 1f;
//TODO make a smarter system where the starting offset can be modified by the predicted position and size of already-drawn overlays/stacks?
var currentOffset = Vector2.Zero;
// Ghosts near the cursor are made transparent/invisible
// TODO would be "cheaper" if playerinfo already contained a ghost bool, this gets called every frame for every onscreen player!
if (_entityManager.HasComponent<GhostComponent>(entity))
{
// We want the map positions here, so we don't have to worry about resolution and such shenanigans
var mobPosition = aabb.Center;
var mousePosition = _eyeManager
.ScreenToMap(_userInterfaceManager.MousePositionScaled.Position * uiScale)
.Position;
var dist = Vector2.Distance(mobPosition, mousePosition);
if (dist < _ghostHideDistance)
continue;
alpha = Math.Clamp((dist - _ghostHideDistance) / (_ghostFadeDistance - _ghostHideDistance), 0f, 1f);
colorDisconnected.A = alpha;
}
// If the new overlay text block is within merge distance of any previous ones
// merge them into a stack so they don't hide each other
var stack = drawnOverlays.FindAll(x =>
Vector2.Distance(_eyeManager.ScreenToMap(x.Item1).Position, aabb.Center) <= _overlayMergeDistance);
if (stack.Count > 0)
{
screenCoordinates = stack.First().Item1 + centerOffset;
// Replacing this overlay's coordinates for the later save with the stack root's coordinates
// so that other overlays don't try to stack to these coordinates
screenCoordinatesCenter = stack.First().Item1;
var i = 1;
foreach (var s in stack)
{
// additional entries after maximum stack size is reached will be drawn over the last entry
if (i <= _overlayStackMax - 1)
currentOffset = lineoffset + s.Item2 ;
i++;
}
}
// Character name
var color = Color.Aquamarine;
color.A = alpha;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? color : colorDisconnected);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.CharacterName, uiScale, playerInfo.Connected ? Color.Aquamarine : Color.White);
currentOffset += lineoffset;
// Username
color = Color.Yellow;
color.A = alpha;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? color : colorDisconnected);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.Username, uiScale, playerInfo.Connected ? Color.Yellow : Color.White);
currentOffset += lineoffset;
// Playtime
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && _overlayPlaytime)
if (!string.IsNullOrEmpty(playerInfo.PlaytimeString) && playTime)
{
color = Color.Orange;
color.A = alpha;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? color : colorDisconnected);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.PlaytimeString, uiScale, playerInfo.Connected ? Color.Orange : Color.White);
currentOffset += lineoffset;
}
// Job
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && _overlayStartingJob)
if (!string.IsNullOrEmpty(playerInfo.StartingJob) && startingJob)
{
color = Color.GreenYellow;
color.A = alpha;
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? Color.GreenYellow : Color.White);
currentOffset += lineoffset;
}
// Classic Antag Label
if (_overlayClassic && playerInfo.Antag)
if (classic && playerInfo.Antag)
{
var symbol = _overlaySymbols ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
var label = _overlaySymbols
? Loc.GetString("player-tab-character-name-antag-symbol",
("symbol", symbol),
("name", _antagLabelClassic))
: _antagLabelClassic;
color = Color.OrangeRed;
color.A = alpha;
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, _antagLabelClassic, uiScale, Color.OrangeRed);
currentOffset += lineoffset;
}
// Role Type
else if (!_overlayClassic && _filter.Contains(playerInfo.RoleProto))
else if (!classic && _filter.Contains(playerInfo.RoleProto))
{
var symbol = _overlaySymbols && playerInfo.Antag ? playerInfo.RoleProto.Symbol : string.Empty;
var role = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var label = _overlaySymbols
? Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", role))
: role;
color = playerInfo.RoleProto.Color;
color.A = alpha;
args.ScreenHandle.DrawString(_fontBold, screenCoordinates + currentOffset, label, uiScale, color);
currentOffset += lineoffset;
}
var label = Loc.GetString(playerInfo.RoleProto.Name).ToUpper();
var color = playerInfo.RoleProto.Color;
//Save the coordinates and size of the text block, for stack merge check
drawnOverlays.Add((screenCoordinatesCenter, currentOffset));
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, label, uiScale, color);
currentOffset += lineoffset;
}
}
}
}

View File

@@ -14,7 +14,6 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IEyeManager _eyeManager = default!;
[Dependency] private readonly EntityLookupSystem _entityLookup = default!;
[Dependency] private readonly IUserInterfaceManager _userInterfaceManager = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
private AdminNameOverlay _adminNameOverlay = default!;
@@ -23,14 +22,7 @@ namespace Content.Client.Administration.Systems
private void InitializeOverlay()
{
_adminNameOverlay = new AdminNameOverlay(
this,
EntityManager,
_eyeManager,
_resourceCache,
_entityLookup,
_userInterfaceManager,
_configurationManager);
_adminNameOverlay = new AdminNameOverlay(this, EntityManager, _eyeManager, _resourceCache, _entityLookup, _userInterfaceManager);
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
}

View File

@@ -2,14 +2,14 @@
xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.Administration.UI.CustomControls">
<PanelContainer StyleClasses="BackgroundDark">
<SplitContainer Orientation="Vertical" ResizeMode="NotResizable">
<SplitContainer Orientation="Vertical">
<SplitContainer Orientation="Horizontal" VerticalExpand="True">
<cc:PlayerListControl Access="Public" Name="ChannelSelector" HorizontalExpand="True" SizeFlagsStretchRatio="2" />
<BoxContainer Orientation="Vertical" HorizontalExpand="True" SizeFlagsStretchRatio="2">
<BoxContainer Access="Public" Name="BwoinkArea" VerticalExpand="True" />
</BoxContainer>
</SplitContainer>
<BoxContainer Orientation="Horizontal" SetHeight="30" >
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<CheckBox Name="AdminOnly" Access="Public" Text="{Loc 'admin-ahelp-admin-only'}" ToolTip="{Loc 'admin-ahelp-admin-only-tooltip'}" />
<Control HorizontalExpand="True" MinWidth="5" />
<CheckBox Name="PlaySound" Access="Public" Text="{Loc 'admin-bwoink-play-sound'}" Pressed="True" />

View File

@@ -2,13 +2,11 @@ using System.Linq;
using Content.Client.Administration.Systems;
using Content.Client.UserInterface.Controls;
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using static Content.Client.Administration.UI.Tabs.PlayerTab.PlayerTabHeader;
using static Robust.Client.UserInterface.Controls.BaseButton;
@@ -18,7 +16,6 @@ namespace Content.Client.Administration.UI.Tabs.PlayerTab;
public sealed partial class PlayerTab : Control
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
[Dependency] private readonly IPlayerManager _playerMan = default!;
private const string ArrowUp = "↑";
@@ -44,10 +41,6 @@ public sealed partial class PlayerTab : Control
_adminSystem.OverlayEnabled += OverlayEnabled;
_adminSystem.OverlayDisabled += OverlayDisabled;
_config.OnValueChanged(CCVars.AdminPlayerlistSeparateSymbols, PlayerListSettingsChanged);
_config.OnValueChanged(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerListSettingsChanged);
_config.OnValueChanged(CCVars.AdminPlayerlistRoleTypeColor, PlayerListSettingsChanged);
OverlayButton.OnPressed += OverlayButtonPressed;
ShowDisconnectedButton.OnPressed += ShowDisconnectedPressed;
@@ -113,11 +106,6 @@ public sealed partial class PlayerTab : Control
#region ListContainer
private void PlayerListSettingsChanged(bool _)
{
RefreshPlayerList(_adminSystem.PlayerList);
}
private void RefreshPlayerList(IReadOnlyList<PlayerInfo> players)
{
_players = players;
@@ -208,7 +196,8 @@ public sealed partial class PlayerTab : Control
Header.Username => Compare(x.Username, y.Username),
Header.Character => Compare(x.CharacterName, y.CharacterName),
Header.Job => Compare(x.StartingJob, y.StartingJob),
Header.RoleType => y.SortWeight - x.SortWeight,
Header.Antagonist => x.Antag.CompareTo(y.Antag),
Header.RoleType => Compare(x.RoleProto.Name , y.RoleProto.Name),
Header.Playtime => TimeSpan.Compare(x.OverallPlaytime ?? default, y.OverallPlaytime ?? default),
_ => 1
};

View File

@@ -19,6 +19,11 @@
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="AntagonistLabel"
SizeFlagsStretchRatio="1"
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"

View File

@@ -1,10 +1,8 @@
using Content.Shared.Administration;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
namespace Content.Client.Administration.UI.Tabs.PlayerTab;
@@ -16,25 +14,17 @@ public sealed partial class PlayerTabEntry : PanelContainer
public PlayerTabEntry(PlayerInfo player, StyleBoxFlat styleBoxFlat)
{
RobustXamlLoader.Load(this);
var config = IoCManager.Resolve<IConfigurationManager>();
UsernameLabel.Text = player.Username;
if (!player.Connected)
UsernameLabel.StyleClasses.Add("Disabled");
JobLabel.Text = player.StartingJob;
var separateAntagSymbols = config.GetCVar(CCVars.AdminPlayerlistSeparateSymbols);
var genericAntagSymbol = player.Antag ? Loc.GetString("player-tab-antag-prefix") : string.Empty;
var roleSymbol = player.Antag ? player.RoleProto.Symbol : string.Empty;
var symbol = separateAntagSymbols ? roleSymbol : genericAntagSymbol;
CharacterLabel.Text = Loc.GetString("player-tab-character-name-antag-symbol", ("symbol", symbol), ("name", player.CharacterName));
if (player.Antag && config.GetCVar(CCVars.AdminPlayerlistHighlightedCharacterColor))
CharacterLabel.FontColorOverride = player.RoleProto.Color;
CharacterLabel.Text = player.CharacterName;
if (player.IdentityName != player.CharacterName)
CharacterLabel.Text += $" [{player.IdentityName}]";
AntagonistLabel.Text = Loc.GetString(player.Antag ? "player-tab-is-antag-yes" : "player-tab-is-antag-no");
RoleTypeLabel.Text = Loc.GetString(player.RoleProto.Name);
if (config.GetCVar(CCVars.AdminPlayerlistRoleTypeColor))
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
RoleTypeLabel.FontColorOverride = player.RoleProto.Color;
BackgroundColorPanel.PanelOverride = styleBoxFlat;
OverallPlaytimeLabel.Text = player.PlaytimeString;
PlayerEntity = player.NetEntity;

View File

@@ -25,6 +25,13 @@
Text="{Loc player-tab-job}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="AntagonistLabel"
SizeFlagsStretchRatio="1"
HorizontalExpand="True"
ClipText="True"
Text="{Loc player-tab-antagonist}"
MouseFilter="Pass"/>
<cc:VSeparator/>
<Label Name="RoleTypeLabel"
SizeFlagsStretchRatio="2"
HorizontalExpand="True"

View File

@@ -18,6 +18,7 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.OnKeyBindDown += UsernameClicked;
CharacterLabel.OnKeyBindDown += CharacterClicked;
JobLabel.OnKeyBindDown += JobClicked;
AntagonistLabel.OnKeyBindDown += AntagonistClicked;
RoleTypeLabel.OnKeyBindDown += RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown += PlaytimeClicked;
}
@@ -29,6 +30,7 @@ public sealed partial class PlayerTabHeader : Control
Header.Username => UsernameLabel,
Header.Character => CharacterLabel,
Header.Job => JobLabel,
Header.Antagonist => AntagonistLabel,
Header.RoleType => RoleTypeLabel,
Header.Playtime => PlaytimeLabel,
_ => throw new ArgumentOutOfRangeException(nameof(header), header, null)
@@ -40,6 +42,7 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.Text = Loc.GetString("player-tab-username");
CharacterLabel.Text = Loc.GetString("player-tab-character");
JobLabel.Text = Loc.GetString("player-tab-job");
AntagonistLabel.Text = Loc.GetString("player-tab-antagonist");
RoleTypeLabel.Text = Loc.GetString("player-tab-roletype");
PlaytimeLabel.Text = Loc.GetString("player-tab-playtime");
}
@@ -70,6 +73,11 @@ public sealed partial class PlayerTabHeader : Control
HeaderClicked(args, Header.Job);
}
private void AntagonistClicked(GUIBoundKeyEventArgs args)
{
HeaderClicked(args, Header.Antagonist);
}
private void RoleTypeClicked(GUIBoundKeyEventArgs args)
{
HeaderClicked(args, Header.RoleType);
@@ -89,6 +97,7 @@ public sealed partial class PlayerTabHeader : Control
UsernameLabel.OnKeyBindDown -= UsernameClicked;
CharacterLabel.OnKeyBindDown -= CharacterClicked;
JobLabel.OnKeyBindDown -= JobClicked;
AntagonistLabel.OnKeyBindDown -= AntagonistClicked;
RoleTypeLabel.OnKeyBindDown -= RoleTypeClicked;
PlaytimeLabel.OnKeyBindDown -= PlaytimeClicked;
}
@@ -99,6 +108,7 @@ public sealed partial class PlayerTabHeader : Control
Username,
Character,
Job,
Antagonist,
RoleType,
Playtime
}

View File

@@ -52,7 +52,7 @@ public sealed class ClientAlertsSystem : AlertsSystem
if (args.Current is not AlertComponentState cast)
return;
alerts.Comp.Alerts = new(cast.Alerts);
alerts.Comp.Alerts = cast.Alerts;
UpdateHud(alerts);
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.CartridgeLoader.Cartridges;
namespace Content.Client.CartridgeLoader.Cartridges;
public sealed class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem;

View File

@@ -1,32 +0,0 @@
<Control xmlns="https://spacestation14.io" xmlns:system="clr-namespace:System;assembly=System.Runtime">
<BoxContainer Name="MainContainer"
Orientation="Horizontal"
SetWidth="250">
<Button Name="MainButton"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="ButtonSquare"
Margin="-1 0 0 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
<Label Name="TaskLabel"
StyleClasses="LabelSubText" />
<Label Name="TaskForLabel"
StyleClasses="LabelSubText"
Margin="0 -5 0 0" />
</BoxContainer>
</BoxContainer>
</Button>
<Button Name="DoneButton"
VerticalExpand="True"
Text="{Loc 'nano-task-ui-done'}">
<Button.StyleClasses>
<system:String>ButtonSmall</system:String>
<system:String>OpenLeft</system:String>
</Button.StyleClasses>
</Button>
</BoxContainer>
</Control>

View File

@@ -1,33 +0,0 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Maths;
using Content.Shared.CartridgeLoader.Cartridges;
namespace Content.Client.CartridgeLoader.Cartridges;
/// <summary>
/// Represents a single control for a single NanoTask item
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class NanoTaskItemControl : Control
{
public Action<int>? OnMainPressed;
public Action<int>? OnDonePressed;
public NanoTaskItemControl(NanoTaskItemAndId item)
{
RobustXamlLoader.Load(this);
TaskLabel.Text = item.Data.Description;
TaskLabel.FontColorOverride = Color.White;
TaskForLabel.Text = item.Data.TaskIsFor;
MainButton.OnPressed += _ => OnMainPressed?.Invoke(item.Id);
DoneButton.OnPressed += _ => OnDonePressed?.Invoke(item.Id);
MainButton.Disabled = item.Data.IsTaskDone;
DoneButton.Text = item.Data.IsTaskDone ? Loc.GetString("nano-task-ui-revert-done") : Loc.GetString("nano-task-ui-done");
}
}

View File

@@ -1,67 +0,0 @@
<DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc nano-task-ui-item-title}"
MinSize="300 300">
<PanelContainer StyleClasses="AngleRect">
<BoxContainer Orientation="Vertical" Margin="4">
<!-- Task Description Input -->
<BoxContainer Orientation="Vertical" Margin="0 4">
<Label Text="{Loc nano-task-ui-description-label}"
StyleClasses="LabelHeading" />
<PanelContainer StyleClasses="ButtonSquare">
<LineEdit Name="DescriptionInput"
PlaceHolder="{Loc nano-task-ui-description-placeholder}" />
</PanelContainer>
</BoxContainer>
<!-- Task Requester Input -->
<BoxContainer Orientation="Vertical" Margin="0 4">
<Label Text="{Loc nano-task-ui-requester-label}"
StyleClasses="LabelHeading" />
<PanelContainer StyleClasses="ButtonSquare">
<LineEdit Name="RequesterInput"
PlaceHolder="{Loc nano-task-ui-requester-placeholder}" />
</PanelContainer>
</BoxContainer>
<!-- Severity Buttons -->
<BoxContainer Orientation="Horizontal"
HorizontalAlignment="Center"
Margin="0 8 0 0">
<Button Name="LowButton"
Text="{Loc nano-task-ui-priority-low}"
StyleClasses="OpenRight"
MinSize="60 0" />
<Button Name="MediumButton"
Text="{Loc nano-task-ui-priority-medium}"
StyleClasses="ButtonSquare"
MinSize="60 0" />
<Button Name="HighButton"
Text="{Loc nano-task-ui-priority-high}"
StyleClasses="OpenLeft"
MinSize="60 0" />
</BoxContainer>
<!-- Verb Buttons -->
<BoxContainer Orientation="Horizontal"
HorizontalAlignment="Right"
Margin="0 8 0 0">
<Button Name="CancelButton"
Text="{Loc nano-task-ui-cancel}"
StyleClasses="OpenRight"
MinSize="60 0" />
<Button Name="DeleteButton"
Text="{Loc nano-task-ui-delete}"
StyleClasses="ButtonSquare"
MinSize="60 0" />
<Button Name="PrintButton"
Text="{Loc nano-task-ui-print}"
StyleClasses="ButtonSquare"
MinSize="60 0" />
<Button Name="SaveButton"
Text="{Loc nano-task-ui-save}"
StyleClasses="OpenLeft"
MinSize="60 0" />
</BoxContainer>
</BoxContainer>
</PanelContainer>
</DefaultWindow>

View File

@@ -1,109 +0,0 @@
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.UserInterface.Controls;
using Content.Shared.CartridgeLoader.Cartridges;
namespace Content.Client.CartridgeLoader.Cartridges;
/// <summary>
/// Popup displayed to edit a NanoTask item
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class NanoTaskItemPopup : DefaultWindow
{
private readonly ButtonGroup _priorityGroup = new();
private int? _editingTaskId = null;
public Action<int, NanoTaskItem>? TaskSaved;
public Action<int>? TaskDeleted;
public Action<NanoTaskItem>? TaskCreated;
public Action<NanoTaskItem>? TaskPrinted;
private NanoTaskItem MakeItem()
{
return new(
description: DescriptionInput.Text,
taskIsFor: RequesterInput.Text,
isTaskDone: false,
priority: _priorityGroup.Pressed switch {
var item when item == LowButton => NanoTaskPriority.Low,
var item when item == MediumButton => NanoTaskPriority.Medium,
var item when item == HighButton => NanoTaskPriority.High,
_ => NanoTaskPriority.Medium,
}
);
}
public NanoTaskItemPopup()
{
RobustXamlLoader.Load(this);
LowButton.Group = _priorityGroup;
MediumButton.Group = _priorityGroup;
HighButton.Group = _priorityGroup;
CancelButton.OnPressed += _ => Close();
DeleteButton.OnPressed += _ =>
{
if (_editingTaskId is int id)
{
TaskDeleted?.Invoke(id);
}
};
PrintButton.OnPressed += _ =>
{
TaskPrinted?.Invoke(MakeItem());
};
SaveButton.OnPressed += _ =>
{
if (_editingTaskId is int id)
{
TaskSaved?.Invoke(id, MakeItem());
}
else
{
TaskCreated?.Invoke(MakeItem());
}
};
DescriptionInput.OnTextChanged += args =>
{
if (args.Text.Length > NanoTaskItem.MaximumStringLength)
DescriptionInput.Text = args.Text[..NanoTaskItem.MaximumStringLength];
};
RequesterInput.OnTextChanged += args =>
{
if (args.Text.Length > NanoTaskItem.MaximumStringLength)
RequesterInput.Text = args.Text[..NanoTaskItem.MaximumStringLength];
};
}
public void SetEditingTaskId(int? id)
{
_editingTaskId = id;
DeleteButton.Visible = id is not null;
}
public void ResetInputs(NanoTaskItem? item)
{
if (item is NanoTaskItem task)
{
var button = task.Priority switch {
NanoTaskPriority.High => HighButton,
NanoTaskPriority.Medium => MediumButton,
NanoTaskPriority.Low => LowButton,
};
button.Pressed = true;
DescriptionInput.Text = task.Description;
RequesterInput.Text = task.TaskIsFor;
}
else
{
MediumButton.Pressed = true;
DescriptionInput.Text = "";
RequesterInput.Text = "";
}
}
}

View File

@@ -1,82 +0,0 @@
using System.Linq;
using Content.Client.UserInterface.Fragments;
using Content.Shared.CartridgeLoader;
using Content.Shared.CartridgeLoader.Cartridges;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
namespace Content.Client.CartridgeLoader.Cartridges;
/// <summary>
/// UI fragment responsible for displaying NanoTask controls in a PDA and coordinating with the NanoTaskCartridgeSystem for state
/// </summary>
public sealed partial class NanoTaskUi : UIFragment
{
private NanoTaskUiFragment? _fragment;
private NanoTaskItemPopup? _popup;
public override Control GetUIFragmentRoot()
{
return _fragment!;
}
public override void Setup(BoundUserInterface userInterface, EntityUid? fragmentOwner)
{
_fragment = new NanoTaskUiFragment();
_popup = new NanoTaskItemPopup();
_fragment.NewTask += () =>
{
_popup.ResetInputs(null);
_popup.SetEditingTaskId(null);
_popup.OpenCentered();
};
_fragment.OpenTask += id =>
{
if (_fragment.Tasks.Find(task => task.Id == id) is not NanoTaskItemAndId task)
return;
_popup.ResetInputs(task.Data);
_popup.SetEditingTaskId(task.Id);
_popup.OpenCentered();
};
_fragment.ToggleTaskCompletion += id =>
{
if (_fragment.Tasks.Find(task => task.Id == id) is not NanoTaskItemAndId task)
return;
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, new(
description: task.Data.Description,
taskIsFor: task.Data.TaskIsFor,
isTaskDone: !task.Data.IsTaskDone,
priority: task.Data.Priority
))))));
};
_popup.TaskSaved += (id, data) =>
{
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskUpdateTask(new(id, data)))));
_popup.Close();
};
_popup.TaskDeleted += id =>
{
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskDeleteTask(id))));
_popup.Close();
};
_popup.TaskCreated += data =>
{
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskAddTask(data))));
_popup.Close();
};
_popup.TaskPrinted += data =>
{
userInterface.SendMessage(new CartridgeUiMessage(new NanoTaskUiMessageEvent(new NanoTaskPrintTask(data))));
};
}
public override void UpdateState(BoundUserInterfaceState state)
{
if (state is not NanoTaskUiState nanoTaskState)
return;
_fragment?.UpdateState(nanoTaskState.Tasks);
}
}

View File

@@ -1,58 +0,0 @@
<cartridges:NanoTaskUiFragment xmlns:cartridges="clr-namespace:Content.Client.CartridgeLoader.Cartridges"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns="https://spacestation14.io" Margin="1 0 2 0">
<PanelContainer StyleClasses="BackgroundDark"></PanelContainer>
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<ScrollContainer HorizontalExpand="True" VerticalExpand="True">
<BoxContainer HorizontalExpand="True" VerticalExpand="True" Orientation="Vertical" Margin="8" SeparationOverride="8">
<!-- Heading for High Priority Items -->
<BoxContainer Orientation="Horizontal">
<PanelContainer SetWidth="7" Margin="0 0 8 0">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#e93d58"/>
</PanelContainer.PanelOverride>
</PanelContainer>
<Label Name="HighPriority" StyleClasses="LabelHeading"/>
</BoxContainer>
<!-- Location for High Priority Items -->
<GridContainer Name="HighContainer"
HorizontalExpand="True"
Access="Public"
Columns="2" />
<!-- Heading for Medium Priority Items -->
<BoxContainer Orientation="Horizontal">
<PanelContainer SetWidth="7" Margin="0 0 8 0">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#ef973c"/>
</PanelContainer.PanelOverride>
</PanelContainer>
<Label Name="MediumPriority" StyleClasses="LabelHeading"/>
</BoxContainer>
<!-- Location for Medium Priority Items -->
<GridContainer Name="MediumContainer"
HorizontalExpand="True"
Access="Public"
Columns="2" />
<!-- Location for Low Priority Items -->
<BoxContainer Orientation="Horizontal">
<PanelContainer SetWidth="7" Margin="0 0 8 0">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#3dd425"/>
</PanelContainer.PanelOverride>
</PanelContainer>
<Label Name="LowPriority" StyleClasses="LabelHeading"/>
</BoxContainer>
<!-- Location for Low Priority Items -->
<GridContainer Name="LowContainer"
HorizontalExpand="True"
Access="Public"
Columns="2" />
<Button Name="NewTaskButton" Text="{Loc 'nano-task-ui-new-task'}" HorizontalAlignment="Right"/>
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</cartridges:NanoTaskUiFragment>

View File

@@ -1,52 +0,0 @@
using System.Linq;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Content.Shared.CartridgeLoader.Cartridges;
namespace Content.Client.CartridgeLoader.Cartridges;
/// <summary>
/// Class displaying the main UI of NanoTask
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class NanoTaskUiFragment : BoxContainer
{
public Action<int>? OpenTask;
public Action<int>? ToggleTaskCompletion;
public Action? NewTask;
public List<NanoTaskItemAndId> Tasks = new();
public NanoTaskUiFragment()
{
RobustXamlLoader.Load(this);
Orientation = LayoutOrientation.Vertical;
HorizontalExpand = true;
VerticalExpand = true;
NewTaskButton.OnPressed += _ => NewTask?.Invoke();
}
public void UpdateState(List<NanoTaskItemAndId> tasks)
{
Tasks = tasks;
HighContainer.RemoveAllChildren();
MediumContainer.RemoveAllChildren();
LowContainer.RemoveAllChildren();
HighPriority.Text = Loc.GetString("nano-task-ui-heading-high-priority-tasks", ("amount", tasks.Count(task => task.Data.Priority == NanoTaskPriority.High)));
MediumPriority.Text = Loc.GetString("nano-task-ui-heading-medium-priority-tasks", ("amount", tasks.Count(task => task.Data.Priority == NanoTaskPriority.Medium)));
LowPriority.Text = Loc.GetString("nano-task-ui-heading-low-priority-tasks", ("amount", tasks.Count(task => task.Data.Priority == NanoTaskPriority.Low)));
foreach (var task in tasks)
{
var container = task.Data.Priority switch {
NanoTaskPriority.High => HighContainer,
NanoTaskPriority.Medium => MediumContainer,
NanoTaskPriority.Low => LowContainer,
};
var control = new NanoTaskItemControl(task);
container.AddChild(control);
control.OnMainPressed += id => OpenTask?.Invoke(id);
control.OnDonePressed += id => ToggleTaskCompletion?.Invoke(id);
}
}
}

View File

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

View File

@@ -169,7 +169,7 @@ public sealed class ClientClothingSystem : ClothingSystem
var state = $"equipped-{correctedSlot}";
if (!string.IsNullOrEmpty(clothing.EquippedPrefix))
if (clothing.EquippedPrefix != null)
state = $"{clothing.EquippedPrefix}-equipped-{correctedSlot}";
if (clothing.EquippedState != null)

View File

@@ -3,7 +3,6 @@ using Content.Shared.Access.Systems;
using Content.Shared.Administration;
using Content.Shared.CriminalRecords;
using Content.Shared.Dataset;
using Content.Shared.Random.Helpers;
using Content.Shared.Security;
using Content.Shared.StationRecords;
using Robust.Client.AutoGenerated;
@@ -33,7 +32,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
public readonly EntityUid Console;
[ValidatePrototypeId<LocalizedDatasetPrototype>]
[ValidatePrototypeId<DatasetPrototype>]
private const string ReasonPlaceholders = "CriminalRecordsWantedReasonPlaceholders";
public Action<uint?>? OnKeySelected;
@@ -334,8 +333,8 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
var field = "reason";
var title = Loc.GetString("criminal-records-status-" + status.ToString().ToLower());
var placeholders = _proto.Index<LocalizedDatasetPrototype>(ReasonPlaceholders);
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders))); // just funny it doesn't actually get used
var placeholders = _proto.Index<DatasetPrototype>(ReasonPlaceholders);
var placeholder = Loc.GetString("criminal-records-console-reason-placeholder", ("placeholder", _random.Pick(placeholders.Values))); // just funny it doesn't actually get used
var prompt = Loc.GetString("criminal-records-console-reason");
var entry = new QuickDialogEntry(field, QuickDialogEntryType.LongText, prompt, placeholder);
var entries = new List<QuickDialogEntry>() { entry };

View File

@@ -4,7 +4,6 @@ using Content.Shared.Verbs;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Utility;
@@ -41,17 +40,7 @@ public sealed class ExamineButton : ContainerButton
Disabled = true;
}
TooltipSupplier = sender =>
{
var label = new RichTextLabel();
label.SetMessage(FormattedMessage.FromMarkupOrThrow(verb.Message ?? verb.Text));
var tooltip = new Tooltip();
tooltip.GetChild(0).Children.Clear();
tooltip.GetChild(0).Children.Add(label);
return tooltip;
};
ToolTip = verb.Message ?? verb.Text;
Icon = new TextureRect
{

View File

@@ -298,26 +298,8 @@ namespace Content.Client.Examine
{
Name = "ExamineButtonsHBox",
Orientation = LayoutOrientation.Horizontal,
HorizontalAlignment = Control.HAlignment.Stretch,
VerticalAlignment = Control.VAlignment.Bottom,
};
var hoverExamineBox = new BoxContainer
{
Name = "HoverExamineHBox",
Orientation = LayoutOrientation.Horizontal,
HorizontalAlignment = Control.HAlignment.Left,
VerticalAlignment = Control.VAlignment.Center,
HorizontalExpand = true
};
var clickExamineBox = new BoxContainer
{
Name = "ClickExamineHBox",
Orientation = LayoutOrientation.Horizontal,
HorizontalAlignment = Control.HAlignment.Right,
VerticalAlignment = Control.VAlignment.Center,
HorizontalExpand = true
VerticalAlignment = Control.VAlignment.Bottom,
};
// Examine button time
@@ -334,15 +316,8 @@ namespace Content.Client.Examine
var button = new ExamineButton(examine);
if (examine.HoverVerb)
{
hoverExamineBox.AddChild(button);
}
else
{
button.OnPressed += VerbButtonPressed;
clickExamineBox.AddChild(button);
}
button.OnPressed += VerbButtonPressed;
buttonsHBox.AddChild(button);
}
var vbox = _examineTooltipOpen?.GetChild(0).GetChild(0);
@@ -359,8 +334,6 @@ namespace Content.Client.Examine
{
vbox.Children.Remove(hbox.First());
}
buttonsHBox.AddChild(hoverExamineBox);
buttonsHBox.AddChild(clickExamineBox);
vbox.AddChild(buttonsHBox);
}

View File

@@ -29,25 +29,24 @@
VerticalAlignment="Center"
Access="Public"
Visible="False" />
<!-- CP14 random reactions begin -->
<BoxContainer Name="RandomVariations"
Orientation="Vertical"
VerticalExpand="True"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
<Label Name="RandomVariationsLabel"
Text="{Loc 'cp14-guidebook-random-variations-title'}"
Visible="False"
HorizontalAlignment="Center"
FontColorOverride="#1e6651" />
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
</BoxContainer>
<!-- CP14 random reactions end -->
</BoxContainer>
<!-- CP14 random reactions begin -->
<BoxContainer Name="RandomVariations"
Orientation="Vertical"
VerticalExpand="True"
VerticalAlignment="Center"
HorizontalAlignment="Center">
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
<Label Name="RandomVariationsLabel"
Text="{Loc 'cp14-guidebook-random-variations-title'}"
Visible="False"
HorizontalAlignment="Center"
FontColorOverride="#1e6651" />
<controls:SplitBar MinHeight="10">
</controls:SplitBar>
</BoxContainer>
<!-- CP14 random reactions end -->
</BoxContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 5 0 5" />
</BoxContainer>

View File

@@ -2,7 +2,6 @@ using Content.Shared.CCVar;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.Inventory;
using Content.Shared.Preferences;
using Robust.Client.GameObjects;
using Robust.Shared.Configuration;
@@ -49,7 +48,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
}
private static bool IsHidden(HumanoidAppearanceComponent humanoid, HumanoidVisualLayers layer)
=> humanoid.HiddenLayers.ContainsKey(layer) || humanoid.PermanentlyHidden.Contains(layer);
=> humanoid.HiddenLayers.Contains(layer) || humanoid.PermanentlyHidden.Contains(layer);
private void UpdateLayers(HumanoidAppearanceComponent component, SpriteComponent sprite)
{
@@ -58,7 +57,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
// add default species layers
var speciesProto = _prototypeManager.Index(component.Species);
var baseSprites = _prototypeManager.Index(speciesProto.SpriteSet);
var baseSprites = _prototypeManager.Index<HumanoidSpeciesBaseSpritesPrototype>(speciesProto.SpriteSet);
foreach (var (key, id) in baseSprites.Sprites)
{
oldLayers.Remove(key);
@@ -215,7 +214,7 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
humanoid.MarkingSet = markings;
humanoid.PermanentlyHidden = new HashSet<HumanoidVisualLayers>();
humanoid.HiddenLayers = new Dictionary<HumanoidVisualLayers, SlotFlags>();
humanoid.HiddenLayers = new HashSet<HumanoidVisualLayers>();
humanoid.CustomBaseLayers = customBaseLayers;
humanoid.Sex = profile.Sex;
humanoid.Gender = profile.Gender;
@@ -403,21 +402,23 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
}
}
public override void SetLayerVisibility(
Entity<HumanoidAppearanceComponent> ent,
protected override void SetLayerVisibility(
EntityUid uid,
HumanoidAppearanceComponent humanoid,
HumanoidVisualLayers layer,
bool visible,
SlotFlags? slot,
bool permanent,
ref bool dirty)
{
base.SetLayerVisibility(ent, layer, visible, slot, ref dirty);
base.SetLayerVisibility(uid, humanoid, layer, visible, permanent, ref dirty);
var sprite = Comp<SpriteComponent>(ent);
var sprite = Comp<SpriteComponent>(uid);
if (!sprite.LayerMapTryGet(layer, out var index))
{
if (!visible)
return;
index = sprite.LayerMapReserveBlank(layer);
else
index = sprite.LayerMapReserveBlank(layer);
}
var spriteLayer = sprite[index];
@@ -427,14 +428,13 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
spriteLayer.Visible = visible;
// I fucking hate this. I'll get around to refactoring sprite layers eventually I swear
// Just a week away...
foreach (var markingList in ent.Comp.MarkingSet.Markings.Values)
foreach (var markingList in humanoid.MarkingSet.Markings.Values)
{
foreach (var marking in markingList)
{
if (_markingManager.TryGetMarking(marking, out var markingPrototype) && markingPrototype.BodyPart == layer)
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, ent, sprite);
ApplyMarking(markingPrototype, marking.MarkingColors, marking.Visible, humanoid, sprite);
}
}
}

View File

@@ -84,9 +84,6 @@ namespace Content.Client.Input
human.AddFunction(ContentKeyFunctions.Arcade1);
human.AddFunction(ContentKeyFunctions.Arcade2);
human.AddFunction(ContentKeyFunctions.Arcade3);
//CP14 Keys
human.AddFunction(ContentKeyFunctions.CP14OpenSkillMenu);
//CP14 Keys end
// actions should be common (for ghosts, mobs, etc)
common.AddFunction(ContentKeyFunctions.OpenActionsMenu);
@@ -126,6 +123,10 @@ namespace Content.Client.Input
common.AddFunction(ContentKeyFunctions.OpenDecalSpawnWindow);
common.AddFunction(ContentKeyFunctions.OpenAdminMenu);
common.AddFunction(ContentKeyFunctions.OpenGuidebook);
//CP14 Keys
human.AddFunction(ContentKeyFunctions.CP14OpenKnowledgeMenu);
//CP14 Keys end
}
}
}

View File

@@ -68,13 +68,21 @@ public sealed partial class LatheMenu : DefaultWindow
{
ServerListButton.Visible = false;
}
AmountLineEdit.SetText(latheComponent.DefaultProductionAmount.ToString());
}
MaterialsList.SetOwner(Entity);
}
protected override void Opened()
{
base.Opened();
if (_entityManager.TryGetComponent<LatheComponent>(Entity, out var latheComp))
{
AmountLineEdit.SetText(latheComp.DefaultProductionAmount.ToString());
}
}
/// <summary>
/// Populates the list of all the recipes
/// </summary>

View File

@@ -4,20 +4,7 @@
<BoxContainer Orientation="Vertical">
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">
<BoxContainer Orientation="Vertical" Margin="8">
<Label Text="{Loc 'ui-options-admin-player-panel'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="PlayerlistSeparateSymbolsCheckBox" Text="{Loc 'ui-options-admin-playerlist-separate-symbols'}" />
<CheckBox Name="PlayerlistCharacterColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-character-color'}" />
<CheckBox Name="PlayerlistRoleTypeColorCheckBox" Text="{Loc 'ui-options-admin-playerlist-roletype-color'}" />
<Label Text="{Loc 'ui-options-admin-overlay-title'}"
StyleClasses="LabelKeyText"/>
<CheckBox Name="EnableClassicOverlayCheckBox" Text="{Loc 'ui-options-enable-classic-overlay'}" />
<CheckBox Name="EnableOverlaySymbolsCheckBox" Text="{Loc 'ui-options-enable-overlay-symbols'}" />
<CheckBox Name="EnableOverlayPlaytimeCheckBox" Text="{Loc 'ui-options-enable-overlay-playtime'}" />
<CheckBox Name="EnableOverlayStartingJobCheckBox" Text="{Loc 'ui-options-enable-overlay-starting-job'}" />
<ui:OptionSlider Name="OverlayMergeDistanceSlider" Title="{Loc 'ui-options-overlay-merge-distance'}"/>
<ui:OptionSlider Name="OverlayGhostFadeSlider" Title="{Loc 'ui-options-overlay-ghost-fade-distance'}"/>
<ui:OptionSlider Name="OverlayGhostHideSlider" Title="{Loc 'ui-options-overlay-ghost-hide-distance'}"/>
</BoxContainer>
</ScrollContainer>
<ui:OptionsTabControlRow Name="Control" Access="Public" />

View File

@@ -8,45 +8,13 @@ namespace Content.Client.Options.UI.Tabs;
[GenerateTypedNameReferences]
public sealed partial class AdminOptionsTab : Control
{
private const float OverlayMergeMin = 0.05f;
private const float OverlayMergeMax = 0.95f;
private const int OverlayGhostFadeMin = 0;
private const int OverlayGhostFadeMax = 10;
private const int OverlayGhostHideMin = 0;
private const int OverlayGhostHideMax = 5;
public AdminOptionsTab()
{
RobustXamlLoader.Load(this);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistSeparateSymbols, PlayerlistSeparateSymbolsCheckBox);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistHighlightedCharacterColor, PlayerlistCharacterColorCheckBox);
Control.AddOptionCheckBox(CCVars.AdminPlayerlistRoleTypeColor, PlayerlistRoleTypeColorCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayClassic, EnableClassicOverlayCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlaySymbols, EnableOverlaySymbolsCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayPlaytime, EnableOverlayPlaytimeCheckBox);
Control.AddOptionCheckBox(CCVars.AdminOverlayStartingJob, EnableOverlayStartingJobCheckBox);
Control.Initialize();
Control.AddOptionPercentSlider(
CCVars.AdminOverlayMergeDistance,
OverlayMergeDistanceSlider,
OverlayMergeMin,
OverlayMergeMax);
Control.AddOptionSlider(
CCVars.AdminOverlayGhostFadeDistance,
OverlayGhostFadeSlider,
OverlayGhostFadeMin,
OverlayGhostFadeMax);
Control.AddOptionSlider(
CCVars.AdminOverlayGhostHideDistance,
OverlayGhostHideSlider,
OverlayGhostHideMin,
OverlayGhostHideMax);
}
}

View File

@@ -312,7 +312,7 @@ namespace Content.Client.Options.UI.Tabs
//CP14
AddHeader("ui-options-header-cp14");
AddButton(ContentKeyFunctions.CP14OpenSkillMenu);
AddButton(ContentKeyFunctions.CP14OpenKnowledgeMenu);
//CP14 end
foreach (var control in _keyControls.Values)

View File

@@ -1,7 +1,7 @@
using System.Numerics;
using Content.Client.Parallax;
using Content.Client.Weather;
using Content.Shared._CP14.CloudShadow;
using Content.Shared._CP14.DayCycle.Components;
using Content.Shared.Salvage;
using Content.Shared.Weather;
using Robust.Client.GameObjects;
@@ -62,7 +62,7 @@ public sealed partial class StencilOverlay : Overlay
{
foreach (var (proto, weather) in comp.Weather)
{
if (!_protoManager.TryIndex(proto, out var weatherProto))
if (!_protoManager.TryIndex<WeatherPrototype>(proto, out var weatherProto))
continue;
var alpha = _weather.GetPercent(weather, mapUid);

View File

@@ -11,21 +11,13 @@ public sealed class PaperVisualizerSystem : VisualizerSystem<PaperVisualsCompone
if (args.Sprite == null)
return;
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status, out var writingStatus, args.Component))
if (AppearanceSystem.TryGetData<PaperStatus>(uid, PaperVisuals.Status , out var writingStatus, args.Component))
args.Sprite.LayerSetVisible(PaperVisualLayers.Writing, writingStatus == PaperStatus.Written);
if (AppearanceSystem.TryGetData<string>(uid, PaperVisuals.Stamp, out var stampState, args.Component))
{
if (stampState != string.Empty)
{
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
}
else
{
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, false);
}
args.Sprite.LayerSetState(PaperVisualLayers.Stamp, stampState);
args.Sprite.LayerSetVisible(PaperVisualLayers.Stamp, true);
}
}
}

View File

@@ -5,7 +5,7 @@ namespace Content.Client.Parallax.Data;
/// <summary>
/// Prototype data for a parallax.
/// </summary>
[Prototype]
[Prototype("parallax")]
public sealed partial class ParallaxPrototype : IPrototype
{
/// <inheritdoc/>

View File

@@ -1,9 +1,7 @@
using System.Numerics;
using Content.Client.Parallax.Data;
using System.Numerics;
using Content.Client.Parallax.Managers;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.ViewVariables;
@@ -19,50 +17,27 @@ public sealed class ParallaxControl : Control
[Dependency] private readonly IParallaxManager _parallaxManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
private string _parallaxPrototype = "FastSpace";
[ViewVariables(VVAccess.ReadWrite)] public Vector2 Offset { get; set; }
[ViewVariables(VVAccess.ReadWrite)] public float SpeedX { get; set; } = 0.0f;
[ViewVariables(VVAccess.ReadWrite)] public float SpeedY { get; set; } = 0.0f;
[ViewVariables(VVAccess.ReadWrite)] public float ScaleX { get; set; } = 1.0f;
[ViewVariables(VVAccess.ReadWrite)] public float ScaleY { get; set; } = 1.0f;
[ViewVariables(VVAccess.ReadWrite)] public string ParallaxPrototype
{
get => _parallaxPrototype;
set
{
_parallaxPrototype = value;
_parallaxManager.LoadParallaxByName(value);
}
}
public ParallaxControl()
{
IoCManager.InjectDependencies(this);
Offset = new Vector2(_random.Next(0, 1000), _random.Next(0, 1000));
RectClipContent = true;
_parallaxManager.LoadParallaxByName(_parallaxPrototype);
_parallaxManager.LoadParallaxByName("FastSpace");
}
protected override void Draw(DrawingHandleScreen handle)
{
var currentTime = (float) _timing.RealTime.TotalSeconds;
var offset = Offset + new Vector2(currentTime * SpeedX, currentTime * SpeedY);
foreach (var layer in _parallaxManager.GetParallaxLayers(_parallaxPrototype))
foreach (var layer in _parallaxManager.GetParallaxLayers("FastSpace"))
{
var tex = layer.Texture;
var texSize = new Vector2i(
(int)(tex.Size.X * Size.X * layer.Config.Scale.X / 1920 * ScaleX),
(int)(tex.Size.Y * Size.X * layer.Config.Scale.Y / 1920 * ScaleY)
);
var texSize = (tex.Size.X * (int) Size.X, tex.Size.Y * (int) Size.X) * layer.Config.Scale.Floored() / 1920;
var ourSize = PixelSize;
//Protection from division by zero.
texSize.X = Math.Max(texSize.X, 1);
texSize.Y = Math.Max(texSize.Y, 1);
var currentTime = (float) _timing.RealTime.TotalSeconds;
var offset = Offset + new Vector2(currentTime * 100f, currentTime * 0f);
if (layer.Config.Tiled)
{

View File

@@ -237,12 +237,6 @@ namespace Content.Client.Popups
PopupEntity(message, uid, recipient.Value, type);
}
public override void PopupPredicted(string? message, EntityUid uid, EntityUid? recipient, Filter filter, bool recordReplay, PopupType type = PopupType.Small)
{
if (recipient != null && _timing.IsFirstTimePredicted)
PopupEntity(message, uid, recipient.Value, type);
}
public override void PopupPredicted(string? recipientMessage, string? othersMessage, EntityUid uid, EntityUid? recipient, PopupType type = PopupType.Small)
{
if (recipient != null && _timing.IsFirstTimePredicted)

View File

@@ -1,15 +1,12 @@
using Content.Client.UserInterface.Systems.Sandbox;
using Content.Shared.DrawDepth;
using Content.Shared.SubFloor;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface;
using Robust.Shared.Player;
namespace Content.Client.SubFloor;
public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
{
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
private bool _showAll;
@@ -21,13 +18,8 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
{
if (_showAll == value) return;
_showAll = value;
_ui.GetUIController<SandboxUIController>().SetToggleSubfloors(value);
var ev = new ShowSubfloorRequestEvent()
{
Value = value,
};
RaiseNetworkEvent(ev);
UpdateAll();
}
}
@@ -36,20 +28,6 @@ public sealed class SubFloorHideSystem : SharedSubFloorHideSystem
base.Initialize();
SubscribeLocalEvent<SubFloorHideComponent, AppearanceChangeEvent>(OnAppearanceChanged);
SubscribeNetworkEvent<ShowSubfloorRequestEvent>(OnRequestReceived);
SubscribeLocalEvent<LocalPlayerDetachedEvent>(OnPlayerDetached);
}
private void OnPlayerDetached(LocalPlayerDetachedEvent ev)
{
// Vismask resets so need to reset this.
ShowAll = false;
}
private void OnRequestReceived(ShowSubfloorRequestEvent ev)
{
// When client receives request Queue an update on all vis.
UpdateAll();
}
private void OnAppearanceChanged(EntityUid uid, SubFloorHideComponent component, ref AppearanceChangeEvent args)

View File

@@ -14,7 +14,6 @@ public sealed partial class GhostGui : UIWidget
public event Action? RequestWarpsPressed;
public event Action? ReturnToBodyPressed;
public event Action? GhostRolesPressed;
private int _prevNumberRoles;
public GhostGui()
{
@@ -27,7 +26,6 @@ public sealed partial class GhostGui : UIWidget
GhostWarpButton.OnPressed += _ => RequestWarpsPressed?.Invoke();
ReturnToBodyButton.OnPressed += _ => ReturnToBodyPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesPressed?.Invoke();
GhostRolesButton.OnPressed += _ => GhostRolesButton.StyleClasses.Remove(StyleBase.ButtonCaution);
}
public void Hide()
@@ -43,13 +41,14 @@ public sealed partial class GhostGui : UIWidget
if (roles != null)
{
GhostRolesButton.Text = Loc.GetString("ghost-gui-ghost-roles-button", ("count", roles));
if (roles > _prevNumberRoles)
if (roles > 0)
{
GhostRolesButton.StyleClasses.Add(StyleBase.ButtonCaution);
}
_prevNumberRoles = (int)roles;
else
{
GhostRolesButton.StyleClasses.Remove(StyleBase.ButtonCaution);
}
}
TargetWindow.Populate();

View File

@@ -1,4 +1,3 @@
using Content.Client._CP14.UserInterface.Systems.Skill;
using Content.Client.UserInterface.Systems.Actions;
using Content.Client.UserInterface.Systems.Admin;
using Content.Client.UserInterface.Systems.Bwoink;
@@ -25,7 +24,7 @@ public sealed class GameTopMenuBarUIController : UIController
[Dependency] private readonly SandboxUIController _sandbox = default!;
[Dependency] private readonly GuidebookUIController _guidebook = default!;
[Dependency] private readonly EmotesUIController _emotes = default!;
[Dependency] private readonly CP14SkillUIController _skill = default!; //CP14
[Dependency] private readonly CP14KnowledgeUIController _knowledge = default!; //CP14
private GameTopMenuBar? GameTopMenuBar => UIManager.GetActiveUIWidgetOrNull<GameTopMenuBar>();
@@ -49,7 +48,7 @@ public sealed class GameTopMenuBarUIController : UIController
_action.UnloadButton();
_sandbox.UnloadButton();
_emotes.UnloadButton();
_skill.UnloadButton(); //CP14
_knowledge.UnloadButton(); //CP14
}
public void LoadButtons()
@@ -63,6 +62,6 @@ public sealed class GameTopMenuBarUIController : UIController
_action.LoadButton();
_sandbox.LoadButton();
_emotes.LoadButton();
_skill.LoadButton(); //CP14
_knowledge.LoadButton(); //CP14
}
}

View File

@@ -35,11 +35,11 @@
/>
<!-- CP14 UI part -->
<ui:MenuButton
Name="CP14SkillButton"
Name="CP14KnowledgeButton"
Access="Internal"
Icon="{xe:Tex '/Textures/Interface/students-cap.svg.192dpi.png'}"
ToolTip="{Loc 'cp14-game-hud-open-skill-menu-button-tooltip'}"
BoundKey = "{x:Static is:ContentKeyFunctions.CP14OpenSkillMenu}"
ToolTip="{Loc 'cp14-game-hud-open-knowledge-menu-button-tooltip'}"
BoundKey = "{x:Static is:ContentKeyFunctions.CP14OpenKnowledgeMenu}"
MinSize="42 64"
HorizontalExpand="True"
AppendStyleClass="{x:Static style:StyleBase.ButtonSquare}"

View File

@@ -39,6 +39,7 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
[UISystemDependency] private readonly DebugPhysicsSystem _debugPhysics = default!;
[UISystemDependency] private readonly MarkerSystem _marker = default!;
[UISystemDependency] private readonly SandboxSystem _sandbox = default!;
[UISystemDependency] private readonly SubFloorHideSystem _subfloorHide = default!;
private SandboxWindow? _window;
@@ -116,11 +117,10 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
_window.OnOpen += () => { SandboxButton!.Pressed = true; };
_window.OnClose += () => { SandboxButton!.Pressed = false; };
// TODO: These need moving to opened so at least if they're not synced properly on open they work.
_window.ToggleLightButton.Pressed = !_light.Enabled;
_window.ToggleFovButton.Pressed = !_eye.CurrentEye.DrawFov;
_window.ToggleShadowsButton.Pressed = !_light.DrawShadows;
_window.ToggleSubfloorButton.Pressed = _subfloorHide.ShowAll;
_window.ShowMarkersButton.Pressed = _marker.MarkersVisible;
_window.ShowBbButton.Pressed = (_debugPhysics.Flags & PhysicsDebugFlags.Shapes) != 0x0;
@@ -219,16 +219,4 @@ public sealed class SandboxUIController : UIController, IOnStateChanged<Gameplay
_window.Close();
}
}
#region Buttons
public void SetToggleSubfloors(bool value)
{
if (_window == null)
return;
_window.ToggleSubfloorButton.Pressed = value;
}
#endregion
}

View File

@@ -1,5 +1,4 @@
using Content.Client.SubFloor;
using Robust.Client.AutoGenerated;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
@@ -8,18 +7,8 @@ namespace Content.Client.UserInterface.Systems.Sandbox.Windows;
[GenerateTypedNameReferences]
public sealed partial class SandboxWindow : DefaultWindow
{
[Dependency] private readonly IEntityManager _entManager = null!;
public SandboxWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
}
protected override void Opened()
{
base.Opened();
// Make sure state is up to date.
ToggleSubfloorButton.Pressed = _entManager.System<SubFloorHideSystem>().ShowAll;
}
}

View File

@@ -42,9 +42,6 @@ public sealed class StorageWindow : BaseWindow
private ValueList<EntityUid> _contained = new();
private ValueList<EntityUid> _toRemove = new();
// Manually store this because you can't have a 0x0 GridContainer but we still need to add child controls for 1x1 containers.
private Vector2i _pieceGridSize;
private TextureButton? _backButton;
private bool _isDirty;
@@ -411,14 +408,11 @@ public sealed class StorageWindow : BaseWindow
_contained.Clear();
_contained.AddRange(storageComp.Container.ContainedEntities.Reverse());
var width = boundingGrid.Width + 1;
var height = boundingGrid.Height + 1;
// Build the grid representation
if (_pieceGrid.Rows != _pieceGridSize.Y || _pieceGrid.Columns != _pieceGridSize.X)
if (_pieceGrid.Rows - 1 != boundingGrid.Height || _pieceGrid.Columns - 1 != boundingGrid.Width)
{
_pieceGrid.Rows = height;
_pieceGrid.Columns = width;
_pieceGrid.Rows = boundingGrid.Height + 1;
_pieceGrid.Columns = boundingGrid.Width + 1;
_controlGrid.Clear();
for (var y = boundingGrid.Bottom; y <= boundingGrid.Top; y++)
@@ -436,7 +430,6 @@ public sealed class StorageWindow : BaseWindow
}
}
_pieceGridSize = new(width, height);
_toRemove.Clear();
// Remove entities no longer relevant / Update existing ones

View File

@@ -3,12 +3,10 @@ using Content.Client.ContextMenu.UI;
using Content.Shared.Verbs;
using Robust.Client.GameObjects;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.Utility;
using Robust.Shared.GameObjects;
using Robust.Shared.IoC;
using Robust.Shared.Maths;
using Robust.Shared.Utility;
namespace Content.Client.Verbs.UI
{
@@ -29,17 +27,7 @@ namespace Content.Client.Verbs.UI
public VerbMenuElement(Verb verb) : base(verb.Text)
{
TooltipSupplier = sender =>
{
var label = new RichTextLabel();
label.SetMessage(FormattedMessage.FromMarkupOrThrow(verb.Message ?? verb.Text));
var tooltip = new Tooltip();
tooltip.GetChild(0).Children.Clear();
tooltip.GetChild(0).Children.Add(label);
return tooltip;
};
ToolTip = verb.Message;
Disabled = verb.Disabled;
Verb = verb;

View File

@@ -213,7 +213,7 @@ namespace Content.Client.Verbs
{
// maybe send an informative pop-up message.
if (!string.IsNullOrWhiteSpace(verb.Message))
_popupSystem.PopupEntity(FormattedMessage.RemoveMarkupOrThrow(verb.Message), user);
_popupSystem.PopupEntity(verb.Message, user);
return;
}

View File

@@ -1,5 +1,4 @@
using Content.Shared._CP14.Farming;
using Content.Shared._CP14.Farming.Components;
using Content.Shared.Rounding;
using Robust.Client.GameObjects;
@@ -33,7 +32,7 @@ public sealed class ClientCP14FarmingSystem : CP14SharedFarmingSystem
if (!TryComp<SpriteComponent>(visuals, out var sprite))
return;
if (!PlantQuery.TryComp(visuals, out var plant))
if (!TryComp<CP14PlantComponent>(visuals, out var plant))
return;
var growthState = ContentHelpers.RoundToNearestLevels(plant.GrowthLevel, 1, visuals.Comp.GrowthSteps);

View File

@@ -1,149 +0,0 @@
using System.Numerics;
using Content.Client.Resources;
using Content.Shared._CP14.Fishing.Components;
using Content.Shared._CP14.Fishing.Prototypes;
using Content.Shared._CP14.Fishing.Systems;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Fishing;
public sealed class CP14FishingOverlay : Overlay
{
[Dependency] private readonly IComponentFactory _factory = default!;
[Dependency] private readonly IEntityManager _entity = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
private readonly SpriteSystem _sprite;
private readonly TransformSystem _transform;
private readonly CP14SharedFishingProcessSystem _sharedFishingProcess;
private Texture _backgroundTexture = default!;
private Texture _handleTopTexture = default!;
private Texture _handleMiddleTexture = default!;
private Texture _handleBottomTexture = default!;
private Texture _lootTexture = default!;
private Vector2 _backgroundOffset;
private Vector2 _backgroundHandleOffset;
private Vector2 _backgroundHandleSize;
private Vector2 _progressOffset;
private Vector2 _progressSize;
private EntityUid _process = EntityUid.Invalid;
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public CP14FishingOverlay()
{
IoCManager.InjectDependencies(this);
_sprite = _entity.System<SpriteSystem>();
_transform = _entity.System<TransformSystem>();
_sharedFishingProcess = _entity.System<CP14SharedFishingProcessSystem>();
}
protected override void Draw(in OverlayDrawArgs args)
{
if (_player.LocalEntity is not { } localEntity)
return;
if (!_sharedFishingProcess.TryGetByUser(localEntity, out var fishingProcess))
return;
// Refresh the texture cache, with a new fishing process
if (_process != fishingProcess.Value.Owner)
{
_process = fishingProcess.Value.Owner;
UpdateCachedStyleSheet(_sharedFishingProcess.GetStyle(fishingProcess.Value));
var prototype = _prototype.Index(fishingProcess.Value.Comp.LootProtoId);
var iconPath = CP14FishingIconComponent.DefaultTexturePath;
if (prototype.Components.TryGetComponent(_factory.GetComponentName(typeof(CP14FishingIconComponent)), out var iconComponent))
{
var comp = (CP14FishingIconComponent) iconComponent;
iconPath = comp.TexturePath;
}
_lootTexture = _resourceCache.GetTexture(iconPath);
}
// Getting the position of the player we will be working from
var worldPosition = _transform.GetWorldPosition(localEntity);
// Calculate the shift of the player relative to the bottom of the coordinates
var playerOffset = fishingProcess.Value.Comp.PlayerPositionNormalized * _backgroundHandleSize;
var playerHalfSize = fishingProcess.Value.Comp.PlayerHalfSizeNormalized * _backgroundHandleSize;
var lootOffset = fishingProcess.Value.Comp.LootPositionNormalized * _backgroundHandleSize + Vector2.UnitX * _backgroundHandleSize.X / 2;
DrawBackground(args.WorldHandle, worldPosition - _backgroundOffset);
DrawProgress(args.WorldHandle, worldPosition - _backgroundOffset + _progressOffset, _progressSize, fishingProcess.Value.Comp.Progress);
DrawHandle(args.WorldHandle, worldPosition - _backgroundOffset + _backgroundHandleOffset + playerOffset, playerHalfSize);
DrawLoot(args.WorldHandle, worldPosition - _backgroundOffset + _backgroundHandleOffset + lootOffset);
}
private void DrawBackground(DrawingHandleWorld handle, Vector2 position)
{
handle.DrawTexture(_backgroundTexture, position);
}
private void DrawProgress(DrawingHandleWorld handle, Vector2 position, Vector2 size, float progress)
{
var vectorA = position;
var vectorB = position + new Vector2(size.X, size.Y * progress);
var box = new Box2(vectorA, vectorB);
handle.DrawRect(box, Color.Aqua);
}
private void DrawHandle(DrawingHandleWorld handle, Vector2 position, Vector2 halfSize)
{
var cursor = position - halfSize;
// Bottom
handle.DrawTexture(_handleBottomTexture, cursor);
cursor += new Vector2(0, _handleBottomTexture.Height / 32f);
// Middle
while (cursor.Y < position.Y + halfSize.Y - _handleTopTexture.Height / 32f)
{
handle.DrawTexture(_handleMiddleTexture, cursor);
cursor += new Vector2(0, _handleMiddleTexture.Height / 32f);
}
// Front
handle.DrawTexture(_handleTopTexture, cursor);
}
private void DrawLoot(DrawingHandleWorld handle, Vector2 position)
{
handle.DrawTexture(_lootTexture, position - _lootTexture.Size / 64f);
}
private void UpdateCachedStyleSheet(CP14FishingProcessStyleSheetPrototype styleSheet)
{
_backgroundTexture = _resourceCache.GetTexture(styleSheet.Background.Texture);
_handleTopTexture = _resourceCache.GetTexture(styleSheet.Handle.TopTexture);
_handleMiddleTexture = _resourceCache.GetTexture(styleSheet.Handle.MiddleTexture);
_handleBottomTexture = _resourceCache.GetTexture(styleSheet.Handle.BottomTexture);
_backgroundOffset = styleSheet.Background.Offset + Vector2.UnitX * _backgroundTexture.Width / 32f;
_backgroundHandleOffset = styleSheet.Background.HandleOffset;
_backgroundHandleSize = styleSheet.Background.HandleSize;
_progressOffset = styleSheet.Background.ProgressOffset;
_progressSize = styleSheet.Background.ProgressSize;
}
}

View File

@@ -1,23 +0,0 @@
using Content.Client.Overlays;
using Robust.Client.Graphics;
namespace Content.Client._CP14.Fishing;
public sealed class CP14FishingOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlay = default!;
public override void Initialize()
{
base.Initialize();
_overlay.AddOverlay(new CP14FishingOverlay());
}
public override void Shutdown()
{
base.Shutdown();
_overlay.RemoveOverlay<StencilOverlay>();
}
}

View File

@@ -1,5 +0,0 @@
using Content.Shared._CP14.Fishing.Systems;
namespace Content.Client._CP14.Fishing;
public sealed class CP14FishingProcessSystem : CP14SharedFishingProcessSystem;

View File

@@ -1,35 +0,0 @@
using Content.Client.Hands.Systems;
using Content.Shared._CP14.Fishing.Components;
using Content.Shared._CP14.Fishing.Systems;
using Robust.Client.GameObjects;
using Robust.Shared.Input;
using Robust.Shared.Timing;
namespace Content.Client._CP14.Fishing;
public sealed class CP14FishingRodSystem : CP14SharedFishingRodSystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly InputSystem _input = default!;
[Dependency] private readonly HandsSystem _hands = default!;
public override void Update(float frameTime)
{
base.Update(frameTime);
if (!_timing.IsFirstTimePredicted)
return;
var handUid = _hands.GetActiveHandEntity();
if (!TryComp<CP14FishingRodComponent>(handUid, out var fishingRodComponent))
return;
var reelKey = _input.CmdStates.GetState(EngineKeyFunctions.UseSecondary) == BoundKeyState.Down;
if (fishingRodComponent.Reeling == reelKey)
return;
RaisePredictiveEvent(new RequestFishingRodReelMessage(reelKey));
}
}

View File

@@ -0,0 +1,43 @@
using Content.Shared._CP14.Knowledge;
using Content.Shared._CP14.Knowledge.Events;
using Content.Shared._CP14.Knowledge.Prototypes;
using Robust.Client.Player;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Knowledge;
public sealed class ClientCP14KnowledgeSystem : SharedCP14KnowledgeSystem
{
[Dependency] private readonly IPlayerManager _players = default!;
public event Action<KnowledgeData>? OnKnowledgeUpdate;
public override void Initialize()
{
base.Initialize();
SubscribeNetworkEvent<CP14KnowledgeInfoEvent>(OnCharacterKnowledgeEvent);
}
public void RequestKnowledgeInfo()
{
var entity = _players.LocalEntity;
if (entity is null)
return;
RaiseNetworkEvent(new CP14RequestKnowledgeInfoEvent(GetNetEntity(entity.Value)));
}
private void OnCharacterKnowledgeEvent(CP14KnowledgeInfoEvent msg, EntitySessionEventArgs args)
{
var entity = GetEntity(msg.NetEntity);
var data = new KnowledgeData(entity, msg.AllKnowledge);
OnKnowledgeUpdate?.Invoke(data);
}
public readonly record struct KnowledgeData(
EntityUid Entity,
HashSet<ProtoId<CP14KnowledgePrototype>> AllKnowledge
);
}

View File

@@ -1,65 +0,0 @@
using Content.Shared._CP14.NightVision;
using Robust.Client.Player;
using Robust.Shared.Player;
namespace Content.Client._CP14.NightVision;
public sealed class CP14ClientNightVisionSystem : CP14SharedNightVisionSystem
{
[Dependency] private readonly SharedTransformSystem _transform = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14NightVisionComponent, CP14ToggleNightVisionEvent>(OnToggleNightVision);
SubscribeLocalEvent<CP14NightVisionComponent, PlayerDetachedEvent>(OnPlayerDetached);
}
protected override void OnRemove(Entity<CP14NightVisionComponent> ent, ref ComponentRemove args)
{
base.OnRemove(ent, ref args);
NightVisionOff(ent);
}
private void OnPlayerDetached(Entity<CP14NightVisionComponent> ent, ref PlayerDetachedEvent args)
{
NightVisionOff(ent);
}
private void OnToggleNightVision(Entity<CP14NightVisionComponent> ent, ref CP14ToggleNightVisionEvent args)
{
NightVisionToggle(ent);
}
private void NightVisionOn(Entity<CP14NightVisionComponent> ent)
{
if (_playerManager.LocalSession?.AttachedEntity != ent)
return;
var nightVisionLight = Spawn(ent.Comp.LightPrototype, Transform(ent).Coordinates);
_transform.SetParent(nightVisionLight, ent);
_transform.SetWorldRotation(nightVisionLight, _transform.GetWorldRotation(ent));
ent.Comp.LocalLightEntity = nightVisionLight;
}
private void NightVisionOff(Entity<CP14NightVisionComponent> ent)
{
QueueDel(ent.Comp.LocalLightEntity);
ent.Comp.LocalLightEntity = null;
}
private void NightVisionToggle(Entity<CP14NightVisionComponent> ent)
{
if (ent.Comp.LocalLightEntity == null)
{
NightVisionOn(ent);
}
else
{
NightVisionOff(ent);
}
}
}

View File

@@ -1,5 +1,5 @@
using System.Numerics;
using Content.Shared._CP14.CloudShadow;
using Content.Shared._CP14.DayCycle.Components;
using Robust.Client.Graphics;
using Robust.Shared.Utility;

View File

@@ -1,57 +0,0 @@
using Content.Shared._CP14.Skill;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Skill;
public sealed partial class CP14ClientSkillSystem : CP14SharedSkillSystem
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly SharedAudioSystem _audio = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
public event Action<EntityUid>? OnSkillUpdate;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<CP14SkillStorageComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<CP14SkillStorageComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (ent != _playerManager.LocalEntity)
return;
OnSkillUpdate?.Invoke(ent.Owner);
}
public void RequestSkillData()
{
var localPlayer = _playerManager.LocalEntity;
if (!HasComp<CP14SkillStorageComponent>(localPlayer))
return;
OnSkillUpdate?.Invoke(localPlayer.Value);
}
public void RequestLearnSkill(EntityUid? target, CP14SkillPrototype? skill)
{
if (skill == null || target == null)
return;
var netEv = new CP14TryLearnSkillMessage(GetNetEntity(target.Value), skill.ID);
RaiseNetworkEvent(netEv);
if (_proto.TryIndex(skill.Tree, out var indexedTree))
{
_audio.PlayGlobal(indexedTree.LearnSound, target.Value, AudioParams.Default.WithVolume(6f));
}
}
}

View File

@@ -1,24 +0,0 @@
<Control xmlns="https://spacestation14.io" HorizontalExpand="True">
<BoxContainer Name="MainContainer"
Orientation="Horizontal"
HorizontalExpand="True">
<Button Name="MainButton"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="OpenRight"
Margin="0 0 -1 0">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
HorizontalExpand="True"
Margin="-5 0 0 0">
<Label Name="SkillTreeLabel" />
</BoxContainer>
</BoxContainer>
</Button>
<PanelContainer Name="ColorPanel"
VerticalExpand="True"
SetWidth="7"
Margin="0 1 0 0" />
</BoxContainer>
</Control>

View File

@@ -1,22 +0,0 @@
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._CP14.Skill.Ui;
[GenerateTypedNameReferences]
public sealed partial class CP14SkillTreeButtonControl : Control
{
public event Action? OnPressed;
public CP14SkillTreeButtonControl(Color color, string label)
{
RobustXamlLoader.Load(this);
ColorPanel.PanelOverride = new StyleBoxFlat { BackgroundColor = color };
SkillTreeLabel.Text = label;
MainButton.OnPressed += args => OnPressed?.Invoke();
}
}

View File

@@ -1,7 +0,0 @@
<ui:CP14SkillTreeGraphControl
xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client._CP14.Skill.Ui"
xmlns:ui="clr-namespace:Content.Client._CP14.Skill.Ui"
HorizontalExpand="True"
VerticalExpand="True"
MouseFilter="Stop"/>

View File

@@ -1,227 +0,0 @@
using System.Linq;
using System.Numerics;
using Content.Shared._CP14.Skill;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared._CP14.Skill.Restrictions;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Client.Utility;
using Robust.Shared.Input;
using Robust.Shared.Prototypes;
namespace Content.Client._CP14.Skill.Ui;
[GenerateTypedNameReferences]
public sealed partial class CP14SkillTreeGraphControl : BoxContainer
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
private readonly CP14SharedSkillSystem _skillSystem;
private Entity<CP14SkillStorageComponent>? _player;
private IEnumerable<CP14SkillPrototype> _allSkills;
private CP14SkillPrototype? _hoveredNode;
private CP14SkillPrototype? _selectedNode;
public CP14SkillTreePrototype? Tree;
private bool dragging = false;
private Vector2 _previousMousePosition = Vector2.Zero;
private Vector2 _globalOffset = new (60,60);
private const float GridSize = 25f;
public event Action<CP14SkillPrototype?>? OnNodeSelected;
public event Action<Vector2>? OnOffsetChanged;
public CP14SkillTreeGraphControl()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
_skillSystem = _entManager.System<CP14SharedSkillSystem>();
_allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>();
_proto.PrototypesReloaded += _ => _allSkills = _proto.EnumeratePrototypes<CP14SkillPrototype>().OrderBy(skill => _skillSystem.GetSkillName(skill)).ToList();
OnOffsetChanged?.Invoke(_globalOffset);
}
public void UpdateState(Entity<CP14SkillStorageComponent>? player)
{
_player = player;
OnOffsetChanged?.Invoke(_globalOffset);
}
protected override void KeyBindDown(GUIBoundKeyEventArgs args)
{
base.KeyBindDown(args);
if (args.Handled)
return;
if (args.Function == EngineKeyFunctions.UIClick)
{
dragging = true;
if (_hoveredNode == null)
return;
OnNodeSelected?.Invoke(_hoveredNode);
UserInterfaceManager.ClickSound();
_selectedNode = _hoveredNode;
}
if (args.Function == EngineKeyFunctions.UIRightClick)
{
_globalOffset = new Vector2(60, 60); // Reset offset on right click
OnOffsetChanged?.Invoke(_globalOffset);
}
}
protected override void KeyBindUp(GUIBoundKeyEventArgs args)
{
base.KeyBindUp(args);
if (args.Handled || args.Function != EngineKeyFunctions.UIClick)
return;
dragging = false;
}
protected override void ExitedTree()
{
base.ExitedTree();
OnNodeSelected?.Invoke(null);
}
protected override void Draw(DrawingHandleScreen handle)
{
base.Draw(handle);
_hoveredNode = null;
if (_player == null || Tree == null)
{
return;
}
var cursor = (UserInterfaceManager.MousePositionScaled.Position * UIScale) - GlobalPixelPosition;
if (dragging)
{
var delta = cursor - _previousMousePosition;
_globalOffset += delta;
OnOffsetChanged?.Invoke(_globalOffset);
}
_previousMousePosition = cursor;
var playerSkills = _player.Value.Comp.LearnedSkills;
//Draw connection lines
foreach (var skill in _allSkills)
{
if (skill.Tree != Tree)
continue;
var fromPos = skill.SkillUiPosition * GridSize * UIScale + _globalOffset;
foreach (var req in skill.Restrictions)
{
switch (req)
{
case NeedPrerequisite prerequisite:
if (!_proto.TryIndex(prerequisite.Prerequisite, out var prerequisiteSkill))
continue;
if (prerequisiteSkill.Tree != Tree)
continue;
var learned = playerSkills.Contains(skill.ID);
var toPos = prerequisiteSkill.SkillUiPosition * GridSize * UIScale + _globalOffset;
var color = learned ? Color.White : Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f));
handle.DrawLine(fromPos, toPos, color);
break;
}
}
}
//Draw skill icons over lines
foreach (var skill in _allSkills)
{
if (skill.Tree != Tree)
continue;
//TODO: Not optimized, recalculates every frame. Needs to be calculated on state update and cached.
var canBeLearned = _skillSystem.CanLearnSkill(_player.Value, skill, _player.Value.Comp);
var pos = skill.SkillUiPosition * GridSize * UIScale + _globalOffset;
// Base skill icon
var baseTexture = skill.Icon.Frame0();
var baseSize = new Vector2(baseTexture.Width, baseTexture.Height) * 2;
var baseQuad = new UIBox2(pos - baseSize / 2, pos + baseSize / 2);
var hovered = (cursor - pos).LengthSquared() <= (baseSize.X / 2) * (baseSize.X / 2);
// Frame
var frameTexture = Tree.FrameIcon.Frame0();
var frameSize = new Vector2(frameTexture.Width, frameTexture.Height) * 2;
var frameQuad = new UIBox2(pos - frameSize / 2, pos + frameSize / 2);
handle.DrawTextureRect(frameTexture, frameQuad, canBeLearned ? Color.White : Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f)));
// Selected Skill
if (_selectedNode == skill)
{
var selectedTexture = Tree.SelectedIcon.Frame0();
var selectedSize = new Vector2(selectedTexture.Width, selectedTexture.Height) * 2;
var selectedQuad = new UIBox2(pos - selectedSize / 2, pos + selectedSize / 2);
handle.DrawTextureRect(selectedTexture, selectedQuad, Color.White);
}
// Hovered Skill
if (hovered)
{
_hoveredNode = skill;
var hoveredTexture = Tree.HoveredIcon.Frame0();
var hoveredSize = new Vector2(hoveredTexture.Width, hoveredTexture.Height) * 2;
var hoveredQuad = new UIBox2(pos - hoveredSize / 2, pos + hoveredSize / 2);
handle.DrawTextureRect(hoveredTexture, hoveredQuad, Color.White);
}
var learned = playerSkills.Contains(skill.ID);
var allowedToLearn = _skillSystem.AllowedToLearn(_player.Value, skill, _player.Value.Comp);
// Learned Skill
if (learned)
{
var learnedTexture = Tree.LearnedIcon.Frame0();
var learnedSize = new Vector2(learnedTexture.Width, learnedTexture.Height) * 2;
var learnedQuad = new UIBox2(pos - learnedSize / 2, pos + learnedSize / 2);
handle.DrawTextureRect(learnedTexture, learnedQuad, Color.White);
}
else if (canBeLearned)
{
var availableTexture = Tree.AvailableIcon.Frame0();
var availableSize = new Vector2(availableTexture.Width, availableTexture.Height) * 2;
var availableQuad = new UIBox2(pos - availableSize / 2, pos + availableSize / 2);
handle.DrawTextureRect(availableTexture, availableQuad, Color.White);
}
var iconColor = allowedToLearn switch
{
true when !learned => Color.FromSrgb(new Color(0.7f, 0.7f, 0.7f)),
false when !learned => Color.FromSrgb(new Color(0f, 0f, 0f)),
_ => Color.White
};
handle.DrawTextureRect(baseTexture, baseQuad, iconColor);
}
}
}

View File

@@ -0,0 +1,158 @@
using Content.Client._CP14.Knowledge;
using Content.Client._CP14.UserInterface.Systems.Knowledge.Windows;
using Content.Client.Gameplay;
using Content.Client.UserInterface.Controls;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.UserInterface.Systems.Character;
[UsedImplicitly]
public sealed class CP14KnowledgeUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>,
IOnSystemChanged<ClientCP14KnowledgeSystem>
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[UISystemDependency] private readonly ClientCP14KnowledgeSystem _knowledge = default!;
private CP14KnowledgeWindow? _window;
private MenuButton? KnowledgeButton => UIManager.GetActiveUIWidgetOrNull<MenuBar.Widgets.GameTopMenuBar>()?.CP14KnowledgeButton;
public void OnStateEntered(GameplayState state)
{
DebugTools.Assert(_window == null);
_window = UIManager.CreateWindow<CP14KnowledgeWindow>();
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
CommandBinds.Builder
.Bind(ContentKeyFunctions.CP14OpenKnowledgeMenu,
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
.Register<CP14KnowledgeUIController>();
}
public void OnStateExited(GameplayState state)
{
if (_window != null)
{
_window.Dispose();
_window = null;
}
CommandBinds.Unregister<CP14KnowledgeUIController>();
}
public void OnSystemLoaded(ClientCP14KnowledgeSystem system)
{
system.OnKnowledgeUpdate += KnowledgeUpdated;
_player.LocalPlayerDetached += CharacterDetached;
}
public void OnSystemUnloaded(ClientCP14KnowledgeSystem system)
{
system.OnKnowledgeUpdate -= KnowledgeUpdated;
_player.LocalPlayerDetached -= CharacterDetached;
}
public void UnloadButton()
{
if (KnowledgeButton is null)
return;
KnowledgeButton.OnPressed -= KnowledgeButtonPressed;
}
public void LoadButton()
{
if (KnowledgeButton is null)
return;
KnowledgeButton.OnPressed += KnowledgeButtonPressed;
if (_window is null)
return;
_window.OnClose += DeactivateButton;
_window.OnOpen += ActivateButton;
}
private void DeactivateButton()
{
KnowledgeButton!.Pressed = false;
}
private void ActivateButton()
{
KnowledgeButton!.Pressed = true;
}
private void KnowledgeUpdated(ClientCP14KnowledgeSystem.KnowledgeData data)
{
if (_window is null)
return;
_window.KnowledgeContent.RemoveAllChildren();
var (entity, allKnowledge) = data;
foreach (var knowledge in allKnowledge)
{
if (!_proto.TryIndex(knowledge, out var indexedKnowledge))
continue;
var knowledgeButton = new Button()
{
Access = AccessLevel.Public,
Text = Loc.GetString(indexedKnowledge.Name),
ToolTip = Loc.GetString(indexedKnowledge.Desc),
TextAlign = Label.AlignMode.Center,
};
_window.KnowledgeContent.AddChild(knowledgeButton);
}
}
private void CharacterDetached(EntityUid uid)
{
CloseWindow();
}
private void KnowledgeButtonPressed(BaseButton.ButtonEventArgs args)
{
ToggleWindow();
}
private void CloseWindow()
{
_window?.Close();
}
private void ToggleWindow()
{
if (_window == null)
return;
if (KnowledgeButton != null)
{
KnowledgeButton.SetClickPressed(!_window.IsOpen);
}
if (_window.IsOpen)
{
CloseWindow();
}
else
{
_knowledge.RequestKnowledgeInfo();
_window.Open();
}
}
}

View File

@@ -0,0 +1,13 @@
<windows:CP14KnowledgeWindow
xmlns="https://spacestation14.io"
xmlns:cc="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:windows="clr-namespace:Content.Client._CP14.UserInterface.Systems.Knowledge.Windows"
Title="{Loc 'cp14-knowledge-info-title'}"
MinWidth="400"
MinHeight="200">
<ScrollContainer>
<BoxContainer Name="KnowledgeContent" Margin="5" Access="Public" Orientation="Vertical">
</BoxContainer>
</ScrollContainer>
</windows:CP14KnowledgeWindow>

View File

@@ -2,12 +2,12 @@ using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
namespace Content.Client._CP14.UserInterface.Systems.Skill.Window;
namespace Content.Client._CP14.UserInterface.Systems.Knowledge.Windows;
[GenerateTypedNameReferences]
public sealed partial class CP14SkillWindow : DefaultWindow
public sealed partial class CP14KnowledgeWindow : DefaultWindow
{
public CP14SkillWindow()
public CP14KnowledgeWindow()
{
RobustXamlLoader.Load(this);
}

View File

@@ -1,266 +0,0 @@
using System.Linq;
using System.Numerics;
using System.Text;
using Content.Client._CP14.Skill;
using Content.Client._CP14.Skill.Ui;
using Content.Client._CP14.UserInterface.Systems.Skill.Window;
using Content.Client.Gameplay;
using Content.Client.UserInterface.Controls;
using Content.Shared._CP14.Skill.Components;
using Content.Shared._CP14.Skill.Prototypes;
using Content.Shared.Input;
using JetBrains.Annotations;
using Robust.Client.Player;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controllers;
using Robust.Client.UserInterface.Controls;
using Robust.Client.Utility;
using Robust.Shared.Input.Binding;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client._CP14.UserInterface.Systems.Skill;
[UsedImplicitly]
public sealed class CP14SkillUIController : UIController, IOnStateEntered<GameplayState>, IOnStateExited<GameplayState>,
IOnSystemChanged<CP14ClientSkillSystem>
{
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[UISystemDependency] private readonly CP14ClientSkillSystem _skill = default!;
private CP14SkillWindow? _window;
private CP14SkillPrototype? _selectedSkill;
private MenuButton? SkillButton => UIManager.GetActiveUIWidgetOrNull<Client.UserInterface.Systems.MenuBar.Widgets.GameTopMenuBar>()?.CP14SkillButton;
public void OnStateEntered(GameplayState state)
{
DebugTools.Assert(_window == null);
_window = UIManager.CreateWindow<CP14SkillWindow>();
LayoutContainer.SetAnchorPreset(_window, LayoutContainer.LayoutPreset.CenterTop);
CommandBinds.Builder
.Bind(ContentKeyFunctions.CP14OpenSkillMenu,
InputCmdHandler.FromDelegate(_ => ToggleWindow()))
.Register<CP14SkillUIController>();
_window.LearnButton.OnPressed += _ => _skill.RequestLearnSkill(_player.LocalEntity, _selectedSkill);
_window.GraphControl.OnNodeSelected += SelectNode;
_window.GraphControl.OnOffsetChanged += offset =>
{
_window.ParallaxBackground.Offset = -offset * 0.25f + new Vector2(1000,1000); //hardcoding is bad
};
}
public void OnStateExited(GameplayState state)
{
if (_window != null)
{
_window.GraphControl.OnNodeSelected -= SelectNode;
_window.Dispose();
_window = null;
}
CommandBinds.Unregister<CP14SkillUIController>();
}
public void OnSystemLoaded(CP14ClientSkillSystem system)
{
system.OnSkillUpdate += UpdateState;
_player.LocalPlayerDetached += CharacterDetached;
}
public void OnSystemUnloaded(CP14ClientSkillSystem system)
{
system.OnSkillUpdate -= UpdateState;
_player.LocalPlayerDetached -= CharacterDetached;
}
public void UnloadButton()
{
if (SkillButton is null)
return;
SkillButton.OnPressed -= SkillButtonPressed;
}
public void LoadButton()
{
if (SkillButton is null)
return;
SkillButton.OnPressed += SkillButtonPressed;
if (_window is null)
return;
_window.OnClose += DeactivateButton;
_window.OnOpen += ActivateButton;
}
private void DeactivateButton()
{
SkillButton!.Pressed = false;
}
private void ActivateButton()
{
SkillButton!.Pressed = true;
}
private void SelectNode(CP14SkillPrototype? skill)
{
if (_window is null)
return;
if (_player.LocalEntity == null)
return;
_selectedSkill = skill;
if (skill == null)
{
_window.SkillName.Text = string.Empty;
_window.SkillDescription.Text = string.Empty;
_window.SkillView.Texture = null;
_window.LearnButton.Disabled = true;
}
else
{
_window.SkillName.Text = _skill.GetSkillName(skill);
_window.SkillDescription.SetMessage(GetSkillDescription(skill));
_window.SkillView.Texture = skill.Icon.Frame0();
_window.LearnButton.Disabled = !_skill.CanLearnSkill(_player.LocalEntity.Value, skill);
_window.SkillCost.Text = skill.LearnCost.ToString();
}
}
private FormattedMessage GetSkillDescription(CP14SkillPrototype skill)
{
var msg = new FormattedMessage();
if (_player.LocalEntity == null)
return msg;
var sb = new StringBuilder();
//Description
sb.Append(_skill.GetSkillDescription(skill) + "\n \n");
//Restrictions
foreach (var req in skill.Restrictions)
{
var color = req.Check(_entManager, _player.LocalEntity.Value) ? "green" : "red";
sb.Append($"- [color={color}]{req.GetDescription(_entManager, _proto)}[/color]\n");
}
msg.TryAddMarkup(sb.ToString(), out _);
return msg;
}
private void UpdateState(EntityUid player)
{
if (_window is null)
return;
if (!EntityManager.TryGetComponent<CP14SkillStorageComponent>(player, out var storage))
return;
_window.GraphControl.UpdateState((player, storage));
// Reselect for update state
SelectNode(_selectedSkill);
//If tree not selected, select the first one
if (_window.GraphControl.Tree == null && storage.Progress.Count > 0)
{
var firstTree = storage.Progress.First().Key;
if (_proto.TryIndex(firstTree, out var indexedTree))
{
SelectTree(indexedTree, storage); // Set the first tree from the player's progress
}
}
// Update the experience points for the selected tree
var playerProgress = storage.Progress;
if (_window.GraphControl.Tree is not null && playerProgress.TryGetValue(_window.GraphControl.Tree, out var skillpoint))
{
_window.ExpPointLabel.Text = skillpoint.ToString();
}
_window.LevelLabel.Text = $"{storage.SkillsSumExperience}/{storage.ExperienceMaxCap}";
_window.TreeTabsContainer.RemoveAllChildren();
foreach (var (tree, progress) in storage.Progress)
{
if (!_proto.TryIndex(tree, out var indexedTree))
continue;
var treeButton2 = new CP14SkillTreeButtonControl(indexedTree.Color, Loc.GetString(indexedTree.Name));
treeButton2.ToolTip = Loc.GetString(indexedTree.Desc ?? string.Empty);
treeButton2.OnPressed += () =>
{
SelectTree(indexedTree, storage);
};
_window.TreeTabsContainer.AddChild(treeButton2);
}
}
private void SelectTree(CP14SkillTreePrototype tree, CP14SkillStorageComponent storage)
{
if (_window == null)
return;
_window.GraphControl.Tree = tree;
_window.ParallaxBackground.ParallaxPrototype = tree.Parallax;
_window.TreeName.Text = Loc.GetString(tree.Name);
var playerProgress = storage.Progress;
_window.ExpPointLabel.Text = playerProgress.TryGetValue(tree, out var skillpoint) ? skillpoint.ToString() : "0";
}
private void CharacterDetached(EntityUid uid)
{
CloseWindow();
}
private void SkillButtonPressed(BaseButton.ButtonEventArgs args)
{
ToggleWindow();
}
private void CloseWindow()
{
_window?.Close();
}
private void ToggleWindow()
{
if (_window == null)
return;
if (SkillButton != null)
{
SkillButton.SetClickPressed(!_window.IsOpen);
}
if (_window.IsOpen)
{
CloseWindow();
}
else
{
_skill.RequestSkillData();
_window.Open();
}
}
}

View File

@@ -1,89 +0,0 @@
<window:CP14SkillWindow
xmlns="https://spacestation14.io"
xmlns:window="clr-namespace:Content.Client._CP14.UserInterface.Systems.Skill.Window"
xmlns:graphics="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:parallax="clr-namespace:Content.Client.Parallax"
xmlns:ui1="clr-namespace:Content.Client._CP14.Skill.Ui"
Title="{Loc 'cp14-skill-info-title'}"
MinSize="700 350"
SetSize="980 550">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
<!-- Selected Skill -->
<BoxContainer Margin="10 10 10 10" MaxWidth="240" SetWidth="240" Orientation="Vertical"
HorizontalExpand="False" VerticalExpand="True">
<!-- Skill View -->
<PanelContainer Name="BackPanel" HorizontalAlignment="Center">
<PanelContainer.PanelOverride>
<graphics:StyleBoxTexture Modulate="#1B1B1E" PatchMarginBottom="10" PatchMarginLeft="10"
PatchMarginRight="10" PatchMarginTop="10" />
</PanelContainer.PanelOverride>
<BoxContainer HorizontalExpand="True" VerticalExpand="True" >
<TextureRect Stretch="Scale" Name="SkillView" SetSize="64 64" HorizontalAlignment="Center" VerticalAlignment="Center" MinSize="64 64"
HorizontalExpand="True" VerticalExpand="True" Access="Public"/>
</BoxContainer>
</PanelContainer>
<customControls:HSeparator StyleClasses="HighDivider" Margin="0 15 0 10" />
<!-- Skill Data -->
<BoxContainer Name="NodeViewContainer" Orientation="Vertical" VerticalExpand="True">
<ScrollContainer HScrollEnabled="False" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer Orientation="Vertical" HorizontalExpand="False" VerticalExpand="True">
<BoxContainer Name="InfoContainer" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer HorizontalExpand="True">
<Label Name="SkillName" Access="Public" StyleClasses="LabelHeadingBigger" VAlign="Center"
HorizontalExpand="True" HorizontalAlignment="Center" />
</BoxContainer>
<!-- Skill Cost -->
<BoxContainer HorizontalExpand="True">
<RichTextLabel HorizontalExpand="True" Access="Public" Text="{Loc 'cp14-skill-menu-learncost'}" Margin="0 10 0 10" />
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Left" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Skills/skillpoint.png"/>
<RichTextLabel Name="SkillCost" Access="Public" Text="0"/>
</BoxContainer>
<!-- Skill Description -->
<BoxContainer HorizontalExpand="True">
<RichTextLabel Name="SkillDescription" HorizontalExpand="True" Access="Public"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
</ScrollContainer>
<Control MinHeight="5"/>
<!-- Skill Buttons -->
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Button Name="LearnButton" Text="{Loc 'cp14-skill-menu-learn-button'}" StyleClasses="OpenRight" HorizontalExpand="True" MinHeight="35" Access="Public"/>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<customControls:VSeparator StyleClasses="LowDivider" />
<!-- Skills Tree -->
<BoxContainer Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True">
<BoxContainer HorizontalExpand="True">
<Label Name="TreeName" Access="Public" StyleClasses="LabelHeadingBigger" VAlign="Center"
HorizontalExpand="True" HorizontalAlignment="Center" />
</BoxContainer>
<PanelContainer Margin="10 10 10 10" HorizontalExpand="True" VerticalExpand="True" RectClipContent="True">
<parallax:ParallaxControl Name="ParallaxBackground" ScaleX="4" ScaleY="4" Access="Public" SpeedX="10" SpeedY="5"/>
<BoxContainer Margin="10 10 10 10" Orientation="Horizontal" HorizontalExpand="True" VerticalExpand="True">
<ui1:CP14SkillTreeGraphControl Name="GraphControl" HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Access="Public"/>
</BoxContainer>
<!-- Tree Tabs -->
<BoxContainer Margin="10 10 10 10" VerticalAlignment="Top" HorizontalAlignment="Right" Orientation="Vertical" VerticalExpand="True">
<BoxContainer Name="TreeTabsContainer" Orientation="Vertical" HorizontalExpand="True" VerticalExpand="True" Access="Public"/>
</BoxContainer>
<!-- Experience Data -->
<BoxContainer Margin="10 10 10 10" VerticalAlignment="Bottom" HorizontalAlignment="Center" Orientation="Horizontal" VerticalExpand="True">
<RichTextLabel VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Loc 'cp14-skill-menu-skillpoints'}" StyleClasses="LabelKeyText"/>
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Skills/skillpoint.png"/>
<RichTextLabel Margin="0 0 20 0" Name="ExpPointLabel" VerticalAlignment="Center" HorizontalAlignment="Left" Text="0" StyleClasses="LabelKeyText" Access="Public"/>
<RichTextLabel VerticalAlignment="Center" HorizontalAlignment="Left" Text="{Loc 'cp14-skill-menu-level'}" StyleClasses="LabelKeyText"/>
<TextureRect VerticalAlignment="Center" HorizontalAlignment="Center" TextureScale="2, 2" TexturePath="/Textures/_CP14/Interface/Skills/skillpoint.png"/>
<RichTextLabel Margin="0 0 0 0" Name="LevelLabel" VerticalAlignment="Center" HorizontalAlignment="Left" Text="0" StyleClasses="LabelKeyText" Access="Public"/>
</BoxContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</window:CP14SkillWindow>

View File

@@ -1,94 +0,0 @@
using System.Collections.Generic;
using System.Linq;
using Content.Server.DeviceLinking.Systems;
using Content.Shared.DeviceLinking;
using Content.Shared.Prototypes;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
namespace Content.IntegrationTests.Tests.DeviceLinking;
public sealed class DeviceLinkingTest
{
private const string PortTesterProtoId = "DeviceLinkingSinkPortTester";
[TestPrototypes]
private const string Prototypes = $@"
- type: entity
id: {PortTesterProtoId}
components:
- type: DeviceLinkSource
ports:
- Output
";
/// <summary>
/// Spawns every entity that has a <see cref="DeviceLinkSinkComponent"/>
/// and sends a signal to every port to make sure nothing causes an error.
/// </summary>
[Test]
public async Task AllDeviceLinkSinksWorkTest()
{
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var compFact = server.ResolveDependency<IComponentFactory>();
var mapMan = server.ResolveDependency<IMapManager>();
var mapSys = server.System<SharedMapSystem>();
var deviceLinkSys = server.System<DeviceLinkSystem>();
var prototypes = server.ProtoMan.EnumeratePrototypes<EntityPrototype>();
await server.WaitAssertion(() =>
{
Assert.Multiple(() =>
{
foreach (var proto in prototypes)
{
if (proto.Abstract || pair.IsTestPrototype(proto))
continue;
if (!proto.TryGetComponent<DeviceLinkSinkComponent>(out var protoSinkComp, compFact))
continue;
foreach (var port in protoSinkComp.Ports)
{
// Create a map for each entity/port combo so they can't interfere
mapSys.CreateMap(out var mapId);
var grid = mapMan.CreateGridEntity(mapId);
mapSys.SetTile(grid.Owner, grid.Comp, Vector2i.Zero, new Tile(1));
var coord = new EntityCoordinates(grid.Owner, 0, 0);
// Spawn the sink entity
var sinkEnt = server.EntMan.SpawnEntity(proto.ID, coord);
// Get the actual sink component, since the one we got from the prototype doesn't have its owner set up
Assert.That(server.EntMan.TryGetComponent<DeviceLinkSinkComponent>(sinkEnt, out var sinkComp),
$"{proto.ID} does not have a DeviceLinkSinkComponent!");
// Spawn the tester
var sourceEnt = server.EntMan.SpawnEntity(PortTesterProtoId, coord);
Assert.That(server.EntMan.TryGetComponent<DeviceLinkSourceComponent>(sourceEnt, out var sourceComp),
$"Tester prototype does not have a DeviceLinkSourceComponent!");
// Create a link from the tester's output to the target port on the sink
deviceLinkSys.SaveLinks(null,
sourceEnt,
sinkEnt,
[("Output", port.Id)],
sourceComp,
sinkComp);
// Send a signal to the port
Assert.DoesNotThrow(() => { deviceLinkSys.InvokePort(sourceEnt, "Output", null, sourceComp); },
$"Exception thrown while triggering port {port.Id} of sink device {proto.ID}");
mapSys.DeleteMap(mapId);
}
}
});
});
await pair.CleanReturnAsync();
}
}

View File

@@ -1,91 +0,0 @@
using Content.IntegrationTests.Tests.Interaction;
using Content.Shared.Projectiles;
using Robust.Shared.Network;
namespace Content.IntegrationTests.Tests.Embedding;
public sealed class EmbedTest : InteractionTest
{
/// <summary>
/// Embeddable entity that will be thrown at the target.
/// </summary>
private const string EmbeddableProtoId = "SurvivalKnife";
/// <summary>
/// Target entity that the thrown item will embed into.
/// </summary>
private const string TargetProtoId = "AirlockGlass";
/// <summary>
/// Embeds an entity with a <see cref="EmbeddableProjectileComponent"/> into a target,
/// then disconnects the client. Intended to reveal any clientside issues that might
/// occur due to reparenting during cleanup.
/// </summary>
[Test]
public async Task TestDisconnectWhileEmbedded()
{
// Spawn the target we're going to throw at
await SpawnTarget(TargetProtoId);
// Give the player the embeddable to throw
var projectile = await PlaceInHands(EmbeddableProtoId);
Assert.That(TryComp<EmbeddableProjectileComponent>(projectile, out var embedComp),
$"{EmbeddableProtoId} does not have EmbeddableProjectileComponent");
// Make sure the projectile isn't already embedded into anything
Assert.That(embedComp.EmbeddedIntoUid, Is.Null,
$"Projectile already embedded into {SEntMan.ToPrettyString(embedComp.EmbeddedIntoUid)}");
// Have the player throw the embeddable at the target
await ThrowItem();
// Wait a moment for the item to hit and embed
await RunSeconds(0.5f);
// Make sure the projectile is embedded into the target
Assert.That(embedComp.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
"Projectile not embedded into target");
// Disconnect the client
var cNetMgr = Client.ResolveDependency<IClientNetManager>();
await Client.WaitPost(Client.EntMan.FlushEntities);
await Pair.RunTicksSync(1);
}
/// <summary>
/// Embeds an entity with a <see cref="EmbeddableProjectileComponent"/> into a target,
/// then deletes the target and makes sure the embeddable is not deleted.
/// </summary>
[Test]
public async Task TestEmbedDetach()
{
// Spawn the target we're going to throw at
await SpawnTarget(TargetProtoId);
// Give the player the embeddable to throw
var projectile = await PlaceInHands(EmbeddableProtoId);
Assert.That(TryComp<EmbeddableProjectileComponent>(projectile, out var embedComp),
$"{EmbeddableProtoId} does not have EmbeddableProjectileComponent");
// Make sure the projectile isn't already embedded into anything
Assert.That(embedComp.EmbeddedIntoUid, Is.Null,
$"Projectile already embedded into {SEntMan.ToPrettyString(embedComp.EmbeddedIntoUid)}");
// Have the player throw the embeddable at the target
await ThrowItem();
// Wait a moment for the item to hit and embed
await RunSeconds(0.5f);
// Make sure the projectile is embedded into the target
Assert.That(embedComp.EmbeddedIntoUid, Is.EqualTo(ToServer(Target)),
"Projectile not embedded into target");
// Delete the target
await Delete(Target.Value);
await RunTicks(1);
// Make sure the embeddable wasn't deleted with the target
AssertExists(projectile);
await AssertEntityLookup(EmbeddableProtoId);
}
}

View File

@@ -40,7 +40,6 @@ namespace Content.IntegrationTests.Tests
.Where(p => !pair.IsTestPrototype(p))
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Where(p => !p.Components.ContainsKey("CP14BiomeSpawner")) // CP14 this component delete all entities on this tile
.Select(p => p.ID)
.ToList();
@@ -104,7 +103,6 @@ namespace Content.IntegrationTests.Tests
.Where(p => !pair.IsTestPrototype(p))
.Where(p => !p.Components.ContainsKey("MapGrid")) // This will smash stuff otherwise.
.Where(p => !p.Components.ContainsKey("RoomFill")) // This comp can delete all entities, and spawn others
.Where(p => !p.Components.ContainsKey("CP14BiomeSpawner")) // CP14 this component delete all entities on this tile
.Select(p => p.ID)
.ToList();
foreach (var protoId in protoIds)
@@ -346,7 +344,6 @@ namespace Content.IntegrationTests.Tests
"DebugExceptionStartup",
"GridFill",
"RoomFill",
"CP14BiomeSpawner", // CP14 this component delete all entities on this tile
"Map", // We aren't testing a map entity in this test
"MapGrid",
"Broadphase",

View File

@@ -155,7 +155,7 @@ public sealed class NukeOpsTest
// The game rule exists, and all the stations/shuttles/maps are properly initialized
var rule = entMan.AllComponents<NukeopsRuleComponent>().Single();
var ruleComp = rule.Component;
var gridsRule = entMan.GetComponent<RuleGridsComponent>(rule.Uid);
var gridsRule = entMan.AllComponents<RuleGridsComponent>().Single().Component;
foreach (var grid in gridsRule.MapGrids)
{
Assert.That(entMan.EntityExists(grid));

View File

@@ -19,19 +19,16 @@ public sealed class LocalizedDatasetPrototypeTest
var protos = protoMan.EnumeratePrototypes<LocalizedDatasetPrototype>().OrderBy(p => p.ID);
Assert.Multiple(() =>
// Check each prototype
foreach (var proto in protos)
{
// Check each prototype
foreach (var proto in protos)
// Check each value in the prototype
foreach (var locId in proto.Values)
{
// Check each value in the prototype
foreach (var locId in proto.Values)
{
// Make sure the localization manager has a string for the LocId
Assert.That(localizationMan.HasString(locId), $"LocalizedDataset {proto.ID} with prefix \"{proto.Values.Prefix}\" specifies {proto.Values.Count} entries, but no localized string was found matching {locId}!");
}
// Make sure the localization manager has a string for the LocId
Assert.That(localizationMan.HasString(locId), $"LocalizedDataset {proto.ID} with prefix \"{proto.Values.Prefix}\" specifies {proto.Values.Count} entries, but no localized string was found matching {locId}!");
}
});
}
await pair.CleanReturnAsync();
}

View File

@@ -22,7 +22,6 @@ using Robust.Shared.EntitySerialization.Systems;
using Robust.Shared.IoC;
using Robust.Shared.Utility;
using YamlDotNet.RepresentationModel;
using Robust.Shared.Map.Events;
namespace Content.IntegrationTests.Tests
{
@@ -70,6 +69,7 @@ namespace Content.IntegrationTests.Tests
"Dev",
"MeteorArena",
"Comoss",
"Factoria",
//CrystallEdge Map replacement end
};
@@ -213,12 +213,9 @@ namespace Content.IntegrationTests.Tests
}
var deps = server.ResolveDependency<IEntitySystemManager>().DependencyCollection;
var ev = new BeforeEntityReadEvent();
server.EntMan.EventBus.RaiseEvent(EventSource.Local, ev);
foreach (var map in v7Maps)
{
Assert.That(IsPreInit(map, loader, deps, ev.RenamedPrototypes, ev.DeletedPrototypes));
Assert.That(IsPreInit(map, loader, deps));
}
// Check that the test actually does manage to catch post-init maps and isn't just blindly passing everything.
@@ -231,12 +228,12 @@ namespace Content.IntegrationTests.Tests
// First check that a pre-init version passes
var path = new ResPath($"{nameof(NoSavedPostMapInitTest)}.yml");
Assert.That(loader.TrySaveMap(id, path));
Assert.That(IsPreInit(path, loader, deps, ev.RenamedPrototypes, ev.DeletedPrototypes));
Assert.That(IsPreInit(path, loader, deps));
// and the post-init version fails.
await server.WaitPost(() => mapSys.InitializeMap(id));
Assert.That(loader.TrySaveMap(id, path));
Assert.That(IsPreInit(path, loader, deps, ev.RenamedPrototypes, ev.DeletedPrototypes), Is.False);
Assert.That(IsPreInit(path, loader, deps), Is.False);
await pair.CleanReturnAsync();
}
@@ -269,11 +266,7 @@ namespace Content.IntegrationTests.Tests
});
}
private bool IsPreInit(ResPath map,
MapLoaderSystem loader,
IDependencyCollection deps,
Dictionary<string, string> renamedPrototypes,
HashSet<string> deletedPrototypes)
private bool IsPreInit(ResPath map, MapLoaderSystem loader, IDependencyCollection deps)
{
if (!loader.TryReadFile(map, out var data))
{
@@ -281,12 +274,7 @@ namespace Content.IntegrationTests.Tests
return false;
}
var reader = new EntityDeserializer(deps,
data,
DeserializationOptions.Default,
renamedPrototypes,
deletedPrototypes);
var reader = new EntityDeserializer(deps, data, DeserializationOptions.Default);
if (!reader.TryProcessData())
{
Assert.Fail($"Failed to process {map}");

View File

@@ -35,6 +35,7 @@ public sealed class PrototypeSaveTest
await using var pair = await PoolManager.GetServerClient();
var server = pair.Server;
var mapManager = server.ResolveDependency<IMapManager>();
var entityMan = server.ResolveDependency<IEntityManager>();
var prototypeMan = server.ResolveDependency<IPrototypeManager>();
var seriMan = server.ResolveDependency<ISerializationManager>();

View File

@@ -102,12 +102,6 @@ public sealed class StoreTests
+ $"flag as 'true'. This marks the fact that cost modifier of discount is not applied properly!"
);
// The storeComponent returns discounted items with conditions randomly, so we remove these to sanitize the data.
foreach (var discountedItem in discountedListingItems)
{
discountedItem.Conditions = null;
}
// Refund action requests re-generation of listing items so we will be re-acquiring items from component a lot of times.
var itemIds = discountedListingItems.Select(x => x.ID);
foreach (var itemId in itemIds)
@@ -146,9 +140,6 @@ public sealed class StoreTests
// get refreshed item after refund re-generated items
discountedListingItem = storeComponent.FullListingsCatalog.First(x => x.ID == itemId);
// The storeComponent can give a discounted item a condition at random, so we remove it to sanitize the data.
discountedListingItem.Conditions = null;
var afterRefundBalance = storeComponent.Balance[UplinkSystem.TelecrystalCurrencyPrototype];
Assert.That(afterRefundBalance.Value, Is.EqualTo(originalBalance.Value), "Expected refund to return all discounted cost value.");
Assert.That(

View File

@@ -1,205 +0,0 @@
using System.Linq;
using Content.IntegrationTests.Tests.Interaction;
using Content.Server.VendingMachines;
using Content.Shared.Damage;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.VendingMachines;
namespace Content.IntegrationTests.Tests.Vending;
public sealed class VendingInteractionTest : InteractionTest
{
private const string VendingMachineProtoId = "InteractionTestVendingMachine";
private const string VendedItemProtoId = "InteractionTestItem";
private const string RestockBoxProtoId = "InteractionTestRestockBox";
private const string RestockBoxOtherProtoId = "InteractionTestRestockBoxOther";
[TestPrototypes]
private const string TestPrototypes = $@"
- type: entity
parent: BaseItem
id: {VendedItemProtoId}
name: {VendedItemProtoId}
- type: vendingMachineInventory
id: InteractionTestVendingInventory
startingInventory:
{VendedItemProtoId}: 5
- type: vendingMachineInventory
id: InteractionTestVendingInventoryOther
startingInventory:
{VendedItemProtoId}: 5
- type: entity
parent: BaseVendingMachineRestock
id: {RestockBoxProtoId}
components:
- type: VendingMachineRestock
canRestock:
- InteractionTestVendingInventory
- type: entity
parent: BaseVendingMachineRestock
id: {RestockBoxOtherProtoId}
components:
- type: VendingMachineRestock
canRestock:
- InteractionTestVendingInventoryOther
- type: entity
id: {VendingMachineProtoId}
parent: VendingMachine
components:
- type: VendingMachine
pack: InteractionTestVendingInventory
ejectDelay: 0 # no delay to speed up tests
- type: Sprite
sprite: error.rsi
";
[Test]
public async Task InteractUITest()
{
await SpawnTarget(VendingMachineProtoId);
// Should start with no BUI open
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "BUI was open unexpectedly.");
// Unpowered vending machine does not open BUI
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "BUI opened without power.");
// Power the vending machine
var apc = await SpawnEntity("APCBasic", SEntMan.GetCoordinates(TargetCoords));
await RunTicks(1);
// Interacting with powered vending machine opens BUI
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), "BUI failed to open.");
// Interacting with it again closes the BUI
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "BUI failed to close on interaction.");
// Reopen BUI for the next check
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), "BUI failed to reopen.");
// Remove power
await Delete(apc);
await RunTicks(1);
// The BUI should close when power is lost
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "BUI failed to close on power loss.");
}
[Test]
public async Task DispenseItemTest()
{
await SpawnTarget(VendingMachineProtoId);
var vendorEnt = SEntMan.GetEntity(Target.Value);
var vendingSystem = SEntMan.System<VendingMachineSystem>();
var items = vendingSystem.GetAllInventory(vendorEnt);
// Verify initial item count
Assert.That(items, Is.Not.Empty, $"{VendingMachineProtoId} spawned with no items.");
Assert.That(items.First().Amount, Is.EqualTo(5), $"{VendingMachineProtoId} spawned with unexpected item count.");
// Power the vending machine
await SpawnEntity("APCBasic", SEntMan.GetCoordinates(TargetCoords));
await RunTicks(1);
// Open the BUI
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), "BUI failed to open.");
// Request an item be dispensed
var ev = new VendingMachineEjectMessage(InventoryType.Regular, VendedItemProtoId);
await SendBui(VendingMachineUiKey.Key, ev);
// Make sure the stock decreased
Assert.That(items.First().Amount, Is.EqualTo(4), "Stocked item count did not decrease.");
// Make sure the dispensed item was spawned in to the world
await AssertEntityLookup(
("APCBasic", 1),
(VendedItemProtoId, 1)
);
}
[Test]
public async Task RestockTest()
{
var vendingSystem = SEntMan.System<VendingMachineSystem>();
await SpawnTarget(VendingMachineProtoId);
var vendorEnt = SEntMan.GetEntity(Target.Value);
var items = vendingSystem.GetAllInventory(vendorEnt);
Assert.That(items, Is.Not.Empty, $"{VendingMachineProtoId} spawned with no items.");
Assert.That(items.First().Amount, Is.EqualTo(5), $"{VendingMachineProtoId} spawned with unexpected item count.");
// Try to restock with the maintenance panel closed (nothing happens)
await InteractUsing(RestockBoxProtoId);
Assert.That(items.First().Amount, Is.EqualTo(5), "Restocked without opening maintenance panel.");
// Open the maintenance panel
await InteractUsing(Screw);
// Try to restock using the wrong restock box (nothing happens)
await InteractUsing(RestockBoxOtherProtoId);
Assert.That(items.First().Amount, Is.EqualTo(5), "Restocked with wrong restock box.");
// Restock the machine
await InteractUsing(RestockBoxProtoId);
Assert.That(items.First().Amount, Is.EqualTo(10), "Restocking resulted in unexpected item count.");
}
[Test]
public async Task RepairTest()
{
await SpawnTarget(VendingMachineProtoId);
// Power the vending machine
await SpawnEntity("APCBasic", SEntMan.GetCoordinates(TargetCoords));
await RunTicks(1);
// Break it
await BreakVendor();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "BUI did not close when vending machine broke.");
// Make sure we can't open the BUI while it's broken
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), Is.False, "Opened BUI of broken vending machine.");
// Repair the vending machine
await InteractUsing(Weld);
// Make sure the BUI can open now that the machine has been repaired
await Activate();
Assert.That(IsUiOpen(VendingMachineUiKey.Key), "Failed to open BUI after repair.");
}
private async Task BreakVendor()
{
var damageableSys = SEntMan.System<DamageableSystem>();
Assert.That(TryComp<DamageableComponent>(out var damageableComp), $"{VendingMachineProtoId} does not have DamageableComponent.");
Assert.That(damageableComp.Damage.GetTotal(), Is.EqualTo(FixedPoint2.Zero), $"{VendingMachineProtoId} started with unexpected damage.");
// Damage the vending machine to the point that it breaks
var damageType = ProtoMan.Index<DamageTypePrototype>("Blunt");
var damage = new DamageSpecifier(damageType, FixedPoint2.New(100));
await Server.WaitPost(() => damageableSys.TryChangeDamage(SEntMan.GetEntity(Target), damage, ignoreResistances: true));
await RunTicks(5);
Assert.That(damageableComp.Damage.GetTotal(), Is.GreaterThan(FixedPoint2.Zero), $"{VendingMachineProtoId} did not take damage.");
}
}

View File

@@ -241,7 +241,7 @@ public sealed class AccessOverriderSystem : SharedAccessOverriderSystem
var addedTags = newAccessList.Except(oldTags).Select(tag => "+" + tag).ToList();
var removedTags = oldTags.Except(newAccessList).Select(tag => "-" + tag).ToList();
_adminLogger.Add(LogType.Action, LogImpact.High,
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(player):player} has modified {ToPrettyString(accessReaderEnt.Value):entity} with the following allowed access level holders: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
accessReaderEnt.Value.Comp.AccessLists = ConvertAccessListToHashSet(newAccessList);

View File

@@ -168,7 +168,7 @@ public sealed class IdCardConsoleSystem : SharedIdCardConsoleSystem
/*TODO: ECS SharedIdCardConsoleComponent and then log on card ejection, together with the save.
This current implementation is pretty shit as it logs 27 entries (27 lines) if someone decides to give themselves AA*/
_adminLogger.Add(LogType.Action, LogImpact.High,
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(player):player} has modified {ToPrettyString(targetId):entity} with the following accesses: [{string.Join(", ", addedTags.Union(removedTags))}] [{string.Join(", ", newAccessList)}]");
}

View File

@@ -88,7 +88,7 @@ public sealed class IdCardSystem : SharedIdCardSystem
access.Tags.Add(random.ID);
Dirty(uid, access);
_adminLogger.Add(LogType.Action, LogImpact.High,
_adminLogger.Add(LogType.Action, LogImpact.Medium,
$"{ToPrettyString(args.Microwave)} added {random.ID} access to {ToPrettyString(uid):entity}");
}

View File

@@ -180,7 +180,7 @@ public sealed class ChangeCvarCommand : IConsoleCommand
var oldValue = _configurationManager.GetCVar<object>(cvar);
_configurationManager.SetCVar(cvar, parsed);
_adminLogManager.Add(LogType.AdminCommands,
LogImpact.Extreme,
LogImpact.High,
$"{shell.Player!.Name} ({shell.Player!.UserId}) changed CVAR {cvar} from {oldValue.ToString()} to {parsed.ToString()}"
);

View File

@@ -1,19 +1,19 @@
using System.Linq;
using Content.Server.Administration.Notes;
using Content.Server.Administration.Notes;
using Content.Shared.Administration;
using Robust.Server.Player;
using Robust.Shared.Console;
namespace Content.Server.Administration.Commands;
[AdminCommand(AdminFlags.ViewNotes)]
public sealed class OpenAdminNotesCommand : LocalizedCommands
public sealed class OpenAdminNotesCommand : IConsoleCommand
{
public const string CommandName = "adminnotes";
public override string Command => CommandName;
public string Command => CommandName;
public string Description => "Opens the admin notes panel.";
public string Help => $"Usage: {Command} <notedPlayerUserId OR notedPlayerUsername>";
public override async void Execute(IConsoleShell shell, string argStr, string[] args)
public async void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (shell.Player is not { } player)
{
@@ -33,27 +33,17 @@ public sealed class OpenAdminNotesCommand : LocalizedCommands
if (dbGuid == null)
{
shell.WriteError(Loc.GetString("cmd-adminnotes-wrong-target", ("user", args[0])));
shell.WriteError($"Unable to find {args[0]} netuserid");
return;
}
notedPlayer = dbGuid.UserId;
break;
default:
shell.WriteError(Loc.GetString("cmd-adminnotes-args-error"));
shell.WriteError($"Invalid arguments.\n{Help}");
return;
}
await IoCManager.Resolve<IAdminNotesManager>().OpenEui(player, notedPlayer);
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length != 1)
return CompletionResult.Empty;
var playerMgr = IoCManager.Resolve<IPlayerManager>();
var options = playerMgr.Sessions.Select(c => c.Name).OrderBy(c => c).ToArray();
return CompletionResult.FromHintOptions(options, Loc.GetString("cmd-adminnotes-hint"));
}
}

View File

@@ -48,7 +48,7 @@ namespace Content.Server.Administration.Commands
}
else
{
if (player.Status != SessionStatus.InGame || player.AttachedEntity is not { Valid: true } playerEntity)
if (player.Status != SessionStatus.InGame || player.AttachedEntity is not {Valid: true} playerEntity)
{
shell.WriteLine("You are not in-game!");
return;
@@ -57,16 +57,14 @@ namespace Content.Server.Administration.Commands
var currentMap = _entManager.GetComponent<TransformComponent>(playerEntity).MapID;
var currentGrid = _entManager.GetComponent<TransformComponent>(playerEntity).GridUid;
var xformSystem = _entManager.System<SharedTransformSystem>();
var found = GetWarpPointByName(location)
.OrderBy(p => p.Item1, Comparer<EntityCoordinates>.Create((a, b) =>
{
// Sort so that warp points on the same grid/map are first.
// So if you have two maps loaded with the same warp points,
// it will prefer the warp points on the map you're currently on.
var aGrid = xformSystem.GetGrid(a);
var bGrid = xformSystem.GetGrid(b);
var aGrid = a.GetGridUid(_entManager);
var bGrid = b.GetGridUid(_entManager);
if (aGrid == bGrid)
{
@@ -83,8 +81,8 @@ namespace Content.Server.Administration.Commands
return 1;
}
var mapA = xformSystem.GetMapId(a);
var mapB = xformSystem.GetMapId(b);
var mapA = a.GetMapId(_entManager);
var mapB = a.GetMapId(_entManager);
if (mapA == mapB)
{
@@ -119,8 +117,10 @@ namespace Content.Server.Administration.Commands
return;
}
xformSystem.SetCoordinates(playerEntity, coords);
xformSystem.AttachToGridOrMap(playerEntity);
var xform = _entManager.GetComponent<TransformComponent>(playerEntity);
var xformSystem = _entManager.System<SharedTransformSystem>();
xform.Coordinates = coords;
xformSystem.AttachToGridOrMap(playerEntity, xform);
if (_entManager.TryGetComponent(playerEntity, out PhysicsComponent? physics))
{
_entManager.System<SharedPhysicsSystem>().SetLinearVelocity(playerEntity, Vector2.Zero, body: physics);

View File

@@ -2,19 +2,14 @@
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;
using Content.Server.Administration.Systems;
using Content.Server.Database;
using Content.Server.GameTicking;
using Content.Shared.Administration.Logs;
using Content.Shared.CCVar;
using Content.Shared.Chat;
using Content.Shared.Database;
using Content.Shared.Players.PlayTimeTracking;
using Prometheus;
using Robust.Shared;
using Robust.Shared.Configuration;
using Robust.Shared.Network;
using Robust.Shared.Player;
using Robust.Shared.Reflection;
using Robust.Shared.Timing;
@@ -30,9 +25,6 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
[Dependency] private readonly IDynamicTypeFactory _typeFactory = default!;
[Dependency] private readonly IReflectionManager _reflection = default!;
[Dependency] private readonly IDependencyCollection _dependencies = default!;
[Dependency] private readonly ISharedPlayerManager _player = default!;
[Dependency] private readonly ISharedPlaytimeManager _playtime = default!;
[Dependency] private readonly ISharedChatManager _chat = default!;
public const string SawmillId = "admin.logs";
@@ -74,7 +66,6 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
private int _queueMax;
private int _preRoundQueueMax;
private int _dropThreshold;
private int _highImpactLogPlaytime;
// Per update
private TimeSpan _nextUpdateTime;
@@ -109,8 +100,6 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
value => _preRoundQueueMax = value, true);
_configuration.OnValueChanged(CCVars.AdminLogsDropThreshold,
value => _dropThreshold = value, true);
_configuration.OnValueChanged(CCVars.AdminLogsHighLogPlaytime,
value => _highImpactLogPlaytime = value, true);
if (_metricsEnabled)
{
@@ -311,10 +300,6 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
Players = new List<AdminLogPlayer>(players.Count)
};
var adminLog = false;
var adminSys = _entityManager.SystemOrNull<AdminSystem>();
var logMessage = message;
foreach (var id in players)
{
var player = new AdminLogPlayer
@@ -324,39 +309,8 @@ public sealed partial class AdminLogManager : SharedAdminLogManager, IAdminLogMa
};
log.Players.Add(player);
if (adminSys != null)
{
var cachedInfo = adminSys.GetCachedPlayerInfo(new NetUserId(id));
if (cachedInfo != null && cachedInfo.Antag)
{
logMessage += " [ANTAG: " + cachedInfo.CharacterName + "]";
}
}
if (adminLog)
continue;
if (impact == LogImpact.Extreme) // Always chat-notify Extreme logs
adminLog = true;
if (impact == LogImpact.High) // Only chat-notify High logs if the player is below a threshold playtime
{
if (_highImpactLogPlaytime >= 0 && _player.TryGetSessionById(new NetUserId(id), out var session))
{
var playtimes = _playtime.GetPlayTimes(session);
if (playtimes.TryGetValue(PlayTimeTrackingShared.TrackerOverall, out var overallTime) &&
overallTime <= TimeSpan.FromHours(_highImpactLogPlaytime))
{
adminLog = true;
}
}
}
}
if (adminLog)
_chat.SendAdminAlert(logMessage);
if (preRound)
{
_preRoundLogQueue.Enqueue(log);

View File

@@ -221,7 +221,6 @@ public sealed class AdminSystem : EntitySystem
var name = data.UserName;
var entityName = string.Empty;
var identityName = string.Empty;
var sortWeight = 0;
// Visible (identity) name can be different from real name
if (session?.AttachedEntity != null)
@@ -235,10 +234,8 @@ public sealed class AdminSystem : EntitySystem
// Starting role, antagonist status and role type
RoleTypePrototype roleType = new();
var startingRole = string.Empty;
if (_minds.TryGetMind(session, out var mindId, out var mindComp) && mindComp is not null)
if (_minds.TryGetMind(session, out var mindId, out var mindComp))
{
sortWeight = _role.GetRoleCompByTime(mindComp)?.Comp.SortWeight ?? 0;
if (_proto.TryIndex(mindComp.RoleType, out var role))
roleType = role;
else
@@ -262,19 +259,8 @@ public sealed class AdminSystem : EntitySystem
overallPlaytime = playTime;
}
return new PlayerInfo(
name,
entityName,
identityName,
startingRole,
antag,
roleType,
sortWeight,
GetNetEntity(session?.AttachedEntity),
data.UserId,
connected,
_roundActivePlayers.Contains(data.UserId),
overallPlaytime);
return new PlayerInfo(name, entityName, identityName, startingRole, antag, roleType, GetNetEntity(session?.AttachedEntity), data.UserId,
connected, _roundActivePlayers.Contains(data.UserId), overallPlaytime);
}
private void OnPanicBunkerChanged(bool enabled)

View File

@@ -1,12 +1,10 @@
using Content.Server._CP14.GameTicking.Rules.Components;
using Content.Server.Administration.Commands;
using Content.Server.Antag;
using Content.Server.GameTicking;
using Content.Server.GameTicking.Rules.Components;
using Content.Server.Zombies;
using Content.Shared.Administration;
using Content.Shared.Database;
using Content.Shared.Humanoid;
using Content.Shared.Mind.Components;
using Content.Shared.Roles;
using Content.Shared.Verbs;
@@ -20,7 +18,6 @@ public sealed partial class AdminVerbSystem
{
[Dependency] private readonly AntagSelectionSystem _antag = default!;
[Dependency] private readonly ZombieSystem _zombie = default!;
[Dependency] private readonly GameTicker _gameTicker = default!;
[ValidatePrototypeId<EntityPrototype>]
private const string DefaultTraitorRule = "Traitor";
@@ -40,8 +37,6 @@ public sealed partial class AdminVerbSystem
[ValidatePrototypeId<StartingGearPrototype>]
private const string PirateGearId = "PirateGear";
private readonly EntProtoId _paradoxCloneRuleId = "ParadoxCloneSpawn";
//CP14
[ValidatePrototypeId<EntityPrototype>]
private const string CP14VampireRule = "CP14Vampire";
@@ -184,30 +179,6 @@ public sealed partial class AdminVerbSystem
Message = string.Join(": ", thiefName, Loc.GetString("admin-verb-make-thief")),
};
args.Verbs.Add(thief);
var paradoxCloneName = Loc.GetString("admin-verb-text-make-paradox-clone");
Verb paradox = new()
{
Text = paradoxCloneName,
Category = VerbCategory.Antag,
Icon = new SpriteSpecifier.Rsi(new("/Textures/Interface/Misc/job_icons.rsi"), "ParadoxClone"),
Act = () =>
{
var ruleEnt = _gameTicker.AddGameRule(_paradoxCloneRuleId);
if (!TryComp<ParadoxCloneRuleComponent>(ruleEnt, out var paradoxCloneRuleComp))
return;
paradoxCloneRuleComp.OriginalBody = args.Target; // override the target player
_gameTicker.StartGameRule(ruleEnt);
},
Impact = LogImpact.High,
Message = string.Join(": ", paradoxCloneName, Loc.GetString("admin-verb-make-paradox-clone")),
};
if (HasComp<HumanoidAppearanceComponent>(args.Target)) // only humanoids can be cloned
args.Verbs.Add(paradox);
*/
}
}

View File

@@ -136,7 +136,7 @@ public sealed partial class AdminVerbSystem
Filter.PvsExcept(args.Target), true, PopupType.MediumCaution);
var board = Spawn("ChessBoard", xform.Coordinates);
var session = _tabletopSystem.EnsureSession(Comp<TabletopGameComponent>(board));
_transformSystem.SetMapCoordinates(args.Target, session.Position);
xform.Coordinates = _transformSystem.ToCoordinates(session.Position);
_transformSystem.SetWorldRotationNoLerp((args.Target, xform), Angle.Zero);
},
Impact = LogImpact.Extreme,
@@ -421,7 +421,7 @@ public sealed partial class AdminVerbSystem
{
var xform = Transform(args.Target);
var fixtures = Comp<FixturesComponent>(args.Target);
_transformSystem.Unanchor(args.Target); // Just in case.
xform.Anchored = false; // Just in case.
_physics.SetBodyType(args.Target, BodyType.Dynamic, manager: fixtures, body: physics);
_physics.SetBodyStatus(args.Target, physics, BodyStatus.InAir);
_physics.WakeBody(args.Target, manager: fixtures, body: physics);
@@ -456,7 +456,7 @@ public sealed partial class AdminVerbSystem
{
var xform = Transform(args.Target);
var fixtures = Comp<FixturesComponent>(args.Target);
_transformSystem.Unanchor(args.Target); // Just in case.
xform.Anchored = false; // Just in case.
_physics.SetBodyType(args.Target, BodyType.Dynamic, body: physics);
_physics.SetBodyStatus(args.Target, physics, BodyStatus.InAir);

View File

@@ -637,7 +637,7 @@ public sealed partial class AdminVerbSystem
{
if (_adminManager.HasAdminFlag(player, AdminFlags.Mapping))
{
if (_map.IsPaused(map.MapId))
if (_mapManager.IsMapPaused(map.MapId))
{
Verb unpauseMap = new()
{
@@ -646,7 +646,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/play.png")),
Act = () =>
{
_map.SetPaused(map.MapId, false);
_mapManager.SetMapPaused(map.MapId, false);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-unpause-map-description"),
@@ -663,7 +663,7 @@ public sealed partial class AdminVerbSystem
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/pause.png")),
Act = () =>
{
_map.SetPaused(map.MapId, true);
_mapManager.SetMapPaused(map.MapId, true);
},
Impact = LogImpact.Extreme,
Message = Loc.GetString("admin-trick-pause-map-description"),

View File

@@ -53,7 +53,7 @@ namespace Content.Server.Administration.Systems
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly IAdminManager _adminManager = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly SharedMapSystem _map = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly AdminSystem _adminSystem = default!;
[Dependency] private readonly DisposalTubeSystem _disposalTubes = default!;
@@ -153,10 +153,12 @@ namespace Content.Server.Administration.Systems
var profile = _ticker.GetPlayerProfile(targetActor.PlayerSession);
var mobUid = _spawning.SpawnPlayerMob(coords.Value, null, profile, stationUid);
var targetMind = _mindSystem.GetMind(args.Target);
if (_mindSystem.TryGetMind(args.Target, out var mindId, out var mindComp))
_mindSystem.TransferTo(mindId, mobUid, true, mind: mindComp);
if (targetMind != null)
{
_mindSystem.TransferTo(targetMind.Value, mobUid, true);
}
},
ConfirmationPopup = true,
Impact = LogImpact.High,

View File

@@ -14,7 +14,6 @@ internal sealed class ServerAlertsSystem : AlertsSystem
private void OnGetState(Entity<AlertsComponent> alerts, ref ComponentGetState args)
{
// TODO: Use sourcegen when clone-state bug fixed.
args.State = new AlertComponentState(new(alerts.Comp.Alerts));
args.State = new AlertComponentState(alerts.Comp.Alerts);
}
}

View File

@@ -240,7 +240,7 @@ public sealed class AmeControllerSystem : EntitySystem
return;
var humanReadableState = value ? "Inject" : "Not inject";
_adminLogger.Add(LogType.Action, LogImpact.Medium, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to {humanReadableState}");
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to {humanReadableState}");
}
public void ToggleInjecting(EntityUid uid, EntityUid? user = null, AmeControllerComponent? controller = null)
@@ -267,15 +267,27 @@ public sealed class AmeControllerSystem : EntitySystem
return;
var humanReadableState = controller.Injecting ? "Inject" : "Not inject";
_adminLogger.Add(LogType.Action, LogImpact.Extreme, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to inject {controller.InjectionAmount} while set to {humanReadableState}");
/* This needs to be information which an admin is very likely to want to be informed about in order to be an admin alert or have a sound notification.
At the time of editing, players regularly "overclock" the AME and those cases require no admin attention.
// Admin alert
var safeLimit = int.MaxValue;
if (TryGetAMENodeGroup(uid, out var group))
safeLimit = group.CoreCount * 4;
var logImpact = (oldValue <= safeLimit && value > safeLimit) ? LogImpact.Extreme : LogImpact.Medium;
_adminLogger.Add(LogType.Action, logImpact, $"{EntityManager.ToPrettyString(user.Value):player} has set the AME to inject {controller.InjectionAmount} while set to {humanReadableState}");
if (oldValue <= safeLimit && value > safeLimit)
{
if (_gameTiming.CurTime > controller.EffectCooldown)
{
_chatManager.SendAdminAlert(user.Value, $"increased AME over safe limit to {controller.InjectionAmount}");
_audioSystem.PlayGlobal("/Audio/Misc/adminlarm.ogg",
Filter.Empty().AddPlayers(_adminManager.ActiveAdmins), false, AudioParams.Default.WithVolume(-8f));
controller.EffectCooldown = _gameTiming.CurTime + controller.CooldownDuration;
}
}
*/
}
public void AdjustInjectionAmount(EntityUid uid, int delta, EntityUid? user = null, AmeControllerComponent? controller = null)

View File

@@ -6,7 +6,7 @@ namespace Content.Server.Announcements;
/// <summary>
/// Used for any announcements on the start of a round.
/// </summary>
[Prototype]
[Prototype("roundAnnouncement")]
public sealed partial class RoundAnnouncementPrototype : IPrototype
{
[IdDataField]

View File

@@ -116,7 +116,7 @@ public sealed class InnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
_popup.PopupEntity(message, ent, ent, PopupType.MediumCaution);
_adminLog.Add(LogType.Anomaly,LogImpact.Medium,$"{ToPrettyString(ent)} became anomaly host.");
_adminLog.Add(LogType.Anomaly,LogImpact.Extreme,$"{ToPrettyString(ent)} became anomaly host.");
}
Dirty(ent);
}

View File

@@ -1,5 +1,4 @@
using Content.Server.Antag.Components;
using Content.Shared.GameTicking.Components;
using Content.Server.GameTicking.Rules;
namespace Content.Server.Antag;
@@ -15,20 +14,9 @@ public sealed class AntagRandomSpawnSystem : GameRuleSystem<AntagRandomSpawnComp
SubscribeLocalEvent<AntagRandomSpawnComponent, AntagSelectLocationEvent>(OnSelectLocation);
}
protected override void Added(EntityUid uid, AntagRandomSpawnComponent comp, GameRuleComponent gameRule, GameRuleAddedEvent args)
{
base.Added(uid, comp, gameRule, args);
// we have to select this here because AntagSelectLocationEvent is raised twice because MakeAntag is called twice
// once when a ghost role spawner is created and once when someone takes the ghost role
if (TryFindRandomTile(out _, out _, out _, out var coords))
comp.Coords = coords;
}
private void OnSelectLocation(Entity<AntagRandomSpawnComponent> ent, ref AntagSelectLocationEvent args)
{
if (ent.Comp.Coords != null)
args.Coordinates.Add(_transform.ToMapCoordinates(ent.Comp.Coords.Value));
if (TryFindRandomTile(out _, out _, out _, out var coords))
args.Coordinates.Add(_transform.ToMapCoordinates(coords));
}
}

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