Merge remote-tracking branch 'space-wizards/master'

# Conflicts:
#	.github/CODEOWNERS
#	.github/PULL_REQUEST_TEMPLATE.md
#	Content.Client/Changelog/ChangelogTab.xaml.cs
#	Content.Client/Chemistry/UI/ChemMasterWindow.xaml
#	Content.Client/Entry/EntryPoint.cs
#	Content.Client/GameTicking/Managers/ClientGameTicker.cs
#	Content.Client/Launcher/LauncherConnectingGui.xaml
#	Content.Client/Lobby/LobbyState.cs
#	Content.Client/Lobby/UI/LobbyGui.xaml
#	Content.Client/Paper/UI/PaperWindow.xaml.cs
#	Content.Client/StationRecords/GeneralStationRecordConsoleWindow.xaml.cs
#	Content.Client/Stylesheets/StyleNano.cs
#	Content.Client/UserInterface/Screens/SeparatedChatGameScreen.xaml
#	Content.Client/UserInterface/Systems/Ghost/Widgets/GhostGui.xaml.cs
#	Content.Client/UserInterface/Systems/MenuBar/Widgets/GameTopMenuBar.xaml
#	Content.IntegrationTests/Tests/Destructible/DestructibleDamageGroupTest.cs
#	Content.IntegrationTests/Tests/PostMapInitTest.cs
#	Content.IntegrationTests/Tests/Power/StationPowerTests.cs
#	Content.Packaging/ServerPackaging.cs
#	Content.Server/Administration/Systems/AdminVerbSystem.Smites.cs
#	Content.Server/Administration/Systems/AdminVerbSystem.Tools.cs
#	Content.Server/Atmos/TileAtmosphere.cs
#	Content.Server/Bible/BibleSystem.cs
#	Content.Server/Chat/Systems/ChatSystem.cs
#	Content.Server/Content.Server.csproj
#	Content.Server/Discord/DiscordLink/DiscordLink.cs
#	Content.Server/GameTicking/GameTicker.LobbyBackground.cs
#	Content.Server/GameTicking/GameTicker.Spawning.cs
#	Content.Server/Ghost/GhostSystem.cs
#	Content.Server/Kitchen/Components/MicrowaveComponent.cs
#	Content.Server/Kitchen/EntitySystems/MicrowaveSystem.cs
#	Content.Server/Mech/Systems/MechSystem.cs
#	Content.Server/Medical/CrewMonitoring/CrewMonitoringConsoleSystem.cs
#	Content.Server/Medical/DefibrillatorSystem.cs
#	Content.Server/Medical/HealthAnalyzerSystem.cs
#	Content.Server/Radio/EntitySystems/HeadsetSystem.cs
#	Content.Server/Remotes/DoorRemoteSystem.cs
#	Content.Server/RoundEnd/RoundEndSystem.cs
#	Content.Server/Shuttles/Systems/DockingSystem.Shuttle.cs
#	Content.Server/Silicons/Borgs/BorgSystem.Transponder.cs
#	Content.Server/Tips/TipsSystem.cs
#	Content.Server/Traitor/Uplink/UplinkSystem.cs
#	Content.Server/Weapons/Ranged/Systems/GunSystem.cs
#	Content.Server/Zombies/ZombieSystem.Transform.cs
#	Content.Server/Zombies/ZombieSystem.cs
#	Content.Shared/Bed/BedSystem.cs
#	Content.Shared/Blocking/BlockingSystem.User.cs
#	Content.Shared/Buckle/SharedBuckleSystem.Buckle.cs
#	Content.Shared/Chat/SharedSuicideSystem.cs
#	Content.Shared/Chemistry/Components/HyposprayComponent.cs
#	Content.Shared/Chemistry/EntitySystems/HypospraySystem.cs
#	Content.Shared/Chemistry/EntitySystems/ScoopableSolutionSystem.cs
#	Content.Shared/Chemistry/EntitySystems/SolutionTransferSystem.cs
#	Content.Shared/Chemistry/Events/HyposprayEvents.cs
#	Content.Shared/Content.Shared.csproj
#	Content.Shared/Damage/Systems/DamageableSystem.cs
#	Content.Shared/Friction/TileFrictionController.cs
#	Content.Shared/GameTicking/SharedGameTicker.cs
#	Content.Shared/Interaction/InteractHand.cs
#	Content.Shared/Lock/LockSystem.cs
#	Content.Shared/Medical/Healing/HealingSystem.cs
#	Content.Shared/Mobs/Systems/MobStateSystem.Subscribers.cs
#	Content.Shared/Nutrition/EntitySystems/HungerSystem.cs
#	Content.Shared/Silicons/StationAi/SharedStationAiSystem.Airlock.cs
#	Content.Shared/Throwing/ThrowingSystem.cs
#	Content.Shared/Trigger/Systems/TriggerOnHitscanSystem.cs
#	Content.Shared/Weapons/Hitscan/Systems/HitscanBasicDamageSystem.cs
#	Content.Shared/Weapons/Melee/SharedMeleeWeaponSystem.cs
#	Content.Shared/Weapons/Ranged/Components/BatteryWeaponFireModesComponent.cs
#	Content.Shared/Weapons/Ranged/Components/HitscanBatteryAmmoProviderComponent.cs
#	Content.Shared/Weapons/Ranged/Systems/BatteryWeaponFireModesSystem.cs
#	Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Ballistic.cs
#	Content.Shared/Weapons/Ranged/Systems/SharedGunSystem.Battery.cs
#	Content.Shared/Zombies/ZombieComponent.cs
#	Content.Shared/_Sunrise/Lobby/LobbyBackgroundPrototype.cs
#	README.md
#	Resources/Locale/en-US/_strings/borg/borg.ftl
#	Resources/Locale/en-US/_strings/chemistry/components/hypospray-component.ftl
#	Resources/Locale/en-US/_strings/chemistry/components/injector-component.ftl
#	Resources/Locale/en-US/_strings/commands/pauseatmos-command.ftl
#	Resources/Locale/en-US/_strings/commands/quick-inspect-command.ftl
#	Resources/Locale/en-US/_strings/commands/show-wallmounts-command.ftl
#	Resources/Locale/en-US/_strings/commands/substepatmos-command.ftl
#	Resources/Locale/en-US/_strings/fluids/components/equip-spray-component.ftl
#	Resources/Locale/en-US/_strings/game-ticking/game-presets/preset-revolutionary.ftl
#	Resources/Locale/en-US/_strings/job/job-names.ftl
#	Resources/Locale/en-US/_strings/lobby/lobby-state-background.ftl
#	Resources/Locale/en-US/_strings/reagents/meta/medicine.ftl
#	Resources/Locale/en-US/_strings/reagents/meta/physical-desc.ftl
#	Resources/Locale/en-US/_strings/silicons/borg-module.ftl
#	Resources/Locale/en-US/_strings/store/changeling-catalog.ftl
#	Resources/Locale/en-US/_strings/store/currency.ftl
#	Resources/Locale/en-US/_strings/store/store.ftl
#	Resources/Locale/en-US/_strings/store/uplink-catalog.ftl
#	Resources/Prototypes/Body/Organs/slime.yml
#	Resources/Prototypes/Catalog/Fills/Backpacks/backpack.yml
#	Resources/Prototypes/Catalog/Fills/Backpacks/duffelbag.yml
#	Resources/Prototypes/Catalog/Fills/Boxes/ammunition.yml
#	Resources/Prototypes/Catalog/Fills/Boxes/emergency.yml
#	Resources/Prototypes/Catalog/Fills/Crates/antag.yml
#	Resources/Prototypes/Catalog/Fills/Crates/armory.yml
#	Resources/Prototypes/Catalog/Fills/Crates/botany.yml
#	Resources/Prototypes/Catalog/Fills/Crates/fun.yml
#	Resources/Prototypes/Catalog/Fills/Crates/medical.yml
#	Resources/Prototypes/Catalog/Fills/Crates/salvage.yml
#	Resources/Prototypes/Catalog/Fills/Crates/security.yml
#	Resources/Prototypes/Catalog/Fills/Crates/service.yml
#	Resources/Prototypes/Catalog/Fills/Crates/syndicate.yml
#	Resources/Prototypes/Catalog/Fills/Items/belt.yml
#	Resources/Prototypes/Catalog/Fills/Items/briefcases.yml
#	Resources/Prototypes/Catalog/Fills/Items/firstaidkits.yml
#	Resources/Prototypes/Catalog/Fills/Items/toolboxes.yml
#	Resources/Prototypes/Catalog/Fills/Lockers/dressers.yml
#	Resources/Prototypes/Catalog/Fills/Lockers/heads.yml
#	Resources/Prototypes/Catalog/Fills/Lockers/security.yml
#	Resources/Prototypes/Catalog/VendingMachines/Inventories/theater.yml
#	Resources/Prototypes/Catalog/uplink_catalog.yml
#	Resources/Prototypes/Entities/Clothing/Back/backpacks.yml
#	Resources/Prototypes/Entities/Clothing/Shoes/magboots.yml
#	Resources/Prototypes/Entities/Debugging/debug_sweps.yml
#	Resources/Prototypes/Entities/Debugging/stress_test.yml
#	Resources/Prototypes/Entities/Markers/Spawners/Random/anomaly.yml
#	Resources/Prototypes/Entities/Markers/Spawners/Random/toy.yml
#	Resources/Prototypes/Entities/Mobs/Cyborgs/base_borg_chassis.yml
#	Resources/Prototypes/Entities/Mobs/Cyborgs/xenoborgs.yml
#	Resources/Prototypes/Entities/Mobs/NPCs/animals.yml
#	Resources/Prototypes/Entities/Mobs/NPCs/carp.yml
#	Resources/Prototypes/Entities/Objects/Devices/base_handheld.yml
#	Resources/Prototypes/Entities/Objects/Fun/Instruments/instruments_misc.yml
#	Resources/Prototypes/Entities/Objects/Fun/dice_bag.yml
#	Resources/Prototypes/Entities/Objects/Fun/snap_pops.yml
#	Resources/Prototypes/Entities/Objects/Materials/Sheets/other.yml
#	Resources/Prototypes/Entities/Objects/Materials/parts.yml
#	Resources/Prototypes/Entities/Objects/Misc/identification_cards.yml
#	Resources/Prototypes/Entities/Objects/Misc/subdermal_implants.yml
#	Resources/Prototypes/Entities/Objects/Shields/shields.yml
#	Resources/Prototypes/Entities/Objects/Specific/Chapel/bibles.yml
#	Resources/Prototypes/Entities/Objects/Specific/Janitorial/janitor.yml
#	Resources/Prototypes/Entities/Objects/Specific/Medical/hypospray.yml
#	Resources/Prototypes/Entities/Objects/Specific/Medical/surgery.yml
#	Resources/Prototypes/Entities/Objects/Specific/Robotics/borg_modules.yml
#	Resources/Prototypes/Entities/Objects/Specific/Robotics/mmi.yml
#	Resources/Prototypes/Entities/Objects/Specific/Service/barber.yml
#	Resources/Prototypes/Entities/Objects/Tools/glowstick.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/antimateriel.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Boxes/magnum.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/heavy_rifle.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/light_rifle.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/magnum.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/pistol.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/rifle.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Cartridges/shotgun.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/light_rifle.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/magnum.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/pistol.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Magazines/rifle.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/Projectiles/grenade.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/magnum.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/pistol.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/SpeedLoaders/rifle_light.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Ammunition/explosives.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Battery/battery_guns.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/LMGs/lmgs.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Pistols/pistols.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/hitscan.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Projectiles/projectiles.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/Rifles/rifles.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Guns/SMGs/smgs.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Melee/knife.yml
#	Resources/Prototypes/Entities/Objects/Weapons/Throwable/grenades.yml
#	Resources/Prototypes/Entities/Structures/Furniture/dresser.yml
#	Resources/Prototypes/Entities/Structures/Machines/lathe.yml
#	Resources/Prototypes/Entities/Structures/Shuttles/cannons.yml
#	Resources/Prototypes/Entities/Structures/Walls/walls.yml
#	Resources/Prototypes/GameRules/roundstart.yml
#	Resources/Prototypes/GameRules/unknown_shuttles.yml
#	Resources/Prototypes/InventoryTemplates/aghost_inventory_template.yml
#	Resources/Prototypes/Loadouts/Jobs/Security/security_officer.yml
#	Resources/Prototypes/Loadouts/loadout_groups.yml
#	Resources/Prototypes/Loadouts/role_loadouts.yml
#	Resources/Prototypes/Maps/Pools/default.yml
#	Resources/Prototypes/Maps/amber.yml
#	Resources/Prototypes/Maps/oasis.yml
#	Resources/Prototypes/Maps/reach.yml
#	Resources/Prototypes/Objectives/objectiveGroups.yml
#	Resources/Prototypes/Reagents/Consumable/Drink/alcohol.yml
#	Resources/Prototypes/Reagents/medicine.yml
#	Resources/Prototypes/Reagents/narcotics.yml
#	Resources/Prototypes/Reagents/toxins.yml
#	Resources/Prototypes/Recipes/Reactions/medicine.yml
#	Resources/Prototypes/Roles/Jobs/Security/head_of_security.yml
#	Resources/Prototypes/Roles/Jobs/Security/security_cadet.yml
#	Resources/Prototypes/Roles/Jobs/Security/security_officer.yml
#	Resources/Prototypes/Roles/Jobs/Security/warden.yml
#	Resources/Prototypes/Roles/Jobs/Wildcards/zookeeper.yml
#	Resources/Prototypes/Roles/play_time_trackers.yml
#	Resources/Prototypes/Shuttles/shuttle_incoming_event.yml
#	Resources/Prototypes/Species/species_weights.yml
#	Resources/Prototypes/Store/presets.yml
#	Resources/Prototypes/ai_factions.yml
#	Resources/Prototypes/borg_types.yml
#	Resources/Prototypes/explosion.yml
#	Resources/Prototypes/game_presets.yml
#	Resources/Prototypes/lobbyscreens.yml
#	Resources/Prototypes/silicon-laws.yml
#	Resources/Textures/Clothing/Back/Duffels/syndicate.rsi/icon-med.png
#	Resources/Textures/Clothing/Back/Duffels/syndicate.rsi/meta.json
#	Resources/Textures/Objects/Specific/Mech/mecha.rsi/meta.json
#	Resources/Textures/Objects/Specific/Mech/mecha.rsi/ripley-broken.png
#	Resources/Textures/Objects/Specific/Mech/mecha.rsi/ripley-empty.png
#	Resources/Textures/Objects/Specific/Mech/mecha.rsi/ripley-open.png
#	Resources/Textures/Objects/Specific/Mech/mecha.rsi/ripley.png
#	Resources/Textures/Objects/Specific/Mech/ripley_construction.rsi/ripley_chassis.png
#	Resources/Textures/Objects/Specific/Mech/ripley_construction.rsi/ripley_harness+o.png
#	Resources/Textures/Objects/Specific/Mech/ripley_construction.rsi/ripley_harness.png
#	Resources/Textures/Objects/Specific/Medical/hypospray.rsi/meta.json
#	Resources/Textures/Objects/Specific/Medical/medical.rsi/meta.json
#	Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles.rsi/meta.json
#	Resources/Textures/Objects/Weapons/Guns/Projectiles/projectiles2.rsi/meta.json
#	Resources/Textures/Structures/Storage/canister.rsi/meta.json
#	Resources/Textures/Structures/Storage/tanks.rsi/meta.json
#	Resources/migration.yml
#	RobustToolbox
#	SECURITY.md
#	SpaceStation14.sln
This commit is contained in:
Vigers Ray 2025-12-26 04:19:27 +01:00
commit 2da8b9f545
2189 changed files with 186816 additions and 225027 deletions

View file

@ -351,7 +351,7 @@ resharper_csharp_qualified_using_at_nested_scope = false
resharper_csharp_prefer_qualified_reference = false
resharper_csharp_allow_alias = false
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props,slnx}]
indent_size = 2
[nuget.config]

View file

@ -1,4 +1,5 @@
<!-- Пожалуйста прочитайте эту статью перед тем как выложить PR, чтобы избежать лишних правок в процессе осмотра: https://docs.spacestation14.io/en/getting-started/pr-guideline -->
<!-- Guidelines: https://docs.spacestation14.io/en/getting-started/pr-guideline -->
<!-- Текст в стрелочках является комментариями - они не будут видны в вашем PR. -->
## Кратное описание

View file

@ -21,7 +21,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
run: dotnet restore

View file

@ -36,7 +36,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
run: dotnet restore

View file

@ -36,7 +36,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
run: dotnet restore

View file

@ -39,7 +39,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Get Engine Tag
run: |

View file

@ -33,7 +33,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Get Engine Tag
run: |

View file

@ -51,7 +51,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
run: dotnet restore

View file

@ -26,7 +26,7 @@ jobs:
- name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0
with:
dotnet-version: 9.0.x
dotnet-version: 10.0.x
- name: Install dependencies
run: dotnet restore
- name: Build

3
.gitignore vendored
View file

@ -1,3 +1,6 @@
# MSbuild binlog files
*.binlog
## Ignore Visual Studio temporary files, build results, and
## files generated by popular Visual Studio add-ons.

View file

@ -11,7 +11,7 @@ import time
from pathlib import Path
from typing import List
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
SOLUTION_PATH = Path("..") / "SpaceStation14.slnx"
# If this doesn't match the saved version we overwrite them all.
CURRENT_HOOKS_VERSION = "4"
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"

View file

@ -8,7 +8,7 @@ This isnt an exhaustive list of things that you cant do. Rather, take it i
This code of conduct applies specifically to the Github repositories and its spaces managed by the Space Station 14 project or Space Wizards Federation. Some spaces, such as the Space Station 14 Discord or the official Wizard's Den game servers, have their own rules but are in spirit equal to what may be found in here.
If you believe someone is violating the code of conduct, we ask that you report it by contacting a Maintainer, Project Manager or Wizard staff member through [Discord](https://discord.ss14.io/), [the forums](https://forum.spacestation14.com/), or emailing [telecommunications@spacestation14.com](mailto:telecommunications@spacestation14.com).
If you believe someone is violating the code of conduct, we ask that you report it by contacting a Maintainer, Project Manager or Wizard staff member through [Discord](https://discord.ss14.io/), [the forums](https://forum.spacestation14.com/), or emailing [support@spacestation14.com](mailto:support@spacestation14.com).
- **Be friendly and patient.**
- **Be welcoming.** We strive to be a community that welcomes and supports people of all backgrounds and identities. This includes, but is not limited to members of any race, ethnicity, culture, national origin, colour, immigration status, social and economic class, educational level, sex, sexual orientation, gender identity and expression, age, size, family status, political belief, religion, and mental and physical ability.

11
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,11 @@
# Space Station 14 Contributing Guidelines
Thanks for contributing to Space Station 14.
When contributing, be sure to follow our [codebase conventions](https://docs.spacestation14.com/en/general-development/codebase-info/codebase-organization.html) and [PR guidelines](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html).
Following these guidelines helps us increase review turnaround time, so be sure to review the linked documents in full.
The last major guidelines update was on **December 6th, 2025**.
### Why is this here?
We put this here so that GitHub will notify you when submitting a pull request that the PR guidelines have changed, if you haven't read the latest version.

View file

@ -1,17 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
<PropertyGroup>
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
<TargetFramework>$(TargetFramework)</TargetFramework>
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputType>Exe</OutputType>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<LangVersion>12</LangVersion>
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
<Nullable>disable</Nullable>
</PropertyGroup>
<Import Project="../MSBuild/Content.props" />
<ItemGroup>
<PackageReference Include="BenchmarkDotNet" />
<!-- pin transitive deps -->
<PackageReference Include="System.Management" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
@ -19,10 +22,12 @@
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Benchmarks\Robust.Benchmarks.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
</ItemGroup>
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
<Import Project="..\RobustToolbox\Imports\Client.props" />
<Import Project="..\RobustToolbox\Imports\Server.props" />
<Import Project="..\RobustToolbox\Imports\Shared.props" />
<Import Project="..\RobustToolbox\Imports\Benchmarks.props" />
<Import Project="..\RobustToolbox\Imports\Testing.props" />
</Project>

View file

@ -0,0 +1,191 @@
using System.Collections.Generic;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Destructible;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.Damage.Systems;
using Content.Shared.FixedPoint;
using Content.Shared.Maps;
using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Map;
using Robust.Shared.Maths;
using Robust.Shared.Prototypes;
using Robust.Shared.Random;
namespace Content.Benchmarks;
[Virtual]
[GcServer(true)]
[MemoryDiagnoser]
public class DestructibleBenchmark
{
/// <summary>
/// Number of destructible entities per prototype to spawn with a <see cref="DestructibleComponent"/>.
/// </summary>
[Params(1, 10, 100, 1000, 5000)]
public int EntityCount;
/// <summary>
/// Amount of blunt damage we do to each entity.
/// </summary>
[Params(10000)]
public FixedPoint2 DamageAmount;
[Params("Blunt")]
public ProtoId<DamageTypePrototype> DamageType;
private static readonly EntProtoId WindowProtoId = "Window";
private static readonly EntProtoId WallProtoId = "WallReinforced";
private static readonly EntProtoId HumanProtoId = "MobHuman";
private static readonly ProtoId<ContentTileDefinition> TileRef = "Plating";
private readonly EntProtoId[] _prototypes = [WindowProtoId, WallProtoId, HumanProtoId];
private readonly List<Entity<DamageableComponent>> _damageables = new();
private readonly List<Entity<DamageableComponent, DestructibleComponent>> _destructbiles = new();
private TestMapData _currentMapData = default!;
private DamageSpecifier _damage;
private TestPair _pair = default!;
private IEntityManager _entMan = default!;
private IPrototypeManager _protoMan = default!;
private IRobustRandom _random = default!;
private ITileDefinitionManager _tileDefMan = default!;
private DamageableSystem _damageable = default!;
private DestructibleSystem _destructible = default!;
private SharedMapSystem _map = default!;
[GlobalSetup]
public async Task SetupAsync()
{
ProgramShared.PathOffset = "../../../../";
PoolManager.Startup();
_pair = await PoolManager.GetServerClient();
var server = _pair.Server;
_entMan = server.ResolveDependency<IEntityManager>();
_protoMan = server.ResolveDependency<IPrototypeManager>();
_random = server.ResolveDependency<IRobustRandom>();
_tileDefMan = server.ResolveDependency<ITileDefinitionManager>();
_damageable = _entMan.System<DamageableSystem>();
_destructible = _entMan.System<DestructibleSystem>();
_map = _entMan.System<SharedMapSystem>();
if (!_protoMan.Resolve(DamageType, out var type))
return;
_damage = new DamageSpecifier(type, DamageAmount);
_random.SetSeed(69420); // Randomness needs to be deterministic for benchmarking.
}
[IterationSetup]
public void IterationSetup()
{
var plating = _tileDefMan[TileRef].TileId;
var server = _pair.Server;
_currentMapData = _pair.CreateTestMap().GetAwaiter().GetResult();
// We make a rectangular grid of destructible entities, and then damage them all simultaneously to stress test the system.
// Needed for managing the performance of destructive effects and damage application.
server.WaitPost(() =>
{
// Set up a thin line of tiles to place our objects on. They should be anchored for a "realistic" scenario...
for (var x = 0; x < EntityCount; x++)
{
for (var y = 0; y < _prototypes.Length; y++)
{
_map.SetTile(_currentMapData.Grid, _currentMapData.Grid, new Vector2i(x, y), new Tile(plating));
}
}
for (var x = 0; x < EntityCount; x++)
{
var y = 0;
foreach (var protoId in _prototypes)
{
var coords = new EntityCoordinates(_currentMapData.Grid, x + 0.5f, y + 0.5f);
_entMan.SpawnEntity(protoId, coords);
y++;
}
}
var query = _entMan.EntityQueryEnumerator<DamageableComponent, DestructibleComponent>();
_destructbiles.EnsureCapacity(EntityCount);
_damageables.EnsureCapacity(EntityCount);
while (query.MoveNext(out var uid, out var damageable, out var destructible))
{
_damageables.Add((uid, damageable));
_destructbiles.Add((uid, damageable, destructible));
}
})
.GetAwaiter()
.GetResult();
}
[Benchmark]
public async Task PerformDealDamage()
{
await _pair.Server.WaitPost(() =>
{
_damageable.ApplyDamageToAllEntities(_damageables, _damage);
});
}
[Benchmark]
public async Task PerformTestTriggers()
{
await _pair.Server.WaitPost(() =>
{
_destructible.TestAllTriggers(_destructbiles);
});
}
[Benchmark]
public async Task PerformTestBehaviors()
{
await _pair.Server.WaitPost(() =>
{
_destructible.TestAllBehaviors(_destructbiles);
});
}
[IterationCleanup]
public void IterationCleanupAsync()
{
// We need to nuke the entire map and respawn everything as some destructible effects
// spawn entities and whatnot.
_pair.Server.WaitPost(() =>
{
_map.QueueDeleteMap(_currentMapData.MapId);
})
.Wait();
// Deletion of entities is often queued (QueueDel) which must be processed by running ticks
// or else it will grow infinitely and leak memory.
_pair.Server.WaitRunTicks(2)
.GetAwaiter()
.GetResult();
_destructbiles.Clear();
_damageables.Clear();
}
[GlobalCleanup]
public async Task CleanupAsync()
{
await _pair.DisposeAsync();
PoolManager.Shutdown();
}
}

View file

@ -0,0 +1,253 @@
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Atmos;
using Content.Server.Atmos.EntitySystems;
using Content.Server.Atmos.Reactions;
using Content.Shared.Atmos;
using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.GameObjects;
using Robust.Shared.Maths;
namespace Content.Benchmarks;
/// <summary>
/// Benchmarks the performance of different gas reactions.
/// Tests each reaction type with realistic gas mixtures to measure computational cost.
/// </summary>
[Virtual]
[GcServer(true)]
[MemoryDiagnoser]
public class GasReactionBenchmark
{
private const int Iterations = 1000;
private TestPair _pair = default!;
private AtmosphereSystem _atmosphereSystem = default!;
// Grid and tile for reactions that need a holder
private EntityUid _testGrid = default!;
private TileAtmosphere _testTile = default!;
// Reaction instances
private PlasmaFireReaction _plasmaFireReaction = default!;
private TritiumFireReaction _tritiumFireReaction = default!;
private FrezonProductionReaction _frezonProductionReaction = default!;
private FrezonCoolantReaction _frezonCoolantReaction = default!;
private AmmoniaOxygenReaction _ammoniaOxygenReaction = default!;
private N2ODecompositionReaction _n2oDecompositionReaction = default!;
private WaterVaporReaction _waterVaporReaction = default!;
// Gas mixtures for each reaction type
private GasMixture _plasmaFireMixture = default!;
private GasMixture _tritiumFireMixture = default!;
private GasMixture _frezonProductionMixture = default!;
private GasMixture _frezonCoolantMixture = default!;
private GasMixture _ammoniaOxygenMixture = default!;
private GasMixture _n2oDecompositionMixture = default!;
private GasMixture _waterVaporMixture = default!;
[GlobalSetup]
public async Task SetupAsync()
{
ProgramShared.PathOffset = "../../../../";
PoolManager.Startup();
_pair = await PoolManager.GetServerClient();
var server = _pair.Server;
// Create test map and grid
var mapData = await _pair.CreateTestMap();
_testGrid = mapData.Grid;
await server.WaitPost(() =>
{
var entMan = server.ResolveDependency<IEntityManager>();
_atmosphereSystem = entMan.System<AtmosphereSystem>();
_plasmaFireReaction = new PlasmaFireReaction();
_tritiumFireReaction = new TritiumFireReaction();
_frezonProductionReaction = new FrezonProductionReaction();
_frezonCoolantReaction = new FrezonCoolantReaction();
_ammoniaOxygenReaction = new AmmoniaOxygenReaction();
_n2oDecompositionReaction = new N2ODecompositionReaction();
_waterVaporReaction = new WaterVaporReaction();
SetupGasMixtures();
SetupTile();
});
}
private void SetupGasMixtures()
{
// Plasma Fire: Plasma + Oxygen at high temperature
// Temperature must be > PlasmaMinimumBurnTemperature for reaction to occur
_plasmaFireMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.PlasmaMinimumBurnTemperature + 100f // ~673K
};
_plasmaFireMixture.AdjustMoles(Gas.Plasma, 20f);
_plasmaFireMixture.AdjustMoles(Gas.Oxygen, 100f);
// Tritium Fire: Tritium + Oxygen at high temperature
// Temperature must be > FireMinimumTemperatureToExist for reaction to occur
_tritiumFireMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.FireMinimumTemperatureToExist + 100f // ~473K
};
_tritiumFireMixture.AdjustMoles(Gas.Tritium, 20f);
_tritiumFireMixture.AdjustMoles(Gas.Oxygen, 100f);
// Frezon Production: Oxygen + Tritium + Nitrogen catalyst
// Optimal temperature for efficiency (80% of max efficiency temp)
_frezonProductionMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.FrezonProductionMaxEfficiencyTemperature * 0.8f // ~48K
};
_frezonProductionMixture.AdjustMoles(Gas.Oxygen, 50f);
_frezonProductionMixture.AdjustMoles(Gas.Tritium, 50f);
_frezonProductionMixture.AdjustMoles(Gas.Nitrogen, 10f);
// Frezon Coolant: Frezon + Nitrogen
// Temperature must be > FrezonCoolLowerTemperature (23.15K) for reaction to occur
_frezonCoolantMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.T20C + 50f // ~343K
};
_frezonCoolantMixture.AdjustMoles(Gas.Frezon, 30f);
_frezonCoolantMixture.AdjustMoles(Gas.Nitrogen, 100f);
// Ammonia + Oxygen reaction (concentration-dependent, no temp requirement)
_ammoniaOxygenMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.T20C + 100f // ~393K
};
_ammoniaOxygenMixture.AdjustMoles(Gas.Ammonia, 40f);
_ammoniaOxygenMixture.AdjustMoles(Gas.Oxygen, 40f);
// N2O Decomposition (no temperature requirement, just needs N2O moles)
_n2oDecompositionMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.T20C + 100f // ~393K
};
_n2oDecompositionMixture.AdjustMoles(Gas.NitrousOxide, 100f);
// Water Vapor - needs water vapor to condense
_waterVaporMixture = new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.T20C
};
_waterVaporMixture.AdjustMoles(Gas.WaterVapor, 50f);
}
private void SetupTile()
{
// Create a tile atmosphere to use as holder for all reactions
var testIndices = new Vector2i(0, 0);
_testTile = new TileAtmosphere(_testGrid, testIndices, new GasMixture(Atmospherics.CellVolume)
{
Temperature = Atmospherics.T20C
});
}
private static GasMixture CloneMixture(GasMixture original)
{
return new GasMixture(original);
}
[Benchmark]
public async Task PlasmaFireReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_plasmaFireMixture);
_plasmaFireReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task TritiumFireReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_tritiumFireMixture);
_tritiumFireReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task FrezonProductionReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_frezonProductionMixture);
_frezonProductionReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task FrezonCoolantReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_frezonCoolantMixture);
_frezonCoolantReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task AmmoniaOxygenReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_ammoniaOxygenMixture);
_ammoniaOxygenReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task N2ODecompositionReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_n2oDecompositionMixture);
_n2oDecompositionReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[Benchmark]
public async Task WaterVaporReaction()
{
await _pair.Server.WaitPost(() =>
{
for (var i = 0; i < Iterations; i++)
{
var mixture = CloneMixture(_waterVaporMixture);
_waterVaporReaction.React(mixture, _testTile, _atmosphereSystem, 1f);
}
});
}
[GlobalCleanup]
public async Task CleanupAsync()
{
await _pair.DisposeAsync();
PoolManager.Shutdown();
}
}

View file

@ -5,7 +5,7 @@ using System.Threading.Tasks;
using BenchmarkDotNet.Attributes;
using Content.IntegrationTests;
using Content.IntegrationTests.Pair;
using Content.Server.Maps;
using Content.Shared.Maps;
using Robust.Shared;
using Robust.Shared.Analyzers;
using Robust.Shared.EntitySerialization.Systems;

View file

@ -23,6 +23,8 @@ public class RaiseEventBenchmark
PoolManager.Startup(typeof(BenchSystem).Assembly);
_pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
var entMan = _pair.Server.EntMan;
var fact = _pair.Server.ResolveDependency<IComponentFactory>();
var bus = (EntityEventBus)entMan.EventBus;
_sys = entMan.System<BenchSystem>();
_pair.Server.WaitPost(() =>
@ -30,6 +32,8 @@ public class RaiseEventBenchmark
var uid = entMan.Spawn();
_sys.Ent = new(uid, entMan.GetComponent<TransformComponent>(uid));
_sys.Ent2 = new(_sys.Ent.Owner, _sys.Ent.Comp);
_sys.NetId = fact.GetRegistration<TransformComponent>().NetID!.Value;
_sys.EvSubs = bus.GetNetCompEventHandlers<BenchSystem.BenchEv>();
})
.GetAwaiter()
.GetResult();
@ -60,6 +64,12 @@ public class RaiseEventBenchmark
return _sys.RaiseICompEvent();
}
[Benchmark]
public int RaiseNetEvent()
{
return _sys.RaiseNetIdEvent();
}
[Benchmark]
public int RaiseCSharpEvent()
{
@ -74,6 +84,8 @@ public class RaiseEventBenchmark
public delegate void EntityEventHandler(EntityUid uid, TransformComponent comp, ref BenchEv ev);
public event EntityEventHandler? OnCSharpEvent;
public ushort NetId;
internal EntityEventBus.DirectedEventHandler?[] EvSubs = default!;
public override void Initialize()
{
@ -92,7 +104,7 @@ public class RaiseEventBenchmark
public int RaiseCompEvent()
{
var ev = new BenchEv();
EntityManager.EventBus.RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
RaiseComponentEvent(Ent.Owner, Ent.Comp, ref ev);
return ev.N;
}
@ -100,7 +112,16 @@ public class RaiseEventBenchmark
{
// Raise with an IComponent instead of concrete type
var ev = new BenchEv();
EntityManager.EventBus.RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
RaiseComponentEvent(Ent2.Owner, Ent2.Comp, ref ev);
return ev.N;
}
public int RaiseNetIdEvent()
{
// Raise a "IComponent" event using a net-id index delegate array (for PVS & client game-state events)
var ev = new BenchEv();
ref var unitEv = ref Unsafe.As<BenchEv, EntityEventBus.Unit>(ref ev);
EvSubs[NetId]?.Invoke(Ent2.Owner, Ent2.Comp, ref unitEv);
return ev.N;
}
@ -118,6 +139,7 @@ public class RaiseEventBenchmark
}
[ByRefEvent]
[ComponentEvent(Exclusive = false)]
public struct BenchEv
{
public int N;

View file

@ -1,4 +1,4 @@
<GridContainer xmlns="https://spacestation14.io"
Columns="5"
Columns="4"
HorizontalAlignment="Center">
</GridContainer>

View file

@ -2,7 +2,7 @@
MinSize="650 290">
<BoxContainer Orientation="Vertical">
<GridContainer Columns="2">
<GridContainer Columns="3" HorizontalExpand="True">
<GridContainer Name="PrivilegedIdGrid" Columns="3" HorizontalExpand="True">
<Label Text="{Loc 'access-overrider-window-privileged-id'}" />
<Button Name="PrivilegedIdButton" Access="Public"/>
<Label Name="PrivilegedIdLabel" />

View file

@ -53,6 +53,8 @@ namespace Content.Client.Access.UI
public void UpdateState(IPrototypeManager protoManager, AccessOverriderBoundUserInterfaceState state)
{
PrivilegedIdGrid.Visible = state.ShowPrivilegedIdGrid;
PrivilegedIdLabel.Text = state.PrivilegedIdName;
PrivilegedIdButton.Text = state.IsPrivilegedIdPresent
? Loc.GetString("access-overrider-window-eject-button")
@ -77,7 +79,9 @@ namespace Content.Client.Access.UI
missingPrivileges.Add(privilege);
}
MissingPrivilegesLabel.Text = Loc.GetString("access-overrider-window-missing-privileges");
MissingPrivilegesLabel.Text = state.ShowPrivilegedIdGrid ?
Loc.GetString("access-overrider-window-missing-privileges") :
Loc.GetString("access-overrider-window-missing-privileges-no-id");
MissingPrivilegesText.Text = string.Join(", ", missingPrivileges);
}

View file

@ -49,12 +49,12 @@ namespace Content.Client.Access.UI
icons.Sort((x, y) => string.Compare(x.LocalizedJobName, y.LocalizedJobName, StringComparison.CurrentCulture));
foreach (var jobIcon in icons)
{
String styleBase = StyleBase.ButtonOpenBoth;
String styleBase = StyleClass.ButtonOpenBoth;
var modulo = i % JobIconColumnCount;
if (modulo == 0)
styleBase = StyleBase.ButtonOpenRight;
styleBase = StyleClass.ButtonOpenRight;
else if (modulo == JobIconColumnCount - 1)
styleBase = StyleBase.ButtonOpenLeft;
styleBase = StyleClass.ButtonOpenLeft;
// Generate buttons
var jobIconButton = new Button

View file

@ -119,11 +119,11 @@ public sealed partial class GroupedAccessLevelChecklist : BoxContainer
if (_groupedAccessLevels.Count > 1)
{
if (AccessGroupList.ChildCount == 0)
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenLeft);
accessGroupButton.AddStyleClass(StyleClass.ButtonOpenLeft);
else if (_groupedAccessLevels.Count > 1 && AccessGroupList.ChildCount == (_groupedAccessLevels.Count - 1))
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenRight);
accessGroupButton.AddStyleClass(StyleClass.ButtonOpenRight);
else
accessGroupButton.AddStyleClass(StyleBase.ButtonOpenBoth);
accessGroupButton.AddStyleClass(StyleClass.ButtonOpenBoth);
}
accessGroupButton.Pressed = _accessGroupTabIndex == orderedAccessGroups.IndexOf(accessGroup);

View file

@ -23,9 +23,10 @@ namespace Content.Client.Actions.UI
public ActionAlertTooltip(FormattedMessage name, FormattedMessage? desc, string? requires = null)
{
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSystem;
_gameTiming = IoCManager.Resolve<IGameTiming>();
SetOnlyStyleClass(StyleNano.StyleClassTooltipPanel);
SetOnlyStyleClass(StyleClass.TooltipPanel);
BoxContainer vbox;
AddChild(vbox = new BoxContainer
@ -36,7 +37,7 @@ namespace Content.Client.Actions.UI
var nameLabel = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionTitle}
StyleClasses = { StyleClass.TooltipTitle }
};
nameLabel.SetMessage(name);
vbox.AddChild(nameLabel);
@ -46,7 +47,7 @@ namespace Content.Client.Actions.UI
var description = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionDescription}
StyleClasses = { StyleClass.TooltipDesc }
};
description.SetMessage(desc);
vbox.AddChild(description);
@ -55,7 +56,7 @@ namespace Content.Client.Actions.UI
vbox.AddChild(_cooldownLabel = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionCooldown},
StyleClasses = { StyleClass.TooltipDesc },
Visible = false
});
@ -64,7 +65,7 @@ namespace Content.Client.Actions.UI
var requiresLabel = new RichTextLabel
{
MaxWidth = TooltipTextMaxWidth,
StyleClasses = {StyleNano.StyleClassTooltipActionRequirements}
StyleClasses = { StyleClass.TooltipDesc }
};
if (!FormattedMessage.TryFromMarkup("[color=#635c5c]" + requires + "[/color]", out var markup))

View file

@ -1,7 +0,0 @@
using Content.Shared.Administration.Components;
using Robust.Shared.GameStates;
namespace Content.Client.Administration.Components;
[RegisterComponent]
public sealed partial class KillSignComponent : SharedKillSignComponent;

View file

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

View file

@ -1,48 +1,79 @@
using System.Numerics;
using Content.Client.Administration.Components;
using Content.Shared.Administration.Components;
using Robust.Client.GameObjects;
using Robust.Shared.Utility;
using Robust.Client.Player;
namespace Content.Client.Administration.Systems;
public sealed class KillSignSystem : EntitySystem
{
[Dependency] private readonly SpriteSystem _sprite = default!;
[Dependency] private readonly IPlayerManager _player = default!;
public override void Initialize()
{
SubscribeLocalEvent<KillSignComponent, ComponentStartup>(KillSignAdded);
SubscribeLocalEvent<KillSignComponent, ComponentShutdown>(KillSignRemoved);
SubscribeLocalEvent<KillSignComponent, AfterAutoHandleStateEvent>(AfterAutoHandleState);
}
private void KillSignRemoved(EntityUid uid, KillSignComponent component, ComponentShutdown args)
private void KillSignRemoved(Entity<KillSignComponent> ent, ref ComponentShutdown args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
return;
if (!_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var layer, false))
return;
_sprite.RemoveLayer((uid, sprite), layer);
RemoveKillsign(ent);
}
private void KillSignAdded(EntityUid uid, KillSignComponent component, ComponentStartup args)
private void KillSignAdded(Entity<KillSignComponent> ent, ref ComponentStartup args)
{
if (!TryComp<SpriteComponent>(uid, out var sprite))
AddKillsign(ent);
}
private void AfterAutoHandleState(Entity<KillSignComponent> ent, ref AfterAutoHandleStateEvent args)
{
// After receiving a new state for the component, we remove the old killsign and build a new one.
// This is so changes to the sprite can be displayed live and allowing them to be edited via ViewVariables.
// This could just update an existing sprite, but this is both easier and runs rarely anyway.
RemoveKillsign(ent);
AddKillsign(ent);
}
private void AddKillsign(Entity<KillSignComponent> ent)
{
// If we hide from owner and we ARE the owner, don't add a killsign.
// This could use session specific networking to FULLY hide it, but I am too lazy right now.
if (ent.Comp.HideFromOwner && _player.LocalEntity == ent)
return;
if (_sprite.LayerMapTryGet((uid, sprite), KillSignKey.Key, out var _, false))
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
var adj = _sprite.GetLocalBounds((uid, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
if (_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var _, false))
return;
var layer = _sprite.AddLayer((uid, sprite), new SpriteSpecifier.Rsi(new ResPath("Objects/Misc/killsign.rsi"), "sign"));
_sprite.LayerMapSet((uid, sprite), KillSignKey.Key, layer);
if (ent.Comp.Sprite == null)
return;
_sprite.LayerSetOffset((uid, sprite), layer, new Vector2(0.0f, adj));
var adj = _sprite.GetLocalBounds((ent, sprite)).Height / 2 + ((1.0f / 32) * 6.0f);
var layer = _sprite.AddLayer((ent, sprite), ent.Comp.Sprite);
_sprite.LayerMapSet((ent, sprite), KillSignKey.Key, layer);
_sprite.LayerSetScale((ent, sprite), layer, ent.Comp.Scale);
_sprite.LayerSetOffset((ent, sprite), layer, ent.Comp.DoOffset ? new Vector2(0.0f, adj) : new Vector2(0.0f, 0.0f));
if (ent.Comp.ForceUnshaded)
sprite.LayerSetShader(layer, "unshaded");
}
private void RemoveKillsign(Entity<KillSignComponent> ent)
{
if (!TryComp<SpriteComponent>(ent, out var sprite))
return;
if (!_sprite.LayerMapTryGet((ent, sprite), KillSignKey.Key, out var layer, false))
return;
_sprite.RemoveLayer((ent, sprite), layer);
}
private enum KillSignKey
{
Key,

View file

@ -7,7 +7,7 @@
</PanelContainer.PanelOverride>
<Control HorizontalAlignment="Center" VerticalAlignment="Center" MaxWidth="600">
<PanelContainer StyleClasses="AngleRect" />
<PanelContainer StyleClasses="BackgroundPanel" />
<BoxContainer Orientation="Vertical" Margin="4">
<RichTextLabel Name="Description" />

View file

@ -12,17 +12,19 @@ namespace Content.Client.Administration.UI.AdminRemarks;
[GenerateTypedNameReferences]
public sealed partial class AdminMessagePopupWindow : Control
{
[Dependency] private readonly IStylesheetManager _styleMan = default!;
private float _timer = float.MaxValue;
public event Action? OnDismissPressed;
public event Action? OnAcceptPressed;
public AdminMessagePopupWindow()
{
RobustXamlLoader.Load(this);
IoCManager.InjectDependencies(this);
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
Stylesheet = _styleMan.SheetSystem;
AcceptButton.OnPressed += OnAcceptButtonPressed;
DismissButton.OnPressed += OnDismissButtonPressed;
@ -49,7 +51,8 @@ public sealed partial class AdminMessagePopupWindow : Control
MessageContainer.AddChild(new AdminMessagePopupMessage(message));
}
Description.SetMessage(FormattedMessage.FromMarkupOrThrow(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
Description.SetMessage(
FormattedMessage.FromMarkup(Loc.GetString("admin-notes-message-desc", ("count", state.Messages.Length))));
}
private void OnDismissButtonPressed(BaseButton.ButtonEventArgs obj)

View file

@ -299,19 +299,19 @@ public sealed partial class BanPanel : DefaultWindow
}
/// <summary>
/// Adds a check button specifically for one "role" in a "group"
/// Adds a toggle button specifically for one "role" in a "group"
/// E.g. it would add the Chief Medical Officer "role" into the "Medical" group.
/// </summary>
private void AddRoleCheckbox(string group, string role, GridContainer roleGroupInnerContainer, Button roleGroupCheckbox)
{
var roleCheckboxContainer = new BoxContainer();
var roleCheckButton = new Button
var roleToggleButton = new Button
{
Name = role,
Text = role,
ToggleMode = true,
};
roleCheckButton.OnToggled += args =>
roleToggleButton.OnToggled += args =>
{
// Checks the role group checkbox if all the children are pressed
if (args.Pressed && _roleCheckboxes[group].All(e => e.Item1.Pressed))
@ -348,12 +348,12 @@ public sealed partial class BanPanel : DefaultWindow
roleCheckboxContainer.AddChild(jobIconTexture);
}
roleCheckboxContainer.AddChild(roleCheckButton);
roleCheckboxContainer.AddChild(roleToggleButton);
roleGroupInnerContainer.AddChild(roleCheckboxContainer);
_roleCheckboxes.TryAdd(group, []);
_roleCheckboxes[group].Add((roleCheckButton, rolePrototype));
_roleCheckboxes[group].Add((roleToggleButton, rolePrototype));
}
public void UpdateBanFlag(bool newFlag)

View file

@ -15,7 +15,7 @@ public sealed class AdminLogLabel : RichTextLabel
OnVisibilityChanged += VisibilityChanged;
}
public SharedAdminLog Log { get; }
public new SharedAdminLog Log { get; }
public HSeparator Separator { get; }

View file

@ -11,6 +11,9 @@ namespace Content.Client.Administration.UI.CustomControls;
[GenerateTypedNameReferences]
public sealed partial class PlayerListEntry : BoxContainer
{
private readonly ResPath _pinnedResPath = new("/Textures/Interface/Bwoink/pinned.png");
private readonly ResPath _unPinnedResPath = new("/Textures/Interface/Bwoink/un_pinned.png");
public PlayerListEntry()
{
RobustXamlLoader.Load(this);
@ -44,15 +47,6 @@ public sealed partial class PlayerListEntry : BoxContainer
private void UpdatePinButtonTexture(bool isPinned)
{
if (isPinned)
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonUnpinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonPinned);
}
else
{
PlayerEntryPinButton?.RemoveStyleClass(StyleNano.StyleClassPinButtonPinned);
PlayerEntryPinButton?.AddStyleClass(StyleNano.StyleClassPinButtonUnpinned);
}
PlayerEntryPinButton.TexturePath = isPinned ? _pinnedResPath.CanonPath : _unPinnedResPath.CanonPath;
}
}

View file

@ -27,7 +27,7 @@ namespace Content.Client.Administration.UI
[Dependency] private readonly IClientAdminManager _adminManager = default!;
private readonly Menu _menu;
private readonly List<DefaultWindow> _subWindows = new();
private readonly List<BaseWindow> _subWindows = new();
private Dictionary<int, PermissionsEuiState.AdminRankData> _ranks =
new();
@ -216,7 +216,7 @@ namespace Content.Client.Administration.UI
var titleControl = new Label { Text = admin.Title ?? Loc.GetString("permissions-eui-edit-admin-title-control-text").ToLowerInvariant() };
if (admin.Title == null) // none
{
titleControl.StyleClasses.Add(StyleBase.StyleClassItalic);
titleControl.StyleClasses.Add(StyleClass.Italic);
}
al.AddChild(titleControl);
@ -240,7 +240,7 @@ namespace Content.Client.Administration.UI
var rankControl = new Label { Text = rank };
if (italic)
{
rankControl.StyleClasses.Add(StyleBase.StyleClassItalic);
rankControl.StyleClasses.Add(StyleClass.Italic);
}
al.AddChild(rankControl);
@ -340,10 +340,9 @@ namespace Content.Client.Administration.UI
tab.AddChild(adminVBox);
tab.AddChild(rankVBox);
Contents.AddChild(tab);
ContentsContainer.AddChild(tab);
ContentsContainer.MinSize = new(600, 400);
}
protected override Vector2 ContentsMinimumSize => new Vector2(600, 400);
}
private sealed class EditAdminWindow : DefaultWindow
@ -419,21 +418,21 @@ namespace Content.Client.Administration.UI
var inherit = new Button
{
Text = "I",
StyleClasses = { StyleBase.ButtonOpenRight },
StyleClasses = { StyleClass.ButtonOpenRight },
Disabled = disable,
Group = group,
};
var sub = new Button
{
Text = "-",
StyleClasses = { StyleBase.ButtonOpenBoth },
StyleClasses = { StyleClass.ButtonOpenBoth },
Disabled = disable,
Group = group
};
var plus = new Button
{
Text = "+",
StyleClasses = { StyleBase.ButtonOpenLeft },
StyleClasses = { StyleClass.ButtonOpenLeft },
Disabled = disable,
Group = group
};
@ -479,7 +478,7 @@ namespace Content.Client.Administration.UI
bottomButtons.AddChild(SaveButton);
Contents.AddChild(new BoxContainer
ContentsContainer.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =
@ -605,7 +604,7 @@ namespace Content.Client.Administration.UI
bottomButtons.AddChild(SaveButton);
Contents.AddChild(new BoxContainer
ContentsContainer.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =

View file

@ -14,7 +14,7 @@
VerticalExpand="True"
Margin="0 0 0 0"
VerticalAlignment="Center">
<Label Text="{Loc 'anomaly-generator-fuel-display'}" StyleClasses="StatusFieldTitle" />
<Label Text="{Loc 'anomaly-generator-fuel-display'}" StyleClasses="highlight" />
<ProgressBar Name="FuelBar"
HorizontalExpand="True"
MaxValue="1"
@ -27,8 +27,8 @@
Text="0 %" />
</ProgressBar>
</BoxContainer>
<RichTextLabel Name="CooldownLabel" StyleClasses="StatusFieldTitle" />
<RichTextLabel Name="ReadyLabel" StyleClasses="StatusFieldTitle" />
<RichTextLabel Name="CooldownLabel" StyleClasses="highlight" />
<RichTextLabel Name="ReadyLabel" StyleClasses="highlight" />
</BoxContainer>
<!--Sprite View-->
<PanelContainer Margin="12 0 0 0"

View file

@ -338,7 +338,7 @@ namespace Content.Client.Arcade
menuInnerPanel.AddChild(menuContainer);
#endregion
Contents.AddChild(_mainPanel);
ContentsContainer.AddChild(_mainPanel);
CanKeyboardFocus = true;
}

View file

@ -86,7 +86,7 @@ namespace Content.Client.Arcade
newGame.OnPressed += _ => OnPlayerAction?.Invoke(SharedSpaceVillainArcadeComponent.PlayerAction.NewGame);
grid.AddChild(newGame);
Contents.AddChild(grid);
ContentsContainer.AddChild(grid);
}
private void UpdateMetadata(SharedSpaceVillainArcadeComponent.SpaceVillainArcadeMetaDataUpdateMessage message)

View file

@ -0,0 +1,5 @@
using Content.Shared.Atmos.EntitySystems;
namespace Content.Client.Atmos.EntitySystems;
public sealed class DeltaPressureSystem : SharedDeltaPressureSystem;

View file

@ -1,6 +1,6 @@
using Content.Client.Atmos.Monitor.UI.Widgets;
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Client.Stylesheets.Palette;
using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Monitor;
@ -186,11 +186,9 @@ public sealed partial class AirAlarmWindow : FancyWindow
{
return curAlarm switch
{
AtmosAlarmType.Danger => StyleNano.DangerousRedFore,
AtmosAlarmType.Warning => StyleNano.ConcerningOrangeFore,
_ => StyleNano.GoodGreenFore,
AtmosAlarmType.Danger => Palettes.Status.Critical,
AtmosAlarmType.Warning => Palettes.Status.Warning,
_ => Palettes.Status.Good,
};
}
}

View file

@ -1,3 +1,4 @@
using Content.Client.Stylesheets;
using Content.Shared.Atmos.Monitor;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
@ -42,11 +43,11 @@ public sealed partial class ThresholdBoundControl : BoxContainer
if (enabled)
{
CBoundLabel.RemoveStyleClass("Disabled");
CBoundLabel.RemoveStyleClass(StyleClass.LabelWeak);
}
else
{
CBoundLabel.SetOnlyStyleClass("Disabled");
CBoundLabel.SetOnlyStyleClass(StyleClass.LabelWeak);
}
}

View file

@ -3,6 +3,7 @@ using Content.Client.Atmos.Components;
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Prototypes;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
@ -23,6 +24,7 @@ namespace Content.Client.Atmos.Overlays
private readonly IEntityManager _entManager;
private readonly IMapManager _mapManager;
private readonly SharedAtmosphereSystem _atmosphereSystem;
private readonly SharedMapSystem _mapSystem;
private readonly SharedTransformSystem _xformSys;
@ -54,6 +56,7 @@ namespace Content.Client.Atmos.Overlays
{
_entManager = entManager;
_mapManager = IoCManager.Resolve<IMapManager>();
_atmosphereSystem = entManager.System<SharedAtmosphereSystem>();
_mapSystem = entManager.System<SharedMapSystem>();
_xformSys = xformSys;
_shader = protoMan.Index(UnshadedShader).Instance();
@ -67,7 +70,7 @@ namespace Content.Client.Atmos.Overlays
for (var i = 0; i < _gasCount; i++)
{
var gasPrototype = protoMan.Index<GasPrototype>(system.VisibleGasId[i].ToString());
var gasPrototype = _atmosphereSystem.GetGas(system.VisibleGasId[i]);
SpriteSpecifier overlay;

View file

@ -4,7 +4,7 @@
MinSize="480 400" Title="Canister">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-canister-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<Label Text="{Loc comp-gas-canister-ui-canister-status}" StyleClasses="LabelHeading"/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc comp-gas-canister-ui-canister-pressure}"/>
<Label Name="CanisterPressureLabel" Align="Center" HorizontalExpand="True"/>
@ -16,7 +16,7 @@
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-holding-tank-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<Label Text="{Loc comp-gas-canister-ui-holding-tank-status}" StyleClasses="LabelHeading"/>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc comp-gas-canister-ui-holding-tank-label}"/>
<Label Name="TankLabelLabel" Text="{Loc comp-gas-canister-ui-holding-tank-label-empty}" Align="Center" HorizontalExpand="True"/>
@ -29,7 +29,7 @@
</BoxContainer>
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<Label Text="{Loc comp-gas-canister-ui-release-valve-status}" FontColorOverride="{x:Static s:StyleNano.NanoGold}" StyleClasses="LabelBig"/>
<Label Text="{Loc comp-gas-canister-ui-release-valve-status}" StyleClasses="LabelHeading"/>
<BoxContainer Orientation="Horizontal">
<BoxContainer Orientation="Vertical">
<Label Text="{Loc comp-gas-canister-ui-release-pressure}"/>

View file

@ -52,14 +52,18 @@ namespace Content.Client.Atmos.UI
private void OnSelectGasPressed()
{
if (_window is null) return;
if (_window is null)
return;
if (_window.SelectedGas is null)
{
SendMessage(new GasFilterSelectGasMessage(null));
}
else
{
if (!int.TryParse(_window.SelectedGas, out var gas)) return;
if (!Enum.TryParse<Gas>(_window.SelectedGas, out var gas))
return;
SendMessage(new GasFilterSelectGasMessage(gas));
}
}

View file

@ -1,8 +0,0 @@
using Content.Shared.Bed;
namespace Content.Client.Bed;
public sealed class BedSystem : SharedBedSystem
{
}

View file

@ -7,10 +7,7 @@
Orientation="Vertical"
HorizontalExpand="True"
Margin="0 0 0 5">
<PanelContainer>
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="{xNamespace:Static style:StyleNano.ButtonColorDisabled}" />
</PanelContainer.PanelOverride>
<PanelContainer StyleClasses="PanelDark">
<Collapsible Name="Collapsible">
<CollapsibleHeading Name="Heading" MinHeight="35"/>
<CollapsibleBody Name="Body">

View file

@ -14,10 +14,8 @@
<PanelContainer
VerticalExpand="True"
HorizontalExpand="True"
Margin="15">
<PanelContainer.PanelOverride>
<graphics:StyleBoxFlat BackgroundColor="{xNamespace:Static style:StyleNano.PanelDark}" />
</PanelContainer.PanelOverride>
Margin="15"
StyleClasses="PanelDark">
<ScrollContainer VerticalExpand="True" HorizontalExpand="True">
<Control>
<Label Text="{Loc 'cryostorage-ui-label-no-bodies'}" Name="EmptyLabel" HorizontalAlignment="Center" VerticalAlignment="Center"/>

View file

@ -1,7 +1,7 @@
<DefaultWindow
xmlns="https://spacestation14.io"
Title="{Loc 'ui-bql-results-title'}">
<BoxContainer Orientation="Vertical">
<BoxContainer Orientation="Vertical" MinSize="500 700">
<Label Name="StatusLabel" />
<ScrollContainer VerticalExpand="True">
<BoxContainer Orientation="Vertical" Name="ItemList" VerticalExpand="True" />

View file

@ -21,8 +21,6 @@ internal sealed partial class ToolshedVisualizeWindow : DefaultWindow
RobustXamlLoader.Load(this);
}
protected override Vector2 ContentsMinimumSize => new(500, 700);
public void Update((string name, NetEntity entity)[] entities)
{
StatusLabel.Text = _loc.GetString("ui-bql-results-status", ("count", entities.Length));

View file

@ -3,7 +3,7 @@
Margin="10 10 10 0"
HorizontalExpand="True"
Visible="True">
<PanelContainer StyleClasses="AngleRect" HorizontalExpand="True">
<PanelContainer StyleClasses="BackgroundPanel" HorizontalExpand="True">
<BoxContainer Orientation="Vertical"
HorizontalExpand="True">
<BoxContainer Orientation="Horizontal">

View file

@ -7,7 +7,6 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Serilog;
namespace Content.Client.Cargo.UI;

View file

@ -1,8 +1,12 @@
using Content.Client.Message;
using Content.Client.RichText;
using Content.Client.UserInterface.RichText;
using Content.Shared.MassMedia.Systems;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.RichText;
using Robust.Client.UserInterface.XAML;
using Robust.Shared.Utility;
namespace Content.Client.CartridgeLoader.Cartridges;
@ -31,16 +35,17 @@ public sealed partial class NewsReaderUiFragment : BoxContainer
Author.Visible = true;
PageName.Text = article.Title;
PageText.SetMarkupPermissive(article.Content);
PageText.SetMessage(FormattedMessage.FromMarkupPermissive(article.Content), UserFormattableTags.BaseAllowedTags);
PageNum.Text = $"{targetNum}/{totalNum}";
NotificationSwitch.Text = Loc.GetString(notificationOn ? "news-read-ui-notification-on" : "news-read-ui-notification-off");
string shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
var shareTime = article.ShareTime.ToString(@"hh\:mm\:ss");
ShareTime.SetMarkup(Loc.GetString("news-read-ui-time-prefix-text") + " " + shareTime);
Author.SetMarkup(Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author != null ? article.Author : Loc.GetString("news-read-ui-no-author")));
var author = Loc.GetString("news-read-ui-author-prefix") + " " + (article.Author ?? Loc.GetString("news-read-ui-no-author"));
Author.SetMessage(FormattedMessage.FromMarkupPermissive(author), UserFormattableTags.BaseAllowedTags);
Prev.Disabled = targetNum <= 1;
Next.Disabled = targetNum >= totalNum;

View file

@ -1,4 +1,4 @@
using Content.Client.Stylesheets;
using Content.Client.Stylesheets.Palette;
using Content.Client.UserInterface.Controls;
using Content.Shared.Changeling.Components;
using Content.Shared.Changeling.Systems;
@ -11,8 +11,8 @@ namespace Content.Client.Changeling.UI;
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);
private static readonly Color SelectedOptionBackground = Palettes.Green.Element.WithAlpha(128);
private static readonly Color SelectedOptionHoverBackground = Palettes.Green.HoveredElement.WithAlpha(128);
protected override void Open()
{

View file

@ -36,12 +36,12 @@ namespace Content.Client.Changelog
if (_changelogManager.NewChangelogEntries)
{
Text = Loc.GetString("changelog-button-new-entries");
StyleClasses.Add(StyleBase.ButtonCaution);
StyleClasses.Add(StyleClass.Negative);
}
else
{
Text = Loc.GetString("changelog-button");
StyleClasses.Remove(StyleBase.ButtonCaution);
StyleClasses.Remove(StyleClass.Negative);
}
}
}

View file

@ -52,6 +52,7 @@ namespace Content.Client.Changelog
// Open changelog purely to compare to the last viewed date.
var changelogs = await LoadChangelog();
UpdateChangelogs(changelogs);
_configManager.OnValueChanged(CCVars.ServerId, OnServerIdCVarChanged);
}
private void UpdateChangelogs(List<Changelog> changelogs)
@ -81,6 +82,11 @@ namespace Content.Client.Changelog
MaxId = changelog.Entries.Max(c => c.Id);
CheckLastSeenEntry();
}
private void CheckLastSeenEntry()
{
var path = new ResPath($"/changelog_last_seen_{_configManager.GetCVar(CCVars.ServerId)}");
if (_resource.UserData.TryReadAllText(path, out var lastReadIdText))
{
@ -92,6 +98,11 @@ namespace Content.Client.Changelog
NewChangelogEntriesChanged?.Invoke();
}
private void OnServerIdCVarChanged(string newValue)
{
CheckLastSeenEntry();
}
public Task<List<Changelog>> LoadChangelog()
{
return Task.Run(() =>

View file

@ -69,7 +69,7 @@ public sealed partial class ChangelogTab : Control
ChangelogBody.AddChild(new Label
{
Text = dayNice,
StyleClasses = { StyleBase.StyleClassLabelHeading },
StyleClasses = { StyleClass.LabelHeading },
Margin = new Thickness(4, 6, 0, 0)
});
@ -128,7 +128,7 @@ public sealed partial class ChangelogTab : Control
};
readDivider.AddChild(hBox);
readDivider.AddChild(new PanelContainer { StyleClasses = { StyleBase.ClassLowDivider } });
readDivider.AddChild(new PanelContainer { StyleClasses = { StyleClass.LowDivider } });
ChangelogBody.AddChild(readDivider);
if (first)

View file

@ -2,8 +2,9 @@
xmlns:ui="clr-namespace:Content.Client.Changelog"
Title="{Loc 'changelog-window-title'}"
MinSize="500 400"
SetSize="500 400">
<PanelContainer StyleClasses="AngleRect" />
SetSize="500 400"
Stylesheet="Interface">
<PanelContainer StyleClasses="BackgroundPanel" />
<BoxContainer Orientation="Vertical">
<TabContainer Name="Tabs" Access="Public" HorizontalExpand="True" VerticalExpand="True" />
<PanelContainer StyleClasses="LowDivider" />

View file

@ -21,8 +21,7 @@ namespace Content.Client.Changelog
public ChangelogWindow()
{
RobustXamlLoader.Load(this);
WindowTitle.AddStyleClass(StyleBase.StyleClassLabelHeading);
Stylesheet = IoCManager.Resolve<IStylesheetManager>().SheetSpace;
WindowTitle.AddStyleClass(StyleClass.LabelHeading);
}
protected override void Opened()

View file

@ -32,6 +32,11 @@ internal sealed class ChatManager : IChatManager
// See server-side manager. This just exists for shared code.
}
public void SendAdminAlertNoFormatOrEscape(string message)
{
// See server-side manager. This just exists for shared code.
}
public void SendMessage(string text, ChatSelectChannel channel)
{
var str = text.ToString();

View file

@ -1,16 +0,0 @@
using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
namespace Content.Client.Chemistry.EntitySystems;
public sealed class HyposprayStatusControlSystem : EntitySystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<HyposprayComponent>(ent => new HyposprayStatusControl(ent, _solutionContainers));
}
}

View file

@ -0,0 +1,20 @@
using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Robust.Shared.Prototypes;
namespace Content.Client.Chemistry.EntitySystems;
public sealed class InjectorStatusControlSystem : EntitySystem
{
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<InjectorComponent>(injector => new InjectorStatusControl(injector, _solutionContainers, _prototypeManager));
}
}

View file

@ -1,16 +0,0 @@
using Content.Client.Chemistry.UI;
using Content.Client.Items;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
namespace Content.Client.Chemistry.EntitySystems;
public sealed class InjectorSystem : SharedInjectorSystem
{
public override void Initialize()
{
base.Initialize();
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainer));
}
}

View file

@ -33,8 +33,8 @@
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-buffer-text'}" />
<Control HorizontalExpand="True" />
<Button MinSize="80 0" Margin="0 0 10 0" Name="BufferSortButton" Access="Public" Text="{Loc 'chem-master-window-sort-type-none'}" />
<Button MinSize="80 0" Name="BufferTransferButton" Access="Public" Text="{Loc 'chem-master-window-transfer-button'}" ToggleMode="True" StyleClasses="OpenRight" />
<Button MinSize="80 0" Name="BufferSortButton" Access="Public" Text="{Loc 'chem-master-window-sort-type-none'}" StyleClasses="OpenBoth" />
<Button MinSize="80 0" Name="BufferDiscardButton" Access="Public" Text="{Loc 'chem-master-window-discard-button'}" ToggleMode="True" StyleClasses="OpenLeft" />
</BoxContainer>
@ -82,7 +82,7 @@
<Label Text="{Loc 'chem-master-window-packaging-text'}" />
<Control HorizontalExpand="True"/>
<Label Text="{Loc 'chem-master-window-beaker-label'}" /> <!-- Starlight Beaker Changes-->
<Label Name="BufferCurrentVolume" StyleClasses="LabelSecondaryColor" />
<Label Name="BufferCurrentVolume" StyleClasses="LabelWeak" />
</BoxContainer>
<!-- Wrap the packaging info-->
@ -112,9 +112,9 @@
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-pills-label'}" />
<Control HorizontalExpand="True" MinSize="50 0" />
<Label Text="{Loc 'chem-master-window-pills-number-label'}" Margin="5 0 0 0" StyleClasses="LabelSecondaryColor" />
<Label Text="{Loc 'chem-master-window-pills-number-label'}" Margin="5 0 0 0" StyleClasses="LabelWeak" />
<SpinBox MinSize="100 0" Name="PillNumber" Access="Public" Value="0" />
<Label Text="{Loc 'chem-master-window-dose-label'}" Margin="5 0 0 0" StyleClasses="LabelSecondaryColor" />
<Label Text="{Loc 'chem-master-window-dose-label'}" Margin="5 0 0 0" StyleClasses="LabelWeak" />
<SpinBox MinSize="100 0" Name="PillDosage" Access="Public" Value="1" />
<Button MinSize="80 0" Name="CreatePillButton" Access="Public" Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>
@ -134,7 +134,7 @@
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'chem-master-window-bottles-label'}" />
<Control HorizontalExpand="True" MinSize="50 0" />
<Label Text="{Loc 'chem-master-window-dose-label'}" Margin="5 0 0 0" StyleClasses="LabelSecondaryColor" />
<Label Text="{Loc 'chem-master-window-dose-label'}" Margin="5 0 0 0" StyleClasses="LabelWeak" />
<SpinBox MinSize="100 0" Name="BottleDosage" Access="Public" Value="0" />
<Button MinSize="80 0" Name="CreateBottleButton" Access="Public" Text="{Loc 'chem-master-window-create-button'}" />
</BoxContainer>

View file

@ -53,14 +53,14 @@ namespace Content.Client.Chemistry.UI
{
// For every button decide which stylebase to have
// Every row has 10 buttons
String styleBase = StyleBase.ButtonOpenBoth;
String styleBase = StyleClass.ButtonOpenBoth;
uint modulo = i % 10;
if (i > 0 && modulo == 0)
styleBase = StyleBase.ButtonOpenRight;
styleBase = StyleClass.ButtonOpenRight;
else if (i > 0 && modulo == 9)
styleBase = StyleBase.ButtonOpenLeft;
styleBase = StyleClass.ButtonOpenLeft;
else if (i == 0)
styleBase = StyleBase.ButtonOpenRight;
styleBase = StyleClass.ButtonOpenRight;
// Generate buttons
PillTypeButtons[i] = new Button
@ -117,16 +117,16 @@ namespace Content.Client.Chemistry.UI
var buttonConfigs = new (string text, ChemMasterReagentAmount amount, string styleClass)[]
{
("1", ChemMasterReagentAmount.U1, StyleBase.ButtonOpenBoth),
("5", ChemMasterReagentAmount.U5, StyleBase.ButtonOpenBoth),
("10", ChemMasterReagentAmount.U10, StyleBase.ButtonOpenBoth),
("15", ChemMasterReagentAmount.U15, StyleBase.ButtonOpenBoth),
("20", ChemMasterReagentAmount.U20, StyleBase.ButtonOpenBoth),
("25", ChemMasterReagentAmount.U25, StyleBase.ButtonOpenBoth),
("30", ChemMasterReagentAmount.U30, StyleBase.ButtonOpenBoth),
("50", ChemMasterReagentAmount.U50, StyleBase.ButtonOpenBoth),
("100", ChemMasterReagentAmount.U100, StyleBase.ButtonOpenBoth),
(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, StyleBase.ButtonOpenLeft),
("1", ChemMasterReagentAmount.U1, StyleClass.ButtonOpenBoth),
("5", ChemMasterReagentAmount.U5, StyleClass.ButtonOpenBoth),
("10", ChemMasterReagentAmount.U10, StyleClass.ButtonOpenBoth),
("15", ChemMasterReagentAmount.U15, StyleClass.ButtonOpenBoth),
("20", ChemMasterReagentAmount.U20, StyleClass.ButtonOpenBoth),
("25", ChemMasterReagentAmount.U25, StyleClass.ButtonOpenBoth),
("30", ChemMasterReagentAmount.U30, StyleClass.ButtonOpenBoth),
("50", ChemMasterReagentAmount.U50, StyleClass.ButtonOpenBoth),
("100", ChemMasterReagentAmount.U100, StyleClass.ButtonOpenBoth),
(Loc.GetString("chem-master-window-buffer-all-amount"), ChemMasterReagentAmount.All, StyleClass.ButtonOpenLeft),
};
var buttons = new List<ReagentButton>();
@ -280,7 +280,7 @@ namespace Content.Client.Chemistry.UI
var bufferVol = new Label
{
Text = $"{state.BufferCurrentVolume}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
StyleClasses = { StyleClass.LabelWeak }
};
bufferHBox.AddChild(bufferVol);
@ -349,7 +349,7 @@ namespace Content.Client.Chemistry.UI
new Label
{
Text = $"{info.CurrentVolume}/{info.MaxVolume}",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
StyleClasses = { StyleClass.LabelWeak }
}
}
});
@ -404,7 +404,7 @@ namespace Content.Client.Chemistry.UI
new Label
{
Text = $"{quantity}u",
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor }
StyleClasses = { StyleClass.LabelWeak }
},
// Padding

View file

@ -1,58 +0,0 @@
using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.FixedPoint;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Timing;
namespace Content.Client.Chemistry.UI;
public sealed class HyposprayStatusControl : Control
{
private readonly Entity<HyposprayComponent> _parent;
private readonly RichTextLabel _label;
private readonly SharedSolutionContainerSystem _solutionContainers;
private FixedPoint2 PrevVolume;
private FixedPoint2 PrevMaxVolume;
private bool PrevOnlyAffectsMobs;
public HyposprayStatusControl(Entity<HyposprayComponent> parent, SharedSolutionContainerSystem solutionContainers)
{
_parent = parent;
_solutionContainers = solutionContainers;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
AddChild(_label);
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
return;
// only updates the UI if any of the details are different than they previously were
if (PrevVolume == solution.Volume
&& PrevMaxVolume == solution.MaxVolume
&& PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs)
return;
PrevVolume = solution.Volume;
PrevMaxVolume = solution.MaxVolume;
PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs;
var modeStringLocalized = Loc.GetString((_parent.Comp.OnlyAffectsMobs && _parent.Comp.CanContainerDraw) switch
{
false => "hypospray-all-mode-text",
true => "hypospray-mobs-only-mode-text",
});
_label.SetMarkup(Loc.GetString("hypospray-volume-label",
("currentVolume", solution.Volume),
("totalVolume", solution.MaxVolume),
("modeString", modeStringLocalized)));
}
}

View file

@ -2,29 +2,35 @@ using Content.Client.Message;
using Content.Client.Stylesheets;
using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Chemistry.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
namespace Content.Client.Chemistry.UI;
public sealed class InjectorStatusControl : Control
{
private readonly IPrototypeManager _prototypeManager;
private readonly Entity<InjectorComponent> _parent;
private readonly SharedSolutionContainerSystem _solutionContainers;
private readonly RichTextLabel _label;
private FixedPoint2 PrevVolume;
private FixedPoint2 PrevMaxVolume;
private FixedPoint2 PrevTransferAmount;
private InjectorToggleMode PrevToggleState;
private FixedPoint2 _prevVolume;
private FixedPoint2 _prevMaxVolume;
private FixedPoint2? _prevTransferAmount;
private InjectorBehavior _prevBehavior;
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers)
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers, IPrototypeManager prototypeManager)
{
_prototypeManager = prototypeManager;
_parent = parent;
_solutionContainers = solutionContainers;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
AddChild(_label);
}
@ -32,33 +38,38 @@ public sealed class InjectorStatusControl : Control
{
base.FrameUpdate(args);
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution)
|| !_prototypeManager.Resolve(_parent.Comp.ActiveModeProtoId, out var activeMode))
return;
// 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
&& PrevToggleState == _parent.Comp.ToggleState)
if (_prevVolume == solution.Volume
&& _prevMaxVolume == solution.MaxVolume
&& _prevTransferAmount == _parent.Comp.CurrentTransferAmount
&& _prevBehavior == activeMode.Behavior)
return;
PrevVolume = solution.Volume;
PrevMaxVolume = solution.MaxVolume;
PrevTransferAmount = _parent.Comp.CurrentTransferAmount;
PrevToggleState = _parent.Comp.ToggleState;
_prevVolume = solution.Volume;
_prevMaxVolume = solution.MaxVolume;
_prevTransferAmount = _parent.Comp.CurrentTransferAmount;
_prevBehavior = activeMode.Behavior;
// Update current volume and injector state
var modeStringLocalized = Loc.GetString(_parent.Comp.ToggleState switch
// Seeing transfer volume is only important for injectors that can change it.
if (activeMode.TransferAmounts.Count > 1 && _parent.Comp.CurrentTransferAmount.HasValue)
{
_label.SetMarkup(Loc.GetString("injector-volume-transfer-label",
("currentVolume", solution.Volume),
("totalVolume", solution.MaxVolume),
("modeString", Loc.GetString(activeMode.Name)),
("transferVolume", _parent.Comp.CurrentTransferAmount.Value)));
}
else
{
InjectorToggleMode.Draw => "injector-draw-text",
InjectorToggleMode.Inject => "injector-inject-text",
_ => "injector-invalid-injector-toggle-mode"
});
_label.SetMarkup(Loc.GetString("injector-volume-label",
("currentVolume", solution.Volume),
("totalVolume", solution.MaxVolume),
("modeString", modeStringLocalized),
("transferVolume", _parent.Comp.CurrentTransferAmount)));
("modeString", Loc.GetString(activeMode.Name))));
}
}
}

View file

@ -107,7 +107,7 @@ namespace Content.Client.Chemistry.UI
var quantityLabel = new Label
{
Text = Loc.GetString("reagent-dispenser-window-quantity-label-text", ("quantity", quantity)),
StyleClasses = { StyleNano.StyleClassLabelSecondaryColor },
StyleClasses = { StyleClass.LabelWeak },
};
ContainerInfo.Children.Add(new BoxContainer

View file

@ -29,7 +29,7 @@ public sealed class SolutionStatusControl : PollingItemStatusControl<SolutionSta
_parent = parent;
_entityManager = entityManager;
_solutionContainers = solutionContainers;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
AddChild(_label);
}

View file

@ -17,7 +17,7 @@ namespace Content.Client.Cloning.UI
Title = Loc.GetString("accept-cloning-window-title");
Contents.AddChild(new BoxContainer
ContentsContainer.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =

View file

@ -67,7 +67,7 @@ public sealed partial class ChameleonMenu : DefaultWindow
MinSize = new Vector2(48, 48),
HorizontalExpand = true,
Group = group,
StyleClasses = {StyleBase.ButtonSquare},
StyleClasses = {StyleClass.ButtonSquare},
ToggleMode = true,
Pressed = _selectedId == id,
ToolTip = proto.Name

View file

@ -0,0 +1,52 @@
using System.Linq;
using Content.Shared.CCVar;
using Content.Shared.Input;
using Robust.Client.Input;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
namespace Content.Client.Commands;
/// <summary>
/// Sets the a <see cref="CCVars.DebugQuickInspect"/> CVar to the name of a component, which allows the client to quickly open a VV window for that component
/// by using the Alt+C or Alt+B hotkeys.
/// </summary>
public sealed class QuickInspectCommand : LocalizedEntityCommands
{
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly IInputManager _inputManager = default!;
public override string Command => "quickinspect";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
if (args.Length != 1)
{
shell.WriteLine(Loc.GetString("shell-wrong-arguments-number"));
return;
}
_configurationManager.SetCVar(CCVars.DebugQuickInspect, args[0]);
var serverKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectServerComponent);
var clientKey = _inputManager.GetKeyFunctionButtonString(ContentKeyFunctions.InspectClientComponent);
shell.WriteLine(Loc.GetString($"cmd-quickinspect-success", ("component", args[0]), ("serverKeybind", serverKey), ("clientKeybind", clientKey)));
}
public override CompletionResult GetCompletion(IConsoleShell shell, string[] args)
{
if (args.Length == 1)
{
// Not ideal since it only shows client-side components, but you can still type in any name you want.
// If you know how to get server component names on the client then please fix this.
var options = EntityManager.ComponentFactory.AllRegisteredTypes
.Select(p => new CompletionOption(
EntityManager.ComponentFactory.GetComponentName(p)
));
return CompletionResult.FromOptions(options);
}
return CompletionResult.Empty;
}
}

View file

@ -0,0 +1,24 @@
using Content.Client.Wall;
using Robust.Client.Graphics;
using Robust.Shared.Console;
namespace Content.Client.Commands;
/// <summary>
/// Shows the area in which entities with <see cref="Content.Shared.Wall.WallMountComponent" /> can be interacted from.
/// </summary>
public sealed class ShowWallmountsCommand : LocalizedCommands
{
[Dependency] private readonly IOverlayManager _overlay = default!;
public override string Command => "showwallmounts";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var existing = _overlay.RemoveOverlay<WallmountDebugOverlay>();
if (!existing)
_overlay.AddOverlay(new WallmountDebugOverlay());
shell.WriteLine(Loc.GetString("cmd-showwallmounts-status", ("status", !existing)));
}
}

View file

@ -68,7 +68,7 @@ namespace Content.Client.Configurable.UI
outerColumn.AddChild(Column);
baseContainer.AddChild(outerColumn);
baseContainer.AddChild(confirmButton);
Contents.AddChild(baseContainer);
ContentsContainer.AddChild(baseContainer);
}
private void OnConfirm(ButtonEventArgs args)

View file

@ -1,26 +1,24 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
<TargetFramework>$(TargetFramework)</TargetFramework>
<LangVersion>12</LangVersion>
<IsPackable>false</IsPackable>
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
<OutputPath>..\bin\Content.Client\</OutputPath>
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
<Nullable>enable</Nullable>
<Configurations>Debug;Release;Tools;DebugOpt</Configurations>
<Platforms>AnyCPU</Platforms>
</PropertyGroup>
<Import Project="../MSBuild/Content.props" />
<ItemGroup>
<PackageReference Include="Nett" />
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
<PackageReference Include="SixLabors.ImageSharp" />
<PackageReference Include="Pidgin" />
<PackageReference Include="Robust.Shared.AuthLib" />
</ItemGroup>
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
<Import Project="..\RobustToolbox\Imports\Client.props" />
<Import Project="..\RobustToolbox\Imports\Shared.props" />
<ItemGroup>
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
<ProjectReference Include="..\Sunrise\Content.Sunrise.Interfaces.Shared\Content.Sunrise.Interfaces.Shared.csproj" />
<ProjectReference Include="..\Sunrise\Content.Sunrise.Interfaces.Client\Content.Sunrise.Interfaces.Client.csproj" />

View file

@ -13,7 +13,8 @@
Name="IconLabel"
HorizontalAlignment="Right"
VerticalAlignment="Bottom"
StyleClasses="contextMenuIconLabel"
StyleClasses="LabelSubText"
Align="Right"
Visible="false"/>
</SpriteView>
<RichTextLabel

View file

@ -16,7 +16,6 @@ namespace Content.Client.ContextMenu.UI
{
public const string StyleClassContextMenuButton = "contextMenuButton";
public const string StyleClassContextMenuExpansionTexture = "contextMenuExpansionTexture";
public const string StyleClassEntityMenuIconLabel = "contextMenuIconLabel";
public const float ElementMargin = 2;
public const float ElementHeight = 32;

View file

@ -34,7 +34,7 @@ public sealed class CrayonSystem : SharedCrayonSystem
_crayon = crayon;
_charges = charges;
_capacity = entityManage.GetComponent<LimitedChargesComponent>(_crayon.Owner).MaxCharges;
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
AddChild(_label);
}

View file

@ -137,7 +137,8 @@ namespace Content.Client.Crayon.UI
private void ButtonOnPressed(ButtonEventArgs obj)
{
if (obj.Button.Name == null) return;
if (obj.Button.Name == null)
return;
_selected = obj.Button.Name;
_autoSelected = null;

View file

@ -258,7 +258,7 @@ public sealed partial class CreditsWindow : DefaultWindow
foreach (var entry in CreditsManager.GetLicenses(_resourceManager).OrderBy(p => p.Name))
{
licensesContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = entry.Name });
{ StyleClasses = { StyleClass.LabelHeading }, Text = entry.Name });
// We split these line by line because otherwise
// the LGPL causes Clyde to go out of bounds in the rendering code.
@ -299,7 +299,7 @@ public sealed partial class CreditsWindow : DefaultWindow
first = false;
patronsContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = $"{tier.Key}" });
{ StyleClasses = { StyleClass.LabelHeading }, Text = $"{tier.Key}" });
var msg = string.Join(", ", tier.OrderBy(p => p.Name).Select(p => p.Name));
@ -347,7 +347,7 @@ public sealed partial class CreditsWindow : DefaultWindow
first = false;
ss14ContributorsContainer.AddChild(new Label
{ StyleClasses = { StyleBase.StyleClassLabelHeading }, Text = title });
{ StyleClasses = { StyleClass.LabelHeading }, Text = title });
var label = new RichTextLabel();
var text = _resourceManager.ContentFileReadAllText($"/Credits/{path}");

View file

@ -276,7 +276,7 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
private void SetStatus(SecurityStatus status)
{
if (status == SecurityStatus.Wanted || status == SecurityStatus.Suspected)
if (status == SecurityStatus.Wanted || status == SecurityStatus.Suspected || status == SecurityStatus.Hostile)
{
GetReason(status);
return;
@ -322,6 +322,8 @@ public sealed partial class CriminalRecordsConsoleWindow : FancyWindow
SecurityStatus.Detained => "hud_incarcerated",
SecurityStatus.Discharged => "hud_discharged",
SecurityStatus.Suspected => "hud_suspected",
SecurityStatus.Hostile => "hud_hostile",
SecurityStatus.Eliminated => "hud_eliminated",
_ => "SecurityIconNone"
};
}

View file

@ -1,5 +1,6 @@
using System.Linq;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Robust.Client.GameObjects;

View file

@ -157,15 +157,12 @@ public sealed partial class DecalPlacerWindow : DefaultWindow
{
var panelContainer = new PanelContainer
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = StyleNano.ButtonColorDefault
},
Children =
{
button
}
};
panelContainer.SetOnlyStyleClass(StyleClass.PanelLight);
Grid.AddChild(panelContainer);
}
else

View file

@ -0,0 +1,5 @@
using Content.Shared.DeviceLinking.Systems;
namespace Content.Client.DeviceLinking.Systems;
public sealed class RandomGateSystem : SharedRandomGateSystem;

View file

@ -0,0 +1,37 @@
using Content.Shared.DeviceLinking;
using JetBrains.Annotations;
using Robust.Client.UserInterface;
namespace Content.Client.DeviceLinking.UI;
[UsedImplicitly]
public sealed class RandomGateBoundUserInterface : BoundUserInterface
{
private RandomGateSetupWindow? _window;
public RandomGateBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) { }
protected override void Open()
{
base.Open();
_window = this.CreateWindow<RandomGateSetupWindow>();
_window.OnApplyPressed += OnProbabilityChanged;
}
private void OnProbabilityChanged(string value)
{
if (!float.TryParse(value, out var probability))
return;
SendPredictedMessage(new RandomGateProbabilityChangedMessage(probability));
}
protected override void UpdateState(BoundUserInterfaceState state)
{
base.UpdateState(state);
if (state is not RandomGateBoundUserInterfaceState castState || _window == null)
return;
_window.SetProbability(castState.SuccessProbability * 100);
}
}

View file

@ -0,0 +1,19 @@
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'random-gate-menu-setup'}"
MinSize="260 115">
<BoxContainer Orientation="Vertical" Margin="5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc 'random-gate-menu-settings'}" VerticalAlignment="Center" />
<Control HorizontalExpand="True" />
<LineEdit Name="ProbabilityInput" MinSize="70 0" />
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Control HorizontalExpand="True" />
<Button Name="ApplyButton"
Text="{Loc 'random-gate-menu-apply'}"
HorizontalAlignment="Right" />
</BoxContainer>
</BoxContainer>
</controls:FancyWindow>

View file

@ -0,0 +1,28 @@
using Content.Client.UserInterface.Controls;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
namespace Content.Client.DeviceLinking.UI;
/// <summary>
/// Window for setting up the random gate probability.
/// </summary>
[GenerateTypedNameReferences]
public sealed partial class RandomGateSetupWindow : FancyWindow
{
/// <summary>
/// Event triggered when the "Apply" button is pressed.
/// </summary>
public event Action<string>? OnApplyPressed;
public RandomGateSetupWindow()
{
RobustXamlLoader.Load(this);
ApplyButton.OnPressed += _ => OnApplyPressed?.Invoke(ProbabilityInput.Text);
}
public void SetProbability(float probability)
{
ProbabilityInput.Text = probability.ToString("0.00");
}
}

View file

@ -47,7 +47,7 @@
Access="Public"
Text="{Loc 'ui-disposal-unit-button-eject'}"
StyleClasses="OpenBoth" />
<CheckButton Name="Power"
<Button Name="Power"
Access="Public"
Text="{Loc 'ui-disposal-unit-button-power'}"
StyleClasses="OpenLeft" />

View file

@ -34,7 +34,7 @@
Access="Public"
Text="{Loc 'ui-disposal-unit-button-eject'}"
StyleClasses="OpenBoth" />
<CheckButton Name="Power"
<Button Name="Power"
Access="Public"
Text="{Loc 'ui-disposal-unit-button-power'}"
StyleClasses="OpenLeft" />

View file

@ -27,6 +27,15 @@ public sealed class DrunkOverlay : Overlay
private const float VisualThreshold = 10.0f;
private const float PowerDivisor = 250.0f;
/// <remarks>
/// This is a magic number based on my person preference of how quickly the bloodloss effect should kick in.
/// It is entirely arbitrary, and you should change it if it sucks.
/// Honestly should be refactored to be based on amount of blood lost but that's out of scope for what I'm doing atm.
/// Also caps all booze visual effects to a max intensity of 100 seconds or 100 booze power.
/// </remarks>
private const float MaxBoozePower = 100f;
private const float BoozePowerScale = 8f;
private float _visualScale = 0;
@ -50,15 +59,9 @@ public sealed class DrunkOverlay : Overlay
var time = status.Item2;
var power = SharedDrunkSystem.MagicNumber;
var power = time == null ? MaxBoozePower : (float) Math.Min((time - _timing.CurTime).Value.TotalSeconds, MaxBoozePower);
if (time != null)
{
var curTime = _timing.CurTime;
power = (float) (time - curTime).Value.TotalSeconds;
}
CurrentBoozePower += 8f * (power * 0.5f - CurrentBoozePower) * args.DeltaSeconds / (power+1);
CurrentBoozePower += BoozePowerScale * (power - CurrentBoozePower) * args.DeltaSeconds / (power+1);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)

View file

@ -110,7 +110,6 @@ namespace Content.Client.Entry
_componentFactory.IgnoreMissingComponents();
// Do not add to these, they are legacy.
_componentFactory.RegisterClass<SharedGravityGeneratorComponent>();
_componentFactory.RegisterClass<SharedAmeControllerComponent>();
// Do not add to the above, they are legacy
@ -125,8 +124,6 @@ namespace Content.Client.Entry
_prototypeManager.RegisterIgnore("htnPrimitive");
_prototypeManager.RegisterIgnore("gameMap");
_prototypeManager.RegisterIgnore("gameMapPool");
// Sunrise-Edit
// _prototypeManager.RegisterIgnore("lobbyBackground");
_prototypeManager.RegisterIgnore("gamePreset");
_prototypeManager.RegisterIgnore("noiseChannel");
_prototypeManager.RegisterIgnore("playerConnectionWhitelist");

View file

@ -56,7 +56,7 @@
HorizontalAlignment="Center"
VerticalAlignment="Center"
Scale="0.9 0.9"
StyleClasses="Refresh" />
StyleClasses="RefreshButton" />
</Button>
</BoxContainer>
</BoxContainer>

View file

@ -73,7 +73,19 @@ public sealed class PuddleSystem : SharedPuddleSystem
// Maybe someday we'll have clientside prediction for entity spawning, but not today.
// Until then, these methods do nothing on the client.
/// <inheritdoc/>
public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null)
{
puddleUid = EntityUid.Invalid;
solution = new Solution();
return false;
}
public override bool TrySplashSpillAt(EntityUid entity,
EntityCoordinates coordinates,
Solution spilled,
out EntityUid puddleUid,
bool sound = true,
EntityUid? user = null)
{
puddleUid = EntityUid.Invalid;
return false;

View file

@ -0,0 +1,7 @@
using Content.Shared.Fluids.Components;
using Content.Shared.Fluids.EntitySystems;
using Robust.Shared.Map;
namespace Content.Client.Fluids;
public sealed class SpraySystem : SharedSpraySystem;

View file

@ -21,7 +21,7 @@ public sealed class HandheldGpsStatusControl : Control
_parent = parent;
_entMan = IoCManager.Resolve<IEntityManager>();
_transform = _entMan.System<TransformSystem>();
_label = new RichTextLabel { StyleClasses = { StyleNano.StyleClassItemStatus } };
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
AddChild(_label);
UpdateGpsDetails();
}

View file

@ -11,6 +11,7 @@ using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Robust.Shared.Audio;
using Content.Shared.GameTicking.Prototypes;
namespace Content.Client.GameTicking.Managers
{
@ -31,7 +32,7 @@ namespace Content.Client.GameTicking.Managers
// Sunrise-Start
[ViewVariables] public string? LobbyType { get; private set; }
[ViewVariables] public string? LobbyParallax { get; private set; }
[ViewVariables] public string? LobbyAnimation { get; private set; }
[ViewVariables] public ProtoId<LobbyBackgroundPrototype>? LobbyAnimation { get; private set; }
[ViewVariables] public string? LobbyArt { get; private set; }
// Sunrise-End
[ViewVariables] public bool DisallowedLateJoin { get; private set; }

View file

@ -3,6 +3,7 @@ using System.Numerics;
using Content.Client.Clickable;
using Content.Client.UserInterface;
using Content.Client.Viewport;
using Content.Shared.CCVar;
using Content.Shared.Input;
using Robust.Client.ComponentTrees;
using Robust.Client.GameObjects;
@ -13,6 +14,7 @@ using Robust.Client.State;
using Robust.Client.UserInterface;
using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls;
using Robust.Shared.Configuration;
using Robust.Shared.Console;
using Robust.Shared.Graphics;
using Robust.Shared.Input;
@ -40,6 +42,7 @@ namespace Content.Client.Gameplay
[Dependency] private readonly IEntityManager _entityManager = default!;
[Dependency] private readonly IViewVariablesManager _vvm = default!;
[Dependency] private readonly IConsoleHost _conHost = default!;
[Dependency] private readonly IConfigurationManager _configurationManager = default!;
private ClickableEntityComparer _comparer = default!;
@ -83,6 +86,8 @@ namespace Content.Client.Gameplay
_comparer = new ClickableEntityComparer();
CommandBinds.Builder
.Bind(ContentKeyFunctions.InspectEntity, new PointerInputCmdHandler(HandleInspect, outsidePrediction: true))
.Bind(ContentKeyFunctions.InspectServerComponent, new PointerInputCmdHandler(HandleInspectServerComponent, outsidePrediction: true))
.Bind(ContentKeyFunctions.InspectClientComponent, new PointerInputCmdHandler(HandleInspectClientComponent, outsidePrediction: true))
.Register<GameplayStateBase>();
}
@ -99,6 +104,21 @@ namespace Content.Client.Gameplay
return true;
}
private bool HandleInspectServerComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
if (_entityManager.TryGetNetEntity(uid, out var net))
_conHost.ExecuteCommand($"vv /entity/{net.Value.Id}/{component}");
return true;
}
private bool HandleInspectClientComponent(ICommonSession? session, EntityCoordinates coords, EntityUid uid)
{
var component = _configurationManager.GetCVar(CCVars.DebugQuickInspect);
_conHost.ExecuteCommand($"vv /c/entity/{uid}/{component}");
return true;
}
public EntityUid? GetClickedEntity(MapCoordinates coordinates)
{
return GetClickedEntity(coordinates, _eyeManager.CurrentEye);

View file

@ -159,7 +159,7 @@ public sealed partial class GatewayWindow : FancyWindow,
if (Pressable())
{
openButton.AddStyleClass(StyleBase.ButtonCaution);
openButton.AddStyleClass(StyleClass.Negative);
}
var buttonContainer = new BoxContainer()

View file

@ -15,7 +15,7 @@ public sealed class ReturnToBodyMenu : DefaultWindow
{
Title = Loc.GetString("ghost-return-to-body-title");
Contents.AddChild(new BoxContainer
ContentsContainer.AddChild(new BoxContainer
{
Orientation = LayoutOrientation.Vertical,
Children =

View file

@ -0,0 +1,8 @@
using Content.Shared.Gravity;
namespace Content.Client.Gravity;
public sealed class GravityGeneratorSystem : SharedGravityGeneratorSystem
{
}

View file

@ -12,14 +12,14 @@ public sealed partial class GravitySystem : SharedGravitySystem
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<SharedGravityGeneratorComponent, AppearanceChangeEvent>(OnAppearanceChange);
SubscribeLocalEvent<GravityGeneratorComponent, AppearanceChangeEvent>(OnAppearanceChange);
InitializeShake();
}
/// <summary>
/// Ensures that the visible state of gravity generators are synced with their sprites.
/// </summary>
private void OnAppearanceChange(EntityUid uid, SharedGravityGeneratorComponent comp, ref AppearanceChangeEvent args)
private void OnAppearanceChange(EntityUid uid, GravityGeneratorComponent comp, ref AppearanceChangeEvent args)
{
if (args.Sprite == null)
return;

View file

@ -1,28 +1,22 @@
using System.Linq;
using System.Numerics;
using Content.Client.Message;
using Content.Shared.Atmos;
using Content.Client.UserInterface.Controls;
using Content.Shared.Alert;
using Content.Shared.Damage;
using Content.Shared.Damage.Components;
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint;
using Content.Shared.Humanoid;
using Content.Shared.Humanoid.Prototypes;
using Content.Shared.IdentityManagement;
using Content.Shared.Inventory;
using Content.Shared.MedicalScanner;
using Content.Shared.Mobs;
using Content.Shared.Mobs.Components;
using Content.Shared.Mobs.Systems;
using Content.Shared.Nutrition.Components;
using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls;
using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;

View file

@ -116,7 +116,7 @@ public sealed partial class ChameleonControllerMenu : FancyWindow
var button = new Button
{
HorizontalExpand = true,
StyleClasses = {StyleBase.ButtonSquare},
StyleClasses = {StyleClass.ButtonSquare},
ToolTip = Loc.GetString(name),
Text = Loc.GetString(name),
Margin = new Thickness(0, 0, 15, 0),

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