Compare commits

..
8 Commits
Author SHA1 Message Date
Tobias Berger 414be58480 Remove piping automation since I could not manage to port that easily 2026-06-03 23:38:35 +02:00
Tobias Berger 36ee8429c5 Fix cherrypick mistakes 2026-06-03 21:52:36 +02:00
Ilya246 eaa5cfec1f ok
Co-authored-by: LaCumbiaDelCoronavirus <90893484+LaCumbiaDelCoronavirus@users.noreply.github.com>
(cherry picked from commit b55c30497f4e22873b5fab6319d148ee885e72fe)
2026-06-03 21:33:35 +02:00
Ilya246 1b4e3dc602 unmodify upstream
(cherry picked from commit 0275bc6542dbcd13af6ee9db2f5d0db174d23b84)
2026-06-03 21:33:35 +02:00
Ilya246 368918d46a more
(cherry picked from commit c033e516f20478efd8509eac6ee15930a5d473e8)
2026-06-03 21:32:01 +02:00
Ilya246 04efdf82f6 ops
(cherry picked from commit 5a925c0f1bf8db999728ceaeba458f0c81f128e4)
2026-06-03 21:30:38 +02:00
deltanedas ac50abeab8 make lathes automatable
(cherry picked from commit 3767aba3a1036db46acc9bd1087c4497dbe53a7a)
2026-06-03 21:10:52 +02:00
Roudenn c9dbb1c9a1 Everything comes with built-in automation
(cherry picked from commit 42c2e0fc62aa00bbe64e28e2dc53f4d038164435)
2026-06-03 21:07:16 +02:00
5114 changed files with 207587 additions and 654776 deletions
+19 -10
View File
@@ -1,25 +1,34 @@
# Last match in file takes precedence. # Last match in file takes precedence.
# See https://docs.github.com/en/repositories/managing-your-repositorys-settings-and-features/customizing-your-repository/about-code-owners#example-of-a-codeowners-file
* @DeltaV-Station/maintainers # C# code
/Content.*/ @DeltaV-Station/maintainers
# Any assets
/Resources/ @DeltaV-Station/maintainers
# Server config files # Server config files
/Resources/ConfigPresets/ @DeltaV-Station/lead-maintainers /Resources/ConfigPresets/ @Toby222
# Github workflows
/.github/ @DeltaV-Station/lead-maintainers
# Lobby art and music - automatically direction issues since its immediately visible to players # Lobby art and music - automatically direction issues since its immediately visible to players
/Resources/Audio/Lobby/ @DeltaV-Station/direction @DeltaV-Station/maintainers /Resources/Audio/Lobby/ @DeltaV-Station/direction
/Resources/Textures/LobbyScreens/ @DeltaV-Station/direction @DeltaV-Station/maintainers /Resources/Textures/LobbyScreens/ @DeltaV-Station/direction
# Maps # Maps
/Resources/Maps/ @DeltaV-Station/maptainers /Resources/Maps/ @DeltaV-Station/maptainers
/Resources/Prototypes/Maps/ @DeltaV-Station/maptainers /Resources/Prototypes/Maps/ @DeltaV-Station/maptainers
/Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers @DeltaV-Station/maintainers /Content.IntegrationTests/Tests/PostMapInitTest.cs @DeltaV-Station/maptainers
# Server rules # Server rules
/Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators /Resources/ServerInfo/Guidebook/_DV/Rules/ @DeltaV-Station/head-administrators
# Wiki # Wiki
/.wiki/ @DeltaV-Station/direction /.wiki/ @DeltaV-Station/direction
# Tools and scripts
/Tools/ @Toby222
# Workflows, codeowners, templates, etc.
/.github/ @Toby222
# Standalone files in the root repo
/* @Toby222
+1 -1
View File
@@ -26,7 +26,7 @@ This just saves them the trouble. Note that AGPL or MIT licensing is only applic
- [ ] I confirm that I am the creator of the code in this PR, and allow licensing it under the following license(s), or that the original author(s) has given me permission to do so: - [ ] I confirm that I am the creator of the code in this PR, and allow licensing it under the following license(s), or that the original author(s) has given me permission to do so:
- [X] AGPL (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-AGPLv3.txt) <!-- This one is required to contribute to DeltaV --> - [X] AGPL (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-AGPLv3.txt) <!-- This one is required to contribute to DeltaV -->
- [X] MIT (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-MIT.txt) <!-- Optional - A lot of other SS14 forks use MIT --> - [X] MIT (https://github.com/DeltaV-Station/Delta-v/blob/master/LICENSE-MIT.txt) <!-- Optional - A lot of other SS14 forks use MIT -->
<!-- Feel free to add more licenses as you see fit --> <!-- Feel free to add more licenses as you see fit -->
**Changelog** **Changelog**
<!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay. <!-- Add a Changelog entry to make players aware of new features or changes that could affect gameplay.
+2 -13
View File
@@ -32,7 +32,6 @@ jobs:
run: | run: |
cd RobustToolbox/ cd RobustToolbox/
git submodule update --init --recursive git submodule update --init --recursive
rm Robust.Shared.Tests/Networking/NetEncryptionDoSTest.cs # DeltaV - work around bug in the backport
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0 uses: actions/setup-dotnet@v4.1.0
@@ -46,23 +45,13 @@ jobs:
run: dotnet build --configuration DebugOpt --no-restore /m run: dotnet build --configuration DebugOpt --no-restore /m
- name: Run Content.Tests - name: Run Content.Tests
shell: pwsh run: dotnet test --no-build --configuration DebugOpt Content.Tests/Content.Tests.csproj -- NUnit.ConsoleOut=0
run: dotnet test --no-build --configuration DebugOpt Content.Tests/Content.Tests.csproj -- NUnit.ConsoleOut=0 NUnit.TestOutputXml="logs" NUnit.WorkDirectory="$(pwd)/test_results"
- name: Run Content.IntegrationTests - name: Run Content.IntegrationTests
shell: pwsh shell: pwsh
run: | run: |
$env:DOTNET_gcServer=1 $env:DOTNET_gcServer=1
dotnet test --no-build --configuration DebugOpt Content.IntegrationTests/Content.IntegrationTests.csproj -- NUnit.ConsoleOut=0 NUnit.MapWarningTo=Failed NUnit.TestOutputXml="logs" NUnit.WorkDirectory="$(pwd)/test_results" dotnet test --no-build --configuration DebugOpt Content.IntegrationTests/Content.IntegrationTests.csproj -- NUnit.ConsoleOut=0 NUnit.MapWarningTo=Failed
- name: Archive NUnit3 test results.
if: always()
uses: actions/upload-artifact@v4
with:
name: nunit3-results-${{ matrix.os }}
path: test_results/*
retention-days: 7
compression-level: 9
ci-success: ci-success:
name: Build & Test Debug name: Build & Test Debug
needs: needs:
-1
View File
@@ -29,7 +29,6 @@ jobs:
run: | run: |
cd RobustToolbox cd RobustToolbox
git fetch --depth=1 git fetch --depth=1
rm Robust.Shared.Tests/Networking/NetEncryptionDoSTest.cs # DeltaV - work around bug in the backport
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
+6 -13
View File
@@ -48,11 +48,10 @@ jobs:
cd RobustToolbox/ cd RobustToolbox/
git submodule update --init --recursive git submodule update --init --recursive
# ubuntu-latest has .NET 10 - name: Setup .NET Core
# - name: Setup .NET Core uses: actions/setup-dotnet@v4.1.0
# uses: actions/setup-dotnet@v4.1.0 with:
# with: dotnet-version: 10.0.x
# dotnet-version: 10.0.x
- name: Install dependencies - name: Install dependencies
run: dotnet restore run: dotnet restore
@@ -61,13 +60,7 @@ jobs:
run: dotnet build Content.Packaging --configuration Release --no-restore /m run: dotnet build Content.Packaging --configuration Release --no-restore /m
- name: Package server - name: Package server
run: dotnet run --project Content.Packaging server --log-build --platform win-x64 --platform win-arm64 --platform linux-x64 --platform linux-arm64 --platform osx-x64 --platform osx-arm64 run: dotnet run --project Content.Packaging server --platform win-x64 --platform win-arm64 --platform linux-x64 --platform linux-arm64 --platform osx-x64 --platform osx-arm64
- name: Package client - name: Package client
run: dotnet run --project Content.Packaging client --log-build --no-wipe-release run: dotnet run --project Content.Packaging client --no-wipe-release
- uses: actions/upload-artifact@v4
with:
name: binlogs
path: release/*.binlog
retention-days: 7
-1
View File
@@ -23,7 +23,6 @@ jobs:
run: | run: |
cd RobustToolbox/ cd RobustToolbox/
git submodule update --init --recursive git submodule update --init --recursive
rm Robust.Shared.Tests/Networking/NetEncryptionDoSTest.cs # DeltaV - work around bug in the backport
- name: Setup .NET Core - name: Setup .NET Core
uses: actions/setup-dotnet@v4.1.0 uses: actions/setup-dotnet@v4.1.0
with: with:
+1 -1
View File
@@ -269,7 +269,7 @@ Sentencing modifiers are to be applied by the sentencing officer, judge, or arbi
| style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]] | style="border: 1px solid black;" | [[File:SL_BreakingAndEntering.png]]
! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering ! style="border: 1px solid black;" | {{anchor|Breaking and Entering}}Breaking and Entering
| style="border: 1px solid black;" | 5 minutes | style="border: 1px solid black;" | 5 minutes
| style="border: 1px solid black;" | To break and enter into an area where one is not authorised nor invited. | style="border: 1px solid black;" | To break and enter into a high security area where one is not authorised nor invited, with intent to commit a crime within.
|- |-
| style="border: 1px solid black;" | 203 | style="border: 1px solid black;" | 203
| style="border: 1px solid black;" | [[File:SL_Rioting.png]] | style="border: 1px solid black;" | [[File:SL_Rioting.png]]
+2 -34
View File
@@ -31,7 +31,7 @@ Examples of Pets include, but are not limited to:
:* hand-held "personal AI" devices :* hand-held "personal AI" devices
== Synthetic == == Synthetic ==
Any Entity consisting primarily of non-organic matter, designed and/or constructed by NanoTrasen or its Employees for the purpose of performing tasks aboard a Space Station. Synthetics are bound by software limitations, typically referred to as Synthetic or Silicon Laws, and physically incorporated into their chassis. Any damage to a Synthetic- including un-authorized modification of their Laws- may leave the offender liable to charges of Sabotage. Any Entity consisting primarily of non-organic matter, designed and/or constructed by NanoTrasen or its Employees for the purpose of performing tasks aboard a Space Station. Synthetics are bound by software limitations, typically referred to as Synthetic or Silicon Laws, and physically incorporated into their chassis. Any damage to a Synthetic- including un-authorized modification of their Laws- may leave the offender liable to charges of Sabotage. Irrevocable destruction of a Synthetic may leave the offender liable to the crime of Grand Sabotage.
Examples of Synthetics include, but are not limited to: Examples of Synthetics include, but are not limited to:
:* Crew-constructed Cyborgs :* Crew-constructed Cyborgs
:* The Station AI :* The Station AI
@@ -86,7 +86,7 @@ Any individual requiring any above item in the course of their job, or for regul
Should the holder commit crimes through the use of such authorized items, the authorizing party may be found guilty of Accessory to said crimes, in addition to Endangerment and Abuse of Power where applicable. The Commanding Officer is also authorized to supervise the approval and revocation of documentation as they see fit. Should the holder commit crimes through the use of such authorized items, the authorizing party may be found guilty of Accessory to said crimes, in addition to Endangerment and Abuse of Power where applicable. The Commanding Officer is also authorized to supervise the approval and revocation of documentation as they see fit.
== Controlled Substances == == Controlled Substances ==
Any individual requiring a Controlled Substance in the course of their job, or for regular function, must request access to a prescription or special dispensation from the Chief Medical Officer. Medical personnel are also permitted to [[Standard Operating Procedure#Prescribing Medication|prescribe medication]] for chronic illnesses or mental health. Any such document must state the substance(s) being prescribed, the amount(s) where applicable, and the stamp and signature of the authorizing party. Any individual requiring a Controlled Substance in the course of their job, or for regular function, must request access to a perscription or special dispensation from the Chief Medical Officer. The Psychologist is also permitted to grant prescriptions for psychoactive drugs. Any such document must state the substance(s) being prescribed, the amount(s) where applicable, and the stamp and signature of the authorizing party.
A list of such Substances follow: A list of such Substances follow:
@@ -370,38 +370,6 @@ Given good medical reason and with documentation, the Chief Medical Officer of a
In order for a medical evaluation to be considered lawfully applicable, the CMO and relevant head of department must both agree that the specified crewmate is unfit for duty in order to proceed with the demotion. In the case of the Commanding Officer, Central Command should be contacted as soon as possible. In order for a medical evaluation to be considered lawfully applicable, the CMO and relevant head of department must both agree that the specified crewmate is unfit for duty in order to proceed with the demotion. In the case of the Commanding Officer, Central Command should be contacted as soon as possible.
==Prescribing Medication==
In the event that a patient requires stronger medication outside of medbay, Medical personnel have the ability to prescribe medicine to a patient. Prescriptions must be written on valid prescription paper, as well as signed and stamped with a valid medical personnel's stamp and signature. Any such document must state the substance(s) being prescribed as well as the amount(s) where applicable. The chief medical officer also has the right to revoke any prescription with valid medical reasoning. Prescriptions are not valid if they are both intended for and signed and stamped by the same person (e.g. You cannot prescribe something to yourself as a doctor).
=== Prescription Medicine ===
Prescriptions that are valid with any medical personnel's stamp and signature are as follows:
* diphenylmethylamine
* equilibrazine
* tranquinase
* neurozenium
* serenitol
* soretizone
Highly-addictive mental health prescriptions that are only valid with the chief medical officer's or psychologist's stamp and signature are as follows:
* blissifylovene
* happiness
Highly-addictive or potentially-harmful prescriptions that are only valid with the chief medical officer's stamp and signature are as follows:
* agonolexyne
* desoxyephedrine
* nocturine
=== Dangerous Chemical Authorizations ===
Other dangerous chemicals that may be authorized and given out ONLY with valid written permission with the chief medical officer's stamp are as follows:
* mindbreaker, heartbreaker, and mute toxins
* amatoxin and other dangerous poisons
* phlogiston, napalm, and chlorine trifluoride
* vestine and romerol
* Any other [[Standard Operating Procedure#Controlled Substances|controlled substances]] not listed here
The chief medical officer is also required to inform and/or forward authorization of any dangerous chemical to Security.
==Quarantine== ==Quarantine==
Medical staff are afforded the right to quarantine individuals that currently suffer from infectious or contagious diseases, and may confine them within pre-designated quarantine areas in the course of treatment, until the point that they are no longer infectious or harmful to the rest of the crew. Medical staff are afforded the right to quarantine individuals that currently suffer from infectious or contagious diseases, and may confine them within pre-designated quarantine areas in the course of treatment, until the point that they are no longer infectious or harmful to the rest of the crew.
+18 -78
View File
@@ -73,88 +73,28 @@ If you are adding a lot of C# code, then take advantage of partial classes. Put
Otherwise, **add comments on or around any changed lines.** Otherwise, **add comments on or around any changed lines.**
### Single-Line Changes A comment on a new imported namespace:
Format should look like this.
```cs ```cs
/* Importing Namespaces - Include optional comment if its not obvious what its being used for. */ using Content.Server.Psionics.Glimmer; // DeltaV
using Content.Server._DV.Psionics.Glimmer; // DeltaV
using Content.Shared.Damage.Systems; // DeltaV - Addition of HandHeldArmor
/* Changing an upstream line - Same line as the change */
if (!TryComp<EyeComponent>(ent, out var eye) || _disabled) // DeltaV - check if disabled
/* Adding - Either same line or above the line. */
EnsureComp<PotentialPsionicComponent>(entity); // Deltav - Psionics
/* "Deleting" - Don't actually delete, just comment out and say why. This only applies to upstream code. */
// args.StatusIcons.Add(_prototype.Index(component.Icon)); // DeltaV - commented out. status icon now added above
``` ```
> * Its pretty obvious in the example above that importing `Content.Server._DV.Psionics.Glimmer` means we'll be interacting with glimmer so putting `// DeltaV - Add Glimmer` is needlessly redundant. A pair of comments enclosing a block of added code:
> * It's not as obvious what the `Content.Shared.Damage.Systems` namespace is used for, since its so broad, so adding a comment what feature is using it helps.
> * Actual code changes should almost always include the comment after ``// DeltaV`.
### Multi-Line Changes
Depending on how much you are editing, putting a comment on EACH line may be excessive, so if you have a larger block of code you are changing, denote it like so:
```cs ```cs
// BEGIN DeltaV - Remove innate radio and radios from pockets private EntityUid Slice(...)
for (var i = 1; i <= 4; i++) // Arachnids have 4 pockets
{ {
if (_inventory.TryGetSlotEntity(target, $"pocket{i}", out var headset) && HasComp<HeadsetComponent>(headset)) ...
_inventory.TryUnequip(target, $"pocket{i}", true, true);
_transform.SetLocalRotation(sliceUid, 0);
// DeltaV - start of deep frier stuff
var slicedEv = new FoodSlicedEvent(user, uid, sliceUid);
RaiseLocalEvent(uid, ref slicedEv);
// DeltaV - end of deep frier stuff
...
} }
RemComp<ActiveRadioComponent>(target); // If the zombie has an innate radio, get rid of it.
// END DeltaV
``` ```
> * Denoting these with a BEGIN and END clearly shows they are block of code without having to read the entire comment. This makes it easier to tell when you're dealing with single-line comments versus a block with merging in conflicts.
> * Case and order of the first two words is less of a concern. `// DeltaV Begin` or `// Begin DeltaV` will work fine too.
> * Try to make your blocks as small as possible, but use your discretion.
> * If you deleting multiple lines, use line comments (``//``) if its a few lines but if its a larger block (like commenting out an entire function), it is preferable to use block comments (`/* */`).
#### Soft Exceptions to the Multi-Line "Rules"
Some multi-line changes can use a single-line comment in certain scenarios. But if you are UNSURE, just use `// BEGIN DeltaV` and `// END DeltaV` comments like the previous section does and it'll be fine.
I'll give some examples.
```cs
/* This change comments out 3 lines but only needs a single line comment because commenting out the if statement implies that its logic will be commented out too. */
// if (obj.WasModified<TraitPrototype>()) // DeltaV - Refreshed in TraitsTab
// {
// _profileEditor.RefreshTraits();
// }
/* Same principle here. This adds two lines but the if statement implies the next line so commenting both lines isn't really needed. */
if (_flight.IsFlying(entity.Owner)) // DeltaV - Harpy Flight
return true;
```
### New Methods or Component Variables
Sometimes, you'll need to implement a whole new method or component variable and instead of wrapping it in `// BEGIN DeltaV` and `// END DeltaV`, you can just denote that it's a DeltaV function in the summary block before the function. This denotes the WHOLE function as a DeltaV addition.
```cs
/* New Method Example */
/// <summary>
/// DeltaV - Handle revealing ninja if cloaked when attacked by a hitscan attack.
/// </summary>
private void OnNinjaAttacked(Entity<SpaceNinjaComponent> ent, ref DamageChangedEvent args)
{
...
}
/* New Component Variable Example */
/// <summary>
/// DeltaV - If disabled the action will not disable when no charges remain. Use if you want to handle no charges differently.
/// </summary>
[DataField]
public bool DisableWhenEmpty = true;
```
In short:
* Use `// BEGIN DeltaV` and `// END Delta` to denote a *block* of changes.
* Keep blocks as small as possible.
* Use `// DeltaV` on or before the line if its not a block of changes.
* Use exceptions when they make sense.
### Changing Upstream Localization Fluent .ftl files ### Changing Upstream Localization Fluent .ftl files
**Move all changed locale strings to a new DeltaV file** - use a `.ftl` file in the `_DV` folder. Comment out the old strings in the upstream file, and explain that they were moved. **Move all changed locale strings to a new DeltaV file** - use a `.ftl` file in the `_DV` folder. Comment out the old strings in the upstream file, and explain that they were moved.
@@ -164,10 +104,10 @@ Example:
Commented out old string in `Resources\Locale\en-US\xenoarchaeology\artifact-analyzer.ftl` Commented out old string in `Resources\Locale\en-US\xenoarchaeology\artifact-analyzer.ftl`
``` ```
# DeltaV - moved to _DV file # DeltaV - moved to _DV file
# analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state -> #analysis-console-info-effect-value = [font="Monospace" size=11][color=gray]{ $state ->
# [true] {$info} # [true] {$info}
# *[false] Unlock nodes to gain info # *[false] Unlock nodes to gain info
# }[/color][/font] #}[/color][/font]
``` ```
The new version of the string in `Resources\Locale\en-US\_DV\xenoarchaeology\artifact-analyzer.ftl` The new version of the string in `Resources\Locale\en-US\_DV\xenoarchaeology\artifact-analyzer.ftl`
@@ -1,6 +1,5 @@
#nullable enable #nullable enable
using System; using System;
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@@ -45,7 +44,7 @@ public class ComponentQueryBenchmark
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(typeof(QueryBenchSystem).Assembly); PoolManager.Startup(typeof(QueryBenchSystem).Assembly);
_pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
_entMan = _pair.Server.ResolveDependency<IEntityManager>(); _entMan = _pair.Server.ResolveDependency<IEntityManager>();
_itemQuery = _entMan.GetEntityQuery<ItemComponent>(); _itemQuery = _entMan.GetEntityQuery<ItemComponent>();
@@ -7,7 +7,6 @@
<AllowUnsafeBlocks>true</AllowUnsafeBlocks> <AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<IsTestingPlatformApplication>false</IsTestingPlatformApplication> <IsTestingPlatformApplication>false</IsTestingPlatformApplication>
<Nullable>disable</Nullable> <Nullable>disable</Nullable>
<DefineConstants>$(DefineConstants);ALLOW_BAD_PRACTICES</DefineConstants>
</PropertyGroup> </PropertyGroup>
<Import Project="../MSBuild/Content.props" /> <Import Project="../MSBuild/Content.props" />
<ItemGroup> <ItemGroup>
+3 -3
View File
@@ -1,11 +1,11 @@
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using BenchmarkDotNet.Diagnosers;
using Content.IntegrationTests; using Content.IntegrationTests;
using Content.IntegrationTests.Pair; using Content.IntegrationTests.Pair;
using Content.Server.Atmos.Components;
using Content.Server.Atmos.EntitySystems; using Content.Server.Atmos.EntitySystems;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Robust.Shared; using Robust.Shared;
using Robust.Shared.Analyzers; using Robust.Shared.Analyzers;
@@ -69,7 +69,7 @@ public class DeltaPressureBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
var server = _pair.Server; var server = _pair.Server;
var mapdata = await _pair.CreateTestMap(); var mapdata = await _pair.CreateTestMap();
+1 -2
View File
@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
@@ -70,7 +69,7 @@ public class DestructibleBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
var server = _pair.Server; var server = _pair.Server;
_entMan = server.ResolveDependency<IEntityManager>(); _entMan = server.ResolveDependency<IEntityManager>();
@@ -1,5 +1,4 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
@@ -61,7 +60,7 @@ public class DeviceNetworkingBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(typeof(DeviceNetworkingBenchmark).Assembly); PoolManager.Startup(typeof(DeviceNetworkingBenchmark).Assembly);
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
var server = _pair.Server; var server = _pair.Server;
await server.WaitPost(() => await server.WaitPost(() =>
+1 -2
View File
@@ -1,4 +1,3 @@
using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
@@ -52,7 +51,7 @@ public class GasReactionBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
var server = _pair.Server; var server = _pair.Server;
// Create test map and grid // Create test map and grid
+2 -3
View File
@@ -1,5 +1,4 @@
using System.IO; using System.Threading.Tasks;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
using Content.IntegrationTests.Pair; using Content.IntegrationTests.Pair;
@@ -28,7 +27,7 @@ public class HeatCapacityBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
await _pair.Connect(); await _pair.Connect();
_cEntMan = _pair.Client.ResolveDependency<IEntityManager>(); _cEntMan = _pair.Client.ResolveDependency<IEntityManager>();
_sEntMan = _pair.Server.ResolveDependency<IEntityManager>(); _sEntMan = _pair.Server.ResolveDependency<IEntityManager>();
+1 -2
View File
@@ -1,6 +1,5 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@@ -30,7 +29,7 @@ public class MapLoadBenchmark
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
var server = _pair.Server; var server = _pair.Server;
Paths = server.ResolveDependency<IPrototypeManager>() Paths = server.ResolveDependency<IPrototypeManager>()
+1 -2
View File
@@ -1,6 +1,5 @@
#nullable enable #nullable enable
using System; using System;
using System.IO;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@@ -51,7 +50,7 @@ public class PvsBenchmark
#endif #endif
PoolManager.Startup(); PoolManager.Startup();
_pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
_entMan = _pair.Server.ResolveDependency<IEntityManager>(); _entMan = _pair.Server.ResolveDependency<IEntityManager>();
_pair.Server.CfgMan.SetCVar(CVars.NetPVS, true); _pair.Server.CfgMan.SetCVar(CVars.NetPVS, true);
_pair.Server.CfgMan.SetCVar(CVars.ThreadParallelCount, 0); _pair.Server.CfgMan.SetCVar(CVars.ThreadParallelCount, 0);
+1 -2
View File
@@ -1,5 +1,4 @@
#nullable enable #nullable enable
using System.IO;
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
@@ -22,7 +21,7 @@ public class RaiseEventBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(typeof(BenchSystem).Assembly); PoolManager.Startup(typeof(BenchSystem).Assembly);
_pair = PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)).GetAwaiter().GetResult(); _pair = PoolManager.GetServerClient().GetAwaiter().GetResult();
var entMan = _pair.Server.EntMan; var entMan = _pair.Server.EntMan;
var fact = _pair.Server.ResolveDependency<IComponentFactory>(); var fact = _pair.Server.ResolveDependency<IComponentFactory>();
var bus = (EntityEventBus)entMan.EventBus; var bus = (EntityEventBus)entMan.EventBus;
@@ -1,5 +1,4 @@
using System.IO; using System.Threading.Tasks;
using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Content.IntegrationTests; using Content.IntegrationTests;
using Content.IntegrationTests.Pair; using Content.IntegrationTests.Pair;
@@ -37,7 +36,7 @@ public class SpawnEquipDeleteBenchmark
{ {
ProgramShared.PathOffset = "../../../../"; ProgramShared.PathOffset = "../../../../";
PoolManager.Startup(); PoolManager.Startup();
_pair = await PoolManager.GetServerClient(testContext: new ExternalTestContext("Benchmark", StreamWriter.Null)); _pair = await PoolManager.GetServerClient();
var server = _pair.Server; var server = _pair.Server;
var mapData = await _pair.CreateTestMap(); var mapData = await _pair.CreateTestMap();
@@ -1,39 +0,0 @@
using Content.Client.Overlays;
using Content.Shared.Access.Systems;
using Content.Shared.StatusIcon;
using Content.Shared.StatusIcon.Components;
using Robust.Shared.Prototypes;
namespace Content.Client.Access.Systems;
public sealed class JobStatusSystem : SharedJobStatusSystem
{
[Dependency] private readonly ShowJobIconsSystem _showJobIcons = default!;
[Dependency] private readonly ShowCrewIconsSystem _showCrewIcons = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
private static readonly ProtoId<SecurityIconPrototype> CrewBorderIcon = "CrewBorderIcon";
private static readonly ProtoId<SecurityIconPrototype> CrewUncertainBorderIcon = "CrewUncertainBorderIcon";
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<JobStatusComponent, GetStatusIconsEvent>(OnGetStatusIconsEvent);
}
// show the status icons if the player has the correponding HUDs
private void OnGetStatusIconsEvent(Entity<JobStatusComponent> ent, ref GetStatusIconsEvent ev)
{
if (_showJobIcons.IsActive && ent.Comp.JobStatusIcon != null)
ev.StatusIcons.Add(_prototype.Index(ent.Comp.JobStatusIcon));
if (_showCrewIcons.IsActive)
{
if (_showCrewIcons.UncertainCrewBorder)
ev.StatusIcons.Add(_prototype.Index(CrewUncertainBorderIcon));
else if (ent.Comp.IsCrew)
ev.StatusIcons.Add(_prototype.Index(CrewBorderIcon));
}
}
}
@@ -2,9 +2,11 @@ using System.Linq; // DeltaV
using Content.Shared.Access; using Content.Shared.Access;
using Content.Shared.Access.Components; using Content.Shared.Access.Components;
using Content.Shared.Access.Systems; using Content.Shared.Access.Systems;
using Content.Shared.CCVar;
using Content.Shared.Containers.ItemSlots; using Content.Shared.Containers.ItemSlots;
using Content.Shared.CrewManifest; using Content.Shared.CrewManifest;
using Content.Shared.Roles; using Content.Shared.Roles;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using static Content.Shared.Access.Components.IdCardConsoleComponent; using static Content.Shared.Access.Components.IdCardConsoleComponent;
@@ -13,13 +15,21 @@ namespace Content.Client.Access.UI
public sealed class IdCardConsoleBoundUserInterface : BoundUserInterface public sealed class IdCardConsoleBoundUserInterface : BoundUserInterface
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly IConfigurationManager _cfgManager = default!;
private readonly SharedIdCardConsoleSystem _idCardConsoleSystem = default!; private readonly SharedIdCardConsoleSystem _idCardConsoleSystem = default!;
private IdCardConsoleWindow? _window; private IdCardConsoleWindow? _window;
// CCVar.
private int _maxNameLength;
private int _maxIdJobLength;
public IdCardConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey) public IdCardConsoleBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
_idCardConsoleSystem = EntMan.System<SharedIdCardConsoleSystem>(); _idCardConsoleSystem = EntMan.System<SharedIdCardConsoleSystem>();
_maxNameLength =_cfgManager.GetCVar(CCVars.MaxNameLength);
_maxIdJobLength = _cfgManager.GetCVar(CCVars.MaxIdJobLength);
} }
protected override void Open() protected override void Open()
@@ -69,6 +79,12 @@ namespace Content.Client.Access.UI
public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, ProtoId<JobPrototype> newJobPrototype) public void SubmitData(string newFullName, string newJobTitle, List<ProtoId<AccessLevelPrototype>> newAccessList, ProtoId<JobPrototype> newJobPrototype)
{ {
if (newFullName.Length > _maxNameLength)
newFullName = newFullName[.._maxNameLength];
if (newJobTitle.Length > _maxIdJobLength)
newJobTitle = newJobTitle[.._maxIdJobLength];
SendMessage(new WriteToTargetIdMessage( SendMessage(new WriteToTargetIdMessage(
newFullName, newFullName,
newJobTitle, newJobTitle,
@@ -23,6 +23,10 @@ namespace Content.Client.Access.UI
private readonly IdCardConsoleBoundUserInterface _owner; private readonly IdCardConsoleBoundUserInterface _owner;
// CCVar.
private int _maxNameLength;
private int _maxIdJobLength;
private AccessLevelControl _accessButtons = new(); private AccessLevelControl _accessButtons = new();
private readonly List<string> _jobPrototypeIds = new(); private readonly List<string> _jobPrototypeIds = new();
@@ -42,8 +46,11 @@ namespace Content.Client.Access.UI
_owner = owner; _owner = owner;
_maxNameLength = _cfgManager.GetCVar(CCVars.MaxNameLength);
_maxIdJobLength = _cfgManager.GetCVar(CCVars.MaxIdJobLength);
FullNameLineEdit.OnTextEntered += _ => SubmitData(); FullNameLineEdit.OnTextEntered += _ => SubmitData();
FullNameLineEdit.IsValid = s => s.Length <= _cfgManager.GetCVar(CCVars.MaxNameLength); FullNameLineEdit.IsValid = s => s.Length <= _maxNameLength;
FullNameLineEdit.OnTextChanged += _ => FullNameLineEdit.OnTextChanged += _ =>
{ {
FullNameSaveButton.Disabled = FullNameSaveButton.Text == _lastFullName; FullNameSaveButton.Disabled = FullNameSaveButton.Text == _lastFullName;
@@ -51,7 +58,7 @@ namespace Content.Client.Access.UI
FullNameSaveButton.OnPressed += _ => SubmitData(); FullNameSaveButton.OnPressed += _ => SubmitData();
JobTitleLineEdit.OnTextEntered += _ => SubmitData(); JobTitleLineEdit.OnTextEntered += _ => SubmitData();
JobTitleLineEdit.IsValid = s => s.Length <= _cfgManager.GetCVar(CCVars.MaxIdJobLength); JobTitleLineEdit.IsValid = s => s.Length <= _maxIdJobLength;
JobTitleLineEdit.OnTextChanged += _ => JobTitleLineEdit.OnTextChanged += _ =>
{ {
JobTitleSaveButton.Disabled = JobTitleLineEdit.Text == _lastJobTitle; JobTitleSaveButton.Disabled = JobTitleLineEdit.Text == _lastJobTitle;
@@ -8,14 +8,12 @@ using Content.Shared.CCVar;
using Content.Shared.Ghost; using Content.Shared.Ghost;
using Content.Shared.Mind; using Content.Shared.Mind;
using Content.Shared.Roles; using Content.Shared.Roles;
using Content.Shared.SSDIndicator; // DeltaV - SSD time indicator
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.ResourceManagement; using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Enums; using Robust.Shared.Enums;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration; namespace Content.Client.Administration;
@@ -28,7 +26,6 @@ internal sealed class AdminNameOverlay : Overlay
private readonly IUserInterfaceManager _userInterfaceManager; private readonly IUserInterfaceManager _userInterfaceManager;
private readonly SharedRoleSystem _roles; private readonly SharedRoleSystem _roles;
private readonly IPrototypeManager _prototypeManager; private readonly IPrototypeManager _prototypeManager;
private readonly IGameTiming _timing; // DeltaV - Add timing
private readonly Font _font; private readonly Font _font;
private readonly Font _fontBold; private readonly Font _fontBold;
private AdminOverlayAntagFormat _overlayFormat; private AdminOverlayAntagFormat _overlayFormat;
@@ -56,8 +53,7 @@ internal sealed class AdminNameOverlay : Overlay
IUserInterfaceManager userInterfaceManager, IUserInterfaceManager userInterfaceManager,
IConfigurationManager config, IConfigurationManager config,
SharedRoleSystem roles, SharedRoleSystem roles,
IPrototypeManager prototypeManager, IPrototypeManager prototypeManager)
IGameTiming timing) // DeltaV - Add timing
{ {
_system = system; _system = system;
_entityManager = entityManager; _entityManager = entityManager;
@@ -66,7 +62,6 @@ internal sealed class AdminNameOverlay : Overlay
_userInterfaceManager = userInterfaceManager; _userInterfaceManager = userInterfaceManager;
_roles = roles; _roles = roles;
_prototypeManager = prototypeManager; _prototypeManager = prototypeManager;
_timing = timing; // DeltaV - Add timing
ZIndex = 200; ZIndex = 200;
// Setting these to a specific ttf would break the antag symbols // Setting these to a specific ttf would break the antag symbols
_font = resourceCache.NotoStack(); _font = resourceCache.NotoStack();
@@ -236,18 +231,6 @@ internal sealed class AdminNameOverlay : Overlay
currentOffset += lineoffset; currentOffset += lineoffset;
} }
// DeltaV - SSD Time START
if (_entityManager.TryGetComponent<SSDIndicatorComponent>(entity, out var ssdIndicator)
&& ssdIndicator.SsdSince is {} ssdSince)
{
color = Color.MediumPurple;
color.A = alpha;
var ssdText = Loc.GetString("admin-overlay-ssd-time", ("time", (_timing.CurTime - ssdSince).ToString("%hh':'mm':'ss")));
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, ssdText, uiScale, color);
currentOffset += lineoffset;
}
// DeltaV END
// Determine antag symbol // Determine antag symbol
string? symbol; string? symbol;
switch (_overlaySymbolStyle) switch (_overlaySymbolStyle)
@@ -5,7 +5,6 @@ using Robust.Client.ResourceManagement;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Robust.Shared.Configuration; using Robust.Shared.Configuration;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
using Robust.Shared.Timing; // DeltaV - SSD time indicator
namespace Content.Client.Administration.Systems namespace Content.Client.Administration.Systems
{ {
@@ -20,7 +19,6 @@ namespace Content.Client.Administration.Systems
[Dependency] private readonly IConfigurationManager _configurationManager = default!; [Dependency] private readonly IConfigurationManager _configurationManager = default!;
[Dependency] private readonly SharedRoleSystem _roles = default!; [Dependency] private readonly SharedRoleSystem _roles = default!;
[Dependency] private readonly IPrototypeManager _proto = default!; [Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IGameTiming _timing = default!; // DeltaV - added for SSD time indicator
private AdminNameOverlay _adminNameOverlay = default!; private AdminNameOverlay _adminNameOverlay = default!;
@@ -38,8 +36,7 @@ namespace Content.Client.Administration.Systems
_userInterfaceManager, _userInterfaceManager,
_configurationManager, _configurationManager,
_roles, _roles,
_proto, _proto);
_timing); // DeltaV - Add timing
_adminManager.AdminStatusUpdated += OnAdminStatusUpdated; _adminManager.AdminStatusUpdated += OnAdminStatusUpdated;
} }
@@ -1,5 +1,4 @@
using System.Linq; using System.Numerics;
using System.Numerics;
using Content.Client.Administration.UI.BanList.Bans; using Content.Client.Administration.UI.BanList.Bans;
using Content.Client.Administration.UI.BanList.RoleBans; using Content.Client.Administration.UI.BanList.RoleBans;
using Content.Client.Eui; using Content.Client.Eui;
@@ -74,7 +73,7 @@ public sealed class BanListEui : BaseEui
return date.ToString("MM/dd/yyyy h:mm tt"); return date.ToString("MM/dd/yyyy h:mm tt");
} }
public static void SetData<T>(IBanListLine<T> line, SharedBan ban) where T : SharedBan public static void SetData<T>(IBanListLine<T> line, SharedServerBan ban) where T : SharedServerBan
{ {
line.Reason.Text = ban.Reason; line.Reason.Text = ban.Reason;
line.BanTime.Text = FormatDate(ban.BanTime); line.BanTime.Text = FormatDate(ban.BanTime);
@@ -95,20 +94,20 @@ public sealed class BanListEui : BaseEui
line.BanningAdmin.Text = ban.BanningAdminName; line.BanningAdmin.Text = ban.BanningAdminName;
} }
private void OnLineIdsClicked<T>(IBanListLine<T> line) where T : SharedBan private void OnLineIdsClicked<T>(IBanListLine<T> line) where T : SharedServerBan
{ {
_popup?.Close(); _popup?.Close();
_popup = null; _popup = null;
var ban = line.Ban; var ban = line.Ban;
var id = ban.Id == null ? string.Empty : Loc.GetString("ban-list-id", ("id", ban.Id.Value)); var id = ban.Id == null ? string.Empty : Loc.GetString("ban-list-id", ("id", ban.Id.Value));
var ip = ban.Addresses.Length == 0 var ip = ban.Address == null
? string.Empty ? string.Empty
: Loc.GetString("ban-list-ip", ("ip", string.Join(',', ban.Addresses.Select(a => a.address)))); : Loc.GetString("ban-list-ip", ("ip", ban.Address.Value.address));
var hwid = ban.HWIds.Length == 0 ? string.Empty : Loc.GetString("ban-list-hwid", ("hwid", string.Join(',', ban.HWIds))); var hwid = ban.HWId == null ? string.Empty : Loc.GetString("ban-list-hwid", ("hwid", ban.HWId));
var guid = ban.UserIds.Length == 0 var guid = ban.UserId == null
? string.Empty ? string.Empty
: Loc.GetString("ban-list-guid", ("guid", string.Join(',', ban.UserIds))); : Loc.GetString("ban-list-guid", ("guid", ban.UserId.Value.ToString()));
_popup = new BanListIdsPopup(id, ip, hwid, guid); _popup = new BanListIdsPopup(id, ip, hwid, guid);
@@ -16,7 +16,7 @@ public sealed partial class BanListControl : Control
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
} }
public void SetBans(List<SharedBan> bans) public void SetBans(List<SharedServerBan> bans)
{ {
for (var i = Bans.ChildCount - 1; i >= 1; i--) for (var i = Bans.ChildCount - 1; i >= 1; i--)
{ {
@@ -7,13 +7,13 @@ using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Administration.UI.BanList.Bans; namespace Content.Client.Administration.UI.BanList.Bans;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class BanListLine : BoxContainer, IBanListLine<SharedBan> public sealed partial class BanListLine : BoxContainer, IBanListLine<SharedServerBan>
{ {
public SharedBan Ban { get; } public SharedServerBan Ban { get; }
public event Action<BanListLine>? IdsClicked; public event Action<BanListLine>? IdsClicked;
public BanListLine(SharedBan ban) public BanListLine(SharedServerBan ban)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -3,7 +3,7 @@ using Robust.Client.UserInterface.Controls;
namespace Content.Client.Administration.UI.BanList; namespace Content.Client.Administration.UI.BanList;
public interface IBanListLine<T> where T : SharedBan public interface IBanListLine<T> where T : SharedServerBan
{ {
T Ban { get; } T Ban { get; }
Label Reason { get; } Label Reason { get; }
@@ -16,7 +16,7 @@ public sealed partial class RoleBanListControl : Control
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
} }
public void SetRoleBans(List<SharedBan> bans) public void SetRoleBans(List<SharedServerRoleBan> bans)
{ {
for (var i = RoleBans.ChildCount - 1; i >= 1; i--) for (var i = RoleBans.ChildCount - 1; i >= 1; i--)
{ {
@@ -7,13 +7,13 @@ using static Robust.Client.UserInterface.Controls.BaseButton;
namespace Content.Client.Administration.UI.BanList.RoleBans; namespace Content.Client.Administration.UI.BanList.RoleBans;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class RoleBanListLine : BoxContainer, IBanListLine<SharedBan> public sealed partial class RoleBanListLine : BoxContainer, IBanListLine<SharedServerRoleBan>
{ {
public SharedBan Ban { get; } public SharedServerRoleBan Ban { get; }
public event Action<RoleBanListLine>? IdsClicked; public event Action<RoleBanListLine>? IdsClicked;
public RoleBanListLine(SharedBan ban) public RoleBanListLine(SharedServerRoleBan ban)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
@@ -21,7 +21,7 @@ public sealed partial class RoleBanListLine : BoxContainer, IBanListLine<SharedB
IdsHidden.OnPressed += IdsPressed; IdsHidden.OnPressed += IdsPressed;
BanListEui.SetData(this, ban); BanListEui.SetData(this, ban);
Role.Text = string.Join(", ", ban.Roles ?? []); Role.Text = ban.Role;
} }
private void IdsPressed(ButtonEventArgs buttonEventArgs) private void IdsPressed(ButtonEventArgs buttonEventArgs)
@@ -70,7 +70,7 @@ public sealed partial class AdminNotesLine : BoxContainer
TimeLabel.Text = Note.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"); TimeLabel.Text = Note.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss");
ServerLabel.Text = Note.ServerName ?? "Unknown"; ServerLabel.Text = Note.ServerName ?? "Unknown";
RoundLabel.Text = Note.Rounds.Length == 0 ? "Unknown round" : "Round " + string.Join(',', Note.Rounds); RoundLabel.Text = Note.Round == null ? "Unknown round" : "Round " + Note.Round;
AdminLabel.Text = Note.CreatedByName; AdminLabel.Text = Note.CreatedByName;
PlaytimeLabel.Text = $"{Note.PlaytimeAtNote.TotalHours: 0.0}h"; PlaytimeLabel.Text = $"{Note.PlaytimeAtNote.TotalHours: 0.0}h";
@@ -143,12 +143,7 @@ public sealed partial class AdminNotesLine : BoxContainer
private string FormatRoleBanMessage() private string FormatRoleBanMessage()
{ {
var rolesText = string.Join( var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {string.Join(", ", Note.BannedRoles ?? new[] { "unknown" })} ");
", ",
// Explicit cast here to avoid sandbox violation.
(IEnumerable<BanRoleDef>?)Note.BannedRoles ?? [new BanRoleDef("what", "You should not be seeing this")]);
var banMessage = new StringBuilder($"{Loc.GetString("admin-notes-banned-from")} {rolesText} ");
return FormatBanMessageCommon(banMessage); return FormatBanMessageCommon(banMessage);
} }
@@ -32,9 +32,9 @@ public sealed partial class AdminNotesLinePopup : Popup
IdLabel.Text = Loc.GetString("admin-notes-id", ("id", note.Id)); IdLabel.Text = Loc.GetString("admin-notes-id", ("id", note.Id));
TypeLabel.Text = Loc.GetString("admin-notes-type", ("type", note.NoteType)); TypeLabel.Text = Loc.GetString("admin-notes-type", ("type", note.NoteType));
SeverityLabel.Text = Loc.GetString("admin-notes-severity", ("severity", note.NoteSeverity ?? NoteSeverity.None)); SeverityLabel.Text = Loc.GetString("admin-notes-severity", ("severity", note.NoteSeverity ?? NoteSeverity.None));
RoundIdLabel.Text = note.Rounds.Length == 0 RoundIdLabel.Text = note.Round == null
? Loc.GetString("admin-notes-round-id-unknown") ? Loc.GetString("admin-notes-round-id-unknown")
: Loc.GetString("admin-notes-round-id", ("id", string.Join(',', note.Rounds))); : Loc.GetString("admin-notes-round-id", ("id", note.Round));
CreatedByLabel.Text = Loc.GetString("admin-notes-created-by", ("author", note.CreatedByName)); CreatedByLabel.Text = Loc.GetString("admin-notes-created-by", ("author", note.CreatedByName));
CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss"))); CreatedAtLabel.Text = Loc.GetString("admin-notes-created-at", ("date", note.CreatedAt.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")));
EditedByLabel.Text = Loc.GetString("admin-notes-last-edited-by", ("author", note.EditedByName)); EditedByLabel.Text = Loc.GetString("admin-notes-last-edited-by", ("author", note.EditedByName));
@@ -25,8 +25,8 @@ public sealed class ClientInnerBodyAnomalySystem : SharedInnerBodyAnomalySystem
var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap); var index = _sprite.LayerMapReserve((ent.Owner, sprite), ent.Comp.LayerMap);
if (TryComp<HumanoidProfileComponent>(ent, out var humanoid) && if (TryComp<HumanoidAppearanceComponent>(ent, out var humanoidAppearance) &&
ent.Comp.SpeciesSprites.TryGetValue(humanoid.Species, out var speciesSprite)) ent.Comp.SpeciesSprites.TryGetValue(humanoidAppearance.Species, out var speciesSprite))
{ {
_sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite); _sprite.LayerSetSprite((ent.Owner, sprite), index, speciesSprite);
} }
@@ -1,4 +1,4 @@
<controls:FancyWindow xmlns="https://spacestation14.io" <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'anomaly-generator-ui-title'}" Title="{Loc 'anomaly-generator-ui-title'}"
@@ -32,7 +32,7 @@
</BoxContainer> </BoxContainer>
<!--Sprite View--> <!--Sprite View-->
<PanelContainer Margin="12 0 0 0" <PanelContainer Margin="12 0 0 0"
StyleClasses="BackgroundPanelDark" StyleClasses="Inset"
VerticalAlignment="Center"> VerticalAlignment="Center">
<SpriteView Name="EntityView" <SpriteView Name="EntityView"
SetSize="96 96" SetSize="96 96"
@@ -0,0 +1,9 @@
using Content.Shared.Atmos.Components;
namespace Content.Client.Atmos.Components;
[RegisterComponent]
public sealed partial class MapAtmosphereComponent : SharedMapAtmosphereComponent
{
}
@@ -1,7 +1,6 @@
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Atmos.Monitor; using Content.Shared.Atmos.Monitor;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Temperature; using Content.Shared.Temperature;
@@ -23,7 +22,6 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
private readonly IEntityManager _entManager; private readonly IEntityManager _entManager;
private readonly IResourceCache _cache; private readonly IResourceCache _cache;
private readonly SharedAtmosphereSystem _atmosphere;
private Dictionary<AtmosAlarmType, string> _alarmStrings = new Dictionary<AtmosAlarmType, string>() private Dictionary<AtmosAlarmType, string> _alarmStrings = new Dictionary<AtmosAlarmType, string>()
{ {
@@ -39,7 +37,6 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
_entManager = IoCManager.Resolve<IEntityManager>(); _entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>(); _cache = IoCManager.Resolve<IResourceCache>();
_atmosphere = _entManager.System<SharedAtmosphereSystem>();
NetEntity = uid; NetEntity = uid;
Coordinates = coordinates; Coordinates = coordinates;
@@ -152,7 +149,7 @@ public sealed partial class AtmosAlarmEntryContainer : BoxContainer
foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs) foreach ((var gas, (var mol, var percent, var alert)) in keyValuePairs)
{ {
FixedPoint2 gasPercent = percent * 100f; FixedPoint2 gasPercent = percent * 100f;
var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
var gasLabel = new Label() var gasLabel = new Label()
{ {
@@ -1,7 +1,6 @@
using Content.Client.Stylesheets; using Content.Client.Stylesheets;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
@@ -20,14 +19,12 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer
private readonly IEntityManager _entManager; private readonly IEntityManager _entManager;
private readonly IResourceCache _cache; private readonly IResourceCache _cache;
private readonly SharedAtmosphereSystem _atmosphere;
public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data) public AtmosMonitoringEntryContainer(AtmosMonitoringConsoleEntry data)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_entManager = IoCManager.Resolve<IEntityManager>(); _entManager = IoCManager.Resolve<IEntityManager>();
_cache = IoCManager.Resolve<IResourceCache>(); _cache = IoCManager.Resolve<IResourceCache>();
_atmosphere = _entManager.System<SharedAtmosphereSystem>();
Data = data; Data = data;
@@ -135,7 +132,7 @@ public sealed partial class AtmosMonitoringEntryContainer : BoxContainer
var gasPercent = (FixedPoint2)0f; var gasPercent = (FixedPoint2)0f;
gasPercent = percent * 100f; gasPercent = percent * 100f;
var gasAbbreviation = Loc.GetString(_atmosphere.GetGas(gas).Abbreviation); var gasAbbreviation = Atmospherics.GasAbbreviations.GetValueOrDefault(gas, Loc.GetString("gas-unknown-abbreviation"));
var gasLabel = new Label() var gasLabel = new Label()
{ {
@@ -10,9 +10,7 @@ namespace Content.Client.Atmos.EntitySystems
[UsedImplicitly] [UsedImplicitly]
internal sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem internal sealed class AtmosDebugOverlaySystem : SharedAtmosDebugOverlaySystem
{ {
[Dependency] private readonly IOverlayManager _overlayManager = default!; public readonly Dictionary<EntityUid, AtmosDebugOverlayMessage> TileData = new();
public readonly Dictionary<EntityUid, AtmosDebugOverlayMessage> TileData = [];
// Configuration set by debug commands and used by AtmosDebugOverlay { // Configuration set by debug commands and used by AtmosDebugOverlay {
/// <summary>Value source for display</summary> /// <summary>Value source for display</summary>
@@ -27,8 +25,6 @@ namespace Content.Client.Atmos.EntitySystems
public bool CfgCBM = false; public bool CfgCBM = false;
// } // }
private AtmosDebugOverlay? _overlay;
public override void Initialize() public override void Initialize()
{ {
base.Initialize(); base.Initialize();
@@ -38,6 +34,10 @@ namespace Content.Client.Atmos.EntitySystems
SubscribeNetworkEvent<AtmosDebugOverlayDisableMessage>(HandleAtmosDebugOverlayDisableMessage); SubscribeNetworkEvent<AtmosDebugOverlayDisableMessage>(HandleAtmosDebugOverlayDisableMessage);
SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved); SubscribeLocalEvent<GridRemovalEvent>(OnGridRemoved);
var overlayManager = IoCManager.Resolve<IOverlayManager>();
if(!overlayManager.HasOverlay<AtmosDebugOverlay>())
overlayManager.AddOverlay(new AtmosDebugOverlay(this));
} }
private void OnGridRemoved(GridRemovalEvent ev) private void OnGridRemoved(GridRemovalEvent ev)
@@ -51,25 +51,19 @@ namespace Content.Client.Atmos.EntitySystems
private void HandleAtmosDebugOverlayMessage(AtmosDebugOverlayMessage message) private void HandleAtmosDebugOverlayMessage(AtmosDebugOverlayMessage message)
{ {
TileData[GetEntity(message.GridId)] = message; TileData[GetEntity(message.GridId)] = message;
if (_overlay is not null)
return;
_overlay = new AtmosDebugOverlay(this);
_overlayManager.AddOverlay(_overlay);
} }
private void HandleAtmosDebugOverlayDisableMessage(AtmosDebugOverlayDisableMessage ev) private void HandleAtmosDebugOverlayDisableMessage(AtmosDebugOverlayDisableMessage ev)
{ {
TileData.Clear(); TileData.Clear();
RemoveOverlay();
} }
public override void Shutdown() public override void Shutdown()
{ {
base.Shutdown(); base.Shutdown();
var overlayManager = IoCManager.Resolve<IOverlayManager>();
RemoveOverlay(); if (overlayManager.HasOverlay<AtmosDebugOverlay>())
overlayManager.RemoveOverlay<AtmosDebugOverlay>();
} }
public void Reset(RoundRestartCleanupEvent ev) public void Reset(RoundRestartCleanupEvent ev)
@@ -81,15 +75,6 @@ namespace Content.Client.Atmos.EntitySystems
{ {
return TileData.ContainsKey(gridId); return TileData.ContainsKey(gridId);
} }
private void RemoveOverlay()
{
if (_overlay is null)
return;
_overlayManager.RemoveOverlay(_overlay);
_overlay = null;
}
} }
internal enum AtmosDebugOverlayMode : byte internal enum AtmosDebugOverlayMode : byte
@@ -1,6 +1,5 @@
using System.Runtime.CompilerServices; using System.Runtime.CompilerServices;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Reactions;
namespace Content.Client.Atmos.EntitySystems; namespace Content.Client.Atmos.EntitySystems;
@@ -14,29 +13,6 @@ public sealed partial class AtmosphereSystem
implementation. implementation.
*/ */
/// <inheritdoc/>
/// <remarks>No-op on client as reactions aren't entirely in shared.
/// Don't call it. Smile.</remarks>
public override ReactionResult React(GasMixture mixture, IGasMixtureHolder? holder)
{
// Reactions don't work on client so don't even try.
throw new NotImplementedException();
}
public override bool IsMixtureFuel(GasMixture mixture, float epsilon = Atmospherics.Epsilon)
{
var tmp = new float[Atmospherics.AdjustedNumberOfGases];
NumericsHelpers.Multiply(mixture.Moles, GasFuelMask, tmp);
return NumericsHelpers.HorizontalAdd(tmp) > epsilon;
}
public override bool IsMixtureOxidizer(GasMixture mixture, float epsilon = Atmospherics.Epsilon)
{
var tmp = new float[Atmospherics.AdjustedNumberOfGases];
NumericsHelpers.Multiply(mixture.Moles, GasOxidizerMask, tmp);
return NumericsHelpers.HorizontalAdd(tmp) > epsilon;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)] [MethodImpl(MethodImplOptions.AggressiveInlining)]
protected override float GetHeatCapacityCalculation(float[] moles, bool space) protected override float GetHeatCapacityCalculation(float[] moles, bool space)
{ {
@@ -51,7 +27,7 @@ public sealed partial class AtmosphereSystem
// though this isnt the hottest code path so it should be fine // though this isnt the hottest code path so it should be fine
// the gc can eat a little as a treat // the gc can eat a little as a treat
var tmp = new float[moles.Length]; var tmp = new float[moles.Length];
NumericsHelpers.Multiply(moles, GasMolarHeatCapacities, tmp); NumericsHelpers.Multiply(moles, GasSpecificHeats, tmp);
// Adjust heat capacity by speedup, because this is primarily what // Adjust heat capacity by speedup, because this is primarily what
// determines how quickly gases heat up/cool. // determines how quickly gases heat up/cool.
return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity); return MathF.Max(NumericsHelpers.HorizontalAdd(tmp), Atmospherics.MinimumHeatCapacity);
@@ -1,30 +0,0 @@
using Content.Client.Atmos.Overlays;
using JetBrains.Annotations;
using Robust.Client.Graphics;
namespace Content.Client.Atmos.EntitySystems;
/// <summary>
/// System responsible for rendering atmos fire animations using <see cref="GasTileFireOverlay"/>.
/// </summary>
[UsedImplicitly]
public sealed class GasTileFireOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private GasTileFireOverlay _fireOverlay = default!;
public override void Initialize()
{
base.Initialize();
_fireOverlay = new GasTileFireOverlay();
_overlayMan.AddOverlay(_fireOverlay);
}
public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay<GasTileFireOverlay>();
}
}
@@ -1,30 +0,0 @@
using Content.Client.Atmos.Overlays;
using JetBrains.Annotations;
using Robust.Client.Graphics;
namespace Content.Client.Atmos.EntitySystems;
/// <summary>
/// System responsible for rendering heat distortion using <see cref="GasTileHeatBlurOverlay"/>.
/// </summary>
[UsedImplicitly]
public sealed class GasTileHeatBlurOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private GasTileHeatBlurOverlay _gasTileHeatBlurOverlay = default!;
public override void Initialize()
{
base.Initialize();
_gasTileHeatBlurOverlay = new GasTileHeatBlurOverlay();
_overlayMan.AddOverlay(_gasTileHeatBlurOverlay);
}
public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay<GasTileHeatBlurOverlay>();
}
}
@@ -1,85 +1,106 @@
using Content.Client.Atmos.Overlays;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components; using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems; using Content.Shared.Atmos.EntitySystems;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.GameStates; using Robust.Shared.GameStates;
namespace Content.Client.Atmos.EntitySystems; namespace Content.Client.Atmos.EntitySystems
[UsedImplicitly]
public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
{ {
public override void Initialize() [UsedImplicitly]
public sealed class GasTileOverlaySystem : SharedGasTileOverlaySystem
{ {
base.Initialize(); [Dependency] private readonly IResourceCache _resourceCache = default!;
SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate); [Dependency] private readonly IOverlayManager _overlayMan = default!;
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState); [Dependency] private readonly SpriteSystem _spriteSys = default!;
} [Dependency] private readonly SharedTransformSystem _xformSys = default!;
private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args) private GasTileOverlay _overlay = default!;
{
Dictionary<Vector2i, GasOverlayChunk> modifiedChunks;
switch (args.Current) public override void Initialize()
{ {
// is this a delta or full state? base.Initialize();
case GasTileOverlayDeltaState delta: SubscribeNetworkEvent<GasOverlayUpdateEvent>(HandleGasOverlayUpdate);
SubscribeLocalEvent<GasTileOverlayComponent, ComponentHandleState>(OnHandleState);
_overlay = new GasTileOverlay(this, EntityManager, _resourceCache, ProtoMan, _spriteSys, _xformSys);
_overlayMan.AddOverlay(_overlay);
}
public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay<GasTileOverlay>();
}
private void OnHandleState(EntityUid gridUid, GasTileOverlayComponent comp, ref ComponentHandleState args)
{
Dictionary<Vector2i, GasOverlayChunk> modifiedChunks;
switch (args.Current)
{ {
modifiedChunks = delta.ModifiedChunks; // is this a delta or full state?
foreach (var index in comp.Chunks.Keys) case GasTileOverlayDeltaState delta:
{ {
if (!delta.AllChunks.Contains(index)) modifiedChunks = delta.ModifiedChunks;
comp.Chunks.Remove(index); foreach (var index in comp.Chunks.Keys)
} {
if (!delta.AllChunks.Contains(index))
comp.Chunks.Remove(index);
}
break; break;
} }
case GasTileOverlayState state: case GasTileOverlayState state:
{
modifiedChunks = state.Chunks;
foreach (var index in comp.Chunks.Keys)
{ {
if (!state.Chunks.ContainsKey(index)) modifiedChunks = state.Chunks;
comp.Chunks.Remove(index); foreach (var index in comp.Chunks.Keys)
{
if (!state.Chunks.ContainsKey(index))
comp.Chunks.Remove(index);
}
break;
} }
default:
break; return;
} }
default:
return;
}
foreach (var (index, data) in modifiedChunks) foreach (var (index, data) in modifiedChunks)
{
comp.Chunks[index] = data;
}
}
private void HandleGasOverlayUpdate(GasOverlayUpdateEvent ev)
{
foreach (var (nent, removedIndicies) in ev.RemovedChunks)
{
var grid = GetEntity(nent);
if (!TryComp(grid, out GasTileOverlayComponent? comp))
continue;
foreach (var index in removedIndicies)
{ {
comp.Chunks.Remove(index); comp.Chunks[index] = data;
} }
} }
foreach (var (nent, gridData) in ev.UpdatedChunks) private void HandleGasOverlayUpdate(GasOverlayUpdateEvent ev)
{ {
var grid = GetEntity(nent); foreach (var (nent, removedIndicies) in ev.RemovedChunks)
if (!TryComp(grid, out GasTileOverlayComponent? comp))
continue;
foreach (var chunkData in gridData)
{ {
comp.Chunks[chunkData.Index] = chunkData; var grid = GetEntity(nent);
if (!TryComp(grid, out GasTileOverlayComponent? comp))
continue;
foreach (var index in removedIndicies)
{
comp.Chunks.Remove(index);
}
}
foreach (var (nent, gridData) in ev.UpdatedChunks)
{
var grid = GetEntity(nent);
if (!TryComp(grid, out GasTileOverlayComponent? comp))
continue;
foreach (var chunkData in gridData)
{
comp.Chunks[chunkData.Index] = chunkData;
}
} }
} }
} }
@@ -1,31 +0,0 @@
using Content.Client.Atmos.Overlays;
using JetBrains.Annotations;
using Robust.Client.Graphics;
namespace Content.Client.Atmos.EntitySystems;
/// <summary>
/// System responsible for rendering visible atmos gasses (like plasma for example) using <see cref="GasTileVisibleGasOverlay"/>.
/// </summary>
[UsedImplicitly]
public sealed class GasTileVisibleGasOverlaySystem : EntitySystem
{
[Dependency] private readonly IOverlayManager _overlayMan = default!;
private GasTileVisibleGasOverlay _visibleGasOverlay = default!;
public override void Initialize()
{
base.Initialize();
_visibleGasOverlay = new GasTileVisibleGasOverlay();
_overlayMan.AddOverlay(_visibleGasOverlay);
}
public override void Shutdown()
{
base.Shutdown();
_overlayMan.RemoveOverlay<GasTileVisibleGasOverlay>();
}
}
@@ -7,7 +7,7 @@
<!-- Status (pressure, temperature, alarm state, device total, address, etc) --> <!-- Status (pressure, temperature, alarm state, device total, address, etc) -->
<BoxContainer Orientation="Horizontal" Margin="0 0 0 2"> <BoxContainer Orientation="Horizontal" Margin="0 0 0 2">
<!-- Left column (view of entity) --> <!-- Left column (view of entity) -->
<PanelContainer Margin="2 0 6 0" StyleClasses="BackgroundPanelDark" VerticalAlignment="Center" VerticalExpand="True"> <PanelContainer Margin="2 0 6 0" StyleClasses="Inset" VerticalAlignment="Center" VerticalExpand="True">
<SpriteView Name="EntityView" OverrideDirection="South" Scale="2 2" SetSize="64 64"/> <SpriteView Name="EntityView" OverrideDirection="South" Scale="2 2" SetSize="64 64"/>
</PanelContainer> </PanelContainer>
<!-- Center column (pressure, temperature, alarm state) --> <!-- Center column (pressure, temperature, alarm state) -->
@@ -1,253 +0,0 @@
using Content.Client.Atmos.EntitySystems;
using Content.Client.Graphics;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Robust.Client.Graphics;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using System.Numerics;
namespace Content.Client.Atmos.Overlays;
/// <summary>
/// Renders a thermal heatmap overlay for gas tiles, used for equipment like thermal glasses.
/// /// </summary>
public sealed class GasTileDangerousTemperatureOverlay : Overlay
{
public override bool RequestScreenTexture { get; set; } = false;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IClyde _clyde = default!;
private GasTileOverlaySystem? _gasTileOverlay;
private readonly SharedTransformSystem _xformSys;
private EntityQuery<GasTileOverlayComponent> _overlayQuery;
private readonly OverlayResourceCache<CachedResources> _resources = new();
private List<Entity<MapGridComponent>> _grids = new();
// Cache used to transform ThermalByte into Color for overlay
private readonly Color[] _colorCache = new Color[256];
public override OverlaySpace Space => OverlaySpace.WorldSpaceBelowFOV;
public GasTileDangerousTemperatureOverlay()
{
IoCManager.InjectDependencies(this);
_xformSys = _entManager.System<SharedTransformSystem>();
_overlayQuery = _entManager.GetEntityQuery<GasTileOverlayComponent>();
for (byte i = 0; i <= ThermalByte.TempResolution; i++)
{
_colorCache[i] = PreCalculateColor(i);
}
_colorCache[ThermalByte.StateVacuum] = Color.Teal;
_colorCache[ThermalByte.StateVacuum].A = 0.6f;
_colorCache[ThermalByte.AtmosImpossible] = Color.Transparent;
#if DEBUG // This shouldn't happend so tell me if you see this LimeGreen on the screen
_colorCache[ThermalByte.ReservedFuture0] = Color.LimeGreen;
_colorCache[ThermalByte.ReservedFuture1] = Color.LimeGreen;
_colorCache[ThermalByte.ReservedFuture2] = Color.LimeGreen;
#else
_colorCache[ThermalByte.ReservedFuture0] = Color.Transparent;
_colorCache[ThermalByte.ReservedFuture1] = Color.Transparent;
_colorCache[ThermalByte.ReservedFuture2] = Color.Transparent;
#endif
}
/// <summary>
/// Used for Calculating onscreen color from ThermalByte core value
/// /// </summary>
private static Color PreCalculateColor(byte byteTemp)
{
// Color Thresholds in Kelvin
// -150 C
const float deepFreezeK = 123.15f;
// -50 C
const float freezeStartK = 223.15f;
// 0 C
const float waterFreezeK = 273.15f;
// 50 C
const float heatStartK = 323.15f;
// 100 C
const float waterBoilK = 373.15f;
// 300 C
const float superHeatK = 573.15f;
var tempK = byteTemp * ThermalByte.TempDegreeResolution;
// Neutral Zone Check (0C to 50C)
// If between 273.15K and 323.15K, it's transparent.
if (tempK >= waterFreezeK && tempK < heatStartK)
{
return Color.Transparent;
}
Color resultingColor;
switch (tempK)
{
case < deepFreezeK:
resultingColor = Color.FromHex("#330066");
resultingColor.A = 0.7f;
break;
case < freezeStartK:
// Interpolate Deep Purple -> Blue
// Range: 123.15 to 223.15 (Span: 100)
resultingColor = Color.InterpolateBetween(
Color.FromHex("#330066"),
Color.Blue,
(tempK - deepFreezeK) * 0.01f);
resultingColor.A = 0.6f;
break;
case < waterFreezeK:
// Interpolate Blue -> Transparent
// Range: 223.15 to 273.15 (Span: 50)
resultingColor = Color.InterpolateBetween(
new Color(Color.Blue.R, Color.Blue.G, Color.Blue.B, 0.6f),
new Color(Color.Blue.R, Color.Blue.G, Color.Blue.B, 0.2f),
(tempK - freezeStartK) * 0.02f);
break;
case < waterBoilK:
// Interpolate Transparent -> Yellow
// Range: 323.15 to 373.15 (Span: 50)
resultingColor = Color.InterpolateBetween(
new Color(Color.Yellow.R, Color.Yellow.G, Color.Yellow.B, 0.2f),
new Color(Color.Yellow.R, Color.Yellow.G, Color.Yellow.B, 0.6f),
(tempK - heatStartK) * 0.02f);
break;
case < superHeatK:
// Interpolate Yellow -> Red
// Range: 373.15 to 573.15 (Span: 200)
resultingColor = Color.InterpolateBetween(
Color.Yellow,
Color.Red,
(tempK - waterBoilK) * 0.005f);
resultingColor.A = 0.6f;
break;
default:
resultingColor = Color.DarkRed;
resultingColor.A = 0.7f;
break;
}
return resultingColor;
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return false;
_gasTileOverlay ??= _entManager.System<GasTileOverlaySystem>();
if (_gasTileOverlay == null)
return false;
var target = args.Viewport.RenderTarget;
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (res.TemperatureTarget is null || res.TemperatureTarget.Texture.Size != target.Size)
{
res.TemperatureTarget?.Dispose();
res.TemperatureTarget = _clyde.CreateRenderTarget(
target.Size,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
name: nameof(GasTileDangerousTemperatureOverlay));
}
var drawHandle = args.WorldHandle;
var worldBounds = args.WorldBounds;
var worldAABB = args.WorldAABB;
var mapId = args.MapId;
var worldToViewportLocal = args.Viewport.GetWorldToLocalMatrix();
drawHandle.RenderInRenderTarget(res.TemperatureTarget,
() =>
{
_grids.Clear();
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref _grids);
foreach (var grid in _grids)
{
if (!_overlayQuery.TryGetComponent(grid.Owner, out var comp))
continue;
var gridTileSizeVec = grid.Comp.TileSizeVector;
var gridTileCenterVec = grid.Comp.TileSizeHalfVector;
var gridEntToWorld = _xformSys.GetWorldMatrix(grid.Owner);
var gridEntToViewportLocal = gridEntToWorld * worldToViewportLocal;
drawHandle.SetTransform(gridEntToViewportLocal);
var worldToGridLocal = _xformSys.GetInvWorldMatrix(grid.Owner);
var floatBounds = worldToGridLocal.TransformBox(worldBounds).Enlarged(grid.Comp.TileSize);
var localBounds = new Box2i(
(int)MathF.Floor(floatBounds.Left),
(int)MathF.Floor(floatBounds.Bottom),
(int)MathF.Ceiling(floatBounds.Right),
(int)MathF.Ceiling(floatBounds.Top));
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var tileGas))
{
var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(tilePosition))
continue;
var gasColor = _colorCache[tileGas.ByteGasTemperature.Value];
if (gasColor.A <= 0f)
continue;
drawHandle.DrawRect(
Box2.CenteredAround(tilePosition + gridTileCenterVec, gridTileSizeVec),
gasColor
);
}
}
}
},
new Color(0, 0, 0, 0));
drawHandle.SetTransform(Matrix3x2.Identity);
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (res.TemperatureTarget != null)
args.WorldHandle.DrawTextureRect(res.TemperatureTarget.Texture, args.WorldBounds);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
protected override void DisposeBehavior()
{
_resources.Dispose();
base.DisposeBehavior();
}
private sealed class CachedResources : IDisposable
{
public IRenderTexture? TemperatureTarget;
public void Dispose()
{
TemperatureTarget?.Dispose();
}
}
}
@@ -1,172 +0,0 @@
using Content.Client.Atmos.EntitySystems;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Species;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using System.Numerics;
namespace Content.Client.Atmos.Overlays;
/// <summary>
/// Overlay responsible for rendering atmos fire animation.
/// </summary>
public sealed class GasTileFireOverlay : Overlay
{
[Dependency] private readonly IPrototypeManager _protoMan = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly SharedTransformSystem _xformSys;
private readonly SharedMapSystem _mapSystem = default!;
private readonly ShaderInstance _shader;
private readonly float[] _timer;
private readonly float[][] _frameDelays;
private readonly int[] _frameCounter;
// TODO combine textures into a single texture atlas.
private readonly Texture[][] _frames;
private const int FireStates = 3;
private const string FireRsiPath = "/Textures/Effects/fire.rsi";
public const int GasOverlayZIndex = (int)Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
public GasTileFireOverlay()
{
IoCManager.InjectDependencies(this);
_xformSys = _entManager.System<SharedTransformSystem>();
_mapSystem = _entManager.System<SharedMapSystem>();
_shader = _protoMan.Index(UnshadedShader).Instance();
ZIndex = GasOverlayZIndex;
_timer = new float[FireStates];
_frameDelays = new float[FireStates][];
_frameCounter = new int[FireStates];
_frames = new Texture[FireStates][];
var fire = _resourceCache.GetResource<RSIResource>(FireRsiPath).RSI;
for (var i = 0; i < FireStates; i++)
{
if (!fire.TryGetState((i + 1).ToString(), out var state))
throw new ArgumentOutOfRangeException($"Fire RSI doesn't have state \"{i}\"!");
_frames[i] = state.GetFrames(RsiDirection.South);
_frameDelays[i] = state.GetDelays();
_frameCounter[i] = 0;
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
for (var i = 0; i < FireStates; i++)
{
var delays = _frameDelays[i];
if (delays.Length == 0)
continue;
var frameCount = _frameCounter[i];
_timer[i] += args.DeltaSeconds;
var time = delays[frameCount];
if (_timer[i] < time) continue;
_timer[i] -= time;
_frameCounter[i] = (frameCount + 1) % _frames[i].Length;
}
}
protected override void Draw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return;
var drawHandle = args.WorldHandle;
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var overlayQuery = _entManager.GetEntityQuery<GasTileOverlayComponent>();
var gridState = (args.WorldBounds,
args.WorldHandle,
_frames,
_frameCounter,
_shader,
overlayQuery,
xformQuery,
_xformSys);
var mapUid = _mapSystem.GetMapOrInvalid(args.MapId);
if (args.Space != OverlaySpace.WorldSpaceEntities)
return;
// TODO: WorldBounds callback.
_mapManager.FindGridsIntersecting(args.MapId, args.WorldAABB, ref gridState,
static (EntityUid uid, MapGridComponent grid,
ref (Box2Rotated WorldBounds,
DrawingHandleWorld drawHandle,
Texture[][] frames,
int[] frameCounter,
ShaderInstance shader,
EntityQuery<GasTileOverlayComponent> overlayQuery,
EntityQuery<TransformComponent> xformQuery,
SharedTransformSystem xformSys) state) =>
{
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
!state.xformQuery.TryGetComponent(uid, out var gridXform))
{
return true;
}
var (_, _, worldMatrix, invMatrix) = state.xformSys.GetWorldPositionRotationMatrixWithInv(gridXform);
state.drawHandle.SetTransform(worldMatrix);
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
var localBounds = new Box2i(
(int)MathF.Floor(floatBounds.Left),
(int)MathF.Floor(floatBounds.Bottom),
(int)MathF.Ceiling(floatBounds.Right),
(int)MathF.Ceiling(floatBounds.Top));
// Currently it would be faster to group drawing by gas rather than by chunk, but if the textures are
// ever moved to a single atlas, that should no longer be the case. So this is just grouping draw calls
// by chunk, even though its currently slower.
state.drawHandle.UseShader(state.shader);
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var gas))
{
if (gas.FireState == 0)
continue;
var index = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(index))
continue;
var fireState = gas.FireState - 1;
var texture = state.frames[fireState][state.frameCounter[fireState]];
state.drawHandle.DrawTexture(texture, index);
}
}
return true;
});
drawHandle.UseShader(null);
drawHandle.SetTransform(Matrix3x2.Identity);
}
}
@@ -1,263 +0,0 @@
using Content.Client.Atmos.EntitySystems;
using Content.Client.Graphics;
using Content.Client.Resources;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.CCVar;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Configuration;
using Robust.Shared.Enums;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using System.Numerics;
using Color = Robust.Shared.Maths.Color;
using Texture = Robust.Client.Graphics.Texture;
namespace Content.Client.Atmos.Overlays;
/// <summary>
/// Overlay responsible for rendering heat distortion shader.
/// </summary>
public sealed class GasTileHeatBlurOverlay : Overlay
{
public override bool RequestScreenTexture { get; set; } = true;
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private static readonly ProtoId<ShaderPrototype> HeatOverlayShader = "HeatBlur";
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
[Dependency] private readonly IPrototypeManager _proto = default!;
[Dependency] private readonly IClyde _clyde = default!;
[Dependency] private readonly IConfigurationManager _configManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
private readonly SharedTransformSystem _xformSys;
private readonly ShaderInstance _shader;
private readonly Texture _noiseTexture;
private readonly Texture _heatGradientTexture;
private List<Entity<MapGridComponent>> _intersectingGrids = new();
private readonly OverlayResourceCache<CachedResources> _resources = new();
// Overlay settings
private const float
ShaderSpilling = 2.5f; // for example 4f - spills shader one tile from hotspot, 2.5f - spills it half tile
private const float ShaderStrength = 0.04f; // Makes waves stronger
private const float ShaderScale = 1f; // Makes more waves
private const float ShaderSpeed = 0.4f; // Makes waves run faster
// Overlay settings for reduced motion setting
private const float ShaderStrengthForReducedMotion = 0.01f;
private const float ShaderScaleReducedMotion = 0.5f;
private const float ShaderSpeedReducedMotion = 0.25f;
private const int MinDistortionTemp = 300; // Distortion starts to show up at this temperature in Kelvins
private const int MaxDistortionTemp = 2000; // Maximum distortion strength at this temperature in Kelvins
public override OverlaySpace Space => OverlaySpace.WorldSpace;
public GasTileHeatBlurOverlay()
{
IoCManager.InjectDependencies(this);
_xformSys = _entManager.System<SharedTransformSystem>();
_noiseTexture = _resourceCache.GetTexture("/Textures/Effects/HeatBlur/perlin_noise.png");
_heatGradientTexture = _resourceCache.GetTexture("/Textures/Effects/HeatBlur/soft_circle.png");
_shader = _proto.Index(HeatOverlayShader).InstanceUnique();
_configManager.OnValueChanged(CCVars.ReducedMotion, SetReducedMotion, invokeImmediately: true);
}
private void SetReducedMotion(bool reducedMotion)
{
_shader.SetParameter("strength_scale", reducedMotion ? ShaderStrengthForReducedMotion : ShaderStrength);
_shader.SetParameter("spatial_scale", reducedMotion ? ShaderScaleReducedMotion : ShaderScale);
_shader.SetParameter("speed_scale", reducedMotion ? ShaderSpeedReducedMotion : ShaderSpeed);
}
protected override bool BeforeDraw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return false;
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
var target = args.Viewport.RenderTarget;
// Probably the resolution of the game window changed, remake the textures.
if (res.HeatTarget?.Texture.Size != target.Size)
{
res.HeatTarget?.Dispose();
res.HeatTarget = _clyde.CreateRenderTarget(
target.Size,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
name: nameof(GasTileHeatBlurOverlaySystem));
}
if (res.HeatBlurTarget?.Texture.Size != target.Size)
{
res.HeatBlurTarget?.Dispose();
res.HeatBlurTarget = _clyde.CreateRenderTarget(
target.Size,
new RenderTargetFormatParameters(RenderTargetColorFormat.Rgba8Srgb),
name: $"{nameof(GasTileHeatBlurOverlaySystem)}-blur");
}
var overlayQuery = _entManager.GetEntityQuery<GasTileOverlayComponent>();
args.WorldHandle.UseShader(_proto.Index(UnshadedShader).Instance());
var mapId = args.MapId;
var worldAABB = args.WorldAABB;
var worldBounds = args.WorldBounds;
var worldHandle = args.WorldHandle;
var worldToViewportLocal = args.Viewport.GetWorldToLocalMatrix();
// If there is no distortion after checking all visible tiles, we can bail early
var anyDistortion = false;
// We're rendering in the context of the heat target texture, which will encode data as to where and how strong
// the heat distortion will be
args.WorldHandle.RenderInRenderTarget(res.HeatTarget,
() =>
{
_intersectingGrids.Clear();
_mapManager.FindGridsIntersecting(mapId, worldAABB, ref _intersectingGrids);
foreach (var grid in _intersectingGrids)
{
if (!overlayQuery.TryGetComponent(grid.Owner, out var comp))
continue;
var gridEntToWorld = _xformSys.GetWorldMatrix(grid.Owner);
var gridEntToViewportLocal = gridEntToWorld * worldToViewportLocal;
if (!Matrix3x2.Invert(gridEntToViewportLocal, out var viewportLocalToGridEnt))
continue;
var uvToUi = Matrix3Helpers.CreateScale(res.HeatTarget.Size.X, -res.HeatTarget.Size.Y);
var uvToGridEnt = uvToUi * viewportLocalToGridEnt;
// Because we want the actual distortion to be calculated based on the grid coordinates*, we need
// to pass a matrix transformation to go from the viewport coordinates to grid coordinates.
// * (why? because otherwise the effect would shimmer like crazy as you moved around, think
// moving a piece of warped glass above a picture instead of placing the warped glass on the
// paper and moving them together)
_shader.SetParameter("grid_ent_from_viewport_local", uvToGridEnt);
// Draw commands (like DrawRect) will be using grid coordinates from here
worldHandle.SetTransform(gridEntToViewportLocal);
// We only care about tiles that fit in these bounds
var worldToGridLocal = _xformSys.GetInvWorldMatrix(grid.Owner);
var floatBounds = worldToGridLocal.TransformBox(worldBounds).Enlarged(grid.Comp.TileSize);
var localBounds = new Box2i(
(int)MathF.Floor(floatBounds.Left),
(int)MathF.Floor(floatBounds.Bottom),
(int)MathF.Ceiling(floatBounds.Right),
(int)MathF.Ceiling(floatBounds.Top));
// for each tile and its gas --->
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var tileGas))
{
// Check and make sure the tile is within the viewport/screen
var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(tilePosition))
continue;
// Get the distortion strength from the temperature and bail if it's not hot enough
var strength = GetHeatDistortionStrength(tileGas.ByteGasTemperature);
if (strength <= 0f)
continue;
anyDistortion = true;
// Encode the strength in the red channel
// alpha set to 1 as tile is active
worldHandle.DrawTextureRect(
_heatGradientTexture,
Box2.CenteredAround(tilePosition + grid.Comp.TileSizeHalfVector,
grid.Comp.TileSizeVector * ShaderSpilling),
new Color(strength, 0f, 0f));
}
}
}
},
// This clears the buffer to all zero first...
new Color(0, 0, 0, 0));
// no distortion, no need to render
if (!anyDistortion)
{
args.WorldHandle.UseShader(null);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
return false;
}
return true;
}
protected override void Draw(in OverlayDrawArgs args)
{
var res = _resources.GetForViewport(args.Viewport, static _ => new CachedResources());
if (ScreenTexture is null || res.HeatTarget is null || res.HeatBlurTarget is null)
return;
_shader.SetParameter("SCREEN_TEXTURE", ScreenTexture);
_shader.SetParameter("NOISE_TEXTURE", _noiseTexture);
args.WorldHandle.UseShader(_shader);
args.WorldHandle.DrawTextureRect(res.HeatTarget.Texture, args.WorldBounds);
args.WorldHandle.UseShader(null);
args.WorldHandle.SetTransform(Matrix3x2.Identity);
}
protected override void DisposeBehavior()
{
_resources.Dispose();
_configManager.UnsubValueChanged(CCVars.ReducedMotion, SetReducedMotion);
base.DisposeBehavior();
}
/// <summary>
/// Gets the strength of the heat distortion effect based on the temperature of the tile.
/// The strength is a value between 0 and 1, where 0 means no distortion and 1 means maximum distortion.
/// </summary>
/// <param name="temp">The temperature of the tile.</param>
/// <returns>The strength of the heat distortion effect.</returns>
/// <seealso cref="ThermalByte"/>
private static float GetHeatDistortionStrength(ThermalByte temp)
{
if (!temp.TryGetTemperature(out var kelvinTemp))
{
return 0f;
}
var strength = (kelvinTemp - MinDistortionTemp) / (MaxDistortionTemp - MinDistortionTemp);
return MathHelper.Clamp01(strength);
}
internal sealed class CachedResources : IDisposable
{
public IRenderTexture? HeatTarget;
public IRenderTexture? HeatBlurTarget;
public void Dispose()
{
HeatTarget?.Dispose();
HeatBlurTarget?.Dispose();
}
}
}
@@ -0,0 +1,302 @@
using System.Numerics;
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;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
namespace Content.Client.Atmos.Overlays
{
public sealed class GasTileOverlay : Overlay
{
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly IEntityManager _entManager;
private readonly IMapManager _mapManager;
private readonly SharedAtmosphereSystem _atmosphereSystem;
private readonly SharedMapSystem _mapSystem;
private readonly SharedTransformSystem _xformSys;
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
private readonly ShaderInstance _shader;
// Gas overlays
private readonly float[] _timer;
private readonly float[][] _frameDelays;
private readonly int[] _frameCounter;
// TODO combine textures into a single texture atlas.
private readonly Texture[][] _frames;
// Fire overlays
private const int FireStates = 3;
private const string FireRsiPath = "/Textures/Effects/fire.rsi";
private readonly float[] _fireTimer = new float[FireStates];
private readonly float[][] _fireFrameDelays = new float[FireStates][];
private readonly int[] _fireFrameCounter = new int[FireStates];
private readonly Texture[][] _fireFrames = new Texture[FireStates][];
private int _gasCount;
public const int GasOverlayZIndex = (int) Shared.DrawDepth.DrawDepth.Effects; // Under ghosts, above mostly everything else
public GasTileOverlay(GasTileOverlaySystem system, IEntityManager entManager, IResourceCache resourceCache, IPrototypeManager protoMan, SpriteSystem spriteSys, SharedTransformSystem xformSys)
{
_entManager = entManager;
_mapManager = IoCManager.Resolve<IMapManager>();
_atmosphereSystem = entManager.System<SharedAtmosphereSystem>();
_mapSystem = entManager.System<SharedMapSystem>();
_xformSys = xformSys;
_shader = protoMan.Index(UnshadedShader).Instance();
ZIndex = GasOverlayZIndex;
_gasCount = system.VisibleGasId.Length;
_timer = new float[_gasCount];
_frameDelays = new float[_gasCount][];
_frameCounter = new int[_gasCount];
_frames = new Texture[_gasCount][];
for (var i = 0; i < _gasCount; i++)
{
var gasPrototype = _atmosphereSystem.GetGas(system.VisibleGasId[i]);
SpriteSpecifier overlay;
if (!string.IsNullOrEmpty(gasPrototype.GasOverlaySprite) && !string.IsNullOrEmpty(gasPrototype.GasOverlayState))
overlay = new SpriteSpecifier.Rsi(new (gasPrototype.GasOverlaySprite), gasPrototype.GasOverlayState);
else if (!string.IsNullOrEmpty(gasPrototype.GasOverlayTexture))
overlay = new SpriteSpecifier.Texture(new (gasPrototype.GasOverlayTexture));
else
continue;
switch (overlay)
{
case SpriteSpecifier.Rsi animated:
var rsi = resourceCache.GetResource<RSIResource>(animated.RsiPath).RSI;
var stateId = animated.RsiState;
if (!rsi.TryGetState(stateId, out var state))
continue;
_frames[i] = state.GetFrames(RsiDirection.South);
_frameDelays[i] = state.GetDelays();
_frameCounter[i] = 0;
break;
case SpriteSpecifier.Texture texture:
_frames[i] = new[] { spriteSys.Frame0(texture) };
_frameDelays[i] = Array.Empty<float>();
break;
}
}
var fire = resourceCache.GetResource<RSIResource>(FireRsiPath).RSI;
for (var i = 0; i < FireStates; i++)
{
if (!fire.TryGetState((i + 1).ToString(), out var state))
throw new ArgumentOutOfRangeException($"Fire RSI doesn't have state \"{i}\"!");
_fireFrames[i] = state.GetFrames(RsiDirection.South);
_fireFrameDelays[i] = state.GetDelays();
_fireFrameCounter[i] = 0;
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
for (var i = 0; i < _gasCount; i++)
{
var delays = _frameDelays[i];
if (delays.Length == 0)
continue;
var frameCount = _frameCounter[i];
_timer[i] += args.DeltaSeconds;
var time = delays[frameCount];
if (_timer[i] < time)
continue;
_timer[i] -= time;
_frameCounter[i] = (frameCount + 1) % _frames[i].Length;
}
for (var i = 0; i < FireStates; i++)
{
var delays = _fireFrameDelays[i];
if (delays.Length == 0)
continue;
var frameCount = _fireFrameCounter[i];
_fireTimer[i] += args.DeltaSeconds;
var time = delays[frameCount];
if (_fireTimer[i] < time) continue;
_fireTimer[i] -= time;
_fireFrameCounter[i] = (frameCount + 1) % _fireFrames[i].Length;
}
}
protected override void Draw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return;
var drawHandle = args.WorldHandle;
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var overlayQuery = _entManager.GetEntityQuery<GasTileOverlayComponent>();
var gridState = (args.WorldBounds,
args.WorldHandle,
_gasCount,
_frames,
_frameCounter,
_fireFrames,
_fireFrameCounter,
_shader,
overlayQuery,
xformQuery,
_xformSys);
var mapUid = _mapSystem.GetMapOrInvalid(args.MapId);
if (_entManager.TryGetComponent<MapAtmosphereComponent>(mapUid, out var atmos))
DrawMapOverlay(drawHandle, args, mapUid, atmos);
if (args.Space != OverlaySpace.WorldSpaceEntities)
return;
// TODO: WorldBounds callback.
_mapManager.FindGridsIntersecting(args.MapId, args.WorldAABB, ref gridState,
static (EntityUid uid, MapGridComponent grid,
ref (Box2Rotated WorldBounds,
DrawingHandleWorld drawHandle,
int gasCount,
Texture[][] frames,
int[] frameCounter,
Texture[][] fireFrames,
int[] fireFrameCounter,
ShaderInstance shader,
EntityQuery<GasTileOverlayComponent> overlayQuery,
EntityQuery<TransformComponent> xformQuery,
SharedTransformSystem xformSys) state) =>
{
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
!state.xformQuery.TryGetComponent(uid, out var gridXform))
{
return true;
}
var (_, _, worldMatrix, invMatrix) = state.xformSys.GetWorldPositionRotationMatrixWithInv(gridXform);
state.drawHandle.SetTransform(worldMatrix);
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
var localBounds = new Box2i(
(int) MathF.Floor(floatBounds.Left),
(int) MathF.Floor(floatBounds.Bottom),
(int) MathF.Ceiling(floatBounds.Right),
(int) MathF.Ceiling(floatBounds.Top));
// Currently it would be faster to group drawing by gas rather than by chunk, but if the textures are
// ever moved to a single atlas, that should no longer be the case. So this is just grouping draw calls
// by chunk, even though its currently slower.
state.drawHandle.UseShader(null);
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var gas))
{
if (gas.Opacity == null!)
continue;
var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(tilePosition))
continue;
for (var i = 0; i < state.gasCount; i++)
{
var opacity = gas.Opacity[i];
if (opacity > 0)
state.drawHandle.DrawTexture(state.frames[i][state.frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
}
}
}
// And again for fire, with the unshaded shader
state.drawHandle.UseShader(state.shader);
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var gas))
{
if (gas.FireState == 0)
continue;
var index = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(index))
continue;
var fireState = gas.FireState - 1;
var texture = state.fireFrames[fireState][state.fireFrameCounter[fireState]];
state.drawHandle.DrawTexture(texture, index);
}
}
return true;
});
drawHandle.UseShader(null);
drawHandle.SetTransform(Matrix3x2.Identity);
}
private void DrawMapOverlay(
DrawingHandleWorld handle,
OverlayDrawArgs args,
EntityUid map,
MapAtmosphereComponent atmos)
{
var mapGrid = _entManager.HasComponent<MapGridComponent>(map);
// map-grid atmospheres get drawn above grids
if (mapGrid && args.Space != OverlaySpace.WorldSpaceEntities)
return;
// Normal map atmospheres get drawn below grids
if (!mapGrid && args.Space != OverlaySpace.WorldSpaceBelowWorld)
return;
var bottomLeft = args.WorldAABB.BottomLeft.Floored();
var topRight = args.WorldAABB.TopRight.Ceiled();
for (var x = bottomLeft.X; x <= topRight.X; x++)
{
for (var y = bottomLeft.Y; y <= topRight.Y; y++)
{
var tilePosition = new Vector2(x, y);
for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
{
var opacity = atmos.OverlayData.Opacity[i];
if (opacity > 0)
handle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
}
}
}
}
}
}
@@ -1,248 +0,0 @@
using Content.Client.Atmos.Components;
using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.ResourceManagement;
using Robust.Shared.Enums;
using Robust.Shared.Graphics.RSI;
using Robust.Shared.Map;
using Robust.Shared.Map.Components;
using Robust.Shared.Prototypes;
using Robust.Shared.Timing;
using Robust.Shared.Utility;
using System.Numerics;
using DrawDepth = Content.Shared.DrawDepth.DrawDepth;
namespace Content.Client.Atmos.Overlays;
/// <summary>
/// Overlay responsible for rendering visible atmos gasses (like plasma for example) usin.
/// </summary>
public sealed class GasTileVisibleGasOverlay : Overlay
{
[Dependency] private readonly IEntityManager _entManager = default!;
[Dependency] private readonly IResourceCache _resourceCache = default!;
[Dependency] private readonly IPrototypeManager _protoManager = default!;
[Dependency] private readonly IMapManager _mapManager = default!;
private static readonly ProtoId<ShaderPrototype> UnshadedShader = "unshaded";
private readonly SharedAtmosphereSystem _atmosphereSystem;
private readonly SharedMapSystem _mapSystem;
private readonly SharedTransformSystem _xformSys;
private readonly SharedGasTileOverlaySystem _gasTileOverlaySystem;
private readonly SpriteSystem _spriteSystem;
public override OverlaySpace Space => OverlaySpace.WorldSpaceEntities | OverlaySpace.WorldSpaceBelowWorld;
private readonly ShaderInstance _shader;
// Gas overlays
private readonly float[] _timer;
private readonly float[][] _frameDelays;
private readonly int[] _frameCounter;
// TODO combine textures into a single texture atlas.
private readonly Texture[][] _frames;
private readonly int _gasCount;
public const int GasOverlayZIndex = (int)DrawDepth.Gasses; // Under ghosts and fire, above mostly everything else
public GasTileVisibleGasOverlay()
{
IoCManager.InjectDependencies(this);
_atmosphereSystem = _entManager.System<SharedAtmosphereSystem>();
_mapSystem = _entManager.System<SharedMapSystem>();
_xformSys = _entManager.System<SharedTransformSystem>();
_gasTileOverlaySystem = _entManager.System<SharedGasTileOverlaySystem>();
_spriteSystem = _entManager.System<SpriteSystem>();
_shader = _protoManager.Index(UnshadedShader).Instance();
ZIndex = GasOverlayZIndex;
_gasCount = _gasTileOverlaySystem.VisibleGasId.Length;
_timer = new float[_gasCount];
_frameDelays = new float[_gasCount][];
_frameCounter = new int[_gasCount];
_frames = new Texture[_gasCount][];
for (var i = 0; i < _gasCount; i++)
{
var gasPrototype = _atmosphereSystem.GetGas(_gasTileOverlaySystem.VisibleGasId[i]);
switch (gasPrototype.GasOverlaySprite)
{
case SpriteSpecifier.Rsi animated:
var rsi = _resourceCache.GetResource<RSIResource>(animated.RsiPath).RSI;
var stateId = animated.RsiState;
if (!rsi.TryGetState(stateId, out var state))
continue;
_frames[i] = state.GetFrames(RsiDirection.South);
_frameDelays[i] = state.GetDelays();
_frameCounter[i] = 0;
break;
case SpriteSpecifier.Texture texture:
_frames[i] = new[] { _spriteSystem.Frame0(texture) };
_frameDelays[i] = Array.Empty<float>();
break;
}
}
}
protected override void FrameUpdate(FrameEventArgs args)
{
base.FrameUpdate(args);
for (var i = 0; i < _gasCount; i++)
{
var delays = _frameDelays[i];
if (delays.Length == 0)
continue;
var frameCount = _frameCounter[i];
_timer[i] += args.DeltaSeconds;
var time = delays[frameCount];
if (_timer[i] < time)
continue;
_timer[i] -= time;
_frameCounter[i] = (frameCount + 1) % _frames[i].Length;
}
}
protected override void Draw(in OverlayDrawArgs args)
{
if (args.MapId == MapId.Nullspace)
return;
var drawHandle = args.WorldHandle;
var xformQuery = _entManager.GetEntityQuery<TransformComponent>();
var overlayQuery = _entManager.GetEntityQuery<GasTileOverlayComponent>();
var gridState = (args.WorldBounds,
args.WorldHandle,
_gasCount,
_frames,
_frameCounter,
_shader,
overlayQuery,
xformQuery,
_xformSys);
var mapUid = _mapSystem.GetMapOrInvalid(args.MapId);
if (_entManager.TryGetComponent<MapAtmosphereComponent>(mapUid, out var atmos))
DrawMapOverlay(drawHandle, args, mapUid, atmos);
if (args.Space != OverlaySpace.WorldSpaceEntities)
return;
// TODO: WorldBounds callback.
_mapManager.FindGridsIntersecting(args.MapId,
args.WorldAABB,
ref gridState,
static (EntityUid uid,
MapGridComponent grid,
ref (Box2Rotated WorldBounds,
DrawingHandleWorld drawHandle,
int gasCount,
Texture[][] frames,
int[] frameCounter,
ShaderInstance shader,
EntityQuery<GasTileOverlayComponent> overlayQuery,
EntityQuery<TransformComponent> xformQuery,
SharedTransformSystem xformSys) state) =>
{
if (!state.overlayQuery.TryGetComponent(uid, out var comp) ||
!state.xformQuery.TryGetComponent(uid, out var gridXform))
{
return true;
}
var (_, _, worldMatrix, invMatrix) = state.xformSys.GetWorldPositionRotationMatrixWithInv(gridXform);
state.drawHandle.SetTransform(worldMatrix);
var floatBounds = invMatrix.TransformBox(state.WorldBounds).Enlarged(grid.TileSize);
var localBounds = new Box2i(
(int)MathF.Floor(floatBounds.Left),
(int)MathF.Floor(floatBounds.Bottom),
(int)MathF.Ceiling(floatBounds.Right),
(int)MathF.Ceiling(floatBounds.Top));
// Currently it would be faster to group drawing by gas rather than by chunk, but if the textures are
// ever moved to a single atlas, that should no longer be the case. So this is just grouping draw calls
// by chunk, even though its currently slower.
state.drawHandle.UseShader(null);
foreach (var chunk in comp.Chunks.Values)
{
var enumerator = new GasChunkEnumerator(chunk);
while (enumerator.MoveNext(out var gas))
{
if (gas.Opacity == null!)
continue;
var tilePosition = chunk.Origin + (enumerator.X, enumerator.Y);
if (!localBounds.Contains(tilePosition))
continue;
for (var i = 0; i < state.gasCount; i++)
{
var opacity = gas.Opacity[i];
if (opacity > 0)
{
state.drawHandle.DrawTexture(state.frames[i][state.frameCounter[i]],
tilePosition,
Color.White.WithAlpha(opacity));
}
}
}
}
return true;
});
drawHandle.UseShader(null);
drawHandle.SetTransform(Matrix3x2.Identity);
}
private void DrawMapOverlay(
DrawingHandleWorld handle,
OverlayDrawArgs args,
EntityUid map,
MapAtmosphereComponent atmos)
{
var mapGrid = _entManager.HasComponent<MapGridComponent>(map);
// map-grid atmospheres get drawn above grids
if (mapGrid && args.Space != OverlaySpace.WorldSpaceEntities)
return;
// Normal map atmospheres get drawn below grids
if (!mapGrid && args.Space != OverlaySpace.WorldSpaceBelowWorld)
return;
var bottomLeft = args.WorldAABB.BottomLeft.Floored();
var topRight = args.WorldAABB.TopRight.Ceiled();
for (var x = bottomLeft.X; x <= topRight.X; x++)
{
for (var y = bottomLeft.Y; y <= topRight.Y; y++)
{
var tilePosition = new Vector2(x, y);
for (var i = 0; i < atmos.OverlayData.Opacity.Length; i++)
{
var opacity = atmos.OverlayData.Opacity[i];
if (opacity > 0)
handle.DrawTexture(_frames[i][_frameCounter[i]], tilePosition, Color.White.WithAlpha(opacity));
}
}
}
}
}
@@ -1,33 +1,41 @@
using Robust.Client.GameObjects;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Content.Shared.Atmos.Components; using static Content.Shared.Atmos.Components.GasAnalyzerComponent;
namespace Content.Client.Atmos.UI; namespace Content.Client.Atmos.UI
public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface
{ {
[ViewVariables] public sealed class GasAnalyzerBoundUserInterface : BoundUserInterface
private GasAnalyzerWindow? _window;
public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
} [ViewVariables]
private GasAnalyzerWindow? _window;
protected override void Open() public GasAnalyzerBoundUserInterface(EntityUid owner, Enum uiKey) : base(owner, uiKey)
{ {
base.Open(); }
_window = this.CreateWindowCenteredLeft<GasAnalyzerWindow>(); protected override void Open()
_window.OnClose += Close; {
} base.Open();
protected override void ReceiveMessage(BoundUserInterfaceMessage message) _window = this.CreateWindowCenteredLeft<GasAnalyzerWindow>();
{ _window.OnClose += Close;
if (_window == null) }
return;
if (message is not GasAnalyzerUserMessage cast) protected override void ReceiveMessage(BoundUserInterfaceMessage message)
return; {
if (_window == null)
return;
if (message is not GasAnalyzerUserMessage cast)
return;
_window.Populate(cast);
}
_window.Populate(cast); protected override void Dispose(bool disposing)
{
base.Dispose(disposing);
if (disposing)
_window?.Dispose();
}
} }
} }
@@ -1,8 +1,6 @@
using System.Numerics; using System.Numerics;
using Content.Client.UserInterface.Controls; using Content.Client.UserInterface.Controls;
using Content.Shared.Atmos; using Content.Shared.Atmos;
using Content.Shared.Atmos.Components;
using Content.Shared.Atmos.EntitySystems;
using Content.Shared.Temperature; using Content.Shared.Temperature;
using Robust.Client.Graphics; using Robust.Client.Graphics;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
@@ -10,6 +8,7 @@ using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.CustomControls; using Robust.Client.UserInterface.CustomControls;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using static Content.Shared.Atmos.Components.GasAnalyzerComponent;
using Direction = Robust.Shared.Maths.Direction; using Direction = Robust.Shared.Maths.Direction;
namespace Content.Client.Atmos.UI namespace Content.Client.Atmos.UI
@@ -17,17 +16,25 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasAnalyzerWindow : DefaultWindow public sealed partial class GasAnalyzerWindow : DefaultWindow
{ {
private readonly SharedAtmosphereSystem _atmosphere;
private NetEntity _currentEntity = NetEntity.Invalid; private NetEntity _currentEntity = NetEntity.Invalid;
public GasAnalyzerWindow() public GasAnalyzerWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_atmosphere = IoCManager.Resolve<IEntityManager>().System<SharedAtmosphereSystem>();
} }
public void Populate(GasAnalyzerUserMessage msg) public void Populate(GasAnalyzerUserMessage msg)
{ {
if (msg.Error != null)
{
CTopBox.AddChild(new Label
{
Text = Loc.GetString("gas-analyzer-window-error-text", ("errorText", msg.Error)),
FontColorOverride = Color.Red
});
return;
}
if (msg.NodeGasMixes.Length == 0) if (msg.NodeGasMixes.Length == 0)
{ {
CTopBox.AddChild(new Label CTopBox.AddChild(new Label
@@ -322,31 +329,31 @@ namespace Content.Client.Atmos.UI
for (var j = 0; j < gasMix.Gases.Length; j++) for (var j = 0; j < gasMix.Gases.Length; j++)
{ {
var gasEntry = gasMix.Gases[j]; var gas = gasMix.Gases[j];
var gasProto = _atmosphere.GetGas(gasEntry.Gas); var color = Color.FromHex($"#{gas.Color}", Color.White);
// Add to the table // Add to the table
tableKey.AddChild(new Label tableKey.AddChild(new Label
{ {
Text = Loc.GetString(gasProto.Name) Text = Loc.GetString(gas.Name)
}); });
tableVal.AddChild(new Label tableVal.AddChild(new Label
{ {
Text = Loc.GetString("gas-analyzer-window-molarity-text", Text = Loc.GetString("gas-analyzer-window-molarity-text",
("mol", $"{gasEntry.Amount:0.00}")), ("mol", $"{gas.Amount:0.00}")),
Align = Label.AlignMode.Right, Align = Label.AlignMode.Right,
}); });
tablePercent.AddChild(new Label tablePercent.AddChild(new Label
{ {
Text = Loc.GetString("gas-analyzer-window-percentage-text", Text = Loc.GetString("gas-analyzer-window-percentage-text",
("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.0}")), ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.0}")),
Align = Label.AlignMode.Right Align = Label.AlignMode.Right
}); });
// Add to the gas bar //TODO: highlight the currently hover one // Add to the gas bar //TODO: highlight the currently hover one
gasBar.AddEntry(gasEntry.Amount, gasProto.Color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text", gasBar.AddEntry(gas.Amount, color, tooltip: Loc.GetString("gas-analyzer-window-molarity-percentage-text",
("gasName", Loc.GetString(gasProto.Name)), ("gasName", gas.Name),
("amount", $"{gasEntry.Amount:0.##}"), ("amount", $"{gas.Amount:0.##}"),
("percentage", $"{(gasEntry.Amount / totalGasAmount * 100):0.#}"))); ("percentage", $"{(gas.Amount / totalGasAmount * 100):0.#}")));
} }
dataContainer.AddChild(gasBar); dataContainer.AddChild(gasBar);
@@ -37,9 +37,10 @@ namespace Content.Client.Atmos.UI
_window.SelectGasPressed += OnSelectGasPressed; _window.SelectGasPressed += OnSelectGasPressed;
} }
private void OnToggleStatusButtonPressed(bool status) private void OnToggleStatusButtonPressed()
{ {
SendMessage(new GasFilterToggleStatusMessage(status)); if (_window is null) return;
SendMessage(new GasFilterToggleStatusMessage(_window.FilterStatus));
} }
private void OnFilterTransferRatePressed(string value) private void OnFilterTransferRatePressed(string value)
+4 -5
View File
@@ -1,12 +1,11 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="480 400" Title="Filter"> MinSize="480 400" Title="Filter">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<controls:SwitchButton <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
Name="ToggleStatusButton" <Label Text="{Loc comp-gas-filter-ui-filter-status}"/>
HorizontalAlignment="Left" <Button Name="ToggleStatusButton"/>
Pressed="True" /> </BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/> <Label Text="{Loc comp-gas-filter-ui-filter-transfer-rate}"/>
@@ -18,10 +18,11 @@ namespace Content.Client.Atmos.UI
{ {
private readonly ButtonGroup _buttonGroup = new(); private readonly ButtonGroup _buttonGroup = new();
public bool FilterStatus = true;
public string? SelectedGas; public string? SelectedGas;
public string? CurrentGasId; public string? CurrentGasId;
public event Action<bool>? ToggleStatusButtonPressed; public event Action? ToggleStatusButtonPressed;
public event Action<string>? FilterTransferRateChanged; public event Action<string>? FilterTransferRateChanged;
public event Action? SelectGasPressed; public event Action? SelectGasPressed;
@@ -29,7 +30,8 @@ namespace Content.Client.Atmos.UI
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
ToggleStatusButton.OnToggled += _ => ToggleStatusButtonPressed?.Invoke(ToggleStatusButton.Pressed); ToggleStatusButton.OnPressed += _ => SetFilterStatus(!FilterStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
FilterTransferRateInput.OnTextChanged += _ => SetFilterRate.Disabled = false; FilterTransferRateInput.OnTextChanged += _ => SetFilterRate.Disabled = false;
SetFilterRate.OnPressed += _ => SetFilterRate.OnPressed += _ =>
@@ -51,7 +53,15 @@ namespace Content.Client.Atmos.UI
public void SetFilterStatus(bool enabled) public void SetFilterStatus(bool enabled)
{ {
ToggleStatusButton.Pressed = enabled; FilterStatus = enabled;
if (enabled)
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-filter-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-filter-ui-status-disabled");
}
} }
public void SetGasFiltered(string? id, string name) public void SetGasFiltered(string? id, string name)
@@ -33,9 +33,10 @@ namespace Content.Client.Atmos.UI
_window.MixerNodePercentageChanged += OnMixerSetPercentagePressed; _window.MixerNodePercentageChanged += OnMixerSetPercentagePressed;
} }
private void OnToggleStatusButtonPressed(bool status) private void OnToggleStatusButtonPressed()
{ {
SendMessage(new GasMixerToggleStatusMessage(status)); if (_window is null) return;
SendMessage(new GasMixerToggleStatusMessage(_window.MixerStatus));
} }
private void OnMixerOutputPressurePressed(string value) private void OnMixerOutputPressurePressed(string value)
+5 -5
View File
@@ -1,12 +1,12 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="200 200" Title="Gas Mixer"> MinSize="200 200" Title="Gas Mixer">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<controls:SwitchButton <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
Name="ToggleStatusButton" <Label Text="{Loc comp-gas-mixer-ui-mixer-status}"/>
HorizontalAlignment="Left" <Control MinSize="5 0" />
Pressed="True" /> <Button Name="ToggleStatusButton"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-mixer-ui-mixer-output-pressure}"/> <Label Text="{Loc comp-gas-mixer-ui-mixer-output-pressure}"/>
<Control MinSize="5 0" /> <Control MinSize="5 0" />
+14 -3
View File
@@ -21,7 +21,9 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasMixerWindow : DefaultWindow public sealed partial class GasMixerWindow : DefaultWindow
{ {
public event Action<bool>? ToggleStatusButtonPressed; public bool MixerStatus = true;
public event Action? ToggleStatusButtonPressed;
public event Action<string>? MixerOutputPressureChanged; public event Action<string>? MixerOutputPressureChanged;
public event Action<string>? MixerNodePercentageChanged; public event Action<string>? MixerNodePercentageChanged;
@@ -31,7 +33,8 @@ namespace Content.Client.Atmos.UI
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
ToggleStatusButton.OnToggled += _ => ToggleStatusButtonPressed?.Invoke(ToggleStatusButton.Pressed); ToggleStatusButton.OnPressed += _ => SetMixerStatus(!MixerStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
MixerPressureOutputInput.OnTextChanged += _ => SetOutputPressureButton.Disabled = false; MixerPressureOutputInput.OnTextChanged += _ => SetOutputPressureButton.Disabled = false;
SetOutputPressureButton.OnPressed += _ => SetOutputPressureButton.OnPressed += _ =>
@@ -80,7 +83,15 @@ namespace Content.Client.Atmos.UI
public void SetMixerStatus(bool enabled) public void SetMixerStatus(bool enabled)
{ {
ToggleStatusButton.Pressed = enabled; MixerStatus = enabled;
if (enabled)
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-mixer-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-mixer-ui-status-disabled");
}
} }
} }
} }
@@ -42,9 +42,12 @@ public sealed class GasPressurePumpBoundUserInterface(EntityUid owner, Enum uiKe
_window.SetOutputPressure(pump.TargetPressure); _window.SetOutputPressure(pump.TargetPressure);
} }
private void OnToggleStatusButtonPressed(bool status) private void OnToggleStatusButtonPressed()
{ {
SendPredictedMessage(new GasPressurePumpToggleStatusMessage(status)); if (_window is null)
return;
SendPredictedMessage(new GasPressurePumpToggleStatusMessage(_window.PumpStatus));
} }
private void OnPumpOutputPressurePressed(float value) private void OnPumpOutputPressurePressed(float value)
@@ -4,10 +4,8 @@
SetSize="340 110" MinSize="340 110" Title="Pressure Pump"> SetSize="340 110" MinSize="340 110" Title="Pressure Pump">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<controls:SwitchButton <Label Text="{Loc comp-gas-pump-ui-pump-status}" Margin="0 0 5 0"/>
Name="ToggleStatusButton" <Button Name="ToggleStatusButton"/>
HorizontalAlignment="Left"
Pressed="True" />
<Control HorizontalExpand="True"/> <Control HorizontalExpand="True"/>
<Button HorizontalAlignment="Right" Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" Disabled="True" Margin="0 0 5 0"/> <Button HorizontalAlignment="Right" Name="SetOutputPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-rate}" Disabled="True" Margin="0 0 5 0"/>
<Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" /> <Button Name="SetMaxPressureButton" Text="{Loc comp-gas-pump-ui-pump-set-max}" />
@@ -11,7 +11,9 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasPressurePumpWindow : FancyWindow public sealed partial class GasPressurePumpWindow : FancyWindow
{ {
public event Action<bool>? ToggleStatusButtonPressed; public bool PumpStatus = true;
public event Action? ToggleStatusButtonPressed;
public event Action<float>? PumpOutputPressureChanged; public event Action<float>? PumpOutputPressureChanged;
public float MaxPressure public float MaxPressure
@@ -31,7 +33,8 @@ namespace Content.Client.Atmos.UI
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
ToggleStatusButton.OnToggled += _ => ToggleStatusButtonPressed?.Invoke(ToggleStatusButton.Pressed); ToggleStatusButton.OnPressed += _ => SetPumpStatus(!PumpStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
PumpPressureOutputInput.OnValueChanged += _ => SetOutputPressureButton.Disabled = false; PumpPressureOutputInput.OnValueChanged += _ => SetOutputPressureButton.Disabled = false;
@@ -56,7 +59,15 @@ namespace Content.Client.Atmos.UI
public void SetPumpStatus(bool enabled) public void SetPumpStatus(bool enabled)
{ {
ToggleStatusButton.Pressed = enabled; PumpStatus = enabled;
if (enabled)
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-pump-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-pump-ui-status-disabled");
}
} }
} }
} }
@@ -37,7 +37,7 @@ namespace Content.Client.Atmos.UI
_window = this.CreateWindow<GasThermomachineWindow>(); _window = this.CreateWindow<GasThermomachineWindow>();
_window.ToggleStatusButton.OnToggled += _ => OnToggleStatusButtonPressed(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value); _window.TemperatureSpinbox.OnValueChanged += _ => OnTemperatureChanged(_window.TemperatureSpinbox.Value);
_window.Entity = Owner; _window.Entity = Owner;
Update(); Update();
@@ -45,6 +45,9 @@ namespace Content.Client.Atmos.UI
private void OnToggleStatusButtonPressed() private void OnToggleStatusButtonPressed()
{ {
if (_window is null) return;
_window.SetActive(!_window.Active);
SendPredictedMessage(new GasThermomachineToggleMessage()); SendPredictedMessage(new GasThermomachineToggleMessage());
} }
@@ -3,11 +3,11 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="300 120" Title="{Loc comp-gas-thermomachine-ui-title-freezer}"> MinSize="300 120" Title="{Loc comp-gas-thermomachine-ui-title-freezer}">
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<controls:SwitchButton <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
Name="ToggleStatusButton" <Label Text="{Loc comp-gas-thermomachine-ui-toggle}"/>
HorizontalAlignment="Left" <Control MinSize="5 0" />
Pressed="True" <Button Access="Public" Name="ToggleStatusButton"/>
Access="Public" /> </BoxContainer>
<BoxContainer Name="SpinboxHBox" Orientation="Horizontal"> <BoxContainer Name="SpinboxHBox" Orientation="Horizontal">
<Label Text="{Loc comp-gas-thermomachine-ui-temperature}"/> <Label Text="{Loc comp-gas-thermomachine-ui-temperature}"/>
</BoxContainer> </BoxContainer>
@@ -12,6 +12,8 @@ public sealed partial class GasThermomachineWindow : FancyWindow
{ {
[Dependency] private readonly IEntityManager _entManager = default!; [Dependency] private readonly IEntityManager _entManager = default!;
public bool Active = true;
public FloatSpinBox TemperatureSpinbox; public FloatSpinBox TemperatureSpinbox;
public EntityUid Entity; public EntityUid Entity;
@@ -28,7 +30,15 @@ public sealed partial class GasThermomachineWindow : FancyWindow
public void SetActive(bool active) public void SetActive(bool active)
{ {
ToggleStatusButton.Pressed = active; Active = active;
if (active)
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-thermomachine-ui-status-disabled");
}
} }
public void SetTemperature(float temperature) public void SetTemperature(float temperature)
@@ -38,9 +38,11 @@ namespace Content.Client.Atmos.UI
Update(); Update();
} }
private void OnToggleStatusButtonPressed(bool status) private void OnToggleStatusButtonPressed()
{ {
SendPredictedMessage(new GasVolumePumpToggleStatusMessage(status)); if (_window is null) return;
SendPredictedMessage(new GasVolumePumpToggleStatusMessage(_window.PumpStatus));
} }
private void OnPumpTransferRatePressed(string value) private void OnPumpTransferRatePressed(string value)
@@ -3,10 +3,11 @@
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="200 120" Title="Volume Pump"> MinSize="200 120" Title="Volume Pump">
<BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<controls:SwitchButton <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
Name="ToggleStatusButton" <Label Text="{Loc comp-gas-pump-ui-pump-status}"/>
HorizontalAlignment="Left" <Control MinSize="5 0" />
Pressed="True" /> <Button Name="ToggleStatusButton"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<Label Text="{Loc comp-gas-pump-ui-pump-transfer-rate}"/> <Label Text="{Loc comp-gas-pump-ui-pump-transfer-rate}"/>
@@ -19,14 +19,17 @@ namespace Content.Client.Atmos.UI
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class GasVolumePumpWindow : FancyWindow public sealed partial class GasVolumePumpWindow : FancyWindow
{ {
public event Action<bool>? ToggleStatusButtonPressed; public bool PumpStatus = true;
public event Action? ToggleStatusButtonPressed;
public event Action<string>? PumpTransferRateChanged; public event Action<string>? PumpTransferRateChanged;
public GasVolumePumpWindow() public GasVolumePumpWindow()
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
ToggleStatusButton.OnToggled += _ => ToggleStatusButtonPressed?.Invoke(ToggleStatusButton.Pressed); ToggleStatusButton.OnPressed += _ => SetPumpStatus(!PumpStatus);
ToggleStatusButton.OnPressed += _ => ToggleStatusButtonPressed?.Invoke();
PumpTransferRateInput.OnTextChanged += _ => SetTransferRateButton.Disabled = false; PumpTransferRateInput.OnTextChanged += _ => SetTransferRateButton.Disabled = false;
SetTransferRateButton.OnPressed += _ => SetTransferRateButton.OnPressed += _ =>
@@ -49,7 +52,15 @@ namespace Content.Client.Atmos.UI
public void SetPumpStatus(bool enabled) public void SetPumpStatus(bool enabled)
{ {
ToggleStatusButton.Pressed = enabled; PumpStatus = enabled;
if (enabled)
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-pump-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-gas-pump-ui-status-disabled");
}
} }
} }
} }
@@ -24,7 +24,7 @@ public sealed class SpaceHeaterBoundUserInterface : BoundUserInterface
_window = this.CreateWindow<SpaceHeaterWindow>(); _window = this.CreateWindow<SpaceHeaterWindow>();
_window.ToggleStatusButton.OnToggled += _ => OnToggleStatusButtonPressed(); _window.ToggleStatusButton.OnPressed += _ => OnToggleStatusButtonPressed();
_window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta); _window.IncreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(_window.TemperatureChangeDelta);
_window.DecreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(-_window.TemperatureChangeDelta); _window.DecreaseTempRange.OnPressed += _ => OnTemperatureRangeChanged(-_window.TemperatureChangeDelta);
_window.ModeSelector.OnItemSelected += OnModeChanged; _window.ModeSelector.OnItemSelected += OnModeChanged;
@@ -34,6 +34,7 @@ public sealed class SpaceHeaterBoundUserInterface : BoundUserInterface
private void OnToggleStatusButtonPressed() private void OnToggleStatusButtonPressed()
{ {
_window?.SetActive(!_window.Active);
SendMessage(new SpaceHeaterToggleMessage()); SendMessage(new SpaceHeaterToggleMessage());
} }
@@ -1,15 +1,13 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
MinSize="280 160" Title="{Loc comp-space-heater-ui-title}"> MinSize="280 160" Title="{Loc comp-space-heater-ui-title}">
<BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10"> <BoxContainer Name="VboxContainer" Orientation="Vertical" Margin="5 5 5 5" SeparationOverride="10">
<BoxContainer Orientation="Horizontal" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
<controls:SwitchButton <BoxContainer Orientation="Horizontal" HorizontalExpand="True">
Name="ToggleStatusButton" <Button Text="{Loc comp-space-heater-ui-status-disabled}" Access="Public" Name="ToggleStatusButton"/>
HorizontalExpand="True" </BoxContainer>
Access="Public" />
<BoxContainer Orientation="Horizontal" SeparationOverride="5"> <BoxContainer Orientation="Horizontal" SeparationOverride="5">
<Label Text="{Loc comp-space-heater-ui-mode}"/> <Label Text="{Loc comp-space-heater-ui-mode}"/>
<OptionButton Access="Public" Name="ModeSelector"/> <OptionButton Access="Public" Name="ModeSelector"/>
@@ -15,6 +15,7 @@ public sealed partial class SpaceHeaterWindow : DefaultWindow
{ {
// To account for a minimum delta temperature for atmos equalization to trigger we use a fixed step for target temperature increment/decrement // To account for a minimum delta temperature for atmos equalization to trigger we use a fixed step for target temperature increment/decrement
public int TemperatureChangeDelta = 5; public int TemperatureChangeDelta = 5;
public bool Active;
// Temperatures range bounds in Kelvin (K) // Temperatures range bounds in Kelvin (K)
public float MinTemp; public float MinTemp;
@@ -48,7 +49,17 @@ public sealed partial class SpaceHeaterWindow : DefaultWindow
public void SetActive(bool active) public void SetActive(bool active)
{ {
Active = active;
ToggleStatusButton.Pressed = active; ToggleStatusButton.Pressed = active;
if (active)
{
ToggleStatusButton.Text = Loc.GetString("comp-space-heater-ui-status-enabled");
}
else
{
ToggleStatusButton.Text = Loc.GetString("comp-space-heater-ui-status-disabled");
}
} }
public void SetTemperature(float targetTemperature) public void SetTemperature(float targetTemperature)
@@ -1,7 +1,6 @@
using System.Linq; using System.Linq;
using Content.Shared.BarSign; using Content.Shared.BarSign;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
namespace Content.Client.BarSign.Ui; namespace Content.Client.BarSign.Ui;
@@ -17,12 +16,13 @@ public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : Bou
{ {
base.Open(); base.Open();
var sign = EntMan.GetComponentOrNull<BarSignComponent>(Owner)?.Current is { } current
? _prototype.Index(current)
: null;
var allSigns = BarSignSystem.GetAllBarSigns(_prototype) var allSigns = BarSignSystem.GetAllBarSigns(_prototype)
.OrderBy(p => Loc.GetString(p.Name)) .OrderBy(p => Loc.GetString(p.Name))
.ToList(); .ToList();
_menu = new(sign, allSigns);
_menu = this.CreateWindow<BarSignMenu>();
_menu.LoadSigns(allSigns);
_menu.OnSignSelected += id => _menu.OnSignSelected += id =>
{ {
@@ -30,17 +30,16 @@ public sealed class BarSignBoundUserInterface(EntityUid owner, Enum uiKey) : Bou
}; };
_menu.OnClose += Close; _menu.OnClose += Close;
_menu.OpenToLeft(); _menu.OpenCentered();
} }
public override void Update() public override void Update()
{ {
if (!EntMan.TryGetComponent<BarSignComponent>(Owner, out var signComp) if (!EntMan.TryGetComponent<BarSignComponent>(Owner, out var signComp))
|| !_prototype.Resolve(signComp.Current, out var signPrototype))
return; return;
_menu?.UpdateState(signPrototype); if (_prototype.Resolve(signComp.Current, out var signPrototype))
_menu?.UpdateState(signPrototype);
} }
} }
+18 -11
View File
@@ -8,13 +8,23 @@ namespace Content.Client.BarSign.Ui;
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class BarSignMenu : FancyWindow public sealed partial class BarSignMenu : FancyWindow
{ {
private List<BarSignPrototype> _cachedPrototypes = new(); private string? _currentId;
private readonly List<BarSignPrototype> _cachedPrototypes = new();
public event Action<string>? OnSignSelected; public event Action<string>? OnSignSelected;
public BarSignMenu() public BarSignMenu(BarSignPrototype? currentSign, List<BarSignPrototype> signs)
{ {
RobustXamlLoader.Load(this); RobustXamlLoader.Load(this);
_currentId = currentSign?.ID;
_cachedPrototypes.Clear();
_cachedPrototypes = signs;
foreach (var proto in _cachedPrototypes)
{
SignOptions.AddItem(Loc.GetString(proto.Name));
}
SignOptions.OnItemSelected += idx => SignOptions.OnItemSelected += idx =>
{ {
@@ -22,21 +32,18 @@ public sealed partial class BarSignMenu : FancyWindow
SignOptions.SelectId(idx.Id); SignOptions.SelectId(idx.Id);
}; };
} if (currentSign != null)
public void LoadSigns(List<BarSignPrototype> signs)
{
_cachedPrototypes.Clear();
_cachedPrototypes = signs;
foreach (var proto in _cachedPrototypes)
{ {
SignOptions.AddItem(Loc.GetString(proto.Name)); var idx = _cachedPrototypes.IndexOf(currentSign);
SignOptions.TrySelectId(idx);
} }
} }
public void UpdateState(BarSignPrototype newSign) public void UpdateState(BarSignPrototype newSign)
{ {
if (_currentId != null && newSign.ID == _currentId)
return;
_currentId = newSign.ID;
var idx = _cachedPrototypes.IndexOf(newSign); var idx = _cachedPrototypes.IndexOf(newSign);
SignOptions.TrySelectId(idx); SignOptions.TrySelectId(idx);
} }
@@ -0,0 +1,7 @@
using Content.Shared.Body.Systems;
namespace Content.Client.Body.Systems;
public sealed class BodySystem : SharedBodySystem
{
}
@@ -0,0 +1,6 @@
using Content.Shared.Body.Systems;
namespace Content.Client.Body.Systems;
/// <inheritdoc/>
public sealed class MetabolizerSystem : SharedMetabolizerSystem;
-264
View File
@@ -1,264 +0,0 @@
using System.Linq;
using Content.Shared.Body;
using Content.Shared.CCVar;
using Content.Shared.Humanoid.Markings;
using Content.Shared.Humanoid;
using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Shared.Configuration;
using Robust.Shared.Prototypes;
using Robust.Shared.Utility;
namespace Content.Client.Body;
public sealed class VisualBodySystem : SharedVisualBodySystem
{
[Dependency] private readonly IConfigurationManager _cfg = default!;
[Dependency] private readonly IPrototypeManager _prototype = default!;
[Dependency] private readonly MarkingManager _marking = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
public override void Initialize()
{
base.Initialize();
SubscribeLocalEvent<VisualOrganComponent, OrganGotInsertedEvent>(OnOrganGotInserted);
SubscribeLocalEvent<VisualOrganComponent, OrganGotRemovedEvent>(OnOrganGotRemoved);
SubscribeLocalEvent<VisualOrganComponent, AfterAutoHandleStateEvent>(OnOrganState);
SubscribeLocalEvent<VisualOrganMarkingsComponent, OrganGotInsertedEvent>(OnMarkingsGotInserted);
SubscribeLocalEvent<VisualOrganMarkingsComponent, OrganGotRemovedEvent>(OnMarkingsGotRemoved);
SubscribeLocalEvent<VisualOrganMarkingsComponent, AfterAutoHandleStateEvent>(OnMarkingsState);
SubscribeLocalEvent<VisualOrganMarkingsComponent, BodyRelayedEvent<HumanoidLayerVisibilityChangedEvent>>(OnMarkingsChangedVisibility);
Subs.CVar(_cfg, CCVars.AccessibilityClientCensorNudity, OnCensorshipChanged, true);
Subs.CVar(_cfg, CCVars.AccessibilityServerCensorNudity, OnCensorshipChanged, true);
}
private void OnCensorshipChanged(bool value)
{
var query = AllEntityQuery<OrganComponent, VisualOrganMarkingsComponent>();
while (query.MoveNext(out var ent, out var organComp, out var markingsComp))
{
if (organComp.Body is not { } body)
continue;
RemoveMarkings((ent, markingsComp), body);
ApplyMarkings((ent, markingsComp), body);
}
}
private void OnOrganGotInserted(Entity<VisualOrganComponent> ent, ref OrganGotInsertedEvent args)
{
ApplyVisual(ent, args.Target);
}
private void OnOrganGotRemoved(Entity<VisualOrganComponent> ent, ref OrganGotRemovedEvent args)
{
RemoveVisual(ent, args.Target);
}
private void OnOrganState(Entity<VisualOrganComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (Comp<OrganComponent>(ent).Body is not { } body)
return;
ApplyVisual(ent, body);
}
private void ApplyVisual(Entity<VisualOrganComponent> ent, EntityUid target)
{
if (!_sprite.LayerMapTryGet(target, ent.Comp.Layer, out var index, true))
return;
_sprite.LayerSetData(target, index, ent.Comp.Data);
}
private void RemoveVisual(Entity<VisualOrganComponent> ent, EntityUid target)
{
if (!_sprite.LayerMapTryGet(target, ent.Comp.Layer, out var index, true))
return;
_sprite.LayerSetRsiState(target, index, RSI.StateId.Invalid);
}
private void OnMarkingsGotInserted(Entity<VisualOrganMarkingsComponent> ent, ref OrganGotInsertedEvent args)
{
ApplyMarkings(ent, args.Target);
}
private void OnMarkingsGotRemoved(Entity<VisualOrganMarkingsComponent> ent, ref OrganGotRemovedEvent args)
{
RemoveMarkings(ent, args.Target);
}
private void OnMarkingsState(Entity<VisualOrganMarkingsComponent> ent, ref AfterAutoHandleStateEvent args)
{
if (Comp<OrganComponent>(ent).Body is not { } body)
return;
RemoveMarkings(ent, body);
ApplyMarkings(ent, body);
}
protected override void SetOrganColor(Entity<VisualOrganComponent> ent, Color color)
{
base.SetOrganColor(ent, color);
if (Comp<OrganComponent>(ent).Body is not { } body)
return;
ApplyVisual(ent, body);
}
protected override void SetOrganMarkings(Entity<VisualOrganMarkingsComponent> ent, Dictionary<HumanoidVisualLayers, List<Marking>> markings)
{
base.SetOrganMarkings(ent, markings);
if (Comp<OrganComponent>(ent).Body is not { } body)
return;
RemoveMarkings(ent, body);
ApplyMarkings(ent, body);
}
protected override void SetOrganAppearance(Entity<VisualOrganComponent> ent, PrototypeLayerData data)
{
base.SetOrganAppearance(ent, data);
if (Comp<OrganComponent>(ent).Body is not { } body)
return;
ApplyVisual(ent, body);
}
private IEnumerable<Marking> AllMarkings(Entity<VisualOrganMarkingsComponent> ent)
{
foreach (var markings in ent.Comp.Markings.Values)
{
foreach (var marking in markings)
{
yield return marking;
}
}
var censorNudity = _cfg.GetCVar(CCVars.AccessibilityClientCensorNudity) || _cfg.GetCVar(CCVars.AccessibilityServerCensorNudity);
if (!censorNudity)
yield break;
var group = _prototype.Index(ent.Comp.MarkingData.Group);
foreach (var layer in ent.Comp.MarkingData.Layers)
{
if (!group.Limits.TryGetValue(layer, out var layerLimits))
continue;
if (layerLimits.NudityDefault.Count < 1)
continue;
var markings = ent.Comp.Markings.GetValueOrDefault(layer) ?? [];
if (markings.Any(marking => _marking.TryGetMarking(marking, out var proto) && proto.BodyPart == layer))
continue;
foreach (var marking in layerLimits.NudityDefault)
{
yield return new(marking, 1);
}
}
}
private void ApplyMarkings(Entity<VisualOrganMarkingsComponent> ent, EntityUid target)
{
var applied = new List<Marking>();
foreach (var marking in AllMarkings(ent))
{
if (!_marking.TryGetMarking(marking, out var proto))
continue;
if (!_sprite.LayerMapTryGet(target, proto.BodyPart, out var index, true))
continue;
for (var i = 0; i < proto.Sprites.Count; i++)
{
var sprite = proto.Sprites[i];
DebugTools.Assert(sprite is SpriteSpecifier.Rsi);
if (sprite is not SpriteSpecifier.Rsi rsi)
continue;
var layerId = $"{proto.ID}-{rsi.RsiState}";
if (!_sprite.LayerMapTryGet(target, layerId, out _, false))
{
var layer = _sprite.AddLayer(target, sprite, index + i + 1);
_sprite.LayerMapSet(target, layerId, layer);
_sprite.LayerSetSprite(target, layerId, rsi);
}
if (marking.MarkingColors is not null && i < marking.MarkingColors.Count)
_sprite.LayerSetColor(target, layerId, marking.MarkingColors[i]);
else
_sprite.LayerSetColor(target, layerId, Color.White);
}
applied.Add(marking);
}
ent.Comp.AppliedMarkings = applied;
}
private void RemoveMarkings(Entity<VisualOrganMarkingsComponent> ent, EntityUid target)
{
foreach (var marking in ent.Comp.AppliedMarkings)
{
if (!_marking.TryGetMarking(marking, out var proto))
continue;
foreach (var sprite in proto.Sprites)
{
DebugTools.Assert(sprite is SpriteSpecifier.Rsi);
if (sprite is not SpriteSpecifier.Rsi rsi)
continue;
var layerId = $"{proto.ID}-{rsi.RsiState}";
if (!_sprite.LayerMapTryGet(target, layerId, out var index, false))
continue;
_sprite.LayerMapRemove(target, layerId);
_sprite.RemoveLayer(target, index);
}
}
}
private void OnMarkingsChangedVisibility(Entity<VisualOrganMarkingsComponent> ent, ref BodyRelayedEvent<HumanoidLayerVisibilityChangedEvent> args)
{
if (!ent.Comp.HideableLayers.Contains(args.Args.Layer))
return;
foreach (var markings in ent.Comp.Markings.Values)
{
foreach (var marking in markings)
{
if (!_marking.TryGetMarking(marking, out var proto))
continue;
if (proto.BodyPart != args.Args.Layer && !(ent.Comp.DependentHidingLayers.TryGetValue(args.Args.Layer, out var dependent) && dependent.Contains(proto.BodyPart)))
continue;
foreach (var sprite in proto.Sprites)
{
DebugTools.Assert(sprite is SpriteSpecifier.Rsi);
if (sprite is not SpriteSpecifier.Rsi rsi)
continue;
var layerId = $"{proto.ID}-{rsi.RsiState}";
if (!_sprite.LayerMapTryGet(args.Body.Owner, layerId, out var index, true))
continue;
_sprite.LayerSetVisible(args.Body.Owner, index, args.Args.Visible);
}
}
}
}
}
@@ -2,7 +2,6 @@ using Content.Client.Cargo.UI;
using Content.Shared.Cargo.Components; using Content.Shared.Cargo.Components;
using JetBrains.Annotations; using JetBrains.Annotations;
using Robust.Client.UserInterface; using Robust.Client.UserInterface;
using Content.Shared._DV.Cargo.Components; // DeltaV: Bounty claim messages
namespace Content.Client.Cargo.BUI; namespace Content.Client.Cargo.BUI;
@@ -31,18 +30,6 @@ public sealed class CargoBountyConsoleBoundUserInterface : BoundUserInterface
{ {
SendMessage(new BountySkipMessage(id)); SendMessage(new BountySkipMessage(id));
}; };
// DeltaV: bounty claim stuff begins
_menu.OnClaimButtonPressed += id =>
{
SendMessage(new BountyClaimedMessage(id));
};
_menu.OnStatusOptionSelected += (id, status) =>
{
SendMessage(new BountySetStatusMessage(id, status));
};
// DeltaV: bounty claim stuff ends
} }
protected override void UpdateState(BoundUserInterfaceState message) protected override void UpdateState(BoundUserInterfaceState message)
@@ -70,9 +70,9 @@ namespace Content.Client.Cargo.BUI
_menu.OnClose += Close; _menu.OnClose += Close;
_menu.OnItemSelected += (row) => _menu.OnItemSelected += (args) =>
{ {
if (row == null) if (args.Button.Parent is not CargoProductRow row)
return; return;
description.Clear(); description.Clear();
@@ -175,23 +175,23 @@ namespace Content.Client.Cargo.BUI
return true; return true;
} }
private void RemoveOrder(CargoOrderData? order) private void RemoveOrder(ButtonEventArgs args)
{ {
if (order == null) if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null)
return; return;
SendMessage(new CargoConsoleRemoveOrderMessage(order.OrderId)); SendMessage(new CargoConsoleRemoveOrderMessage(row.Order.OrderId));
} }
private void ApproveOrder(CargoOrderData? order) private void ApproveOrder(ButtonEventArgs args)
{ {
if (order == null) if (args.Button.Parent?.Parent is not CargoOrderRow row || row.Order == null)
return; return;
if (OrderCount >= OrderCapacity) if (OrderCount >= OrderCapacity)
return; return;
SendMessage(new CargoConsoleApproveOrderMessage(order.OrderId)); SendMessage(new CargoConsoleApproveOrderMessage(row.Order.OrderId));
} }
} }
} }
+4 -14
View File
@@ -10,8 +10,6 @@
<BoxContainer Orientation="Vertical" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" HorizontalExpand="True">
<RichTextLabel Name="RewardLabel"/> <RichTextLabel Name="RewardLabel"/>
<RichTextLabel Name="ManifestLabel"/> <RichTextLabel Name="ManifestLabel"/>
<RichTextLabel Name="ClaimedBylabel"/> <!-- DeltaV bounty claim -->
<RichTextLabel Name="StatusLabel"/> <!-- DeltaV bounty claim -->
</BoxContainer> </BoxContainer>
<Control MinWidth="10"/> <Control MinWidth="10"/>
<BoxContainer Orientation="Vertical" MinWidth="120"> <BoxContainer Orientation="Vertical" MinWidth="120">
@@ -19,22 +17,14 @@
<Button Name="PrintButton" <Button Name="PrintButton"
Text="{Loc 'bounty-console-label-button-text'}" Text="{Loc 'bounty-console-label-button-text'}"
HorizontalExpand="False" HorizontalExpand="False"
StyleClasses="OpenBoth"/> HorizontalAlignment="Right"
StyleClasses="OpenRight"/>
<Button Name="SkipButton" <Button Name="SkipButton"
Text="{Loc 'bounty-console-skip-button-text'}" Text="{Loc 'bounty-console-skip-button-text'}"
HorizontalExpand="True" HorizontalExpand="False"
HorizontalAlignment="Right"
StyleClasses="OpenLeft"/> StyleClasses="OpenLeft"/>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal" MinWidth="120"> <!-- Begin DeltaV bounty claim content -->
<Button Name="ClaimButton"
Text= "{Loc 'bounty-console-claim-button-text'}"
HorizontalExpand="False"
StyleClasses="OpenRight"/>
<OptionButton Name="BountyStatusSelector"
Access="Public"
StyleClasses="OpenBoth"
HorizontalExpand="True"/>
</BoxContainer> <!-- End DeltaV content-->
<RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/> <RichTextLabel Name="IdLabel" HorizontalAlignment="Right" Margin="0 0 5 0"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
@@ -17,8 +17,6 @@ public sealed partial class BountyEntry : BoxContainer
public Action? OnLabelButtonPressed; public Action? OnLabelButtonPressed;
public Action? OnSkipButtonPressed; public Action? OnSkipButtonPressed;
public Action? OnClaimButtonPressed; // DeltaV
public Action? OnStatusOptionSelected; // DeltaV
public TimeSpan EndTime; public TimeSpan EndTime;
public TimeSpan UntilNextSkip; public TimeSpan UntilNextSkip;
@@ -47,20 +45,6 @@ public sealed partial class BountyEntry : BoxContainer
PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke(); PrintButton.OnPressed += _ => OnLabelButtonPressed?.Invoke();
SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke(); SkipButton.OnPressed += _ => OnSkipButtonPressed?.Invoke();
// Begin DeltaV bounty claiming
ClaimButton.OnPressed += _ => OnClaimButtonPressed?.Invoke();
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Undelivered)}"), 0);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.Waiting)}"), 1);
BountyStatusSelector.AddItem(Loc.GetString($"bounty-console-status-{nameof(CargoBountyStatus.OnShuttle)}"), 2);
BountyStatusSelector.Select((int) bounty.Status);
BountyStatusSelector.ToolTip = Loc.GetString($"bounty-console-status-tooltip-{bounty.Status.ToString()}");
var claimedByText = string.IsNullOrEmpty(bounty.ClaimedBy) ? Loc.GetString("bounty-console-claimed-by-none") : bounty.ClaimedBy;
ClaimedBylabel.SetMarkup(Loc.GetString("bounty-console-claimed-by", ("claimant", claimedByText)));
StatusLabel.SetMarkup(Loc.GetString($"bounty-console-status-formatted-{bounty.Status.ToString()}"));
// End DeltaV bounty claiming
} }
private void UpdateSkipButton(float deltaSeconds) private void UpdateSkipButton(float deltaSeconds)
+2 -3
View File
@@ -1,9 +1,8 @@
<!--DeltaV - SetSize was 550, now 750 to accomodate bounty claim UI --> <controls:FancyWindow xmlns="https://spacestation14.io"
<controls:FancyWindow xmlns="https://spacestation14.io"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
Title="{Loc 'bounty-console-menu-title'}" Title="{Loc 'bounty-console-menu-title'}"
SetSize="750 420" SetSize="550 420"
MinSize="400 350"> MinSize="400 350">
<BoxContainer Orientation="Vertical" <BoxContainer Orientation="Vertical"
VerticalExpand="True" VerticalExpand="True"
@@ -11,8 +11,6 @@ public sealed partial class CargoBountyMenu : FancyWindow
{ {
public Action<string>? OnLabelButtonPressed; public Action<string>? OnLabelButtonPressed;
public Action<string>? OnSkipButtonPressed; public Action<string>? OnSkipButtonPressed;
public Action<string>? OnClaimButtonPressed; // DeltaV
public Action<string, int>? OnStatusOptionSelected; // DeltaV
public CargoBountyMenu() public CargoBountyMenu()
{ {
@@ -33,8 +31,6 @@ public sealed partial class CargoBountyMenu : FancyWindow
var entry = new BountyEntry(b, untilNextSkip); var entry = new BountyEntry(b, untilNextSkip);
entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id); entry.OnLabelButtonPressed += () => OnLabelButtonPressed?.Invoke(b.Id);
entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id); entry.OnSkipButtonPressed += () => OnSkipButtonPressed?.Invoke(b.Id);
entry.OnClaimButtonPressed += () => OnClaimButtonPressed?.Invoke(b.Id); // DeltaV
entry.BountyStatusSelector.OnItemSelected += args => OnStatusOptionSelected?.Invoke(b.Id, args.Id); // DeltaV
BountyEntriesContainer.AddChild(entry); BountyEntriesContainer.AddChild(entry);
} }
+83 -223
View File
@@ -1,226 +1,86 @@
<controls:FancyWindow <controls:FancyWindow xmlns="https://spacestation14.io"
xmlns="https://spacestation14.io" xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls"
xmlns:controls="clr-namespace:Content.Client.UserInterface.Controls" SetSize="600 600"
MinSize="540 390" MinSize="600 600">
SetSize="995 600"> <BoxContainer Orientation="Vertical" Margin="15 5 15 10">
<BoxContainer Orientation="Horizontal">
<!-- Main Container --> <Label Text="{Loc 'cargo-console-menu-account-name-label'}"
<BoxContainer Orientation="Vertical" StyleClasses="LabelKeyText" />
VerticalExpand="True"> <RichTextLabel Name="AccountNameLabel"
Text="{Loc 'cargo-console-menu-account-name-none-text'}" />
<TabContainer Name="TabContainer" VerticalExpand="True">
<!-- Sub-Main Container -->
<BoxContainer Orientation="Horizontal"
VerticalExpand="True"
Margin="8 4 8 6">
<!-- Left Part -->
<BoxContainer Orientation="Vertical"
SeparationOverride="4"
Margin="0 0 8 0"
HorizontalExpand="True">
<!-- Info -->
<BoxContainer Orientation="Vertical">
<GridContainer Columns="3">
<!-- Account -->
<Label Text="{Loc 'cargo-console-menu-account-name-label'}"
StyleClasses="LabelKeyText" />
<PanelContainer StyleClasses="LowDivider" Margin="0 -2"/>
<RichTextLabel Name="AccountNameLabel"
Text="{Loc 'cargo-console-menu-account-name-none-text'}"
Margin="4 0"/>
<!-- Balance -->
<Label Text="{Loc 'cargo-console-menu-points-label'}"
StyleClasses="LabelKeyText"/>
<PanelContainer StyleClasses="LowDivider" Margin="0 -2"/>
<RichTextLabel Name="PointsLabel"
Text="$0"
Margin="4 0" />
<!-- Orders Count/Capacity -->
<Label Text="{Loc 'cargo-console-menu-order-capacity-label'}"
StyleClasses="LabelKeyText" />
<PanelContainer StyleClasses="LowDivider" Margin="0 -2 0 -1"/>
<Label Name="ShuttleCapacityLabel"
Text="0/20"
Margin="4 0"/>
</GridContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 4.5 -8 0"/>
</BoxContainer>
<!-- Search -->
<BoxContainer Orientation="Horizontal"
Margin="0 2 0 0">
<LineEdit Name="SearchBar"
PlaceHolder="{Loc 'cargo-console-menu-search-bar-placeholder'}"
HorizontalExpand="True" />
<OptionButton Name="Categories"
Prefix="{Loc 'cargo-console-menu-categories-label'}"
StyleClasses="OpenLeft"/>
</BoxContainer>
<!-- Product list -->
<ScrollContainer
HorizontalExpand="False"
VerticalExpand="True"
HScrollEnabled="False">
<BoxContainer Name="Products"
Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Products get added here by code -->
</BoxContainer>
</ScrollContainer>
</BoxContainer>
<PanelContainer StyleClasses="LowDivider" Margin="0 -8"/>
<!-- Right Part -->
<BoxContainer Orientation="Vertical"
SizeFlagsStretchRatio="0.8"
HorizontalExpand="True"
Name="RightPart">
<!-- Requests Part -->
<BoxContainer Orientation="Vertical"
VerticalExpand="True"
SizeFlagsStretchRatio="2">
<!-- Title -->
<controls:StripeBack>
<Label Text="{Loc 'cargo-console-menu-requests-label'}"
HorizontalAlignment="Center"
Margin="4"/>
</controls:StripeBack>
<PanelContainer VerticalExpand="True"
Margin="0 -4 0 0">
<!-- Background -->
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#040404" />
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical">
<ScrollContainer VerticalExpand="True">
<BoxContainer Name="Requests"
Orientation="Vertical"
StyleClasses="transparentItemList"
VerticalExpand="True"
SeparationOverride="8"
Margin="8">
<!-- Requests are added here by code -->
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>
<!-- Orders Part -->
<BoxContainer Orientation="Vertical"
VerticalExpand="True">
<!-- Title -->
<controls:StripeBack>
<Label Text="{Loc 'cargo-console-menu-orders-label'}"
HorizontalAlignment="Center"
Margin="4"/>
</controls:StripeBack>
<PanelContainer VerticalExpand="True"
Margin="0 -4 0 0">
<!-- Background -->
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#040404" />
</PanelContainer.PanelOverride>
<BoxContainer Orientation="Vertical"
Margin="6">
<ScrollContainer VerticalExpand="True">
<BoxContainer Orientation="Vertical"
StyleClasses="transparentItemList"
VerticalExpand="True"
SeparationOverride="6">
<!-- Orders are added here by code -->
</BoxContainer>
</ScrollContainer>
</BoxContainer>
</PanelContainer>
</BoxContainer>
</BoxContainer>
</BoxContainer>
<!-- Funds tab -->
<BoxContainer Orientation="Vertical" Margin="15">
<BoxContainer Orientation="Horizontal">
<RichTextLabel Name="TransferLimitLabel" Margin="0 0 15 0"/>
<RichTextLabel Name="UnlimitedNotifier" Text="{Loc 'cargo-console-menu-account-action-transfer-limit-unlimited-notifier'}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<RichTextLabel Text="{Loc 'cargo-console-menu-account-action-select'}" Margin="0 0 10 0"/>
<OptionButton Name="ActionOptions"/>
</BoxContainer>
<Control MinHeight="5"/>
<BoxContainer Orientation="Horizontal">
<RichTextLabel Name="AmountText" Text="{ Loc 'cargo-console-menu-account-action-amount'}"/>
<SpinBox Name="TransferSpinBox" MinWidth="100" Value="10"/>
</BoxContainer>
<Control MinHeight="15"/>
<BoxContainer HorizontalAlignment="Center">
<Button Name="AccountActionButton" Text="{ Loc 'cargo-console-menu-account-action-button'}" MinHeight="45" MinWidth="120"/>
</BoxContainer>
<Control VerticalExpand="True"/>
<BoxContainer VerticalAlignment="Bottom" HorizontalAlignment="Center">
<Button Name="AccountLimitToggleButton" Text="{ Loc 'cargo-console-menu-toggle-account-lock-button'}" MinHeight="45" MinWidth="120"/>
</BoxContainer>
</BoxContainer>
</TabContainer>
<!-- Footer -->
<!-- TODO: Create customControls element -->
<BoxContainer Orientation="Vertical"
VerticalAlignment="Bottom">
<PanelContainer StyleClasses="LowDivider" />
<BoxContainer Orientation="Horizontal"
Margin="12 0 6 2"
VerticalAlignment="Bottom">
<!-- Footer title -->
<Label Text="{Loc 'cargo-console-menu-flavor-left'}"
StyleClasses="WindowFooterText" />
<!-- Version -->
<Label Text="{Loc 'cargo-console-menu-flavor-right'}"
StyleClasses="WindowFooterText"
HorizontalAlignment="Right"
HorizontalExpand="True"
Margin="0 0 4 0" />
<TextureRect StyleClasses="NTLogoDark"
Stretch="KeepAspectCentered"
VerticalAlignment="Center"
HorizontalAlignment="Right"
SetSize="19 19"/>
</BoxContainer>
</BoxContainer> </BoxContainer>
<BoxContainer Orientation="Horizontal">
<Label Text="{Loc 'cargo-console-menu-points-label'}"
StyleClasses="LabelKeyText" />
<RichTextLabel Name="PointsLabel"
Text="$0" />
</BoxContainer>
<Control MinHeight="10"/>
<TabContainer Name="TabContainer" VerticalExpand="True">
<BoxContainer Orientation="Vertical" VerticalExpand="True">
<BoxContainer Orientation="Horizontal">
<OptionButton Name="Categories"
Prefix="{Loc 'cargo-console-menu-categories-label'}"
HorizontalExpand="True" />
<LineEdit Name="SearchBar"
PlaceHolder="{Loc 'cargo-console-menu-search-bar-placeholder'}"
HorizontalExpand="True" />
</BoxContainer>
<Control MinHeight="5"/>
<ScrollContainer HorizontalExpand="True"
VerticalExpand="True"
SizeFlagsStretchRatio="2">
<BoxContainer Name="Products"
Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<!-- Products get added here by code -->
</BoxContainer>
</ScrollContainer>
<Control MinHeight="5" Name="OrdersSpacer"/>
<PanelContainer VerticalExpand="True"
SizeFlagsStretchRatio="1"
Name="Orders">
<PanelContainer.PanelOverride>
<gfx:StyleBoxFlat BackgroundColor="#000000" />
</PanelContainer.PanelOverride>
<ScrollContainer VerticalExpand="True">
<BoxContainer Orientation="Vertical" Margin="5">
<Label Text="{Loc 'cargo-console-menu-requests-label'}" />
<BoxContainer Name="Requests"
Orientation="Vertical"
VerticalExpand="True">
<!-- Requests are added here by code -->
</BoxContainer>
</BoxContainer>
</ScrollContainer>
</PanelContainer>
</BoxContainer>
<!-- Funds tab -->
<BoxContainer Orientation="Vertical" Margin="15">
<BoxContainer Orientation="Horizontal">
<RichTextLabel Name="TransferLimitLabel" Margin="0 0 15 0"/>
<RichTextLabel Name="UnlimitedNotifier" Text="{Loc 'cargo-console-menu-account-action-transfer-limit-unlimited-notifier'}"/>
</BoxContainer>
<BoxContainer Orientation="Horizontal">
<RichTextLabel Text="{Loc 'cargo-console-menu-account-action-select'}" Margin="0 0 10 0"/>
<OptionButton Name="ActionOptions"/>
</BoxContainer>
<Control MinHeight="5"/>
<BoxContainer Orientation="Horizontal">
<RichTextLabel Name="AmountText" Text="{ Loc 'cargo-console-menu-account-action-amount'}"/>
<SpinBox Name="TransferSpinBox" MinWidth="100" Value="10"/>
</BoxContainer>
<Control MinHeight="15"/>
<BoxContainer HorizontalAlignment="Center">
<Button Name="AccountActionButton" Text="{ Loc 'cargo-console-menu-account-action-button'}" MinHeight="45" MinWidth="120"/>
</BoxContainer>
<Control VerticalExpand="True"/>
<BoxContainer VerticalAlignment="Bottom" HorizontalAlignment="Center">
<Button Name="AccountLimitToggleButton" Text="{ Loc 'cargo-console-menu-toggle-account-lock-button'}" MinHeight="45" MinWidth="120"/>
</BoxContainer>
</BoxContainer>
</TabContainer>
</BoxContainer> </BoxContainer>
</controls:FancyWindow> </controls:FancyWindow>
@@ -6,7 +6,6 @@ using Content.Shared.Cargo.Components;
using Content.Shared.Cargo.Prototypes; using Content.Shared.Cargo.Prototypes;
using Robust.Client.AutoGenerated; using Robust.Client.AutoGenerated;
using Robust.Client.GameObjects; using Robust.Client.GameObjects;
using Robust.Client.Graphics;
using Robust.Client.UserInterface.Controls; using Robust.Client.UserInterface.Controls;
using Robust.Client.UserInterface.XAML; using Robust.Client.UserInterface.XAML;
using Robust.Shared.Prototypes; using Robust.Shared.Prototypes;
@@ -30,9 +29,9 @@ namespace Content.Client.Cargo.UI
private readonly EntityQuery<CargoOrderConsoleComponent> _orderConsoleQuery; private readonly EntityQuery<CargoOrderConsoleComponent> _orderConsoleQuery;
private readonly EntityQuery<StationBankAccountComponent> _bankQuery; private readonly EntityQuery<StationBankAccountComponent> _bankQuery;
public event Action<CargoProductRow?>? OnItemSelected; public event Action<ButtonEventArgs>? OnItemSelected;
public event Action<CargoOrderData?>? OnOrderApproved; public event Action<ButtonEventArgs>? OnOrderApproved;
public event Action<CargoOrderData?>? OnOrderCanceled; public event Action<ButtonEventArgs>? OnOrderCanceled;
public event Action<ProtoId<CargoAccountPrototype>?, int>? OnAccountAction; public event Action<ProtoId<CargoAccountPrototype>?, int>? OnAccountAction;
@@ -165,7 +164,7 @@ namespace Content.Client.Cargo.UI
}; };
button.MainButton.OnPressed += args => button.MainButton.OnPressed += args =>
{ {
OnItemSelected?.Invoke(button); OnItemSelected?.Invoke(args);
}; };
Products.AddChild(button); Products.AddChild(button);
} }
@@ -211,66 +210,38 @@ namespace Content.Client.Cargo.UI
foreach (var order in orders) foreach (var order in orders)
{ {
if (order.Approved || !_protoManager.Resolve(order.Product, out var productProto)) if (order.Approved)
continue; continue;
var product = _protoManager.Index<EntityPrototype>(productProto.Product); var product = _protoManager.Index<EntityPrototype>(order.ProductId);
var productName = productProto.Name; var productName = product.Name;
var requester = !string.IsNullOrEmpty(order.Requester) ?
order.Requester : Loc.GetString("cargo-console-menu-order-row-alerts-requester-unknown");
var account = _protoManager.Index(order.Account); var account = _protoManager.Index(order.Account);
var row = new CargoOrderRow var row = new CargoOrderRow
{ {
Order = order, Order = order,
Title =
{
Text = Loc.GetString(
"cargo-console-menu-order-row-title",
("productName", productName),
("orderAmount", order.OrderQuantity),
("orderPrice", productProto.Cost)),
},
Stride =
{
PanelOverride = new StyleBoxFlat
{
BackgroundColor = account.Color,
ContentMarginBottomOverride = 2,
},
},
Icon = { Texture = _spriteSystem.Frame0(product) }, Icon = { Texture = _spriteSystem.Frame0(product) },
ProductName = ProductName =
{ {
Text = Loc.GetString( Text = Loc.GetString(
"cargo-console-menu-populate-orders-cargo-order-row-product-name-text", "cargo-console-menu-populate-orders-cargo-order-row-product-name-text",
("orderRequester", requester), ("productName", productName),
("orderAmount", order.OrderQuantity),
("orderRequester", order.Requester),
("accountColor", account.Color), ("accountColor", account.Color),
("account", Loc.GetString(account.Code))) ("account", Loc.GetString(account.Code)))
}, },
Description = Description =
{ {
Text = !string.IsNullOrEmpty(order.Reason) ? Text = Loc.GetString("cargo-console-menu-order-reason-description",
Loc.GetString( ("reason", order.Reason))
"cargo-console-menu-order-row-product-description",
("orderReason", order.Reason))
:
Loc.GetString(
"cargo-console-menu-order-row-product-description",
("orderReason", Loc.GetString("cargo-console-menu-order-row-alerts-reason-absent")))
} }
}; };
row.Cancel.OnPressed += (args) => { OnOrderCanceled?.Invoke(args); };
row.Cancel.OnPressed += (args) => { OnOrderCanceled?.Invoke(order); };
// TODO: Disable based on access. // TODO: Disable based on access.
row.SetApproveVisible(orderConsole.Mode != CargoOrderConsoleMode.SendToPrimary); row.SetApproveVisible(orderConsole.Mode != CargoOrderConsoleMode.SendToPrimary);
row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(order); }; row.Approve.OnPressed += (args) => { OnOrderApproved?.Invoke(args); };
Requests.AddChild(row); Requests.AddChild(row);
} }
} }
@@ -323,7 +294,8 @@ namespace Content.Client.Cargo.UI
TransferSpinBox.Value > bankAccount.Accounts[orderConsole.Account] * orderConsole.TransferLimit || TransferSpinBox.Value > bankAccount.Accounts[orderConsole.Account] * orderConsole.TransferLimit ||
_timing.CurTime < orderConsole.NextAccountActionTime; _timing.CurTime < orderConsole.NextAccountActionTime;
RightPart.Visible = orderConsole.Mode != CargoOrderConsoleMode.PrintSlip; OrdersSpacer.Visible = orderConsole.Mode != CargoOrderConsoleMode.PrintSlip;
Orders.Visible = orderConsole.Mode != CargoOrderConsoleMode.PrintSlip;
} }
} }
} }
@@ -1,53 +1,33 @@
<DefaultWindow xmlns="https://spacestation14.io" <DefaultWindow xmlns="https://spacestation14.io"
Title="{Loc 'cargo-console-order-menu-title'}" Title="{Loc 'cargo-console-order-menu-title'}">
MinSize="460 261">
<BoxContainer Orientation="Vertical"> <BoxContainer Orientation="Vertical">
<GridContainer Columns="2"> <GridContainer Columns="2">
<Label Text="{Loc 'cargo-console-order-menu-product-label'}" <Label Text="{Loc 'cargo-console-order-menu-product-label'}" />
StyleClasses="LabelKeyText" />
<Label Name="ProductName" <Label Name="ProductName"
Access="Public" /> Access="Public" />
<Label Text="{Loc 'cargo-console-order-menu-description-label'}" />
<Label Text="{Loc 'cargo-console-order-menu-description-label'}"
StyleClasses="LabelKeyText" />
<RichTextLabel Name="Description" <RichTextLabel Name="Description"
Access="Public" Access="Public"
HorizontalExpand="True" VerticalExpand="True"
MaxWidth="460" /> SetWidth="350"/>
<Label Text="{Loc 'cargo-console-order-menu-cost-label'}" />
<Label Text="{Loc 'cargo-console-order-menu-cost-label'}"
StyleClasses="LabelKeyText" />
<Label Name="PointCost" <Label Name="PointCost"
Access="Public" /> Access="Public" />
<Label Text="{Loc 'cargo-console-order-menu-requester-label'}" />
<Label Text="{Loc 'cargo-console-order-menu-requester-label'}"
StyleClasses="LabelKeyText" />
<LineEdit Name="Requester" <LineEdit Name="Requester"
Access="Public" /> Access="Public" />
<Label Text="{Loc 'cargo-console-order-menu-reason-label'}" />
<Label Text="{Loc 'cargo-console-order-menu-reason-label'}"
StyleClasses="LabelKeyText" />
<LineEdit Name="Reason" <LineEdit Name="Reason"
Access="Public" /> Access="Public" />
<Label Text="{Loc 'cargo-console-order-menu-amount-label'}" />
<Label Text="{Loc 'cargo-console-order-menu-amount-label'}"
StyleClasses="LabelKeyText" />
<SpinBox Name="Amount" <SpinBox Name="Amount"
Access="Public" Access="Public"
HorizontalExpand="True" HorizontalExpand="True"
Value="1" /> Value="1" />
</GridContainer> </GridContainer>
<Control VerticalExpand="True"/>
<PanelContainer StyleClasses="LowDivider" Margin="0 6 0 2"/>
<Button Name="SubmitButton" <Button Name="SubmitButton"
Access="Public" Access="Public"
Text="{Loc 'cargo-console-order-menu-submit-button'}" Text="{Loc 'cargo-console-order-menu-submit-button'}"
VerticalAlignment="Bottom" /> TextAlign="Center" />
</BoxContainer> </BoxContainer>
</DefaultWindow> </DefaultWindow>
+30 -78
View File
@@ -1,81 +1,33 @@
<PanelContainer <PanelContainer xmlns="https://spacestation14.io"
xmlns="https://spacestation14.io" HorizontalExpand="True"
xmlns:gfx="clr-namespace:Robust.Client.Graphics;assembly=Robust.Client" Margin="0 1">
HorizontalExpand="True" <BoxContainer Orientation="Horizontal"
StyleClasses="BackgroundPanel"> HorizontalExpand="True">
<TextureRect Name="Icon"
<!-- Main Container --> Access="Public"
<BoxContainer Orientation="Vertical" MinSize="32 32"
HorizontalExpand="True" RectClipContent="True" />
SeparationOverride="6" <Control MinWidth="5"/>
Margin="-14 -2"> <BoxContainer Orientation="Vertical"
HorizontalExpand="True"
<BoxContainer Orientation="Vertical"> VerticalExpand="True">
<Control> <RichTextLabel Name="ProductName"
<PanelContainer StyleClasses="WindowHeadingBackground" /> Access="Public"
HorizontalExpand="True"
<BoxContainer Margin="6"> StyleClasses="LabelSubText" />
<Label Name="Title" <Label Name="Description"
Access="Public" Access="Public"
MaxHeight="28" HorizontalExpand="True"
StyleClasses="LabelKeyText"/> StyleClasses="LabelSubText"
</BoxContainer> ClipText="True" />
</Control> </BoxContainer>
<Button Name="Approve"
<PanelContainer Name="Stride"
Access="Public" Access="Public"
StyleClasses="LowDivider" /> Text="{Loc 'cargo-console-menu-cargo-order-row-approve-button'}"
</BoxContainer> StyleClasses="OpenRight" />
<Button Name="Cancel"
<!-- Info --> Access="Public"
<BoxContainer> Text="{Loc 'cargo-console-menu-cargo-order-row-cancel-button'}"
<TextureRect Name="Icon" StyleClasses="OpenLeft" />
Access="Public"
MinSize="32 32"
Margin="4"
Stretch="KeepAspectCentered"
RectClipContent="True"
VerticalAlignment="Center"/>
<PanelContainer StyleClasses="LowDivider" Margin="4 0"/>
<BoxContainer Orientation="Vertical"
HorizontalExpand="True"
VerticalExpand="True">
<RichTextLabel Name="ProductName"
Access="Public"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="LabelSubText" />
<Label Name="Description"
Access="Public"
HorizontalExpand="True"
VerticalExpand="True"
StyleClasses="LabelSubText"
ClipText="True" />
</BoxContainer>
</BoxContainer>
<BoxContainer Orientation="Vertical">
<PanelContainer StyleClasses="LowDivider" />
<!-- Buttons -->
<!-- Btn's position hardcoded (args.Button.Parent?.Parent?.Parent type) in CargoConsoleBUI 158 & 166 line -->
<BoxContainer Margin="6">
<Button Name="Approve"
Access="Public"
Text="{Loc 'cargo-console-menu-order-row-button-approve'}"
StyleClasses="OpenRight"
HorizontalExpand="True"/>
<Button Name="Cancel"
Access="Public"
Text="{Loc 'cargo-console-menu-order-row-button-cancel'}"
StyleClasses="OpenLeft"
HorizontalExpand="True" />
</BoxContainer>
</BoxContainer>
</BoxContainer> </BoxContainer>
</PanelContainer> </PanelContainer>
+25 -33
View File
@@ -1,34 +1,26 @@
<BoxContainer xmlns="https://spacestation14.io" <PanelContainer xmlns="https://spacestation14.io"
HorizontalExpand="True"> HorizontalExpand="True">
<PanelContainer HorizontalExpand="True"> <Button Name="MainButton"
<!-- Btn position hardcoded (args.Button.Parent?.Parent type) in CargoConsoleBUI 71 line --> ToolTip=""
<Button Name="MainButton" Access="Public"
ToolTip="" HorizontalExpand="True"
Access="Public" VerticalExpand="True"
VerticalExpand="False" StyleClasses="OpenBoth"/>
StyleClasses="OpenBoth" /> <BoxContainer Orientation="Horizontal"
HorizontalExpand="True">
<!-- Icon & Name --> <TextureRect Name="Icon"
<BoxContainer Orientation="Horizontal" Access="Public"
HorizontalExpand="True" MinSize="32 32"
Margin="4 0"> RectClipContent="True" />
<Label Name="ProductName"
<TextureRect Name="Icon" Access="Public"
Access="Public" HorizontalExpand="True" />
MinSize="32 32" <PanelContainer StyleClasses="BackgroundDark">
RectClipContent="True" /> <Label Name="PointCost"
<Label Name="ProductName"
Access="Public" Access="Public"
HorizontalExpand="True" MinSize="52 32"
ClipText="True" /> Align="Right"
</BoxContainer> Margin="0 0 5 0"/>
</PanelContainer> </PanelContainer>
</BoxContainer>
<Label Name="PointCost" </PanelContainer>
Access="Public"
MinSize="56 32"
Align="Right"
Margin="0 0 5 0"
HorizontalAlignment="Right"/>
</BoxContainer>
@@ -7,7 +7,7 @@ using Robust.Client.UserInterface.XAML;
namespace Content.Client.Cargo.UI namespace Content.Client.Cargo.UI
{ {
[GenerateTypedNameReferences] [GenerateTypedNameReferences]
public sealed partial class CargoProductRow : BoxContainer public sealed partial class CargoProductRow : PanelContainer
{ {
public CargoProductPrototype? Product { get; set; } public CargoProductPrototype? Product { get; set; }
@@ -34,10 +34,7 @@ namespace Content.Client.Cargo.UI
foreach (var order in orders) foreach (var order in orders)
{ {
if (!protoManager.Resolve(order.Product, out var productProto)) var product = protoManager.Index<EntityPrototype>(order.ProductId);
continue;
var product = protoManager.Index<EntityPrototype>(productProto.Product);
var productName = product.Name; var productName = product.Name;
var account = protoManager.Index(order.Account); var account = protoManager.Index(order.Account);
@@ -9,6 +9,7 @@ public sealed class TypingIndicatorVisualizerSystem : VisualizerSystem<TypingInd
{ {
[Dependency] private readonly IPrototypeManager _prototypeManager = default!; [Dependency] private readonly IPrototypeManager _prototypeManager = default!;
[Dependency] private readonly InventorySystem _inventory = default!; [Dependency] private readonly InventorySystem _inventory = default!;
[Dependency] private readonly SpriteSystem _sprite = default!;
protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args) protected override void OnAppearanceChange(EntityUid uid, TypingIndicatorComponent component, ref AppearanceChangeEvent args)
{ {
@@ -1,7 +1,7 @@
using System.Linq; using System.Linq;
using Content.Shared.Chemistry.EntitySystems; using Content.Shared.Chemistry.EntitySystems;
using Content.Shared.Atmos.Prototypes; using Content.Shared.Atmos.Prototypes;
using Content.Shared.Body; using Content.Shared.Body.Part;
using Content.Shared.Chemistry; using Content.Shared.Chemistry;
using Content.Shared.Chemistry.Components; using Content.Shared.Chemistry.Components;
using Content.Shared.Chemistry.Components.SolutionManager; using Content.Shared.Chemistry.Components.SolutionManager;
@@ -94,7 +94,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
continue; continue;
//these bloat the hell out of blood/fat //these bloat the hell out of blood/fat
if (entProto.HasComponent<OrganComponent>()) if (entProto.HasComponent<BodyPartComponent>())
continue; continue;
//these feel obvious... //these feel obvious...
@@ -116,7 +116,7 @@ public sealed class ChemistryGuideDataSystem : SharedChemistryGuideDataSystem
} }
if (extractableComponent.GrindableSolutionName is { } grindableSolutionId && if (extractableComponent.GrindableSolution is { } grindableSolutionId &&
entProto.TryGetComponent<SolutionContainerManagerComponent>(out var manager, EntityManager.ComponentFactory) && entProto.TryGetComponent<SolutionContainerManagerComponent>(out var manager, EntityManager.ComponentFactory) &&
_solutionContainer.TryGetSolution(manager, grindableSolutionId, out var grindableSolution)) _solutionContainer.TryGetSolution(manager, grindableSolutionId, out var grindableSolution))
{ {
@@ -8,7 +8,7 @@
VerticalExpand="True" VerticalExpand="True"
HorizontalExpand="True" HorizontalExpand="True"
MinSize="100 150"> MinSize="100 150">
<PanelContainer VerticalExpand="True" StyleClasses="BackgroundPanelDark"> <PanelContainer VerticalExpand="True" StyleClasses="Inset">
<BoxContainer Name="GeneticScannerContents" Margin="5 5 5 5" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Name="GeneticScannerContents" Margin="5 5 5 5" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<Label HorizontalAlignment="Center" Text="{Loc 'cloning-console-window-scanner-details-label'}" /> <Label HorizontalAlignment="Center" Text="{Loc 'cloning-console-window-scanner-details-label'}" />
<BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Orientation="Horizontal" VerticalExpand="True" HorizontalExpand="True">
@@ -35,7 +35,7 @@
</BoxContainer> </BoxContainer>
</PanelContainer> </PanelContainer>
<Control MinSize="50 5" /> <Control MinSize="50 5" />
<PanelContainer VerticalExpand="True" StyleClasses="BackgroundPanelDark"> <PanelContainer VerticalExpand="True" StyleClasses="Inset">
<BoxContainer Name="CloningPodContents" Margin="5 5 5 5" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Name="CloningPodContents" Margin="5 5 5 5" Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
<Label HorizontalAlignment="Center" Text="{Loc 'cloning-console-window-pod-details-label'}" /> <Label HorizontalAlignment="Center" Text="{Loc 'cloning-console-window-pod-details-label'}" />
<BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True"> <BoxContainer Orientation="Vertical" VerticalExpand="True" HorizontalExpand="True">
@@ -273,7 +273,7 @@ public sealed class ClientClothingSystem : ClothingSystem
// Select displacement maps // Select displacement maps
var displacementData = inventory.Displacements.GetValueOrDefault(slot); //Default unsexed map var displacementData = inventory.Displacements.GetValueOrDefault(slot); //Default unsexed map
var equipeeSex = CompOrNull<HumanoidProfileComponent>(equipee)?.Sex; var equipeeSex = CompOrNull<HumanoidAppearanceComponent>(equipee)?.Sex;
if (equipeeSex != null) if (equipeeSex != null)
{ {
switch (equipeeSex) switch (equipeeSex)
@@ -1,4 +1,5 @@
using Content.Client.Hands.Systems; using Content.Client.Hands.Systems;
using Content.Client.NPC.HTN;
using Content.Shared.CCVar; using Content.Shared.CCVar;
using Content.Shared.CombatMode; using Content.Shared.CombatMode;
using Robust.Client.Graphics; using Robust.Client.Graphics;
@@ -58,6 +59,11 @@ public sealed class CombatModeSystem : SharedCombatModeSystem
UpdateHud(entity); UpdateHud(entity);
} }
protected override bool IsNpc(EntityUid uid)
{
return HasComp<HTNComponent>(uid);
}
private void UpdateHud(EntityUid entity) private void UpdateHud(EntityUid entity)
{ {
if (entity != _playerManager.LocalEntity || !Timing.IsFirstTimePredicted) if (entity != _playerManager.LocalEntity || !Timing.IsFirstTimePredicted)
@@ -0,0 +1,36 @@
using Content.Shared.Body.Organ;
using Robust.Client.GameObjects;
using Robust.Shared.Console;
using Robust.Shared.Containers;
namespace Content.Client.Commands;
public sealed class HideMechanismsCommand : LocalizedEntityCommands
{
[Dependency] private readonly SharedContainerSystem _containerSystem = default!;
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
public override string Command => "hidemechanisms";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var query = EntityManager.AllEntityQueryEnumerator<OrganComponent, SpriteComponent>();
while (query.MoveNext(out var uid, out _, out var sprite))
{
_spriteSystem.SetContainerOccluded((uid, sprite), false);
var tempParent = uid;
while (_containerSystem.TryGetContainingContainer((tempParent, null, null), out var container))
{
if (!container.ShowContents)
{
_spriteSystem.SetContainerOccluded((uid, sprite), true);
break;
}
tempParent = container.Owner;
}
}
}
}
@@ -0,0 +1,22 @@
using Content.Shared.Body.Organ;
using Robust.Client.GameObjects;
using Robust.Shared.Console;
namespace Content.Client.Commands;
public sealed class ShowMechanismsCommand : LocalizedEntityCommands
{
[Dependency] private readonly SpriteSystem _spriteSystem = default!;
public override string Command => "showmechanisms";
public override void Execute(IConsoleShell shell, string argStr, string[] args)
{
var query = EntityManager.AllEntityQueryEnumerator<OrganComponent, SpriteComponent>();
while (query.MoveNext(out var uid, out _, out var sprite))
{
_spriteSystem.SetContainerOccluded((uid, sprite), false);
}
}
}
@@ -28,6 +28,7 @@ namespace Content.Client.Communications.UI
_menu.OnBroadcast += BroadcastButtonPressed; _menu.OnBroadcast += BroadcastButtonPressed;
_menu.OnAlertLevel += AlertLevelSelected; _menu.OnAlertLevel += AlertLevelSelected;
_menu.OnEmergencyLevel += EmergencyShuttleButtonPressed; _menu.OnEmergencyLevel += EmergencyShuttleButtonPressed;
_menu.OnExfiltrationLevel += ExfiltrationShuttleButtonPressed; // DeltaV - Exfiltration shuttle
} }
public void AlertLevelSelected(string level) public void AlertLevelSelected(string level)
@@ -47,6 +48,13 @@ namespace Content.Client.Communications.UI
CallShuttle(); CallShuttle();
} }
// Begin DeltaV - Exfiltration Shuttle
public void ExfiltrationShuttleButtonPressed()
{
SendMessage(new CommunicationsConsoleExfiltrationShuttleMessage(!_menu!.CountdownStarted));
}
// End DeltaV - Exfiltration Shuttle
public void AnnounceButtonPressed(string message) public void AnnounceButtonPressed(string message)
{ {
var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength); var maxLength = _cfg.GetCVar(CCVars.ChatMaxAnnouncementLength);
@@ -86,6 +94,13 @@ namespace Content.Client.Communications.UI
_menu.CurrentLevel = commsState.CurrentAlert; _menu.CurrentLevel = commsState.CurrentAlert;
_menu.CountdownEnd = commsState.ExpectedCountdownEnd; _menu.CountdownEnd = commsState.ExpectedCountdownEnd;
// Begin DeltaV - Exfiltration Shuttle
_menu.CanExfiltrate = commsState.CanCall;
_menu.ExfiltrationCountdownEnd = commsState.ExpectedExfiltrationCountdownEnd;
_menu.ExfiltrationShuttleButton.Disabled = !_menu.CanExfiltrate;
// End DeltaV - Exfiltration Shuttle
_menu.UpdateCountdown(); _menu.UpdateCountdown();
_menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel); _menu.UpdateAlertLevels(commsState.AlertLevels, _menu.CurrentLevel);
_menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable; _menu.AlertLevelButton.Disabled = !_menu.AlertLevelSelectable;
@@ -52,10 +52,19 @@
<RichTextLabel Name="CountdownLabel"/> <RichTextLabel Name="CountdownLabel"/>
<!-- DeltaV - Exfiltration Shuttle -->
<RichTextLabel Name="ExfiltrationCountdownLabel"/>
<Button Name="EmergencyShuttleButton" <Button Name="EmergencyShuttleButton"
Access="Public" Access="Public"
Text="Placeholder Text" Text="Placeholder Text"
ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/> ToolTip="{Loc 'comms-console-menu-emergency-shuttle-button-tooltip'}"/>
<!-- DeltaV - Exfiltration Shuttle -->
<Button Name="ExfiltrationShuttleButton"
Access="Public"
Text="Placeholder Text"
ToolTip="{Loc 'comms-console-menu-exfiltration-shuttle-button-tooltip'}"/>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
</BoxContainer> </BoxContainer>
@@ -29,6 +29,12 @@ namespace Content.Client.Communications.UI
public event Action<string>? OnAnnounce; public event Action<string>? OnAnnounce;
public event Action<string>? OnBroadcast; public event Action<string>? OnBroadcast;
// Begin DeltaV - Exfiltration Shuttle
public bool CanExfiltrate;
public TimeSpan? ExfiltrationCountdownEnd;
public event Action? OnExfiltrationLevel;
// End DeltaV - Exfiltration Shuttle
public CommunicationsConsoleMenu() public CommunicationsConsoleMenu()
{ {
IoCManager.InjectDependencies(this); IoCManager.InjectDependencies(this);
@@ -72,12 +78,18 @@ namespace Content.Client.Communications.UI
EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke(); EmergencyShuttleButton.OnPressed += _ => OnEmergencyLevel?.Invoke();
EmergencyShuttleButton.Disabled = !CanCall; EmergencyShuttleButton.Disabled = !CanCall;
// Begin DeltaV - Exfiltration Shuttle
ExfiltrationShuttleButton.OnPressed += _ => OnExfiltrationLevel?.Invoke();
ExfiltrationShuttleButton.Disabled = !CanExfiltrate;
// End DeltaV - Exfiltration Shuttle
} }
protected override void FrameUpdate(FrameEventArgs args) protected override void FrameUpdate(FrameEventArgs args)
{ {
base.FrameUpdate(args); base.FrameUpdate(args);
UpdateCountdown(); UpdateCountdown();
UpdateExfiltrationCountdown(); // DeltaV - Exfiltration shuttle
} }
// The current alert could make levels unselectable, so we need to ensure that the UI reacts properly. // The current alert could make levels unselectable, so we need to ensure that the UI reacts properly.
@@ -117,6 +129,25 @@ namespace Content.Client.Communications.UI
} }
} }
// Begin DeltaV - Exfiltration Shuttle
public void UpdateExfiltrationCountdown()
{
if (ExfiltrationCountdownEnd is null)
{
ExfiltrationCountdownLabel.SetMessage(string.Empty);
ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-call-exfiltration");
return;
}
var exfiltrationDiff = MathHelper.Max((ExfiltrationCountdownEnd - _timing.CurTime) ?? TimeSpan.Zero, TimeSpan.Zero);
ExfiltrationShuttleButton.Text = Loc.GetString("comms-console-menu-recall-exfiltration");
var exfiltrationInfoText = Loc.GetString("comms-console-menu-exfiltration-time-remaining",
("time", exfiltrationDiff.ToString(@"hh\:mm\:ss", CultureInfo.CurrentCulture)));
ExfiltrationCountdownLabel.SetMessage(exfiltrationInfoText);
}
// End DeltaV - Exfiltration Shuttle
public void UpdateCountdown() public void UpdateCountdown()
{ {
if (!CountdownStarted) if (!CountdownStarted)
@@ -291,6 +291,7 @@ namespace Content.Client.Construction
_ghosts.Add(comp.GhostId, ghost.Value); _ghosts.Add(comp.GhostId, ghost.Value);
var sprite = Comp<SpriteComponent>(ghost.Value); var sprite = Comp<SpriteComponent>(ghost.Value);
_sprite.SetColor((ghost.Value, sprite), new Color(48, 255, 48, 128));
if (targetProto.TryGetComponent(out IconComponent? icon, EntityManager.ComponentFactory)) if (targetProto.TryGetComponent(out IconComponent? icon, EntityManager.ComponentFactory))
{ {
@@ -305,11 +306,20 @@ namespace Content.Client.Construction
var targetSprite = EnsureComp<SpriteComponent>(dummy); var targetSprite = EnsureComp<SpriteComponent>(dummy);
EntityManager.System<AppearanceSystem>().OnChangeData(dummy, targetSprite); EntityManager.System<AppearanceSystem>().OnChangeData(dummy, targetSprite);
_sprite.CopySprite((dummy, targetSprite), (ghost.Value, sprite)); for (var i = 0; i < targetSprite.AllLayers.Count(); i++)
for (var i = 0; i < sprite.AllLayers.Count(); i++)
{ {
if (!targetSprite[i].Visible || !targetSprite[i].RsiState.IsValid)
continue;
var rsi = targetSprite[i].Rsi ?? targetSprite.BaseRSI;
if (rsi is null || !rsi.TryGetState(targetSprite[i].RsiState, out var state) ||
state.StateId.Name is null)
continue;
_sprite.AddBlankLayer((ghost.Value, sprite), i);
_sprite.LayerSetSprite((ghost.Value, sprite), i, new SpriteSpecifier.Rsi(rsi.Path, state.StateId.Name));
sprite.LayerSetShader(i, "unshaded"); sprite.LayerSetShader(i, "unshaded");
_sprite.LayerSetVisible((ghost.Value, sprite), i, true);
} }
Del(dummy); Del(dummy);
@@ -317,8 +327,6 @@ namespace Content.Client.Construction
else else
return false; return false;
_sprite.SetColor((ghost.Value, sprite), new Color(48, 255, 48, 128));
if (prototype.CanBuildInImpassable) if (prototype.CanBuildInImpassable)
EnsureComp<WallMountComponent>(ghost.Value).Arc = new(Math.Tau); EnsureComp<WallMountComponent>(ghost.Value).Arc = new(Math.Tau);
@@ -1,6 +1,4 @@
using Content.Shared.Damage.Prototypes;
using Content.Shared.FixedPoint; using Content.Shared.FixedPoint;
using Robust.Shared.Prototypes;
namespace Content.Client.Damage; namespace Content.Client.Damage;
@@ -57,7 +55,7 @@ public sealed partial class DamageVisualsComponent : Component
/// (for example, Brute), and has a value /// (for example, Brute), and has a value
/// of a DamageVisualizerSprite (see below) /// of a DamageVisualizerSprite (see below)
/// </summary> /// </summary>
[DataField("damageOverlayGroups")] public Dictionary<ProtoId<DamageGroupPrototype>, DamageVisualizerSprite>? DamageOverlayGroups; [DataField("damageOverlayGroups")] public Dictionary<string, DamageVisualizerSprite>? DamageOverlayGroups;
/// <summary> /// <summary>
/// Sets if you want sprites to overlay the /// Sets if you want sprites to overlay the
@@ -86,7 +84,7 @@ public sealed partial class DamageVisualsComponent : Component
/// what kind of damage combination /// what kind of damage combination
/// you would want, on which threshold. /// you would want, on which threshold.
/// </remarks> /// </remarks>
[DataField("damageGroup")] public ProtoId<DamageGroupPrototype>? DamageGroup; [DataField("damageGroup")] public string? DamageGroup;
/// <summary> /// <summary>
/// Set this if you want incoming damage to be /// Set this if you want incoming damage to be

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