Compare commits

..

18 Commits

Author SHA1 Message Date
Deserty0
aa621ece26 Merge remote-tracking branch 'upstream/master' into funnyfunnystuff 2025-08-23 19:44:16 +10:00
Deserty0
52880de47e должно теперь работать 2025-08-13 21:07:21 +10:00
Deserty0
eb289104ab Update DiscordAuthManager.cs 2025-08-13 05:40:08 +10:00
Deserty0
db49a55481 Update CCvars.CP14DiscordAuth.cs 2025-08-13 05:40:08 +10:00
Deserty0
f01725fdc7 Update CCvars.CP14DiscordAuth.cs 2025-08-13 05:15:57 +10:00
Deserty0
0a6a560066 Update DiscordAuthManager.cs 2025-08-13 05:04:38 +10:00
Deserty0
6f170bb45f lol 2025-08-13 04:55:57 +10:00
Deserty0
cec07307bf 123 2025-08-13 04:49:12 +10:00
Deserty0
bd80a1eb60 Update PanicBunkerTab.xaml 2025-08-13 04:49:12 +10:00
Deserty0
b0c2b92464 Update DiscordAuthManager.cs 2025-08-13 04:15:38 +10:00
Deserty0
35e5025d3e Update CCvars.CP14DiscordAuth.cs 2025-08-13 04:15:37 +10:00
Deserty0
146967b786 looool 2025-08-13 04:15:37 +10:00
Deserty0
4b68d79729 uaua 2025-08-13 04:15:37 +10:00
Deserty0
8899695ed5 Update DiscordAuthManager.cs 2025-08-13 04:15:37 +10:00
Deserty0
1d410590b6 Update DiscordAuthManager.cs 2025-08-13 04:15:37 +10:00
Deserty0
24eb9d7493 Update DiscordAuthManager.cs 2025-08-13 04:15:37 +10:00
Deserty0
565283ae26 Update DiscordAuthManager.cs 2025-08-13 04:15:36 +10:00
Deserty0
d55800c575 funny 2025-08-13 04:15:36 +10:00
4382 changed files with 268528 additions and 437734 deletions

4
.github/CODEOWNERS vendored
View File

@@ -1,2 +1,6 @@
# TheShuEd
* @TheShuEd
# TornadoTechnology
*.cs @Tornado-Technology
*.xaml @Tornado-Technology

22
.github/FUNDING.yml vendored
View File

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

38
.github/workflows/cla.yml vendored Normal file
View File

@@ -0,0 +1,38 @@
name: "CLA Assistant"
on:
issue_comment:
types: [created]
pull_request_target:
types: [opened,closed,synchronize]
paths:
- '**CP14**'
# explicitly configure permissions, in case your GITHUB_TOKEN workflow permissions are set to read-only in repository settings
permissions:
actions: write
contents: write # this can be 'read' if the signatures are in remote repository
pull-requests: write
statuses: write
jobs:
CLAAssistant:
runs-on: ubuntu-latest
steps:
- name: "CLA Assistant"
if: ((github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target') && github.actor != 'TheShuEd'
uses: contributor-assistant/github-action@v2.6.1
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
# the below token should have repo scope and must be manually added by you in the repository's secret
# This token is required only if you have configured to store the signatures in a remote repository/organization
# PERSONAL_ACCESS_TOKEN: ${{ secrets.PERSONAL_ACCESS_TOKEN }}
with:
path-to-signatures: 'signatures/version1/cla.json'
path-to-document: 'https://github.com/crystallpunk-14/crystall-punk-14/blob/master/CLA.md' # e.g. a CLA or a DCO document
# branch should not be protected
branch: 'master'
allowlist: TheShuEd,bot*
# the followings are the optional inputs - If the optional inputs are not given, then default values will be taken
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'

View File

@@ -34,7 +34,7 @@ jobs:
run: dotnet build Content.Packaging --configuration Release --no-restore /m
- name: Package server
run: dotnet run --project Content.Packaging server --platform win-x64 --platform win-arm64 --platform linux-x64 --platform linux-arm64 --platform osx-x64 --platform osx-arm64
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
- name: Package client
run: dotnet run --project Content.Packaging client --no-wipe-release

View File

@@ -41,7 +41,7 @@ jobs:
run: dotnet build Content.Packaging --configuration Release --no-restore /m
- name: Package server
run: dotnet run --project Content.Packaging server --platform win-x64 --platform win-arm64 --platform linux-x64 --platform linux-arm64 --platform osx-x64 --platform osx-arm64
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
- name: Package client
run: dotnet run --project Content.Packaging client --no-wipe-release

View File

@@ -60,7 +60,7 @@ jobs:
run: dotnet build Content.Packaging --configuration Release --no-restore /m
- name: Package server
run: dotnet run --project Content.Packaging server --platform win-x64 --platform win-arm64 --platform linux-x64 --platform linux-arm64 --platform osx-x64 --platform osx-arm64
run: dotnet run --project Content.Packaging server --platform win-x64 --platform linux-x64 --platform osx-x64 --platform linux-arm64
- name: Package client
run: dotnet run --project Content.Packaging client --no-wipe-release

View File

@@ -1,19 +1,17 @@
#!/usr/bin/env python3
"""
Installs git hooks, updates them, updates submodules, that kind of thing.
"""
# Installs git hooks, updates them, updates submodules, that kind of thing.
import os
import shutil
import subprocess
import sys
import os
import shutil
import time
from pathlib import Path
from typing import List
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
# If this doesn't match the saved version we overwrite them all.
CURRENT_HOOKS_VERSION = "3"
CURRENT_HOOKS_VERSION = "2"
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
@@ -27,10 +25,12 @@ def run_command(command: List[str], capture: bool = False) -> subprocess.Complet
sys.stdout.flush()
completed = None
if capture:
completed = subprocess.run(command, stdout=subprocess.PIPE, text=True)
completed = subprocess.run(command, cwd="..", stdout=subprocess.PIPE)
else:
completed = subprocess.run(command)
completed = subprocess.run(command, cwd="..")
if completed.returncode != 0:
print("Error: command exited with code {}!".format(completed.returncode))
@@ -43,7 +43,7 @@ def update_submodules():
Updates all submodules.
"""
if 'GITHUB_ACTIONS' in os.environ:
if ('GITHUB_ACTIONS' in os.environ):
return
if os.path.isfile("DISABLE_SUBMODULE_AUTOUPDATE"):
@@ -76,21 +76,22 @@ def install_hooks():
print("No hooks change detected.")
return
with open("INSTALLED_HOOKS_VERSION", "w") as f:
f.write(CURRENT_HOOKS_VERSION)
print("Hooks need updating.")
hooks_target_dir = Path(run_command(["git", "rev-parse", "--git-path", "hooks"], True).stdout.strip())
hooks_target_dir = Path("..")/".git"/"hooks"
hooks_source_dir = Path("hooks")
# Clear entire tree since we need to kill deleted files too.
for filename in os.listdir(hooks_target_dir):
os.remove(hooks_target_dir / filename)
for filename in os.listdir(str(hooks_target_dir)):
os.remove(str(hooks_target_dir/filename))
for filename in os.listdir(hooks_source_dir):
for filename in os.listdir(str(hooks_source_dir)):
print("Copying hook {}".format(filename))
shutil.copy2(hooks_source_dir / filename, hooks_target_dir / filename)
with open("INSTALLED_HOOKS_VERSION", "w") as f:
f.write(CURRENT_HOOKS_VERSION)
shutil.copy2(str(hooks_source_dir/filename),
str(hooks_target_dir/filename))
def reset_solution():
@@ -106,7 +107,8 @@ def reset_solution():
def check_for_zip_download():
# Check if .git exists,
if run_command(["git", "rev-parse"]).returncode != 0:
cur_dir = os.path.dirname(os.path.dirname(os.path.realpath(__file__)))
if not os.path.isdir(os.path.join(cur_dir, ".git")):
print("It appears that you downloaded this repository directly from GitHub. (Using the .zip download option) \n"
"When downloading straight from GitHub, it leaves out important information that git needs to function. "
"Such as information to download the engine or even the ability to even be able to create contributions. \n"

View File

@@ -1,10 +1,10 @@
#!/bin/bash
gitroot=$(git rev-parse --show-toplevel)
gitroot=`git rev-parse --show-toplevel`
cd "$gitroot/BuildChecker" || exit
cd "$gitroot/BuildChecker"
if [[ $(uname) == MINGW* || $(uname) == CYGWIN* ]]; then
if [[ `uname` == MINGW* || `uname` == CYGWIN* ]]; then
# Windows
py -3 git_helper.py --quiet
else

View File

@@ -1,5 +1,5 @@
#!/bin/bash
# Just call post-checkout since it does the same thing.
gitroot=$(git rev-parse --git-path hooks)
bash "$gitroot/post-checkout"
gitroot=`git rev-parse --show-toplevel`
bash "$gitroot/.git/hooks/post-checkout"

View File

@@ -1,174 +0,0 @@
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos.Components;
using Content.Shared.CCVar;
using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.Configuration;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Benchmarks;
/// <summary>
/// Spawns N number of entities with a <see cref="DeltaPressureComponent"/> and
/// simulates them for a number of ticks M.
/// </summary>
[Virtual]
[GcServer(true)]
//[MemoryDiagnoser]
//[ThreadingDiagnoser]
public class DeltaPressureBenchmark
{
/// <summary>
/// Number of entities (windows, really) to spawn with a <see cref="DeltaPressureComponent"/>.
/// </summary>
[Params(1, 10, 100, 1000, 5000, 10000, 50000, 100000)]
public int EntityCount;
/// <summary>
/// Number of entities that each parallel processing job will handle.
/// </summary>
// [Params(1, 10, 100, 1000, 5000, 10000)] For testing how multithreading parameters affect performance (THESE TESTS TAKE 16+ HOURS TO RUN)
[Params(10)]
public int BatchSize;
/// <summary>
/// Number of entities to process per iteration in the DeltaPressure
/// processing loop.
/// </summary>
// [Params(100, 1000, 5000, 10000, 50000)]
[Params(1000)]
public int EntitiesPerIteration;
private readonly EntProtoId _windowProtoId = "Window";
private readonly EntProtoId _wallProtoId = "WallPlastitaniumIndestructible";
private TestPair _pair = default!;
private IEntityManager _entMan = default!;
private SharedMapSystem _map = default!;
private IRobustRandom _random = default!;
private IConfigurationManager _cvar = default!;
private ITileDefinitionManager _tileDefMan = default!;
private AtmosphereSystem _atmospereSystem = default!;
private Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>
_testEnt;
[GlobalSetup]
public async Task SetupAsync()
{
ProgramShared.PathOffset = "../../../../";
PoolManager.Startup();
_pair = await PoolManager.GetServerClient();
var server = _pair.Server;
var mapdata = await _pair.CreateTestMap();
_entMan = server.ResolveDependency<IEntityManager>();
_map = _entMan.System<SharedMapSystem>();
_random = server.ResolveDependency<IRobustRandom>();
_cvar = server.ResolveDependency<IConfigurationManager>();
_tileDefMan = server.ResolveDependency<ITileDefinitionManager>();
_atmospereSystem = _entMan.System<AtmosphereSystem>();
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
_cvar.SetCVar(CCVars.DeltaPressureParallelToProcessPerIteration, EntitiesPerIteration);
_cvar.SetCVar(CCVars.DeltaPressureParallelBatchSize, BatchSize);
var plating = _tileDefMan["Plating"].TileId;
/*
Basically, we want to have a 5-wide grid of tiles.
Edges are walled, and the length of the grid is determined by N + 2.
Windows should only touch the top and bottom walls, and each other.
*/
var length = EntityCount + 2; // ensures we can spawn exactly N windows between side walls
const int height = 5;
await server.WaitPost(() =>
{
// Fill required tiles (extend grid) with plating
for (var x = 0; x < length; x++)
{
for (var y = 0; y < height; y++)
{
_map.SetTile(mapdata.Grid, mapdata.Grid, new Vector2i(x, y), new Tile(plating));
}
}
// Spawn perimeter walls and windows row in the middle (y = 2)
const int midY = height / 2;
for (var x = 0; x < length; x++)
{
for (var y = 0; y < height; y++)
{
var coords = new EntityCoordinates(mapdata.Grid, x + 0.5f, y + 0.5f);
var isPerimeter = x == 0 || x == length - 1 || y == 0 || y == height - 1;
if (isPerimeter)
{
_entMan.SpawnEntity(_wallProtoId, coords);
continue;
}
// Spawn windows only on the middle row, spanning interior (excluding side walls)
if (y == midY)
{
_entMan.SpawnEntity(_windowProtoId, coords);
}
}
}
});
// Next we run the fixgridatmos command to ensure that we have some air on our grid.
// Wait a little bit as well.
// TODO: Unhardcode command magic string when fixgridatmos is an actual command we can ref and not just
// a stamp-on in AtmosphereSystem.
await _pair.WaitCommand("fixgridatmos " + mapdata.Grid.Owner, 1);
var uid = mapdata.Grid.Owner;
_testEnt = new Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>(
uid,
_entMan.GetComponent<GridAtmosphereComponent>(uid),
_entMan.GetComponent<GasTileOverlayComponent>(uid),
_entMan.GetComponent<MapGridComponent>(uid),
_entMan.GetComponent<TransformComponent>(uid));
}
[Benchmark]
public async Task PerformFullProcess()
{
await _pair.Server.WaitPost(() =>
{
while (!_atmospereSystem.RunProcessingStage(_testEnt, AtmosphereProcessingState.DeltaPressure)) { }
});
}
[Benchmark]
public async Task PerformSingleRunProcess()
{
await _pair.Server.WaitPost(() =>
{
_atmospereSystem.RunProcessingStage(_testEnt, AtmosphereProcessingState.DeltaPressure);
});
}
[GlobalCleanup]
public async Task CleanupAsync()
{
await _pair.DisposeAsync();
PoolManager.Shutdown();
}
}

View File

@@ -47,7 +47,7 @@ public class MapLoadBenchmark
PoolManager.Shutdown();
}
public static string[] MapsSource { get; } = { "Empty", "Saltern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
public static readonly string[] MapsSource = { "Empty", "Saltern", "Box", "Bagel", "Dev", "CentComm", "Core", "TestTeg", "Packed", "Omega", "Reach", "Meta", "Marathon", "MeteorArena", "Fland", "Oasis", "Convex"};
[ParamsSource(nameof(MapsSource))]
public string Map;

View File

@@ -6,6 +6,7 @@ using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Mind;
using Content.Server.Warps;
using Content.Shared.Warps;
using Robust.Shared;
using Robust.Shared.Analyzers;

View File

@@ -29,7 +29,7 @@ namespace Content.Client.Access.UI
foreach (var access in accessLevels)
{
if (!protoManager.Resolve(access, out var accessLevel))
if (!protoManager.TryIndex(access, out var accessLevel))
{
continue;
}

View File

@@ -57,7 +57,7 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
foreach (var accessGroup in _accessGroups)
{
if (!_protoManager.Resolve(accessGroup, out var accessGroupProto))
if (!_protoManager.TryIndex(accessGroup, out var accessGroupProto))
continue;
_groupedAccessLevels.Add(accessGroupProto, new());
@@ -65,13 +65,13 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
// Ensure that the 'general' access group is added to handle
// misc. access levels that aren't associated with any group
if (_protoManager.Resolve(GeneralAccessGroup, out var generalAccessProto))
if (_protoManager.TryIndex(GeneralAccessGroup, out var generalAccessProto))
_groupedAccessLevels.TryAdd(generalAccessProto, new());
// Assign known access levels with their associated groups
foreach (var accessLevel in _accessLevels)
{
if (!_protoManager.Resolve(accessLevel, out var accessLevelProto))
if (!_protoManager.TryIndex(accessLevel, out var accessLevelProto))
continue;
var assigned = false;

View File

@@ -4,7 +4,6 @@ using Content.Shared.Access.Systems;
using Content.Shared.CCVar;
using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest;
using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.IdCardConsoleComponent;
@@ -75,7 +74,7 @@ namespace Content.Client.Access.UI
_window?.UpdateState(castState);
}
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, ProtoId<JobPrototype> newJobPrototype)
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, string newJobPrototype)
{
if (newFullName.Length > _maxNameLength)
newFullName = newFullName[.._maxNameLength];

View File

@@ -123,7 +123,7 @@ namespace Content.Client.Access.UI
foreach (var group in job.AccessGroups)
{
if (!_prototypeManager.Resolve(group, out AccessGroupPrototype? groupPrototype))
if (!_prototypeManager.TryIndex(group, out AccessGroupPrototype? groupPrototype))
{
continue;
}

View File

@@ -0,0 +1,10 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent]
public sealed partial class HeadstandComponent : SharedHeadstandComponent
{
}

View File

@@ -15,7 +15,6 @@ namespace Content.Client.Administration.Managers
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IClientNetManager _netMgr = default!;
[Dependency] private readonly IClientConGroupController _conGroup = default!;
[Dependency] private readonly IClientConsoleHost _host = default!;
[Dependency] private readonly IResourceManager _res = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IUserInterfaceManager _userInterface = default!;
@@ -87,12 +86,12 @@ namespace Content.Client.Administration.Managers
private void UpdateMessageRx(MsgUpdateAdminStatus message)
{
_availableCommands.Clear();
var host = IoCManager.Resolve<IClientConsoleHost>();
// Anything marked as Any we'll just add even if the server doesn't know about it.
foreach (var (command, instance) in _host.AvailableCommands)
foreach (var (command, instance) in host.AvailableCommands)
{
if (Attribute.GetCustomAttribute(instance.GetType(), typeof(AnyCommandAttribute)) == null)
continue;
if (Attribute.GetCustomAttribute(instance.GetType(), typeof(AnyCommandAttribute)) == null) continue;
_availableCommands.Add(command);
}

View File

@@ -0,0 +1,7 @@
using Content.Shared.Administration;
namespace Content.Client.Administration.Systems;
public sealed class AdminFrozenSystem : SharedAdminFrozenSystem
{
}

View File

@@ -1,4 +1,4 @@
using Content.Shared.Administration.Components;
using Content.Client.Administration.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Administration.Systems;

View File

@@ -12,7 +12,7 @@ public sealed partial class AdminMenuWindow : DefaultWindow
public AdminMenuWindow()
{
MinSize = new Vector2(650, 250);
MinSize = new Vector2(650, 280);
Title = Loc.GetString("admin-menu-title");
RobustXamlLoader.Load(this);
MasterTabContainer.SetTabTitle((int) TabIndex.Admin, Loc.GetString("admin-menu-admin-tab"));

View File

@@ -316,9 +316,8 @@ public sealed partial class BanPanel : DefaultWindow
};
// This is adding the icon before the role name
// TODO: This should not be using raw strings for prototypes as it means it won't be validated at all.
// I know the ban manager is doing the same thing, but that should not leak into UI code.
if (_protoMan.TryIndex<JobPrototype>(role, out var jobPrototype) && _protoMan.Resolve(jobPrototype.Icon, out var iconProto))
// Yeah, this is sus, but having to split the functions up and stuff is worse imo.
if (_protoMan.TryIndex<JobPrototype>(role, out var jobPrototype) && _protoMan.TryIndex(jobPrototype.Icon, out var iconProto))
{
var jobIconTexture = new TextureRect
{

View File

@@ -57,43 +57,12 @@ public sealed partial class ObjectsTab : Control
private void TeleportTo(NetEntity nent)
{
var selection = _selections[ObjectTypeOptions.SelectedId];
switch (selection)
{
case ObjectsTabSelection.Grids:
{
// directly teleport to the entity
_console.ExecuteCommand($"tpto {nent}");
}
break;
case ObjectsTabSelection.Maps:
{
// teleport to the map, not to the map entity (which is in nullspace)
if (!_entityManager.TryGetEntity(nent, out var map) || !_entityManager.TryGetComponent<MapComponent>(map, out var mapComp))
break;
_console.ExecuteCommand($"tp 0 0 {mapComp.MapId}");
break;
}
case ObjectsTabSelection.Stations:
{
// teleport to the station's largest grid, not to the station entity (which is in nullspace)
if (!_entityManager.TryGetEntity(nent, out var station))
break;
var largestGrid = _entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetLargestGrid(station.Value);
if (largestGrid == null)
break;
_console.ExecuteCommand($"tpto {largestGrid.Value}");
break;
}
default:
throw new NotImplementedException();
}
_console.ExecuteCommand($"tpto {nent}");
}
private void Delete(NetEntity nent)
{
_console.ExecuteCommand($"delete {nent}");
RefreshObjectList();
}
public void RefreshObjectList()
@@ -107,24 +76,28 @@ public sealed partial class ObjectsTab : Control
switch (selection)
{
case ObjectsTabSelection.Stations:
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().GetStationNames());
entities.AddRange(_entityManager.EntitySysManager.GetEntitySystem<StationSystem>().Stations);
break;
case ObjectsTabSelection.Grids:
{
var query = _entityManager.AllEntityQueryEnumerator<MapGridComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
{
var query = _entityManager.AllEntityQueryEnumerator<MapGridComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
break;
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
}
break;
}
case ObjectsTabSelection.Maps:
{
var query = _entityManager.AllEntityQueryEnumerator<MapComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
{
var query = _entityManager.AllEntityQueryEnumerator<MapComponent, MetaDataComponent>();
while (query.MoveNext(out var uid, out _, out var metadata))
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
break;
entities.Add((metadata.EntityName, _entityManager.GetNetEntity(uid)));
}
break;
}
default:
throw new ArgumentOutOfRangeException(nameof(selection), selection, null);
}

View File

@@ -1,6 +1,5 @@
<PanelContainer xmlns="https://spacestation14.io"
xmlns:customControls="clr-namespace:Content.Client.Administration.UI.CustomControls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Name="BackgroundColorPanel">
<BoxContainer Orientation="Horizontal"
HorizontalExpand="True"
@@ -21,7 +20,7 @@
HorizontalExpand="True"
ClipText="True"/>
<customControls:VSeparator/>
<controls:ConfirmButton Name="DeleteButton"
<Button Name="DeleteButton"
Text="{Loc object-tab-entity-delete}"
SizeFlagsStretchRatio="3"
HorizontalExpand="True"

View File

@@ -27,7 +27,12 @@
<cc:CommandButton Name="ShowReasonButton" Command="panicbunker_show_reason"
ToggleMode="True" Text="{Loc admin-ui-panic-bunker-show-reason}"
ToolTip="{Loc admin-ui-panic-bunker-show-reason-tooltip}" />
<BoxContainer Orientation="Vertical" Margin="0 10 0 0">
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc cp14-admin-ui-suspicious-warning-level-setting}" MinWidth="175" />
<OptionButton Name="SuspiciousWarningLevel" HorizontalAlignment="Left" Margin="4 0"
ToolTip="{Loc cp14-admin-ui-suspicious-warning-level-setting-desc}" />
</BoxContainer>
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal" Margin="2">
<Label Text="{Loc admin-ui-panic-bunker-min-account-age}" MinWidth="175" />
<LineEdit Name="MinAccountAge" MinWidth="50" Margin="0 0 5 0" />

View File

@@ -1,7 +1,9 @@
using Content.Shared.Administration.Events;
using Content.Shared.CCVar;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Client.Administration.UI.Tabs.PanicBunkerTab;
@@ -10,9 +12,13 @@ namespace Content.Client.Administration.UI.Tabs.PanicBunkerTab;
public sealed partial class PanicBunkerTab : Control
{
[Dependency] private readonly IConsoleHost _console = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
private string _minAccountAge;
private string _minOverallMinutes;
// CrystallEdge suspicious activity warning system
private List<SuspiciousWarningLevelSelection> _suspiciousWarningLevelSelections = [];
private string _currentSuspiciousWarningLevel = string.Empty;
public PanicBunkerTab()
{
@@ -28,6 +34,21 @@ public sealed partial class PanicBunkerTab : Control
MinOverallMinutes.OnTextEntered += args => SendMinOverallMinutes(args.Text);
MinOverallMinutes.OnFocusExit += args => SendMinOverallMinutes(args.Text);
_minOverallMinutes = MinOverallMinutes.Text;
// CrystallEdge suspicious activity warning system
foreach (var type in Enum.GetValues<SuspiciousWarningLevelSelection>())
{
_suspiciousWarningLevelSelections.Add(type);
SuspiciousWarningLevel.AddItem(GetLocalizedEnumValue(type));
}
UpdateSuspiciousWarningLevel();
SuspiciousWarningLevel.OnItemSelected += ev =>
{
SuspiciousWarningLevel.SelectId(ev.Id);
SendSuspiciousWarningLevel(_suspiciousWarningLevelSelections[ev.Id]);
};
}
private void SendMinAccountAge(string text)
@@ -74,4 +95,43 @@ public sealed partial class PanicBunkerTab : Control
MinOverallMinutes.Text = status.MinOverallMinutes.ToString();
_minOverallMinutes = MinOverallMinutes.Text;
}
// CrystallEdge suspicious activity warning system
private enum SuspiciousWarningLevelSelection
{
Disabled,
Low,
Medium,
High,
}
private string GetLocalizedEnumValue(SuspiciousWarningLevelSelection selection)
{
return selection switch
{
SuspiciousWarningLevelSelection.Disabled => Loc.GetString("cp14-admin-ui-suspicious-warning-level-disabled"),
SuspiciousWarningLevelSelection.Low => Loc.GetString("cp14-admin-ui-suspicious-warning-level-low"),
SuspiciousWarningLevelSelection.Medium => Loc.GetString("cp14-admin-ui-suspicious-warning-level-medium"),
SuspiciousWarningLevelSelection.High => Loc.GetString("cp14-admin-ui-suspicious-warning-level-high"),
_ => throw new ArgumentOutOfRangeException(nameof(selection), selection, null),
};
}
private void UpdateSuspiciousWarningLevel()
{
_currentSuspiciousWarningLevel = _cfg.GetCVar(CCVars.SuspiciousAccountsWarningLevel);
if (!Enum.TryParse(_currentSuspiciousWarningLevel, true, out SuspiciousWarningLevelSelection currentSuspiciousWarningSelection))
{
throw new ArgumentOutOfRangeException(nameof(_currentSuspiciousWarningLevel),
_currentSuspiciousWarningLevel,
null);
}
SuspiciousWarningLevel.SelectId((int)currentSuspiciousWarningSelection);
}
private void SendSuspiciousWarningLevel(SuspiciousWarningLevelSelection selection)
{
_console.ExecuteCommand($"cp14.suspicious-warning-level {Enum.GetName(selection)}");
}
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Animals.Systems;
namespace Content.Client.Animals.Systems;
public sealed class ParrotMemorySystem : SharedParrotMemorySystem;

View File

@@ -1,40 +0,0 @@
using Robust.Client.Graphics;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client.Anomaly;
/// <summary>
/// This component creates and handles the drawing of a ScreenTexture to be used on the Anomaly Scanner
/// for an indicator of Anomaly Severity.
/// </summary>
/// <remarks>
/// In the future I would like to make this a more generic "DynamicTextureComponent" that can contain a dictionary
/// of texture components like "Bar(offset, size, minimumValue, maximumValue, AppearanceKey, LayerMapKey)" that can
/// just draw a bar or other basic drawn element that will show up on a texture layer.
/// </remarks>
[RegisterComponent]
[Access(typeof(AnomalyScannerSystem))]
public sealed partial class AnomalyScannerScreenComponent : Component
{
/// <summary>
/// This is the texture drawn as a layer on the Anomaly Scanner device.
/// </summary>
public OwnedTexture? ScreenTexture;
/// <summary>
/// A small buffer that we can reuse to draw the severity bar.
/// </summary>
public Rgba32[]? BarBuf;
/// <summary>
/// The position of the top-left of the severity bar in pixels.
/// </summary>
[DataField(readOnly: true)]
public Vector2i Offset = new Vector2i(12, 17);
/// <summary>
/// The width and height of the severity bar in pixels.
/// </summary>
[DataField(readOnly: true)]
public Vector2i Size = new Vector2i(10, 3);
}

View File

@@ -1,110 +0,0 @@
using System.Numerics;
using Content.Shared.Anomaly;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Utility;
using SixLabors.ImageSharp.PixelFormats;
namespace Content.Client.Anomaly;
/// <inheritdoc cref="SharedAnomalyScannerSystem"/>
public sealed class AnomalyScannerSystem : SharedAnomalyScannerSystem
{
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private const float MaxHueDegrees = 360f;
private const float GreenHueDegrees = 110f;
private const float RedHueDegrees = 0f;
private const float GreenHue = GreenHueDegrees / MaxHueDegrees;
private const float RedHue = RedHueDegrees / MaxHueDegrees;
// Just an array to initialize the pixels of a new OwnedTexture
private static readonly Rgba32[] EmptyTexture = new Rgba32[32*32];
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<AnomalyScannerScreenComponent, ComponentInit>(OnComponentInit);
SubscribeLocalEvent<AnomalyScannerScreenComponent, ComponentStartup>(OnComponentStartup);
SubscribeLocalEvent<AnomalyScannerScreenComponent, AppearanceChangeEvent>(OnScannerAppearanceChanged);
}
private void OnComponentInit(Entity<AnomalyScannerScreenComponent> ent, ref ComponentInit args)
{
if(!_sprite.TryGetLayer(ent.Owner, AnomalyScannerVisualLayers.Base, out var layer, true))
return;
// Allocate the OwnedTexture
ent.Comp.ScreenTexture = _clyde.CreateBlankTexture<Rgba32>(layer.PixelSize);
if (layer.PixelSize.X < ent.Comp.Offset.X + ent.Comp.Size.X ||
layer.PixelSize.Y < ent.Comp.Offset.Y + ent.Comp.Size.Y)
{
// If the bar doesn't fit, just bail here, ScreenTexture and BarBuf will remain null, and appearance updates
// will do nothing.
DebugTools.Assert(false, "AnomalyScannerScreenComponent: Bar does not fit within sprite");
return;
}
// Initialize the texture
ent.Comp.ScreenTexture.SetSubImage((0, 0), layer.PixelSize, new ReadOnlySpan<Rgba32>(EmptyTexture));
// Initialize bar drawing buffer
ent.Comp.BarBuf = new Rgba32[ent.Comp.Size.X * ent.Comp.Size.Y];
}
private void OnComponentStartup(Entity<AnomalyScannerScreenComponent> ent, ref ComponentStartup args)
{
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
_sprite.LayerSetTexture((ent, sprite), AnomalyScannerVisualLayers.Screen, ent.Comp.ScreenTexture);
}
private void OnScannerAppearanceChanged(Entity<AnomalyScannerScreenComponent> ent, ref AppearanceChangeEvent args)
{
if (args.Sprite is null || ent.Comp.ScreenTexture is null || ent.Comp.BarBuf is null)
return;
args.AppearanceData.TryGetValue(AnomalyScannerVisuals.AnomalySeverity, out var severityObj);
if (severityObj is not float severity)
severity = 0;
// Get the bar length
var barLength = (int)(severity * ent.Comp.Size.X);
// Calculate the bar color
// Hue "angle" of two colors to interpolate between depending on severity
// Just a lerp from Green hue at severity = 0.5 to Red hue at 1.0
var hue = Math.Clamp(2*GreenHue * (1 - severity), RedHue, GreenHue);
var color = new Rgba32(Color.FromHsv(new Vector4(hue, 1f, 1f, 1f)).RGBA);
var transparent = new Rgba32(0, 0, 0, 255);
for(var y = 0; y < ent.Comp.Size.Y; y++)
{
for (var x = 0; x < ent.Comp.Size.X; x++)
{
ent.Comp.BarBuf[y*ent.Comp.Size.X + x] = x < barLength ? color : transparent;
}
}
// Copy the buffer to the texture
try
{
ent.Comp.ScreenTexture.SetSubImage(
ent.Comp.Offset,
ent.Comp.Size,
new ReadOnlySpan<Rgba32>(ent.Comp.BarBuf)
);
}
catch (IndexOutOfRangeException)
{
Log.Warning($"Bar dimensions out of bounds with the texture on entity {ent.Owner}");
}
}
}

View File

@@ -7,7 +7,7 @@ using Robust.Shared.Timing;
namespace Content.Client.Anomaly;
public sealed partial class AnomalySystem : SharedAnomalySystem
public sealed class AnomalySystem : SharedAnomalySystem
{
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly FloatingVisualizerSystem _floating = default!;
@@ -24,7 +24,6 @@ public sealed partial class AnomalySystem : SharedAnomalySystem
SubscribeLocalEvent<AnomalySupercriticalComponent, ComponentShutdown>(OnShutdown);
}
private void OnStartup(EntityUid uid, AnomalyComponent component, ComponentStartup args)
{
_floating.FloatAnimation(uid, component.FloatingOffset, component.AnimationKey, component.AnimationTime);

View File

@@ -1,6 +1,6 @@
using Content.Shared.Anomaly.Components;
using Content.Shared.Anomaly.Effects;
using Content.Shared.Humanoid;
using Content.Shared.Body.Components;
using Robust.Client.GameObjects;
namespace Content.Client.Anomaly.Effects;
@@ -25,8 +25,9 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
if (TryComp<BodyComponent>(ent, out var body) &&
body.Prototype is not null &&
ent.Comp.SpeciesSprites.TryGetValue(body.Prototype.Value, out var speciesSprite))
{
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
}

View File

@@ -134,7 +134,7 @@ public sealed class AlignAtmosPipeLayers : SnapgridCenter
var newProtoId = altPrototypes[(int)layer];
if (!_protoManager.Resolve(newProtoId, out var newProto))
if (!_protoManager.TryIndex(newProtoId, out var newProto))
return;
if (newProto.Type != ConstructionType.Structure)

View File

@@ -1,11 +1,46 @@
using Content.Client.Atmos.Components;
using Robust.Client.GameObjects;
using Content.Client.UserInterface.Systems.Storage.Controls;
using Content.Shared.Atmos.Piping;
using Content.Shared.Hands;
using Content.Shared.Atmos.Components;
using Content.Shared.Item;
namespace Content.Client.Atmos.EntitySystems;
public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisualsComponent>
{
[Dependency] private readonly SharedItemSystem _itemSystem = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<PipeColorVisualsComponent, GetInhandVisualsEvent>(OnGetVisuals);
SubscribeLocalEvent<PipeColorVisualsComponent, BeforeRenderInGridEvent>(OnDrawInGrid);
}
/// <summary>
/// This method is used to display the color changes of the pipe on the screen..
/// </summary>
private void OnGetVisuals(Entity<PipeColorVisualsComponent> item, ref GetInhandVisualsEvent args)
{
foreach (var (_, layerData) in args.Layers)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
layerData.Color = pipeColor.Color;
}
}
/// <summary>
/// This method is used to change the pipe's color in a container grid.
/// </summary>
private void OnDrawInGrid(Entity<PipeColorVisualsComponent> item, ref BeforeRenderInGridEvent args)
{
if (TryComp(item.Owner, out AtmosPipeColorComponent? pipeColor))
args.Color = pipeColor.Color;
}
protected override void OnAppearanceChange(EntityUid uid, PipeColorVisualsComponent component, ref AppearanceChangeEvent args)
{
if (TryComp<SpriteComponent>(uid, out var sprite)
@@ -15,6 +50,8 @@ public sealed class PipeColorVisualizerSystem : VisualizerSystem<PipeColorVisual
var layer = sprite[PipeVisualLayers.Pipe];
layer.Color = color.WithAlpha(layer.Color.A);
}
_itemSystem.VisualsChanged(uid);
}
}

View File

@@ -59,7 +59,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -72,7 +72,7 @@ public sealed partial class PumpControl : BoxContainer
foreach (var value in Enum.GetValues<VentPressureBound>())
{
_pressureCheck.AddItem(Loc.GetString($"air-alarm-ui-pressure-bound-{value.ToString().ToLower()}"), (int) value);
_pressureCheck.AddItem(Loc.GetString($"{value}"), (int) value);
}
_pressureCheck.SelectId((int) _data.PressureChecks);

View File

@@ -27,15 +27,9 @@
</BoxContainer>
<!-- Lower row: every single gas -->
<Collapsible Margin="2 2 2 2">
<CollapsibleHeading Title="{Loc 'air-alarm-ui-widget-gas-filters'}" />
<CollapsibleHeading Title="Gas filters" />
<CollapsibleBody Margin="20 0 0 0">
<BoxContainer Orientation="Vertical">
<BoxContainer Margin="2">
<Button Name="CSelectAll" Text="{Loc 'air-alarm-ui-scrubber-select-all-gases-label'}" />
<Button Name="CDeselectAll" Text="{Loc 'air-alarm-ui-scrubber-deselect-all-gases-label'}" />
</BoxContainer>
<GridContainer Name="CGasContainer" Columns="3" />
</BoxContainer>
<GridContainer HorizontalExpand="True" Name="CGasContainer" Columns="3" />
</CollapsibleBody>
</Collapsible>
</BoxContainer>

View File

@@ -1,21 +1,15 @@
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor.Components;
using Content.Shared.Atmos.Piping.Unary.Components;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class ScrubberControl : BoxContainer
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
private GasVentScrubberData _data;
private string _address;
@@ -28,18 +22,12 @@ public sealed partial class ScrubberControl : BoxContainer
private FloatSpinBox _volumeRate => CVolumeRate;
private CheckBox _wideNet => CWideNet;
private Button _copySettings => CCopySettings;
private Button _selectAll => CSelectAll;
private Button _deselectAll => CDeselectAll;
private GridContainer _gases => CGasContainer;
private Dictionary<Gas, Button> _gasControls = new();
public ScrubberControl(GasVentScrubberData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
Name = address;
@@ -73,7 +61,7 @@ public sealed partial class ScrubberControl : BoxContainer
foreach (var value in Enum.GetValues<ScrubberPumpDirection>())
{
_pumpDirection.AddItem(Loc.GetString($"air-alarm-ui-pump-direction-{value.ToString().ToLower()}"), (int) value);
_pumpDirection.AddItem(Loc.GetString($"{value}"), (int) value);
}
_pumpDirection.SelectId((int) _data.PumpDirection);
@@ -90,28 +78,12 @@ public sealed partial class ScrubberControl : BoxContainer
ScrubberDataCopied?.Invoke(_data);
};
var allGases = Enum.GetValues<Gas>();
_selectAll.OnPressed += _ =>
foreach (var value in Enum.GetValues<Gas>())
{
_data.FilterGases = new HashSet<Gas>(allGases);
ScrubberDataChanged?.Invoke(_address, _data);
};
_deselectAll.OnPressed += _ =>
{
_data.FilterGases = [];
ScrubberDataChanged?.Invoke(_address, _data);
};
foreach (var value in allGases)
{
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(value);
var gasName = _prototypeManager.Index(gasProtoId).Name;
var gasButton = new Button
{
Name = value.ToString(),
Text = Loc.GetString(gasName),
Text = Loc.GetString($"{value}"),
ToggleMode = true,
HorizontalExpand = true,
Pressed = _data.FilterGases.Contains(value)

View File

@@ -1,22 +1,16 @@
using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor;
using Content.Shared.Atmos.Prototypes;
using Content.Shared.Temperature;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Atmos.Monitor.UI.Widgets;
[GenerateTypedNameReferences]
public sealed partial class SensorInfo : BoxContainer
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IEntityManager _entMan = default!;
public Action<string, AtmosMonitorThresholdType, AtmosAlarmThreshold, Gas?>? OnThresholdUpdate;
public event Action<AtmosSensorData>? SensorDataCopied;
private string _address;
@@ -29,9 +23,6 @@ public sealed partial class SensorInfo : BoxContainer
public SensorInfo(AtmosSensorData data, string address)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
RobustXamlLoader.Load(this);
_address = address;
@@ -54,12 +45,8 @@ public sealed partial class SensorInfo : BoxContainer
var label = new RichTextLabel();
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", Loc.GetString(gasName)),
("gas", $"{gas}"),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));
@@ -67,7 +54,7 @@ public sealed partial class SensorInfo : BoxContainer
_gasLabels.Add(gas, label);
var threshold = data.GasThresholds[gas];
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title"), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
var gasThresholdControl = new ThresholdControl(Loc.GetString($"air-alarm-ui-thresholds-gas-title", ("gas", $"{gas}")), threshold, AtmosMonitorThresholdType.Gas, gas, 100);
gasThresholdControl.Margin = new Thickness(20, 2, 2, 2);
gasThresholdControl.ThresholdDataChanged += (type, alarmThreshold, arg3) =>
{
@@ -103,9 +90,6 @@ public sealed partial class SensorInfo : BoxContainer
public void ChangeData(AtmosSensorData data)
{
IoCManager.InjectDependencies(this);
var atmosphereSystem = _entMan.System<SharedAtmosphereSystem>();
SensorAddress.Title = Loc.GetString("air-alarm-ui-window-listing-title", ("address", _address), ("state", data.AlarmState));
AlarmStateLabel.SetMarkup(Loc.GetString("air-alarm-ui-window-alarm-state-indicator",
@@ -128,12 +112,8 @@ public sealed partial class SensorInfo : BoxContainer
}
var fractionGas = amount / data.TotalMoles;
ProtoId<GasPrototype> gasProtoId = atmosphereSystem.GetGas(gas);
var gasName = _prototypeManager.Index(gasProtoId).Name;
label.SetMarkup(Loc.GetString("air-alarm-ui-gases-indicator",
("gas", Loc.GetString(gasName)),
("gas", $"{gas}"),
("color", AirAlarmWindow.ColorForThreshold(fractionGas, data.GasThresholds[gas])),
("amount", $"{amount:0.####}"),
("percentage", $"{(100 * fractionGas):0.##}")));

View File

@@ -2,6 +2,6 @@
HorizontalExpand="True" Orientation="Vertical"
Margin = "20 0 0 0" MinSize="160 0" >
<Label Name="CBoundLabel" HorizontalAlignment="Center" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'air-alarm-ui-widget-enable'}" Pressed="True" />
<CheckBox Name="CBoundEnabled" HorizontalAlignment="Center" Text="{Loc 'Enable'}" Pressed="True" />
<FloatSpinBox Name="CSpinner" />
</BoxContainer>

View File

@@ -6,7 +6,7 @@
<CollapsibleBody Margin="20 0 0 0">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Horizontal">
<CheckBox Name="CEnabled" Text="{Loc 'air-alarm-ui-widget-enable'}" />
<CheckBox Name="CEnabled" Text="{Loc 'Enabled'}" />
</BoxContainer>
<!-- Upper row: Danger bounds -->
<BoxContainer Name="CDangerBounds" Orientation="Horizontal" Margin="0 0 0 2"/>

View File

@@ -208,7 +208,7 @@ namespace Content.Client.Atmos.UI
});
presBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-pressure-val-text", ("pressure", $"{gasMix.Pressure:0.00}")),
Text = Loc.GetString("gas-analyzer-window-pressure-val-text", ("pressure", $"{gasMix.Pressure:0.##}")),
Align = Label.AlignMode.Right,
HorizontalExpand = true
});
@@ -232,8 +232,8 @@ namespace Content.Client.Atmos.UI
tempBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-temperature-val-text",
("tempK", $"{gasMix.Temperature:0.0}"),
("tempC", $"{TemperatureHelpers.KelvinToCelsius(gasMix.Temperature):0.0}")),
("tempK", $"{gasMix.Temperature:0.#}"),
("tempC", $"{TemperatureHelpers.KelvinToCelsius(gasMix.Temperature):0.#}")),
Align = Label.AlignMode.Right,
HorizontalExpand = true
});

View File

@@ -3,13 +3,17 @@ using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Audio;
using Robust.Shared.Log;
using Robust.Shared.Configuration;
using Robust.Shared.Map;
using Robust.Shared.Physics;
using Robust.Shared.Random;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Linq;
using System.Numerics;
using Robust.Client.GameObjects;
using Robust.Shared.Audio.Effects;
using Robust.Shared.Audio.Systems;
using Robust.Shared.Player;
@@ -27,7 +31,6 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
[Dependency] private readonly SharedTransformSystem _xformSystem = default!;
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IGameTiming _gameTiming = default!;
[Dependency] private readonly IOverlayManager _overlayManager = default!;
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -62,19 +65,18 @@ public sealed class AmbientSoundSystem : SharedAmbientSoundSystem
get => _overlayEnabled;
set
{
if (_overlayEnabled == value)
return;
if (_overlayEnabled == value) return;
_overlayEnabled = value;
var overlayManager = IoCManager.Resolve<IOverlayManager>();
if (_overlayEnabled)
{
_overlay = new AmbientSoundOverlay(EntityManager, this, EntityManager.System<EntityLookupSystem>());
_overlayManager.AddOverlay(_overlay);
overlayManager.AddOverlay(_overlay);
}
else
{
_overlayManager.RemoveOverlay(_overlay!);
overlayManager.RemoveOverlay(_overlay!);
_overlay = null;
}
}

View File

@@ -3,8 +3,11 @@ using Content.Client.Gameplay;
using Content.Shared.Audio;
using Content.Shared.CCVar;
using Content.Shared.GameTicking;
using Content.Shared.Random;
using Content.Shared.Random.Rules;
using Robust.Client.GameObjects;
using Robust.Client.Player;
using Robust.Client.ResourceManagement;
using Robust.Client.State;
using Robust.Shared.Audio;
using Robust.Shared.Audio.Components;
@@ -22,7 +25,6 @@ public sealed partial class ContentAudioSystem
{
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPlayerManager _player = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IRobustRandom _random = default!;
@@ -59,7 +61,7 @@ public sealed partial class ContentAudioSystem
private void InitializeAmbientMusic()
{
Subs.CVar(_configManager, CCVars.AmbientMusicVolume, AmbienceCVarChanged, true);
_sawmill = _logManager.GetSawmill("audio.ambience");
_sawmill = IoCManager.Resolve<ILogManager>().GetSawmill("audio.ambience");
// Reset audio
_nextAudio = TimeSpan.MaxValue;

View File

@@ -58,7 +58,7 @@ public sealed class JukeboxBoundUserInterface : BoundUserInterface
_menu.SetAudioStream(jukebox.AudioStream);
if (_protoManager.Resolve(jukebox.SelectedSongId, out var songProto))
if (_protoManager.TryIndex(jukebox.SelectedSongId, out var songProto))
{
var length = EntMan.System<AudioSystem>().GetAudioLength(songProto.Path.Path.ToString());
_menu.SetSelectedSong(songProto.Name, (float) length.TotalSeconds);

View File

@@ -39,7 +39,7 @@ public sealed class BarSignSystem : VisualizerSystem<BarSignComponent>
if (powered
&& sign.Current != null
&& _prototypeManager.Resolve(sign.Current, out var proto))
&& _prototypeManager.TryIndex(sign.Current, out var proto))
{
SpriteSystem.LayerSetSprite((id, sprite), 0, proto.Icon);
sprite.LayerSetShader(0, "unshaded");

View File

@@ -35,7 +35,7 @@ public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : Bou
public void Update(ProtoId<BarSignPrototype>? sign)
{
if (_prototype.Resolve(sign, out var signPrototype))
if (_prototype.TryIndex(sign, out var signPrototype))
_menu?.UpdateState(signPrototype);
}

View File

@@ -1,8 +1,8 @@
using System.Numerics;
using Content.Shared.Body.Components;
using Content.Shared.CardboardBox;
using Content.Shared.CardboardBox.Components;
using Content.Shared.Examine;
using Content.Shared.Mobs.Components;
using Content.Shared.Movement.Components;
using Robust.Client.GameObjects;
@@ -15,13 +15,13 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
[Dependency] private readonly ExamineSystemShared _examine = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private EntityQuery<MobStateComponent> _mobStateQuery;
private EntityQuery<BodyComponent> _bodyQuery;
public override void Initialize()
{
base.Initialize();
_mobStateQuery = GetEntityQuery<MobStateComponent>();
_bodyQuery = GetEntityQuery<BodyComponent>();
SubscribeNetworkEvent<PlayBoxEffectMessage>(OnBoxEffect);
}
@@ -66,8 +66,8 @@ public sealed class CardboardBoxSystem : SharedCardboardBoxSystem
if (!_examine.InRangeUnOccluded(sourcePos, mapPos, box.Distance, null))
continue;
// no effect for non-mobs that have MobMover, such as mechs and vehicles.
if (!_mobStateQuery.HasComp(mob))
// no effect for anything too exotic
if (!_bodyQuery.HasComp(mob))
continue;
var ent = Spawn(box.Effect, mapPos);

View File

@@ -29,7 +29,7 @@ public sealed partial class BountyEntry : BoxContainer
UntilNextSkip = untilNextSkip;
if (!_prototype.Resolve<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
if (!_prototype.TryIndex<CargoBountyPrototype>(bounty.Bounty, out var bountyPrototype))
return;
var items = new List<string>();

View File

@@ -19,7 +19,7 @@ public sealed partial class BountyHistoryEntry : BoxContainer
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
if (!_prototype.Resolve(bounty.Bounty, out var bountyPrototype))
if (!_prototype.TryIndex(bounty.Bounty, out var bountyPrototype))
return;
var items = new List<string>();

View File

@@ -1,30 +0,0 @@
using Content.Shared.Changeling.Components;
using Content.Shared.Changeling.Systems;
using Robust.Client.GameObjects;
namespace Content.Client.Changeling.Systems;
public sealed class ChangelingIdentitySystem : SharedChangelingIdentitySystem
{
[Dependency] private readonly UserInterfaceSystem _ui = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<ChangelingIdentityComponent, AfterAutoHandleStateEvent>(OnAfterAutoHandleState);
}
private void OnAfterAutoHandleState(Entity<ChangelingIdentityComponent> ent, ref AfterAutoHandleStateEvent args)
{
UpdateUi(ent);
}
public void UpdateUi(EntityUid uid)
{
if (_ui.TryGetOpenUi(uid, ChangelingTransformUiKey.Key, out var bui))
{
bui.Update();
}
}
}

View File

@@ -1,68 +0,0 @@
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Components;
using Content.Shared.Changeling.Systems;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.Changeling.UI;
[UsedImplicitly]
public sealed partial class ChangelingTransformBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
{
private SimpleRadialMenu? _menu;
private static readonly Color SelectedOptionBackground = StyleNano.ButtonColorGoodDefault.WithAlpha(128);
private static readonly Color SelectedOptionHoverBackground = StyleNano.ButtonColorGoodHovered.WithAlpha(128);
protected override void Open()
{
base.Open();
_menu = this.CreateWindow<SimpleRadialMenu>();
Update();
_menu.OpenOverMouseScreenPosition();
}
public override void Update()
{
if (_menu == null)
return;
if (!EntMan.TryGetComponent<ChangelingIdentityComponent>(Owner, out var lingIdentity))
return;
var models = ConvertToButtons(lingIdentity.ConsumedIdentities, lingIdentity?.CurrentIdentity);
_menu.SetButtons(models);
}
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(
IEnumerable<EntityUid> identities,
EntityUid? currentIdentity
)
{
var buttons = new List<RadialMenuOptionBase>();
foreach (var identity in identities)
{
if (!EntMan.TryGetComponent<MetaDataComponent>(identity, out var metadata))
continue;
var option = new RadialMenuActionOption<NetEntity>(SendIdentitySelect, EntMan.GetNetEntity(identity))
{
IconSpecifier = RadialMenuIconSpecifier.With(identity),
ToolTip = metadata.EntityName,
BackgroundColor = (currentIdentity == identity) ? SelectedOptionBackground : null,
HoverBackgroundColor = (currentIdentity == identity) ? SelectedOptionHoverBackground : null
};
buttons.Add(option);
}
return buttons;
}
private void SendIdentitySelect(NetEntity identityId)
{
SendPredictedMessage(new ChangelingTransformIdentitySelectMessage(identityId));
}
}

View File

@@ -27,7 +27,7 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
if (overrideIndicator != null)
currentTypingIndicator = overrideIndicator.Value;
if (!_prototypeManager.Resolve(currentTypingIndicator, out var proto))
if (!_prototypeManager.TryIndex(currentTypingIndicator, out var proto))
{
Log.Error($"Unknown typing indicator id: {component.TypingIndicatorPrototype}");
return;

View File

@@ -2,6 +2,7 @@ using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.GameStates;
namespace Content.Client.Chemistry.EntitySystems;
@@ -10,7 +11,6 @@ public sealed class InjectorSystem : SharedInjectorSystem
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainer));
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainers));
}
}

View File

@@ -38,13 +38,13 @@ public sealed class InjectorStatusControl : Control
// only updates the UI if any of the details are different than they previously were
if (PrevVolume == solution.Volume
&& PrevMaxVolume == solution.MaxVolume
&& PrevTransferAmount == _parent.Comp.CurrentTransferAmount
&& PrevTransferAmount == _parent.Comp.TransferAmount
&& PrevToggleState == _parent.Comp.ToggleState)
return;
PrevVolume = solution.Volume;
PrevMaxVolume = solution.MaxVolume;
PrevTransferAmount = _parent.Comp.CurrentTransferAmount;
PrevTransferAmount = _parent.Comp.TransferAmount;
PrevToggleState = _parent.Comp.ToggleState;
// Update current volume and injector state
@@ -59,6 +59,6 @@ public sealed class InjectorStatusControl : Control
("currentVolume", solution.Volume),
("totalVolume", solution.MaxVolume),
("modeString", modeStringLocalized),
("transferVolume", _parent.Comp.CurrentTransferAmount)));
("transferVolume", _parent.Comp.TransferAmount)));
}
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Cloning;
namespace Content.Client.Cloning;
public sealed partial class CloningSystem : SharedCloningSystem;

View File

@@ -1,10 +1,12 @@
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using System.Numerics;
using Content.Client.DisplacementMap;
using Content.Client.Inventory;
using Content.Shared.Clothing;
using Content.Shared.Clothing.Components;
using Content.Shared.Clothing.EntitySystems;
using Content.Shared.DisplacementMap;
using Content.Shared.Humanoid;
using Content.Shared.Inventory;
using Content.Shared.Inventory.Events;
@@ -12,6 +14,7 @@ using Content.Shared.Item;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Serialization.Manager;
using Robust.Shared.Serialization.TypeSerializers.Implementations;
using Robust.Shared.Utility;
using static Robust.Client.GameObjects.SpriteComponent;
@@ -179,7 +182,6 @@ public sealed class ClientClothingSystem : ClothingSystem
var layer = new PrototypeLayerData();
layer.RsiPath = rsi.Path.ToString();
layer.State = state;
layer.Scale = clothing.Scale;
layers = new() { layer };
return true;

View File

@@ -45,7 +45,7 @@ public sealed class ChameleonBoundUserInterface : BoundUserInterface
var newTargets = new List<EntProtoId>();
foreach (var target in targets)
{
if (string.IsNullOrEmpty(target) || !_proto.Resolve(target, out EntityPrototype? proto))
if (string.IsNullOrEmpty(target) || !_proto.TryIndex(target, out EntityPrototype? proto))
continue;
if (!proto.TryGetComponent(out TagComponent? tag, EntMan.ComponentFactory) || !_tag.HasTag(tag, st.RequiredTag))

View File

@@ -54,7 +54,7 @@ public sealed partial class ChameleonMenu : DefaultWindow
foreach (var id in _possibleIds)
{
if (!_prototypeManager.Resolve(id, out EntityPrototype? proto))
if (!_prototypeManager.TryIndex(id, out EntityPrototype? proto))
continue;
var lowId = id.Id.ToLowerInvariant();

View File

@@ -80,7 +80,7 @@ namespace Content.Client.Construction
{
foreach (var constructionProto in PrototypeManager.EnumeratePrototypes<ConstructionPrototype>())
{
if (!PrototypeManager.Resolve(constructionProto.Graph, out var graphProto))
if (!PrototypeManager.TryIndex(constructionProto.Graph, out var graphProto))
continue;
if (constructionProto.TargetNode is not { } targetNodeId)
@@ -121,14 +121,17 @@ namespace Content.Client.Construction
// If we got the id of the prototype, we exit the “recursion” by clearing the stack.
stack.Clear();
if (!PrototypeManager.Resolve(entityId, out var proto))
if (!PrototypeManager.TryIndex(constructionProto.ID, out ConstructionPrototype? recipe))
continue;
var name = constructionProto.SetName.HasValue ? Loc.GetString(constructionProto.SetName) : proto.Name;
var desc = constructionProto.SetDescription.HasValue ? Loc.GetString(constructionProto.SetDescription) : proto.Description;
if (!PrototypeManager.TryIndex(entityId, out var proto))
continue;
constructionProto.Name = name;
constructionProto.Description = desc;
var name = recipe.SetName.HasValue ? Loc.GetString(recipe.SetName) : proto.Name;
var desc = recipe.SetDescription.HasValue ? Loc.GetString(recipe.SetDescription) : proto.Description;
recipe.Name = name;
recipe.Description = desc;
_recipesMetadataCache.Add(constructionProto.ID, entityId);
} while (stack.Count > 0);
@@ -169,7 +172,7 @@ namespace Content.Client.Construction
"construction-ghost-examine-message",
("name", component.Prototype.Name)));
if (!PrototypeManager.Resolve(component.Prototype.Graph, out var graph))
if (!PrototypeManager.TryIndex(component.Prototype.Graph, out var graph))
return;
var startNode = graph.Nodes[component.Prototype.StartNode];

View File

@@ -528,7 +528,7 @@ namespace Content.Client.Construction.UI
foreach (var id in favorites)
{
if (_prototypeManager.TryIndex(id, out ConstructionPrototype? recipe))
if (_prototypeManager.TryIndex(id, out ConstructionPrototype? recipe, logError: false))
_favoritedRecipes.Add(recipe);
}

View File

@@ -80,7 +80,7 @@ public sealed partial class CreditsWindow : DefaultWindow
private async void PopulateAttributions(BoxContainer attributionsContainer, int count)
{
attributionsContainer.RemoveAllChildren();
attributionsContainer.DisposeAllChildren();
if (_attributions.Count == 0)
{
@@ -253,8 +253,6 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulateLicenses(BoxContainer licensesContainer)
{
licensesContainer.RemoveAllChildren();
foreach (var entry in CreditsManager.GetLicenses(_resourceManager).OrderBy(p => p.Name))
{
licensesContainer.AddChild(new Label
@@ -271,8 +269,6 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulatePatrons(BoxContainer patronsContainer)
{
patronsContainer.RemoveAllChildren();
var patrons = LoadPatrons();
// Do not show "become a patron" button on Steam builds
@@ -322,8 +318,6 @@ public sealed partial class CreditsWindow : DefaultWindow
private void PopulateContributors(BoxContainer ss14ContributorsContainer)
{
ss14ContributorsContainer.RemoveAllChildren();
Button contributeButton;
ss14ContributorsContainer.AddChild(new BoxContainer
@@ -362,7 +356,6 @@ public sealed partial class CreditsWindow : DefaultWindow
AddSection(Loc.GetString("credits-window-contributors-section-title"), "GitHub.txt");
AddSection(Loc.GetString("credits-window-codebases-section-title"), "SpaceStation13.txt");
AddSection(Loc.GetString("credits-window-original-remake-team-section-title"), "OriginalRemake.txt");
AddSection(Loc.GetString("credits-window-immortals-title"), "Immortals.txt", true);
AddSection(Loc.GetString("credits-window-special-thanks-section-title"), "SpecialThanks.txt", true);
var linkGithub = _cfg.GetCVar(CCVars.InfoLinksGithub);

View File

@@ -150,7 +150,7 @@ public sealed class DamageVisualsSystem : VisualizerSystem<DamageVisualsComponen
// If the damage container on our entity's DamageableComponent
// is not null, we can try to check through its groups.
if (damageComponent.DamageContainerID != null
&& _prototypeManager.Resolve<DamageContainerPrototype>(damageComponent.DamageContainerID, out var damageContainer))
&& _prototypeManager.TryIndex<DamageContainerPrototype>(damageComponent.DamageContainerID, out var damageContainer))
{
// Are we using damage overlay sprites by group?
// Check if the container matches the supported groups,

View File

@@ -1,4 +1,3 @@
using System.Diagnostics.CodeAnalysis;
using Content.Shared.DisplacementMap;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@@ -11,11 +10,6 @@ public sealed class DisplacementMapSystem : EntitySystem
[Dependency] private readonly ISerializationManager _serialization = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
private static string? BuildDisplacementLayerKey(object key)
{
return key.ToString() is null ? null : $"{key}-displacement";
}
/// <summary>
/// Attempting to apply a displacement map to a specific layer of SpriteComponent
/// </summary>
@@ -25,23 +19,22 @@ public sealed class DisplacementMapSystem : EntitySystem
/// <param name="key">Unique layer key, which will determine which layer to apply displacement map to</param>
/// <param name="displacementKey">The key of the new displacement map layer added by this function.</param>
/// <returns></returns>
public bool TryAddDisplacement(
DisplacementData data,
public bool TryAddDisplacement(DisplacementData data,
Entity<SpriteComponent> sprite,
int index,
object key,
[NotNullWhen(true)] out string? displacementKey
)
out string displacementKey)
{
displacementKey = BuildDisplacementLayerKey(key);
if (displacementKey is null)
displacementKey = $"{key}-displacement";
if (key.ToString() is null)
return false;
EnsureDisplacementIsNotOnSprite(sprite, key);
if (data.ShaderOverride is not null)
if (data.ShaderOverride != null)
sprite.Comp.LayerSetShader(index, data.ShaderOverride);
_sprite.RemoveLayer(sprite.AsNullable(), displacementKey, false);
//allows you not to write it every time in the YML
foreach (var pair in data.SizeMaps)
{
@@ -77,11 +70,7 @@ public sealed class DisplacementMapSystem : EntitySystem
}
var displacementLayer = _serialization.CreateCopy(displacementDataLayer, notNullableOverride: true);
// This previously assigned a string reading "this is impossible" if key.ToString eval'd to false.
// However, for the sake of sanity, we've changed this to assert non-null - !.
// If this throws an error, we're not sorry. Nanotrasen thanks you for your service fixing this bug.
displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString()!;
displacementLayer.CopyToShaderParameters!.LayerKey = key.ToString() ?? "this is impossible";
_sprite.AddLayer(sprite.AsNullable(), displacementLayer, index);
_sprite.LayerMapSet(sprite.AsNullable(), displacementKey, index);
@@ -89,18 +78,14 @@ public sealed class DisplacementMapSystem : EntitySystem
return true;
}
/// <summary>
/// Ensures that the displacement map associated with the given layer key is not in the Sprite's LayerMap.
/// </summary>
/// <param name="sprite">The sprite to remove the displacement layer from.</param>
/// <param name="key">The key of the layer that is referenced by the displacement layer we want to remove.</param>
/// <param name="logMissing">Whether to report an error if the displacement map isn't on the sprite.</param>
public void EnsureDisplacementIsNotOnSprite(Entity<SpriteComponent> sprite, object key)
/// <inheritdoc cref="TryAddDisplacement"/>
[Obsolete("Use the Entity<SpriteComponent> overload")]
public bool TryAddDisplacement(DisplacementData data,
SpriteComponent sprite,
int index,
object key,
out string displacementKey)
{
var displacementLayerKey = BuildDisplacementLayerKey(key);
if (displacementLayerKey is null)
return;
_sprite.RemoveLayer(sprite.AsNullable(), displacementLayerKey, false);
return TryAddDisplacement(data, (sprite.Owner, sprite), index, key, out displacementKey);
}
}

View File

@@ -142,7 +142,7 @@ public sealed class DoorSystem : SharedDoorSystem
private void UpdateSpriteLayers(Entity<SpriteComponent> sprite, string targetProto)
{
if (!_prototypeManager.Resolve(targetProto, out var target))
if (!_prototypeManager.TryIndex(targetProto, out var target))
return;
if (!target.TryGetComponent(out SpriteComponent? targetSprite, _componentFactory))

View File

@@ -1,6 +1,5 @@
using Content.Shared.Drunk;
using Content.Shared.StatusEffect;
using Content.Shared.StatusEffectNew;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Enums;
@@ -44,21 +43,19 @@ public sealed class DrunkOverlay : Overlay
if (playerEntity == null)
return;
var statusSys = _sysMan.GetEntitySystem<Shared.StatusEffectNew.StatusEffectsSystem>();
if (!statusSys.TryGetMaxTime<DrunkStatusEffectComponent>(playerEntity.Value, out var status))
if (!_entityManager.HasComponent<DrunkComponent>(playerEntity)
|| !_entityManager.TryGetComponent<StatusEffectsComponent>(playerEntity, out var status))
return;
var time = status.Item2;
var statusSys = _sysMan.GetEntitySystem<StatusEffectsSystem>();
if (!statusSys.TryGetTime(playerEntity.Value, SharedDrunkSystem.DrunkKey, out var time, status))
return;
var power = SharedDrunkSystem.MagicNumber;
var curTime = _timing.CurTime;
var timeLeft = (float) (time.Value.Item2 - curTime).TotalSeconds;
if (time != null)
{
var curTime = _timing.CurTime;
power = (float) (time - curTime).Value.TotalSeconds;
}
CurrentBoozePower += 8f * (power * 0.5f - CurrentBoozePower) * args.DeltaSeconds / (power+1);
CurrentBoozePower += 8f * (0.5f*timeLeft - CurrentBoozePower) * args.DeltaSeconds / (timeLeft+1);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)

View File

@@ -1,5 +1,4 @@
using Content.Shared.Drunk;
using Content.Shared.StatusEffectNew;
using Robust.Client.Graphics;
using Robust.Client.Player;
using Robust.Shared.Player;
@@ -17,41 +16,38 @@ public sealed class DrunkSystem : SharedDrunkSystem
{
base.Initialize();
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectAppliedEvent>(OnStatusApplied);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRemovedEvent>(OnStatusRemoved);
SubscribeLocalEvent<DrunkComponent, ComponentInit>(OnDrunkInit);
SubscribeLocalEvent<DrunkComponent, ComponentShutdown>(OnDrunkShutdown);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerAttachedEvent>>(OnPlayerAttached);
SubscribeLocalEvent<DrunkStatusEffectComponent, StatusEffectRelayedEvent<LocalPlayerDetachedEvent>>(OnPlayerDetached);
SubscribeLocalEvent<DrunkComponent, LocalPlayerAttachedEvent>(OnPlayerAttached);
SubscribeLocalEvent<DrunkComponent, LocalPlayerDetachedEvent>(OnPlayerDetached);
_overlay = new();
}
private void OnStatusApplied(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectAppliedEvent args)
{
if (!_overlayMan.HasOverlay<DrunkOverlay>())
_overlayMan.AddOverlay(_overlay);
}
private void OnStatusRemoved(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRemovedEvent args)
{
if (Status.HasEffectComp<DrunkStatusEffectComponent>(args.Target))
return;
if (_player.LocalEntity != args.Target)
return;
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnPlayerAttached(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRelayedEvent<LocalPlayerAttachedEvent> args)
private void OnPlayerAttached(EntityUid uid, DrunkComponent component, LocalPlayerAttachedEvent args)
{
_overlayMan.AddOverlay(_overlay);
}
private void OnPlayerDetached(Entity<DrunkStatusEffectComponent> entity, ref StatusEffectRelayedEvent<LocalPlayerDetachedEvent> args)
private void OnPlayerDetached(EntityUid uid, DrunkComponent component, LocalPlayerDetachedEvent args)
{
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
private void OnDrunkInit(EntityUid uid, DrunkComponent component, ComponentInit args)
{
if (_player.LocalEntity == uid)
_overlayMan.AddOverlay(_overlay);
}
private void OnDrunkShutdown(EntityUid uid, DrunkComponent component, ComponentShutdown args)
{
if (_player.LocalEntity == uid)
{
_overlay.CurrentBoozePower = 0;
_overlayMan.RemoveOverlay(_overlay);
}
}
}

View File

@@ -0,0 +1,7 @@
using Content.Shared.Explosion.EntitySystems;
namespace Content.Client.Explosion;
public sealed class SmokeOnTriggerSystem : SharedSmokeOnTriggerSystem
{
}

View File

@@ -0,0 +1,7 @@
using Content.Shared.Explosion;
using Content.Shared.Explosion.Components;
namespace Content.Client.Explosion;
[RegisterComponent, Access(typeof(TriggerSystem))]
public sealed partial class TriggerOnProximityComponent : SharedTriggerOnProximityComponent {}

View File

@@ -1,12 +1,11 @@
using Content.Shared.Trigger;
using Content.Shared.Trigger.Components.Triggers;
using Robust.Client.Animations;
using Robust.Client.GameObjects;
using Robust.Shared.Animations;
namespace Content.Client.Trigger.Systems;
namespace Content.Client.Explosion;
public sealed class ProximityTriggerAnimationSystem : EntitySystem
public sealed partial class TriggerSystem
{
[Dependency] private readonly AnimationPlayerSystem _player = default!;
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
@@ -19,7 +18,7 @@ public sealed class ProximityTriggerAnimationSystem : EntitySystem
private const string AnimKey = "proximity";
private static readonly Animation FlasherAnimation = new Animation
private static readonly Animation _flasherAnimation = new Animation
{
Length = TimeSpan.FromSeconds(0.6f),
AnimationTracks = {
@@ -43,10 +42,8 @@ public sealed class ProximityTriggerAnimationSystem : EntitySystem
}
};
public override void Initialize()
private void InitializeProximity()
{
base.Initialize();
SubscribeLocalEvent<TriggerOnProximityComponent, ComponentInit>(OnProximityInit);
SubscribeLocalEvent<TriggerOnProximityComponent, AppearanceChangeEvent>(OnProxAppChange);
SubscribeLocalEvent<TriggerOnProximityComponent, AnimationCompletedEvent>(OnProxAnimation);
@@ -97,7 +94,7 @@ public sealed class ProximityTriggerAnimationSystem : EntitySystem
break;
case ProximityTriggerVisuals.Active:
if (_player.HasRunningAnimation(uid, player, AnimKey)) return;
_player.Play((uid, player), FlasherAnimation, AnimKey);
_player.Play((uid, player), _flasherAnimation, AnimKey);
break;
case ProximityTriggerVisuals.Off:
default:

View File

@@ -0,0 +1,10 @@
namespace Content.Client.Explosion;
public sealed partial class TriggerSystem : EntitySystem
{
public override void Initialize()
{
base.Initialize();
InitializeProximity();
}
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Forensics.Systems;
namespace Content.Client.Forensics.Systems;
public sealed class ForensicsSystem : SharedForensicsSystem;

View File

@@ -34,8 +34,6 @@ namespace Content.Client.GameTicking.Managers
[ViewVariables] public TimeSpan StartTime { get; private set; }
[ViewVariables] public new bool Paused { get; private set; }
public override IReadOnlyList<(TimeSpan, string)> AllPreviousGameRules => new List<(TimeSpan, string)>();
[ViewVariables] public IReadOnlyDictionary<NetEntity, Dictionary<ProtoId<JobPrototype>, int?>> JobsAvailable => _jobsAvailable;
[ViewVariables] public IReadOnlyDictionary<NetEntity, string> StationNames => _stationNames;

View File

@@ -1,58 +1,25 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
namespace Content.Client.Ghost;
public sealed class GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : BoundUserInterface(owner, uiKey)
public sealed class GhostRoleRadioBoundUserInterface : BoundUserInterface
{
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
private GhostRoleRadioMenu? _ghostRoleRadioMenu;
private SimpleRadialMenu? _ghostRoleRadioMenu;
public GhostRoleRadioBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{
IoCManager.InjectDependencies(this);
}
protected override void Open()
{
base.Open();
_ghostRoleRadioMenu = this.CreateWindow<SimpleRadialMenu>();
// The purpose of this radial UI is for ghost role radios that allow you to select
// more than one potential option, such as with kobolds/lizards.
// This means that it won't show anything if SelectablePrototypes is empty.
if (!EntMan.TryGetComponent<GhostRoleMobSpawnerComponent>(Owner, out var comp))
return;
var list = ConvertToButtons(comp.SelectablePrototypes);
_ghostRoleRadioMenu.SetButtons(list);
}
private IEnumerable<RadialMenuOptionBase> ConvertToButtons(List<ProtoId<GhostRolePrototype>> protoIds)
{
var list = new List<RadialMenuOptionBase>();
foreach (var ghostRoleProtoId in protoIds)
{
// For each prototype we find we want to create a button that uses the name of the ghost role
// as the hover tooltip, and the icon is taken from either the ghost role entityprototype
// or the indicated icon entityprototype.
if (!_prototypeManager.Resolve(ghostRoleProtoId, out var ghostRoleProto))
continue;
var option = new RadialMenuActionOption<ProtoId<GhostRolePrototype>>(SendGhostRoleRadioMessage, ghostRoleProtoId)
{
ToolTip = Loc.GetString(ghostRoleProto.Name),
// pick the icon if it exists, otherwise fallback to the ghost role's entity
IconSpecifier = ghostRoleProto.IconPrototype != null
&& _prototypeManager.Resolve(ghostRoleProto.IconPrototype, out var iconProto)
? RadialMenuIconSpecifier.With(iconProto)
: RadialMenuIconSpecifier.With(ghostRoleProto.EntityPrototype)
};
list.Add(option);
}
return list;
_ghostRoleRadioMenu = this.CreateWindow<GhostRoleRadioMenu>();
_ghostRoleRadioMenu.SetEntity(Owner);
_ghostRoleRadioMenu.SendGhostRoleRadioMessageAction += SendGhostRoleRadioMessage;
}
private void SendGhostRoleRadioMessage(ProtoId<GhostRolePrototype> protoId)

View File

@@ -0,0 +1,8 @@
<ui:RadialMenu
xmlns:ui="clr-namespace:Content.Client.UserInterface.Controls"
CloseButtonStyleClass="RadialMenuCloseButton"
VerticalExpand="True"
HorizontalExpand="True">
<ui:RadialContainer Name="Main">
</ui:RadialContainer>
</ui:RadialMenu>

View File

@@ -0,0 +1,105 @@
using Content.Client.UserInterface.Controls;
using Content.Shared.Ghost.Roles;
using Content.Shared.Ghost.Roles.Components;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using System.Numerics;
namespace Content.Client.Ghost;
public sealed partial class GhostRoleRadioMenu : RadialMenu
{
[Dependency] private readonly EntityManager _entityManager = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public event Action<ProtoId<GhostRolePrototype>>? SendGhostRoleRadioMessageAction;
public EntityUid Entity { get; set; }
public GhostRoleRadioMenu()
{
IoCManager.InjectDependencies(this);
RobustXamlLoader.Load(this);
}
public void SetEntity(EntityUid uid)
{
Entity = uid;
RefreshUI();
}
private void RefreshUI()
{
// The main control that will contain all the clickable options
var main = FindControl<RadialContainer>("Main");
// The purpose of this radial UI is for ghost role radios that allow you to select
// more than one potential option, such as with kobolds/lizards.
// This means that it won't show anything if SelectablePrototypes is empty.
if (!_entityManager.TryGetComponent<GhostRoleMobSpawnerComponent>(Entity, out var comp))
return;
foreach (var ghostRoleProtoString in comp.SelectablePrototypes)
{
// For each prototype we find we want to create a button that uses the name of the ghost role
// as the hover tooltip, and the icon is taken from either the ghost role entityprototype
// or the indicated icon entityprototype.
if (!_prototypeManager.TryIndex<GhostRolePrototype>(ghostRoleProtoString, out var ghostRoleProto))
continue;
var button = new GhostRoleRadioMenuButton()
{
SetSize = new Vector2(64, 64),
ToolTip = Loc.GetString(ghostRoleProto.Name),
ProtoId = ghostRoleProto.ID,
};
var entProtoView = new EntityPrototypeView()
{
SetSize = new Vector2(48, 48),
VerticalAlignment = VAlignment.Center,
HorizontalAlignment = HAlignment.Center,
Stretch = SpriteView.StretchMode.Fill
};
// pick the icon if it exists, otherwise fallback to the ghost role's entity
if (_prototypeManager.TryIndex(ghostRoleProto.IconPrototype, out var iconProto))
entProtoView.SetPrototype(iconProto);
else
entProtoView.SetPrototype(ghostRoleProto.EntityPrototype);
button.AddChild(entProtoView);
main.AddChild(button);
AddGhostRoleRadioMenuButtonOnClickActions(main);
}
}
private void AddGhostRoleRadioMenuButtonOnClickActions(Control control)
{
var mainControl = control as RadialContainer;
if (mainControl == null)
return;
foreach (var child in mainControl.Children)
{
var castChild = child as GhostRoleRadioMenuButton;
if (castChild == null)
continue;
castChild.OnButtonUp += _ =>
{
SendGhostRoleRadioMessageAction?.Invoke(castChild.ProtoId);
Close();
};
}
}
}
public sealed class GhostRoleRadioMenuButton : RadialMenuTextureButtonWithSector
{
public ProtoId<GhostRolePrototype> ProtoId { get; set; }
}

View File

@@ -136,7 +136,6 @@ namespace Content.Client.Ghost
//_actions.RemoveAction(uid, component.ToggleGhostHearingActionEntity); //Dont need in CP14
//CP14
_actions.RemoveAction(uid, component.CP14ToggleRoofActionEntity);
_actions.RemoveAction(uid, component.CP14RespawnActionEntity);
//CP14 end
if (uid != _playerManager.LocalEntity)

View File

@@ -5,17 +5,14 @@ using Content.Client.Guidebook.Richtext;
using Content.Client.Message;
using Content.Client.UserInterface.ControlExtensions;
using Content.Shared.Body.Prototypes;
using Content.Shared.CCVar;
using Content.Shared.Chemistry.Reaction;
using Content.Shared.Chemistry.Reagent;
using Content.Shared.Contraband;
using JetBrains.Annotations;
using Robust.Client.AutoGenerated;
using Robust.Client.Graphics;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
@@ -30,10 +27,8 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
[Dependency] private readonly IEntitySystemManager _systemManager = default!;
[Dependency] private readonly ILogManager _logManager = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly IConfigurationManager _config = default!;
private readonly ChemistryGuideDataSystem _chemistryGuideData;
private readonly ContrabandSystem _contraband;
private readonly ISawmill _sawmill;
public IPrototype? RepresentedPrototype { get; private set; }
@@ -44,7 +39,6 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
IoCManager.InjectDependencies(this);
_sawmill = _logManager.GetSawmill("guidebook.reagent");
_chemistryGuideData = _systemManager.GetEntitySystem<ChemistryGuideDataSystem>();
_contraband = _systemManager.GetEntitySystem<ContrabandSystem>();
MouseFilter = MouseFilterMode.Stop;
}
@@ -210,25 +204,6 @@ public sealed partial class GuideReagentEmbed : BoxContainer, IDocumentTag, ISea
description.PushNewline();
description.AddMarkupOrThrow(Loc.GetString("guidebook-reagent-physical-description",
("description", reagent.LocalizedPhysicalDescription)));
if (_config.GetCVar(CCVars.ContrabandExamine))
{
// Department-restricted text
if (reagent.AllowedJobs.Count > 0 || reagent.AllowedDepartments.Count > 0)
{
description.PushNewline();
description.AddMarkupPermissive(
_contraband.GenerateDepartmentExamineMessage(reagent.AllowedDepartments, reagent.AllowedJobs, ContrabandItemType.Reagent));
}
// Other contraband text
else if (reagent.ContrabandSeverity != null &&
_prototype.Resolve(reagent.ContrabandSeverity.Value, out var severity))
{
description.PushNewline();
description.AddMarkupPermissive(Loc.GetString(severity.ExamineText, ("type", ContrabandItemType.Reagent)));
}
}
ReagentDescription.SetMessage(description);
}

View File

@@ -53,7 +53,7 @@ public sealed partial class DocumentParsingManager
public bool TryAddMarkup(Control control, ProtoId<GuideEntryPrototype> entryId, bool log = true)
{
if (!_prototype.Resolve(entryId, out var entry))
if (!_prototype.TryIndex(entryId, out var entry))
return false;
using var file = _resourceManager.ContentFileReadText(entry.Text);

View File

@@ -1,6 +1,5 @@
using Content.Shared.HotPotato;
using Robust.Shared.Random;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.HotPotato;
@@ -11,9 +10,6 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
[Dependency] private readonly IRobustRandom _random = default!;
[Dependency] private readonly SharedTransformSystem _transform = default!;
private readonly EntProtoId _hotPotatoEffectId = "HotPotatoEffect";
// TODO: particle system
public override void Update(float frameTime)
{
base.Update(frameTime);
@@ -27,7 +23,7 @@ public sealed class HotPotatoSystem : SharedHotPotatoSystem
if (_timing.CurTime < comp.TargetTime)
continue;
comp.TargetTime = _timing.CurTime + TimeSpan.FromSeconds(comp.EffectCooldown);
Spawn(_hotPotatoEffectId, _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
Spawn("HotPotatoEffect", _transform.GetMapCoordinates(uid).Offset(_random.NextVector2(0.25f)));
}
}
}

View File

@@ -300,26 +300,25 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
private void RemoveMarking(Marking marking, Entity<SpriteComponent> spriteEnt)
{
if (!_markingManager.TryGetMarking(marking, out var prototype))
{
return;
}
foreach (var sprite in prototype.Sprites)
{
if (sprite is not SpriteSpecifier.Rsi rsi)
{
continue;
}
var layerId = $"{marking.MarkingId}-{rsi.RsiState}";
if (!_sprite.LayerMapTryGet(spriteEnt.AsNullable(), layerId, out var index, false))
{
continue;
}
_sprite.LayerMapRemove(spriteEnt.AsNullable(), layerId);
_sprite.RemoveLayer(spriteEnt.AsNullable(), index);
// If this marking is one that can be displaced, we need to remove the displacement as well; otherwise
// altering a marking at runtime can lead to the renderer falling over.
// The Vulps must be shaved.
// (https://github.com/space-wizards/space-station-14/issues/40135).
if (prototype.CanBeDisplaced)
_displacement.EnsureDisplacementIsNotOnSprite(spriteEnt, layerId);
}
}
@@ -358,7 +357,9 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
var sprite = entity.Comp2;
if (!_sprite.LayerMapTryGet((entity.Owner, sprite), markingPrototype.BodyPart, out var targetLayer, false))
{
return;
}
visible &= !IsHidden(humanoid, markingPrototype.BodyPart);
visible &= humanoid.BaseLayers.TryGetValue(markingPrototype.BodyPart, out var setting)
@@ -369,7 +370,9 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
var markingSprite = markingPrototype.Sprites[j];
if (markingSprite is not SpriteSpecifier.Rsi rsi)
return;
{
continue;
}
var layerId = $"{markingPrototype.ID}-{rsi.RsiState}";
@@ -383,18 +386,26 @@ public sealed class HumanoidAppearanceSystem : SharedHumanoidAppearanceSystem
_sprite.LayerSetVisible((entity.Owner, sprite), layerId, visible);
if (!visible || setting == null) // this is kinda implied
{
continue;
}
// Okay so if the marking prototype is modified but we load old marking data this may no longer be valid
// and we need to check the index is correct.
// So if that happens just default to white?
if (colors != null && j < colors.Count)
{
_sprite.LayerSetColor((entity.Owner, sprite), layerId, colors[j]);
}
else
{
_sprite.LayerSetColor((entity.Owner, sprite), layerId, Color.White);
}
if (humanoid.MarkingsDisplacement.TryGetValue(markingPrototype.BodyPart, out var displacementData) && markingPrototype.CanBeDisplaced)
{
_displacement.TryAddDisplacement(displacementData, (entity.Owner, sprite), targetLayer + j + 1, layerId, out _);
}
}
}

View File

@@ -23,12 +23,10 @@ public sealed class ImplanterSystem : SharedImplanterSystem
{
if (_uiSystem.TryGetOpenUi<DeimplantBoundUserInterface>(uid, DeimplantUiKey.Key, out var bui))
{
// TODO: Don't use protoId for deimplanting
// and especially not raw strings!
Dictionary<string, string> implants = new();
foreach (var implant in component.DeimplantWhitelist)
{
if (_proto.Resolve(implant, out var proto))
if (_proto.TryIndex(implant, out var proto))
implants.Add(proto.ID, proto.Name);
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Implants;
namespace Content.Client.Implants;
public sealed class SubdermalImplantSystem : SharedSubdermalImplantSystem;

View File

@@ -62,7 +62,7 @@ public sealed partial class ChameleonControllerMenu : FancyWindow
// Go through every outfit and add them to the correct department.
foreach (var outfit in _outfits)
{
_prototypeManager.Resolve(outfit.Job, out var jobProto);
_prototypeManager.TryIndex(outfit.Job, out var jobProto);
var name = outfit.LoadoutName ?? outfit.Name ?? jobProto?.Name ?? "Prototype has no name or job.";

View File

@@ -49,7 +49,7 @@ public sealed class ImplanterStatusControl : Control
if (_parent.CurrentMode == ImplanterToggleMode.Draw)
{
string implantName = _parent.DeimplantChosen != null
? (_prototype.Resolve(_parent.DeimplantChosen.Value, out EntityPrototype? implantProto) ? implantProto.Name : Loc.GetString("implanter-empty-text"))
? (_prototype.TryIndex(_parent.DeimplantChosen.Value, out EntityPrototype? implantProto) ? implantProto.Name : Loc.GetString("implanter-empty-text"))
: Loc.GetString("implanter-empty-text");
_label.SetMarkup(Loc.GetString("implanter-label-draw",

View File

@@ -294,6 +294,7 @@ public sealed partial class InstrumentSystem : SharedInstrumentSystem
SetMaster(uid, null);
TrySetChannels(uid, data);
instrument.MidiEventBuffer.Clear();
instrument.Renderer.OnMidiEvent += instrument.MidiEventBuffer.Add;
return true;
}

View File

@@ -21,6 +21,7 @@ namespace Content.Client.Inventory
{
[Dependency] private readonly IPlayerManager _playerManager = default!;
[Dependency] private readonly IUserInterfaceManager _ui = default!;
[Dependency] private readonly IGameTiming _timing = default!;
[Dependency] private readonly ClientClothingSystem _clothingVisualsSystem = default!;
[Dependency] private readonly ExamineSystem _examine = default!;
@@ -114,13 +115,6 @@ namespace Content.Client.Inventory
OnLinkInventorySlots?.Invoke(uid, component);
}
protected override void OnInit(Entity<InventoryComponent> ent, ref ComponentInit args)
{
base.OnInit(ent, ref args);
_clothingVisualsSystem.InitClothing(ent.Owner, ent.Comp);
}
public override void Shutdown()
{
CommandBinds.Unregister<ClientInventorySystem>();
@@ -267,6 +261,7 @@ namespace Content.Client.Inventory
TryAddSlotData((ent.Owner, inventorySlots), (SlotData)slot);
}
_clothingVisualsSystem.InitClothing(ent, ent.Comp);
if (ent.Owner == _playerManager.LocalEntity)
ReloadInventory(inventorySlots);
}

View File

@@ -0,0 +1,8 @@
using Content.Shared.Kitchen;
namespace Content.Client.Kitchen;
public sealed class KitchenSpikeSystem : SharedKitchenSpikeSystem
{
}

View File

@@ -30,10 +30,6 @@ namespace Content.Client.Lathe.UI
{
SendMessage(new LatheQueueRecipeMessage(recipe, amount));
};
_menu.QueueDeleteAction += index => SendMessage(new LatheDeleteRequestMessage(index));
_menu.QueueMoveUpAction += index => SendMessage(new LatheMoveRequestMessage(index, -1));
_menu.QueueMoveDownAction += index => SendMessage(new LatheMoveRequestMessage(index, 1));
_menu.DeleteFabricatingAction += () => SendMessage(new LatheAbortFabricationMessage());
}
protected override void UpdateState(BoundUserInterfaceState state)

View File

@@ -1,7 +1,6 @@
<DefaultWindow
xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:system="clr-namespace:System;assembly=System.Runtime"
xmlns:ui="clr-namespace:Content.Client.Materials.UI"
Title="{Loc 'lathe-menu-title'}"
MinSize="550 450"
@@ -111,18 +110,6 @@
HorizontalAlignment="Left"
Margin="130 0 0 0">
</Label>
<Button
Name="DeleteFabricating"
Margin="0"
Text="✖"
SetSize="38 32"
HorizontalAlignment="Right"
ToolTip="{Loc 'lathe-menu-delete-fabricating-tooltip'}">
<Button.StyleClasses>
<system:String>Caution</system:String>
<system:String>OpenLeft</system:String>
</Button.StyleClasses>
</Button>
</PanelContainer>
</BoxContainer>
<ScrollContainer VerticalExpand="True" HScrollEnabled="False">

View File

@@ -11,7 +11,6 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Lathe.UI;
@@ -27,10 +26,6 @@ public sealed partial class LatheMenu : DefaultWindow
public event Action<BaseButton.ButtonEventArgs>? OnServerListButtonPressed;
public event Action<string, int>? RecipeQueueAction;
public event Action<int>? QueueDeleteAction;
public event Action<int>? QueueMoveUpAction;
public event Action<int>? QueueMoveDownAction;
public event Action? DeleteFabricatingAction;
public List<ProtoId<LatheRecipePrototype>> Recipes = new();
@@ -55,21 +50,12 @@ public sealed partial class LatheMenu : DefaultWindow
};
AmountLineEdit.OnTextChanged += _ =>
{
if (int.TryParse(AmountLineEdit.Text, out var amount))
{
if (amount > LatheSystem.MaxItemsPerRequest)
AmountLineEdit.Text = LatheSystem.MaxItemsPerRequest.ToString();
else if (amount < 0)
AmountLineEdit.Text = "0";
}
PopulateRecipes();
};
FilterOption.OnItemSelected += OnItemSelected;
ServerListButton.OnPressed += a => OnServerListButtonPressed?.Invoke(a);
DeleteFabricating.OnPressed += _ => DeleteFabricatingAction?.Invoke();
}
public void SetEntity(EntityUid uid)
@@ -97,7 +83,7 @@ public sealed partial class LatheMenu : DefaultWindow
var recipesToShow = new List<LatheRecipePrototype>();
foreach (var recipe in Recipes)
{
if (!_prototypeManager.Resolve(recipe, out var proto))
if (!_prototypeManager.TryIndex(recipe, out var proto))
continue;
// Category filtering
@@ -129,50 +115,21 @@ public sealed partial class LatheMenu : DefaultWindow
RecipeCount.Text = Loc.GetString("lathe-menu-recipe-count", ("count", recipesToShow.Count));
var sortedRecipesToShow = recipesToShow.OrderBy(_lathe.GetRecipeName);
// Get the existing list of queue controls
var oldChildCount = RecipeList.ChildCount;
RecipeList.Children.Clear();
_entityManager.TryGetComponent(Entity, out LatheComponent? lathe);
int idx = 0;
foreach (var prototype in sortedRecipesToShow)
{
var canProduce = _lathe.CanProduce(Entity, prototype, quantity, component: lathe);
var tooltipFunction = () => GenerateTooltipText(prototype);
if (idx >= oldChildCount)
var control = new RecipeControl(_lathe, prototype, () => GenerateTooltipText(prototype), canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s =>
{
var control = new RecipeControl(_lathe, prototype, tooltipFunction, canProduce, GetRecipeDisplayControl(prototype));
control.OnButtonPressed += s =>
{
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
amount = 1;
RecipeQueueAction?.Invoke(s, amount);
};
RecipeList.AddChild(control);
}
else
{
var child = RecipeList.GetChild(idx) as RecipeControl;
if (child == null)
{
DebugTools.Assert($"Lathe menu recipe control at {idx} is not of type RecipeControl"); // Something's gone terribly wrong.
continue;
}
child.SetRecipe(prototype);
child.SetTooltipSupplier(tooltipFunction);
child.SetCanProduce(canProduce);
child.SetDisplayControl(GetRecipeDisplayControl(prototype));
}
idx++;
}
// Shrink list if new list is shorter than old list.
for (var childIdx = oldChildCount - 1; idx <= childIdx; childIdx--)
{
RecipeList.RemoveChild(childIdx);
if (!int.TryParse(AmountLineEdit.Text, out var amount) || amount <= 0)
amount = 1;
RecipeQueueAction?.Invoke(s, amount);
};
RecipeList.AddChild(control);
}
}
@@ -183,7 +140,7 @@ public sealed partial class LatheMenu : DefaultWindow
foreach (var (id, amount) in prototype.Materials)
{
if (!_prototypeManager.Resolve(id, out var proto))
if (!_prototypeManager.TryIndex(id, out var proto))
continue;
var adjustedAmount = SharedLatheSystem.AdjustMaterial(amount, prototype.ApplyMaterialDiscount, multiplier);
@@ -266,53 +223,25 @@ public sealed partial class LatheMenu : DefaultWindow
/// Populates the build queue list with all queued items
/// </summary>
/// <param name="queue"></param>
public void PopulateQueueList(IReadOnlyCollection<LatheRecipeBatch> queue)
public void PopulateQueueList(IReadOnlyCollection<ProtoId<LatheRecipePrototype>> queue)
{
// Get the existing list of queue controls
var oldChildCount = QueueList.ChildCount;
QueueList.DisposeAllChildren();
var idx = 0;
foreach (var batch in queue)
var idx = 1;
foreach (var recipeProto in queue)
{
var recipe = _prototypeManager.Index(batch.Recipe);
var recipe = _prototypeManager.Index(recipeProto);
var queuedRecipeBox = new BoxContainer();
queuedRecipeBox.Orientation = BoxContainer.LayoutOrientation.Horizontal;
var itemName = _lathe.GetRecipeName(batch.Recipe);
string displayText;
if (batch.ItemsRequested > 1)
displayText = Loc.GetString("lathe-menu-item-batch", ("index", idx + 1), ("name", itemName), ("printed", batch.ItemsPrinted), ("total", batch.ItemsRequested));
else
displayText = Loc.GetString("lathe-menu-item-single", ("index", idx + 1), ("name", itemName));
queuedRecipeBox.AddChild(GetRecipeDisplayControl(recipe));
if (idx >= oldChildCount)
{
var queuedRecipeBox = new QueuedRecipeControl(displayText, idx, GetRecipeDisplayControl(recipe));
queuedRecipeBox.OnDeletePressed += s => QueueDeleteAction?.Invoke(s);
queuedRecipeBox.OnMoveUpPressed += s => QueueMoveUpAction?.Invoke(s);
queuedRecipeBox.OnMoveDownPressed += s => QueueMoveDownAction?.Invoke(s);
QueueList.AddChild(queuedRecipeBox);
}
else
{
var child = QueueList.GetChild(idx) as QueuedRecipeControl;
if (child == null)
{
DebugTools.Assert($"Lathe menu queued recipe control at {idx} is not of type QueuedRecipeControl"); // Something's gone terribly wrong.
continue;
}
child.SetDisplayText(displayText);
child.SetIndex(idx);
child.SetDisplayControl(GetRecipeDisplayControl(recipe));
}
var queuedRecipeLabel = new Label();
queuedRecipeLabel.Text = $"{idx}. {_lathe.GetRecipeName(recipe)}";
queuedRecipeBox.AddChild(queuedRecipeLabel);
QueueList.AddChild(queuedRecipeBox);
idx++;
}
// Shrink list if new list is shorter than old list.
for (var childIdx = oldChildCount - 1; idx <= childIdx; childIdx--)
{
QueueList.RemoveChild(childIdx);
}
}
public void SetQueueInfo(ProtoId<LatheRecipePrototype>? recipeProto)

View File

@@ -1,35 +0,0 @@
<Control xmlns="https://spacestation14.io"
xmlns:system="clr-namespace:System;assembly=System.Runtime">
<BoxContainer Orientation="Horizontal">
<BoxContainer
Name="RecipeDisplayContainer"
Margin="0 0 4 0"
HorizontalAlignment="Center"
VerticalAlignment="Center"
MinSize="32 32"
/>
<Label Name="RecipeName" HorizontalExpand="True" />
<Button
Name="MoveUp"
Margin="0"
Text="⏶"
StyleClasses="OpenRight"
ToolTip="{Loc 'lathe-menu-move-up-tooltip'}"/>
<Button
Name="MoveDown"
Margin="0"
Text="⏷"
StyleClasses="OpenBoth"
ToolTip="{Loc 'lathe-menu-move-down-tooltip'}"/>
<Button
Name="Delete"
Margin="0"
Text="✖"
ToolTip="{Loc 'lathe-menu-delete-item-tooltip'}">
<Button.StyleClasses>
<system:String>Caution</system:String>
<system:String>OpenLeft</system:String>
</Button.StyleClasses>
</Button>
</BoxContainer>
</Control>

View File

@@ -1,56 +0,0 @@
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.Lathe.UI;
[GenerateTypedNameReferences]
public sealed partial class QueuedRecipeControl : Control
{
public Action<int>? OnDeletePressed;
public Action<int>? OnMoveUpPressed;
public Action<int>? OnMoveDownPressed;
private int _index;
public QueuedRecipeControl(string displayText, int index, Control displayControl)
{
RobustXamlLoader.Load(this);
SetDisplayText(displayText);
SetDisplayControl(displayControl);
SetIndex(index);
_index = index;
MoveUp.OnPressed += (_) =>
{
OnMoveUpPressed?.Invoke(_index);
};
MoveDown.OnPressed += (_) =>
{
OnMoveDownPressed?.Invoke(_index);
};
Delete.OnPressed += (_) =>
{
OnDeletePressed?.Invoke(_index);
};
}
public void SetDisplayText(string displayText)
{
RecipeName.Text = displayText;
}
public void SetDisplayControl(Control displayControl)
{
RecipeDisplayContainer.Children.Clear();
RecipeDisplayContainer.AddChild(displayControl);
}
public void SetIndex(int index)
{
_index = index;
}
}

View File

@@ -2,7 +2,6 @@ using Content.Shared.Research.Prototypes;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
namespace Content.Client.Lathe.UI;
@@ -12,47 +11,20 @@ public sealed partial class RecipeControl : Control
public Action<string>? OnButtonPressed;
public Func<string> TooltipTextSupplier;
private ProtoId<LatheRecipePrototype> _recipeId;
private LatheSystem _latheSystem;
public RecipeControl(LatheSystem latheSystem, LatheRecipePrototype recipe, Func<string> tooltipTextSupplier, bool canProduce, Control displayControl)
{
RobustXamlLoader.Load(this);
_latheSystem = latheSystem;
_recipeId = recipe.ID;
RecipeName.Text = latheSystem.GetRecipeName(recipe);
RecipeDisplayContainer.AddChild(displayControl);
Button.Disabled = !canProduce;
TooltipTextSupplier = tooltipTextSupplier;
SetRecipe(recipe);
SetCanProduce(canProduce);
SetDisplayControl(displayControl);
Button.TooltipSupplier = SupplyTooltip;
Button.OnPressed += (_) =>
{
OnButtonPressed?.Invoke(_recipeId);
OnButtonPressed?.Invoke(recipe.ID);
};
Button.TooltipSupplier = SupplyTooltip;
}
public void SetRecipe(LatheRecipePrototype recipe)
{
RecipeName.Text = _latheSystem.GetRecipeName(recipe);
_recipeId = recipe.ID;
}
public void SetTooltipSupplier(Func<string> tooltipTextSupplier)
{
TooltipTextSupplier = tooltipTextSupplier;
}
public void SetCanProduce(bool canProduce)
{
Button.Disabled = !canProduce;
}
public void SetDisplayControl(Control displayControl)
{
RecipeDisplayContainer.Children.Clear();
RecipeDisplayContainer.AddChild(displayControl);
}
private Control? SupplyTooltip(Control sender)

View File

@@ -1,46 +1,36 @@
using Content.Shared.Light.Components;
using Content.Shared.Light.EntitySystems;
using Robust.Client.GameObjects;
namespace Content.Client.Light.EntitySystems;
namespace Content.Client.Light.Visualizers;
public sealed class LightBulbSystem : SharedLightBulbSystem
public sealed class LightBulbSystem : VisualizerSystem<LightBulbComponent>
{
[Dependency] private readonly AppearanceSystem _appearance = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<LightBulbComponent, AppearanceChangeEvent>(OnAppearanceChange);
}
private void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
protected override void OnAppearanceChange(EntityUid uid, LightBulbComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;
// update sprite state
if (_appearance.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
if (AppearanceSystem.TryGetData<LightBulbState>(uid, LightBulbVisuals.State, out var state, args.Component))
{
switch (state)
{
case LightBulbState.Normal:
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.NormalSpriteState);
break;
case LightBulbState.Broken:
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BrokenSpriteState);
break;
case LightBulbState.Burned:
_sprite.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
SpriteSystem.LayerSetRsiState((uid, args.Sprite), LightBulbVisualLayers.Base, comp.BurnedSpriteState);
break;
}
}
// also update sprites color
if (_appearance.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
if (AppearanceSystem.TryGetData<Color>(uid, LightBulbVisuals.Color, out var color, args.Component))
{
_sprite.SetColor((uid, args.Sprite), color);
SpriteSystem.SetColor((uid, args.Sprite), color);
}
}
}

View File

@@ -1,5 +0,0 @@
using Content.Shared.Light.EntitySystems;
namespace Content.Client.Light.EntitySystems;
public sealed class PoweredLightSystem : SharedPoweredLightSystem;

View File

@@ -72,7 +72,6 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
});
_configurationManager.OnValueChanged(CCVars.GameRoleTimers, _ => RefreshProfileEditor());
_configurationManager.OnValueChanged(CCVars.GameRoleLoadoutTimers, _ => RefreshProfileEditor());
_configurationManager.OnValueChanged(CCVars.GameRoleWhitelist, _ => RefreshProfileEditor());
}
@@ -362,7 +361,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
{
foreach (var loadout in group)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
_spawn.EquipStartingGear(uid, loadoutProto);
@@ -385,14 +384,14 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
{
foreach (var loadout in loadouts)
{
if (!_prototypeManager.Resolve(loadout.Prototype, out var loadoutProto))
if (!_prototypeManager.TryIndex(loadout.Prototype, out var loadoutProto))
continue;
// TODO: Need some way to apply starting gear to an entity and replace existing stuff coz holy fucking shit dude.
foreach (var slot in slots)
{
// Try startinggear first
if (_prototypeManager.Resolve(loadoutProto.StartingGear, out var loadoutGear))
if (_prototypeManager.TryIndex(loadoutProto.StartingGear, out var loadoutGear))
{
var itemType = ((IEquipmentLoadout) loadoutGear).GetGear(slot.Name);
@@ -427,7 +426,7 @@ public sealed class LobbyUIController : UIController, IOnStateEntered<LobbyState
}
}
if (!_prototypeManager.Resolve(job.StartingGear, out var gear))
if (!_prototypeManager.TryIndex(job.StartingGear, out var gear))
return;
foreach (var slot in slots)

View File

@@ -9,6 +9,7 @@ using Content.Client.Players.PlayTimeTracking;
using Content.Client.Sprite;
using Content.Client.Stylesheets;
using Content.Client.UserInterface.Systems.Guidebook;
using Content.Shared._CP14.Humanoid;
using Content.Shared.CCVar;
using Content.Shared.Clothing;
using Content.Shared.GameTicking;
@@ -810,7 +811,7 @@ namespace Content.Client.Lobby.UI
if (_prototypeManager.HasIndex<GuideEntryPrototype>(species))
page = new ProtoId<GuideEntryPrototype>(species.Id); // Gross. See above todo comment.
if (_prototypeManager.Resolve(DefaultSpeciesGuidebook, out var guideRoot))
if (_prototypeManager.TryIndex(DefaultSpeciesGuidebook, out var guideRoot))
{
var dict = new Dictionary<ProtoId<GuideEntryPrototype>, GuideEntry>();
dict.Add(DefaultSpeciesGuidebook, guideRoot);
@@ -1020,7 +1021,7 @@ namespace Content.Client.Lobby.UI
_loadoutWindow = new LoadoutWindow(Profile, roleLoadout, roleLoadoutProto, _playerManager.LocalSession, collection)
{
Title = Loc.GetString("loadout-window-title-loadout", ("job", $"{jobProto?.LocalizedName}")),
Title = jobProto?.ID + "-loadout",
};
// Refresh the buttons etc.
@@ -1088,11 +1089,10 @@ namespace Content.Client.Lobby.UI
if (Profile is null) return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
var strategy = _prototypeManager.Index(skin).Strategy;
switch (strategy.InputType)
switch (skin)
{
case SkinColorationStrategyInput.Unary:
case HumanoidSkinColor.HumanToned:
{
if (!Skin.Visible)
{
@@ -1100,14 +1100,13 @@ namespace Content.Client.Lobby.UI
RgbSkinColorContainer.Visible = false;
}
var color = strategy.FromUnary(Skin.Value);
var color = SkinColor.HumanSkinTone((int) Skin.Value);
Markings.CurrentSkinColor = color;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));//
break;
}
case SkinColorationStrategyInput.Color:
case HumanoidSkinColor.Hues:
{
if (!RgbSkinColorContainer.Visible)
{
@@ -1115,13 +1114,54 @@ namespace Content.Client.Lobby.UI
RgbSkinColorContainer.Visible = true;
}
var color = strategy.ClosestSkinColor(_rgbSkinColorSelector.Color);
Markings.CurrentSkinColor = _rgbSkinColorSelector.Color;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(_rgbSkinColorSelector.Color));
break;
}
case HumanoidSkinColor.TintedHues:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
var color = SkinColor.TintedHues(_rgbSkinColorSelector.Color);
Markings.CurrentSkinColor = color;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
case HumanoidSkinColor.VoxFeathers:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
var color = SkinColor.ClosestVoxColor(_rgbSkinColorSelector.Color);
Markings.CurrentSkinColor = color;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
// CP14 - Custom HumanoidSkinColor - Start
case HumanoidSkinColor.TieflingHues:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
var color = CP14SkinColor.MakeTieflingHueValid(_rgbSkinColorSelector.Color);
Markings.CurrentSkinColor = color;
Profile = Profile.WithCharacterAppearance(Profile.Appearance.WithSkinColor(color));
break;
}
// CP14 - Custom HumanoidSkinColor - End
}
ReloadProfilePreview();
@@ -1268,7 +1308,7 @@ namespace Content.Client.Lobby.UI
var sexes = new List<Sex>();
// add species sex options, default to just none if we are in bizzaro world and have no species
if (_prototypeManager.Resolve<SpeciesPrototype>(Profile.Species, out var speciesProto))
if (_prototypeManager.TryIndex<SpeciesPrototype>(Profile.Species, out var speciesProto))
{
foreach (var sex in speciesProto.Sexes)
{
@@ -1298,11 +1338,10 @@ namespace Content.Client.Lobby.UI
return;
var skin = _prototypeManager.Index<SpeciesPrototype>(Profile.Species).SkinColoration;
var strategy = _prototypeManager.Index(skin).Strategy;
switch (strategy.InputType)
switch (skin)
{
case SkinColorationStrategyInput.Unary:
case HumanoidSkinColor.HumanToned:
{
if (!Skin.Visible)
{
@@ -1310,11 +1349,11 @@ namespace Content.Client.Lobby.UI
RgbSkinColorContainer.Visible = false;
}
Skin.Value = strategy.ToUnary(Profile.Appearance.SkinColor);
Skin.Value = SkinColor.HumanSkinToneFromColor(Profile.Appearance.SkinColor);
break;
}
case SkinColorationStrategyInput.Color:
case HumanoidSkinColor.Hues:
{
if (!RgbSkinColorContainer.Visible)
{
@@ -1322,11 +1361,49 @@ namespace Content.Client.Lobby.UI
RgbSkinColorContainer.Visible = true;
}
_rgbSkinColorSelector.Color = strategy.ClosestSkinColor(Profile.Appearance.SkinColor);
// set the RGB values to the direct values otherwise
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
break;
}
case HumanoidSkinColor.TintedHues:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
// set the RGB values to the direct values otherwise
_rgbSkinColorSelector.Color = Profile.Appearance.SkinColor;
break;
}
case HumanoidSkinColor.VoxFeathers:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
_rgbSkinColorSelector.Color = SkinColor.ClosestVoxColor(Profile.Appearance.SkinColor);
break;
}
// CP14 - Custom HumanoidSkinColor - Start
case HumanoidSkinColor.TieflingHues:
{
if (!RgbSkinColorContainer.Visible)
{
Skin.Visible = false;
RgbSkinColorContainer.Visible = true;
}
_rgbSkinColorSelector.Color = CP14SkinColor.MakeTieflingHueValid(Profile.Appearance.SkinColor);
break;
}
// CP14 - Custom HumanoidSkinColor - End
}
}
public void UpdateSpeciesGuidebookIcon()
@@ -1337,7 +1414,7 @@ namespace Content.Client.Lobby.UI
if (species is null)
return;
if (!_prototypeManager.Resolve<SpeciesPrototype>(species, out var speciesProto))
if (!_prototypeManager.TryIndex<SpeciesPrototype>(species, out var speciesProto))
return;
// Don't display the info button if no guide entry is found

View File

@@ -40,7 +40,7 @@ public sealed partial class LoadoutContainer : BoxContainer
SelectButton.TooltipSupplier = _ => tooltip;
}
if (_protoManager.Resolve(proto, out var loadProto))
if (_protoManager.TryIndex(proto, out var loadProto))
{
var ent = loadProto.DummyEntity ?? _entManager.System<LoadoutSystem>().GetFirstOrNull(loadProto);

View File

@@ -62,7 +62,7 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
});
}
if (protoMan.Resolve(loadout.Role, out var roleProto) && roleProto.Points != null && loadout.Points != null)
if (protoMan.TryIndex(loadout.Role, out var roleProto) && roleProto.Points != null && loadout.Points != null)
{
RestrictionsContainer.AddChild(new Label()
{
@@ -112,14 +112,14 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
})
.ToList();
/*
* Determine which element should be displayed first:
* - If any element is currently selected (its button is pressed), use it.
* - Otherwise, fallback to the first element in the list.
*
* This moves the selected item outside of the sublist for better usability,
* making it easier for players to quickly toggle loadout options (e.g. clothing, accessories)
* without having to search inside expanded subgroups.
/*
* Determine which element should be displayed first:
* - If any element is currently selected (its button is pressed), use it.
* - Otherwise, fallback to the first element in the list.
*
* This moves the selected item outside of the sublist for better usability,
* making it easier for players to quickly toggle loadout options (e.g. clothing, accessories)
* without having to search inside expanded subgroups.
*/
var firstElement = uiElements.FirstOrDefault(e => e.Select.Pressed) ?? uiElements[0];
@@ -195,8 +195,8 @@ public sealed partial class LoadoutGroupContainer : BoxContainer
/// <summary>
/// Creates a UI container for a single Loadout item.
///
/// This method was extracted from RefreshLoadouts because the logic for creating
/// individual loadout items is used multiple times inside that method, and duplicating
/// This method was extracted from RefreshLoadouts because the logic for creating
/// individual loadout items is used multiple times inside that method, and duplicating
/// the code made it harder to maintain.
///
/// Logic:

View File

@@ -68,7 +68,7 @@ public sealed partial class LoadoutWindow : FancyWindow
{
foreach (var group in proto.Groups)
{
if (!protoManager.Resolve(group, out var groupProto))
if (!protoManager.TryIndex(group, out var groupProto))
continue;
if (groupProto.Hidden)

View File

@@ -64,9 +64,7 @@ public sealed partial class CrewMonitoringNavMapControl : NavMapControl
if (!LocalizedNames.TryGetValue(netEntity, out var name))
name = "Unknown";
var message = name + "\n" + Loc.GetString("navmap-location",
("x", MathF.Round(blip.Coordinates.X)),
("y", MathF.Round(blip.Coordinates.Y)));
var message = name + "\nLocation: [x = " + MathF.Round(blip.Coordinates.X) + ", y = " + MathF.Round(blip.Coordinates.Y) + "]";
_trackedEntityLabel.Text = message;
_trackedEntityPanel.Visible = true;

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