mirror of
https://github.com/corvax-team/ss14-wl.git
synced 2026-02-14 19:29:57 +01:00
merge remote stable wizden
This commit is contained in:
@@ -351,7 +351,7 @@ resharper_csharp_qualified_using_at_nested_scope = false
|
||||
resharper_csharp_prefer_qualified_reference = false
|
||||
resharper_csharp_allow_alias = false
|
||||
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props}]
|
||||
[*.{csproj,xml,yml,yaml,dll.config,msbuildproj,targets,props,slnx}]
|
||||
indent_size = 2
|
||||
|
||||
[nuget.config]
|
||||
|
||||
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
2
.github/PULL_REQUEST_TEMPLATE.md
vendored
@@ -14,7 +14,7 @@
|
||||
Небольшие исправления/рефакторинг освобождаются от этого требования. -->
|
||||
|
||||
## Требования
|
||||
<!-- Подтвердите следующее, поставив X в скобках [X]: -->
|
||||
<!-- Подтвердите следующее, поставив X в скобках без пробелов [X]: -->
|
||||
- [ ] Я прочитал(а) и следую [Рекомендациям по оформлению Pull Request и Changelog](https://docs.spacestation14.com/en/general-development/codebase-info/pull-request-guidelines.html).
|
||||
- [ ] Я добавил(а) медиафайлы к этому PR или он не требует демонстрации в игре.
|
||||
<!-- Вы должны понимать, что несоблюдение вышеуказанного может привести к закрытию вашего PR по усмотрению сопровождающего -->
|
||||
|
||||
2
.github/workflows/build-docfx.yml
vendored
2
.github/workflows/build-docfx.yml
vendored
@@ -21,7 +21,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/build-map-renderer.yml
vendored
2
.github/workflows/build-map-renderer.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/build-test-debug.yml
vendored
2
.github/workflows/build-test-debug.yml
vendored
@@ -36,7 +36,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/publish-testing.yml
vendored
2
.github/workflows/publish-testing.yml
vendored
@@ -20,7 +20,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v3.2.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
2
.github/workflows/publish.yml
vendored
2
.github/workflows/publish.yml
vendored
@@ -47,7 +47,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Get Engine Tag
|
||||
run: |
|
||||
|
||||
2
.github/workflows/test-packaging.yml
vendored
2
.github/workflows/test-packaging.yml
vendored
@@ -65,7 +65,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
|
||||
2
.github/workflows/yaml-linter.yml
vendored
2
.github/workflows/yaml-linter.yml
vendored
@@ -26,7 +26,7 @@ jobs:
|
||||
- name: Setup .NET Core
|
||||
uses: actions/setup-dotnet@v4.1.0
|
||||
with:
|
||||
dotnet-version: 9.0.x
|
||||
dotnet-version: 10.0.x
|
||||
- name: Install dependencies
|
||||
run: dotnet restore
|
||||
- name: Build
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,3 +1,6 @@
|
||||
# MSbuild binlog files
|
||||
*.binlog
|
||||
|
||||
## Ignore Visual Studio temporary files, build results, and
|
||||
## files generated by popular Visual Studio add-ons.
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ import time
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.sln"
|
||||
SOLUTION_PATH = Path("..") / "SpaceStation14.slnx"
|
||||
# If this doesn't match the saved version we overwrite them all.
|
||||
CURRENT_HOOKS_VERSION = "4"
|
||||
QUIET = len(sys.argv) == 2 and sys.argv[1] == "--quiet"
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<OutputPath>..\bin\Content.Benchmarks\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputType>Exe</OutputType>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="BenchmarkDotNet" />
|
||||
|
||||
<!-- pin transitive deps -->
|
||||
<PackageReference Include="System.Management" />
|
||||
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
|
||||
@@ -19,10 +22,12 @@
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
|
||||
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Benchmarks\Robust.Benchmarks.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Benchmarks.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Testing.props" />
|
||||
</Project>
|
||||
|
||||
@@ -134,7 +134,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
? null
|
||||
: _prototypeManager.Index(playerInfo.RoleProto.Value);
|
||||
|
||||
var roleName = Loc.GetString(rolePrototype?.Name ?? RoleTypePrototype.FallbackName);
|
||||
var roleName = rolePrototype?.Name ?? RoleTypePrototype.FallbackName;
|
||||
var roleColor = rolePrototype?.Color ?? RoleTypePrototype.FallbackColor;
|
||||
var roleSymbol = rolePrototype?.Symbol ?? RoleTypePrototype.FallbackSymbol;
|
||||
|
||||
@@ -213,7 +213,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
{
|
||||
color = Color.GreenYellow;
|
||||
color.A = alpha;
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, Loc.GetString(playerInfo.StartingJob), uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
args.ScreenHandle.DrawString(_font, screenCoordinates + currentOffset, playerInfo.StartingJob, uiScale, playerInfo.Connected ? color : colorDisconnected);
|
||||
currentOffset += lineoffset;
|
||||
}
|
||||
|
||||
@@ -241,7 +241,7 @@ internal sealed class AdminNameOverlay : Overlay
|
||||
color = roleColor;
|
||||
symbol = IsFiltered(playerInfo.RoleProto) ? symbol : string.Empty;
|
||||
text = IsFiltered(playerInfo.RoleProto)
|
||||
? roleName.ToUpper()
|
||||
? Loc.GetString(roleName).ToUpper()
|
||||
: string.Empty;
|
||||
break;
|
||||
case AdminOverlayAntagFormat.Subtype:
|
||||
|
||||
@@ -7,7 +7,6 @@ using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Client.UserInterface.XAML;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
using Serilog;
|
||||
|
||||
namespace Content.Client.Cargo.UI;
|
||||
|
||||
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class HyposprayStatusControlSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<HyposprayComponent>(ent => new HyposprayStatusControl(ent, _solutionContainers));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Robust.Shared.Prototypes;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class InjectorStatusControlSystem : EntitySystem
|
||||
{
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainers = default!;
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
|
||||
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
Subs.ItemStatus<InjectorComponent>(injector => new InjectorStatusControl(injector, _solutionContainers, _prototypeManager));
|
||||
}
|
||||
}
|
||||
@@ -1,16 +0,0 @@
|
||||
using Content.Client.Chemistry.UI;
|
||||
using Content.Client.Items;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Client.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class InjectorSystem : SharedInjectorSystem
|
||||
{
|
||||
public override void Initialize()
|
||||
{
|
||||
base.Initialize();
|
||||
|
||||
Subs.ItemStatus<InjectorComponent>(ent => new InjectorStatusControl(ent, SolutionContainer));
|
||||
}
|
||||
}
|
||||
@@ -48,6 +48,10 @@ namespace Content.Client.Chemistry.UI
|
||||
(uint) _window.BottleDosage.Value, _window.LabelLine));
|
||||
_window.BufferSortButton.OnPressed += _ => SendMessage(
|
||||
new ChemMasterSortingTypeCycleMessage());
|
||||
_window.OutputBufferDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.Internal));
|
||||
_window.OutputBeakerDraw.OnPressed += _ => SendMessage(
|
||||
new ChemMasterOutputDrawSourceMessage(ChemMasterDrawSource.External));
|
||||
|
||||
for (uint i = 0; i < _window.PillTypeButtons.Length; i++)
|
||||
{
|
||||
|
||||
@@ -79,10 +79,13 @@
|
||||
|
||||
<!-- Packaging -->
|
||||
<BoxContainer Orientation="Horizontal">
|
||||
<Label Text="{Loc 'chem-master-window-packaging-text'}" />
|
||||
<Label Text="{Loc 'chem-master-output-source'}" StyleClasses="LabelSecondaryColor" Margin="0 0 5 0"/>
|
||||
<Button MinSize="80 0" Name="OutputBufferDraw" Access="Public" Text="{Loc 'chem-master-output-buffer-draw'}" ToggleMode="True" StyleClasses="OpenRight" />
|
||||
<Button MinSize="80 0" Name="OutputBeakerDraw" Access="Public" Text="{Loc 'chem-master-output-beaker-draw'}" ToggleMode="True" StyleClasses="OpenLeft" />
|
||||
<Control HorizontalExpand="True"/>
|
||||
<Label Text="{Loc 'chem-master-window-buffer-label'}" />
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelWeak" />
|
||||
<!-- Output Draw Source -->
|
||||
<Label Name="DrawSource"/>
|
||||
<Label Name="BufferCurrentVolume" StyleClasses="LabelSecondaryColor" />
|
||||
</BoxContainer>
|
||||
|
||||
<!-- Wrap the packaging info-->
|
||||
|
||||
@@ -150,7 +150,17 @@ namespace Content.Client.Chemistry.UI
|
||||
// Ensure the Panel Info is updated, including UI elements for Buffer Volume, Output Container and so on
|
||||
UpdatePanelInfo(castState);
|
||||
|
||||
BufferCurrentVolume.Text = $" {castState.BufferCurrentVolume?.Int() ?? 0}u";
|
||||
switch (castState.DrawSource)
|
||||
{
|
||||
case ChemMasterDrawSource.Internal:
|
||||
SetBufferText(castState.BufferCurrentVolume, "chem-master-output-buffer-draw");
|
||||
break;
|
||||
case ChemMasterDrawSource.External:
|
||||
SetBufferText(castState.InputContainerInfo?.CurrentVolume, "chem-master-output-beaker-draw");
|
||||
break;
|
||||
default:
|
||||
throw new($"Chemmaster {castState.OutputContainerInfo} draw source is not set");
|
||||
}
|
||||
|
||||
InputEjectButton.Disabled = castState.InputContainerInfo is null;
|
||||
OutputEjectButton.Disabled = castState.OutputContainerInfo is null;
|
||||
@@ -168,9 +178,14 @@ namespace Content.Client.Chemistry.UI
|
||||
var holdsReagents = output?.Reagents != null;
|
||||
var pillNumberMax = holdsReagents ? 0 : remainingCapacity;
|
||||
var bottleAmountMax = holdsReagents ? remainingCapacity : 0;
|
||||
var bufferVolume = castState.BufferCurrentVolume?.Int() ?? 0;
|
||||
var outputVolume = castState.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => castState.BufferCurrentVolume?.Int() ?? 0,
|
||||
ChemMasterDrawSource.External => castState.InputContainerInfo?.CurrentVolume.Int() ?? 0,
|
||||
_ => 0,
|
||||
};
|
||||
|
||||
PillDosage.Value = (int)Math.Min(bufferVolume, castState.PillDosageLimit);
|
||||
PillDosage.Value = (int)Math.Min(outputVolume, castState.PillDosageLimit);
|
||||
|
||||
PillTypeButtons[castState.SelectedPillType].Pressed = true;
|
||||
|
||||
@@ -186,25 +201,35 @@ namespace Content.Client.Chemistry.UI
|
||||
// Avoid division by zero
|
||||
if (PillDosage.Value > 0)
|
||||
{
|
||||
PillNumber.Value = Math.Min(bufferVolume / PillDosage.Value, pillNumberMax);
|
||||
PillNumber.Value = Math.Min(outputVolume / PillDosage.Value, pillNumberMax);
|
||||
}
|
||||
else
|
||||
{
|
||||
PillNumber.Value = 0;
|
||||
}
|
||||
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, bufferVolume);
|
||||
BottleDosage.Value = Math.Min(bottleAmountMax, outputVolume);
|
||||
}
|
||||
/// <summary>
|
||||
/// Generate a product label based on reagents in the buffer.
|
||||
/// Generate a product label based on reagents in the buffer or beaker.
|
||||
/// </summary>
|
||||
/// <param name="state">State data sent by the server.</param>
|
||||
private string GenerateLabel(ChemMasterBoundUserInterfaceState state)
|
||||
{
|
||||
if (state.BufferCurrentVolume == 0)
|
||||
if (
|
||||
state.BufferCurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.Internal ||
|
||||
state.InputContainerInfo?.CurrentVolume == 0 && state.DrawSource == ChemMasterDrawSource.External ||
|
||||
state.InputContainerInfo?.Reagents == null
|
||||
)
|
||||
return "";
|
||||
|
||||
var reagent = state.BufferReagents.OrderBy(r => r.Quantity).First().Reagent;
|
||||
var reagent = (state.DrawSource switch
|
||||
{
|
||||
ChemMasterDrawSource.Internal => state.BufferReagents,
|
||||
ChemMasterDrawSource.External => state.InputContainerInfo.Reagents ?? [],
|
||||
_ => throw new($"Chemmaster {state.OutputContainerInfo} draw source is not set"),
|
||||
}).MinBy(r => r.Quantity)
|
||||
.Reagent;
|
||||
_prototypeManager.TryIndex(reagent.Prototype, out ReagentPrototype? proto);
|
||||
return proto?.LocalizedName ?? "";
|
||||
}
|
||||
@@ -233,6 +258,8 @@ namespace Content.Client.Chemistry.UI
|
||||
_ => Loc.GetString("chem-master-window-sort-type-none")
|
||||
};
|
||||
|
||||
OutputBufferDraw.Pressed = state.DrawSource == ChemMasterDrawSource.Internal;
|
||||
OutputBeakerDraw.Pressed = state.DrawSource == ChemMasterDrawSource.External;
|
||||
|
||||
if (!state.BufferReagents.Any())
|
||||
{
|
||||
@@ -414,6 +441,12 @@ namespace Content.Client.Chemistry.UI
|
||||
get => LabelLineEdit.Text;
|
||||
set => LabelLineEdit.Text = value;
|
||||
}
|
||||
|
||||
private void SetBufferText(FixedPoint2? volume, string text)
|
||||
{
|
||||
BufferCurrentVolume.Text = $" {volume ?? FixedPoint2.Zero}u";
|
||||
DrawSource.Text = Loc.GetString(text);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class ReagentButton : Button
|
||||
|
||||
@@ -1,58 +0,0 @@
|
||||
using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
public sealed class HyposprayStatusControl : Control
|
||||
{
|
||||
private readonly Entity<HyposprayComponent> _parent;
|
||||
private readonly RichTextLabel _label;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
|
||||
private FixedPoint2 PrevVolume;
|
||||
private FixedPoint2 PrevMaxVolume;
|
||||
private bool PrevOnlyAffectsMobs;
|
||||
|
||||
public HyposprayStatusControl(Entity<HyposprayComponent> parent, SharedSolutionContainerSystem solutionContainers)
|
||||
{
|
||||
_parent = parent;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
|
||||
AddChild(_label);
|
||||
}
|
||||
|
||||
protected override void FrameUpdate(FrameEventArgs args)
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
if (PrevVolume == solution.Volume
|
||||
&& PrevMaxVolume == solution.MaxVolume
|
||||
&& PrevOnlyAffectsMobs == _parent.Comp.OnlyAffectsMobs)
|
||||
return;
|
||||
|
||||
PrevVolume = solution.Volume;
|
||||
PrevMaxVolume = solution.MaxVolume;
|
||||
PrevOnlyAffectsMobs = _parent.Comp.OnlyAffectsMobs;
|
||||
|
||||
var modeStringLocalized = Loc.GetString((_parent.Comp.OnlyAffectsMobs && _parent.Comp.CanContainerDraw) switch
|
||||
{
|
||||
false => "hypospray-all-mode-text",
|
||||
true => "hypospray-mobs-only-mode-text",
|
||||
});
|
||||
|
||||
_label.SetMarkup(Loc.GetString("hypospray-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", modeStringLocalized)));
|
||||
}
|
||||
}
|
||||
@@ -2,26 +2,32 @@ using Content.Client.Message;
|
||||
using Content.Client.Stylesheets;
|
||||
using Content.Shared.Chemistry.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Shared.Chemistry.Prototypes;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Timing;
|
||||
|
||||
namespace Content.Client.Chemistry.UI;
|
||||
|
||||
public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
private readonly IPrototypeManager _prototypeManager;
|
||||
|
||||
private readonly Entity<InjectorComponent> _parent;
|
||||
private readonly SharedSolutionContainerSystem _solutionContainers;
|
||||
private readonly RichTextLabel _label;
|
||||
|
||||
private FixedPoint2 PrevVolume;
|
||||
private FixedPoint2 PrevMaxVolume;
|
||||
private FixedPoint2 PrevTransferAmount;
|
||||
private InjectorToggleMode PrevToggleState;
|
||||
private FixedPoint2 _prevVolume;
|
||||
private FixedPoint2 _prevMaxVolume;
|
||||
private FixedPoint2? _prevTransferAmount;
|
||||
private InjectorBehavior _prevBehavior;
|
||||
|
||||
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers)
|
||||
public InjectorStatusControl(Entity<InjectorComponent> parent, SharedSolutionContainerSystem solutionContainers, IPrototypeManager prototypeManager)
|
||||
{
|
||||
_prototypeManager = prototypeManager;
|
||||
|
||||
_parent = parent;
|
||||
_solutionContainers = solutionContainers;
|
||||
_label = new RichTextLabel { StyleClasses = { StyleClass.ItemStatus } };
|
||||
@@ -32,33 +38,38 @@ public sealed class InjectorStatusControl : Control
|
||||
{
|
||||
base.FrameUpdate(args);
|
||||
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution))
|
||||
if (!_solutionContainers.TryGetSolution(_parent.Owner, _parent.Comp.SolutionName, out _, out var solution)
|
||||
|| !_prototypeManager.Resolve(_parent.Comp.ActiveModeProtoId, out var activeMode))
|
||||
return;
|
||||
|
||||
// only updates the UI if any of the details are different than they previously were
|
||||
if (PrevVolume == solution.Volume
|
||||
&& PrevMaxVolume == solution.MaxVolume
|
||||
&& PrevTransferAmount == _parent.Comp.CurrentTransferAmount
|
||||
&& PrevToggleState == _parent.Comp.ToggleState)
|
||||
if (_prevVolume == solution.Volume
|
||||
&& _prevMaxVolume == solution.MaxVolume
|
||||
&& _prevTransferAmount == _parent.Comp.CurrentTransferAmount
|
||||
&& _prevBehavior == activeMode.Behavior)
|
||||
return;
|
||||
|
||||
PrevVolume = solution.Volume;
|
||||
PrevMaxVolume = solution.MaxVolume;
|
||||
PrevTransferAmount = _parent.Comp.CurrentTransferAmount;
|
||||
PrevToggleState = _parent.Comp.ToggleState;
|
||||
_prevVolume = solution.Volume;
|
||||
_prevMaxVolume = solution.MaxVolume;
|
||||
_prevTransferAmount = _parent.Comp.CurrentTransferAmount;
|
||||
_prevBehavior = activeMode.Behavior;
|
||||
|
||||
// Update current volume and injector state
|
||||
var modeStringLocalized = Loc.GetString(_parent.Comp.ToggleState switch
|
||||
// Seeing transfer volume is only important for injectors that can change it.
|
||||
if (activeMode.TransferAmounts.Count > 1 && _parent.Comp.CurrentTransferAmount.HasValue)
|
||||
{
|
||||
InjectorToggleMode.Draw => "injector-draw-text",
|
||||
InjectorToggleMode.Inject => "injector-inject-text",
|
||||
_ => "injector-invalid-injector-toggle-mode"
|
||||
});
|
||||
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", modeStringLocalized),
|
||||
("transferVolume", _parent.Comp.CurrentTransferAmount)));
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-transfer-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", Loc.GetString(activeMode.Name)),
|
||||
("transferVolume", _parent.Comp.CurrentTransferAmount.Value)));
|
||||
}
|
||||
else
|
||||
{
|
||||
_label.SetMarkup(Loc.GetString("injector-volume-label",
|
||||
("currentVolume", solution.Volume),
|
||||
("totalVolume", solution.MaxVolume),
|
||||
("modeString", Loc.GetString(activeMode.Name))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -30,7 +30,10 @@ namespace Content.Client.Construction.UI
|
||||
[Dependency] private readonly IUserInterfaceManager _uiManager = default!;
|
||||
[Dependency] private readonly IPlayerManager _playerManager = default!;
|
||||
[Dependency] private readonly IClientPreferencesManager _preferencesManager = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private readonly SpriteSystem _spriteSystem;
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
private readonly IConstructionMenuView _constructionView;
|
||||
private readonly EntityWhitelistSystem _whitelistSystem;
|
||||
@@ -90,6 +93,7 @@ namespace Content.Client.Construction.UI
|
||||
_constructionView = new ConstructionMenu();
|
||||
_whitelistSystem = _entManager.System<EntityWhitelistSystem>();
|
||||
_spriteSystem = _entManager.System<SpriteSystem>();
|
||||
_sawmill = _logManager.GetSawmill("construction.ui");
|
||||
|
||||
// This is required so that if we load after the system is initialized, we can bind to it immediately
|
||||
if (_systemManager.TryGetEntitySystem<ConstructionSystem>(out var constructionSystem))
|
||||
@@ -284,7 +288,7 @@ namespace Content.Client.Construction.UI
|
||||
|
||||
if (!_constructionSystem!.TryGetRecipePrototype(recipe.ID, out var targetProtoId))
|
||||
{
|
||||
Logger.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
_sawmill.Error("Cannot find the target prototype in the recipe cache with the id \"{0}\" of {1}.",
|
||||
recipe.ID,
|
||||
nameof(ConstructionPrototype));
|
||||
continue;
|
||||
|
||||
@@ -1,26 +1,24 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Client\</OutputPath>
|
||||
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
|
||||
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<Configurations>Debug;Release;Tools;DebugOpt</Configurations>
|
||||
<Platforms>AnyCPU</Platforms>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nett" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
<PackageReference Include="Pidgin" />
|
||||
<PackageReference Include="Robust.Shared.AuthLib" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Corvax\Content.Corvax.Interfaces.Shared\Content.Corvax.Interfaces.Shared.csproj" />
|
||||
<ProjectReference Include="..\Corvax\Content.Corvax.Interfaces.Client\Content.Corvax.Interfaces.Client.csproj" />
|
||||
|
||||
@@ -73,7 +73,19 @@ public sealed class PuddleSystem : SharedPuddleSystem
|
||||
// Maybe someday we'll have clientside prediction for entity spawning, but not today.
|
||||
// Until then, these methods do nothing on the client.
|
||||
/// <inheritdoc/>
|
||||
public override bool TrySplashSpillAt(EntityUid uid, EntityCoordinates coordinates, Solution solution, out EntityUid puddleUid, bool sound = true, EntityUid? user = null)
|
||||
public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity, EntityCoordinates coordinates, out EntityUid puddleUid, out Solution solution, bool sound = true, EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
solution = new Solution();
|
||||
return false;
|
||||
}
|
||||
|
||||
public override bool TrySplashSpillAt(EntityUid entity,
|
||||
EntityCoordinates coordinates,
|
||||
Solution spilled,
|
||||
out EntityUid puddleUid,
|
||||
bool sound = true,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
return false;
|
||||
|
||||
@@ -42,7 +42,7 @@ public sealed class GuidebookRichPrototypeLink : Control, IPrototypeLinkControl
|
||||
public void SetMessage(FormattedMessage message)
|
||||
{
|
||||
_message = message;
|
||||
_richTextLabel.SetMessage(_message);
|
||||
_richTextLabel.SetMessage(_message, tagsAllowed: null);
|
||||
}
|
||||
|
||||
public IPrototype? LinkedPrototype { get; set; }
|
||||
|
||||
@@ -82,7 +82,7 @@ public sealed partial class DocumentParsingManager
|
||||
}
|
||||
|
||||
msg.Pop();
|
||||
rt.SetMessage(msg);
|
||||
rt.SetMessage(msg, tagsAllowed: null);
|
||||
return rt;
|
||||
},
|
||||
TextParser)
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class InfoSection : BoxContainer
|
||||
{
|
||||
TitleLabel.Text = title;
|
||||
if (markup)
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()));
|
||||
Content.SetMessage(FormattedMessage.FromMarkupOrThrow(text.Trim()), tagsAllowed: null);
|
||||
else
|
||||
Content.SetMessage(text);
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Client.Info
|
||||
}
|
||||
public void SetInfoBlob(string markup)
|
||||
{
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup));
|
||||
_richTextLabel.SetMessage(FormattedMessage.FromMarkupOrThrow(markup), tagsAllowed: null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
using Content.Shared.Kitchen.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Client.Kitchen.EntitySystems;
|
||||
|
||||
[UsedImplicitly]
|
||||
public sealed class ReagentGrinderSystem : SharedReagentGrinderSystem;
|
||||
|
||||
@@ -20,8 +20,10 @@ namespace Content.Client.Launcher
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly IClipboardManager _clipboard = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private LauncherConnectingGui? _control;
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
private Page _currentPage;
|
||||
private string? _connectFailReason;
|
||||
@@ -61,6 +63,8 @@ namespace Content.Client.Launcher
|
||||
{
|
||||
_control = new LauncherConnectingGui(this, _random, _prototypeManager, _cfg, _clipboard);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("launcher-ui");
|
||||
|
||||
_userInterfaceManager.StateRoot.AddChild(_control);
|
||||
|
||||
_clientNetManager.ConnectFailed += OnConnectFailed;
|
||||
@@ -115,12 +119,12 @@ namespace Content.Client.Launcher
|
||||
}
|
||||
else
|
||||
{
|
||||
Logger.InfoS("launcher-ui", $"Redial not possible, no Ss14Address");
|
||||
_sawmill.Info($"Redial not possible, no Ss14Address");
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
Logger.ErrorS("launcher-ui", $"Redial exception: {ex}");
|
||||
_sawmill.Error($"Redial exception: {ex}");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using System.Numerics;
|
||||
using Content.Client.UserInterface.Controls;
|
||||
using Prometheus;
|
||||
using Robust.Client.AutoGenerated;
|
||||
using Robust.Client.UserInterface;
|
||||
using Robust.Client.UserInterface.Controls;
|
||||
|
||||
@@ -23,7 +23,6 @@ public sealed class IFFConsoleBoundUserInterface : BoundUserInterface
|
||||
|
||||
_window = this.CreateWindowCenteredLeft<IFFConsoleWindow>();
|
||||
_window.ShowIFF += SendIFFMessage;
|
||||
_window.ShowVessel += SendVesselMessage;
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
@@ -44,14 +43,6 @@ public sealed class IFFConsoleBoundUserInterface : BoundUserInterface
|
||||
});
|
||||
}
|
||||
|
||||
private void SendVesselMessage(bool obj)
|
||||
{
|
||||
SendMessage(new IFFShowVesselMessage()
|
||||
{
|
||||
Show = obj,
|
||||
});
|
||||
}
|
||||
|
||||
protected override void Dispose(bool disposing)
|
||||
{
|
||||
base.Dispose(disposing);
|
||||
|
||||
@@ -9,12 +9,6 @@
|
||||
<Button Name="ShowIFFOnButton" Text="{Loc 'iff-console-on'}" StyleClasses="OpenRight" />
|
||||
<Button Name="ShowIFFOffButton" Text="{Loc 'iff-console-off'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
|
||||
<Label Name="ShowVesselLabel" Text="{Loc 'iff-console-show-vessel-label'}" HorizontalExpand="True" StyleClasses="highlight" />
|
||||
<BoxContainer Orientation="Horizontal" MinWidth="120">
|
||||
<Button Name="ShowVesselOnButton" Text="{Loc 'iff-console-on'}" StyleClasses="OpenRight" />
|
||||
<Button Name="ShowVesselOffButton" Text="{Loc 'iff-console-off'}" StyleClasses="OpenLeft" />
|
||||
</BoxContainer>
|
||||
</GridContainer>
|
||||
</BoxContainer>
|
||||
</controls:FancyWindow>
|
||||
|
||||
@@ -13,9 +13,7 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
IComputerWindow<IFFConsoleBoundUserInterfaceState>
|
||||
{
|
||||
private readonly ButtonGroup _showIFFButtonGroup = new();
|
||||
private readonly ButtonGroup _showVesselButtonGroup = new();
|
||||
public event Action<bool>? ShowIFF;
|
||||
public event Action<bool>? ShowVessel;
|
||||
|
||||
public IFFConsoleWindow()
|
||||
{
|
||||
@@ -24,11 +22,6 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFFOnButton.Group = _showIFFButtonGroup;
|
||||
ShowIFFOnButton.OnPressed += args => ShowIFFPressed(true);
|
||||
ShowIFFOffButton.OnPressed += args => ShowIFFPressed(false);
|
||||
|
||||
ShowVesselOffButton.Group = _showVesselButtonGroup;
|
||||
ShowVesselOnButton.Group = _showVesselButtonGroup;
|
||||
ShowVesselOnButton.OnPressed += args => ShowVesselPressed(true);
|
||||
ShowVesselOffButton.OnPressed += args => ShowVesselPressed(false);
|
||||
}
|
||||
|
||||
private void ShowIFFPressed(bool pressed)
|
||||
@@ -36,19 +29,14 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFF?.Invoke(pressed);
|
||||
}
|
||||
|
||||
private void ShowVesselPressed(bool pressed)
|
||||
{
|
||||
ShowVessel?.Invoke(pressed);
|
||||
}
|
||||
|
||||
public void UpdateState(IFFConsoleBoundUserInterfaceState state)
|
||||
{
|
||||
if ((state.AllowedFlags & IFFFlags.HideLabel) != 0x0)
|
||||
if ((state.AllowedFlags & IFFFlags.HideLabel) != 0x0 || (state.AllowedFlags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowIFFOffButton.Disabled = false;
|
||||
ShowIFFOnButton.Disabled = false;
|
||||
|
||||
if ((state.Flags & IFFFlags.HideLabel) != 0x0)
|
||||
if ((state.Flags & IFFFlags.HideLabel) != 0x0 || (state.Flags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowIFFOffButton.Pressed = true;
|
||||
}
|
||||
@@ -62,25 +50,5 @@ public sealed partial class IFFConsoleWindow : FancyWindow,
|
||||
ShowIFFOffButton.Disabled = true;
|
||||
ShowIFFOnButton.Disabled = true;
|
||||
}
|
||||
|
||||
if ((state.AllowedFlags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowVesselOffButton.Disabled = false;
|
||||
ShowVesselOnButton.Disabled = false;
|
||||
|
||||
if ((state.Flags & IFFFlags.Hide) != 0x0)
|
||||
{
|
||||
ShowVesselOffButton.Pressed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowVesselOnButton.Pressed = true;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
ShowVesselOffButton.Disabled = true;
|
||||
ShowVesselOnButton.Disabled = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,7 +21,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
[Dependency] private readonly IEntityManager _entity = default!;
|
||||
private readonly NameModifierSystem _nameModifier;
|
||||
private readonly PowerCellSystem _powerCell;
|
||||
private readonly PredictedBatterySystem _battery;
|
||||
private readonly SharedBatterySystem _battery;
|
||||
|
||||
public Action? BrainButtonPressed;
|
||||
public Action? EjectBatteryButtonPressed;
|
||||
@@ -44,7 +44,7 @@ public sealed partial class BorgMenu : FancyWindow
|
||||
|
||||
_nameModifier = _entity.System<NameModifierSystem>();
|
||||
_powerCell = _entity.System<PowerCellSystem>();
|
||||
_battery = _entity.System<PredictedBatterySystem>();
|
||||
_battery = _entity.System<SharedBatterySystem>();
|
||||
|
||||
_maxNameLength = _cfgManager.GetCVar(CCVars.MaxNameLength);
|
||||
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
StyleClasses="PanelLight"
|
||||
Margin="5 5 5 0">
|
||||
<BoxContainer Orientation="Horizontal" HorizontalExpand="True" Margin="5 5 0 5">
|
||||
<TextureButton Name="RemoveButton" VerticalAlignment="Top" HorizontalAlignment="Left" Scale="0.5 0.5"/>
|
||||
<SpriteView Name="ModuleView" Margin="0 0 5 0"/>
|
||||
<BoxContainer RectClipContent="True" HorizontalExpand="True">
|
||||
<Label Name="ModuleName" HorizontalExpand="True" HorizontalAlignment="Center"/>
|
||||
</BoxContainer>
|
||||
<TextureButton Name="RemoveButton" VerticalAlignment="Top" HorizontalAlignment="Right" Scale="0.5 0.5"/>
|
||||
</BoxContainer>
|
||||
</PanelContainer>
|
||||
|
||||
@@ -18,7 +18,7 @@ public sealed partial class BorgSystem : SharedBorgSystem
|
||||
[Dependency] private readonly SpriteSystem _sprite = default!;
|
||||
[Dependency] private readonly UserInterfaceSystem _ui = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly PredictedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly AlertsSystem _alerts = default!;
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly IPlayerManager _player = default!;
|
||||
|
||||
@@ -28,7 +28,16 @@ public class ListContainer : Control
|
||||
/// Called when creating a button on the UI.
|
||||
/// The provided <see cref="ListContainerButton"/> is the generated button that Controls should be parented to.
|
||||
/// </summary>
|
||||
public Action<ListData, ListContainerButton>? GenerateItem;
|
||||
public Action<ListData, ListContainerButton>? GenerateItem
|
||||
{
|
||||
get => _generateItem;
|
||||
set {
|
||||
_generateItem = value;
|
||||
// Invalidate _itemHeight so we recalculate the size of children the next
|
||||
// time PopulateList() is called
|
||||
_itemHeight = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/// <inheritdoc cref="BaseButton.OnPressed"/>
|
||||
public Action<BaseButton.ButtonEventArgs, ListData>? ItemPressed;
|
||||
@@ -59,6 +68,7 @@ public class ListContainer : Control
|
||||
private bool _updateChildren = false;
|
||||
private bool _suppressScrollValueChanged;
|
||||
private ButtonGroup? _buttonGroup;
|
||||
public Action<ListData, ListContainerButton>? _generateItem;
|
||||
|
||||
public int ScrollSpeedY { get; set; } = 50;
|
||||
|
||||
|
||||
@@ -117,7 +117,7 @@ public partial class ChatBox : UIWidget
|
||||
formatted.PushColor(color);
|
||||
formatted.AddMarkupOrThrow(message);
|
||||
formatted.Pop();
|
||||
Contents.AddMessage(formatted);
|
||||
Contents.AddMessage(formatted, tagsAllowed: null);
|
||||
}
|
||||
|
||||
public void Focus(ChatSelectChannel? channel = null)
|
||||
|
||||
@@ -26,6 +26,8 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
|
||||
_window.OnNameChange += OnNameSelected;
|
||||
_window.OnVerbChange += verb => SendMessage(new VoiceMaskChangeVerbMessage(verb));
|
||||
_window.OnToggle += OnToggle;
|
||||
_window.OnAccentToggle += OnAccentToggle;
|
||||
_window.OnVoiceChange += voice => SendMessage(new VoiceMaskChangeVoiceMessage(voice)); // Corvax-TTS
|
||||
}
|
||||
|
||||
@@ -34,6 +36,16 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
SendMessage(new VoiceMaskChangeNameMessage(name));
|
||||
}
|
||||
|
||||
private void OnToggle()
|
||||
{
|
||||
SendMessage(new VoiceMaskToggleMessage());
|
||||
}
|
||||
|
||||
private void OnAccentToggle()
|
||||
{
|
||||
SendMessage(new VoiceMaskAccentToggleMessage());
|
||||
}
|
||||
|
||||
protected override void UpdateState(BoundUserInterfaceState state)
|
||||
{
|
||||
if (state is not VoiceMaskBuiState cast || _window == null)
|
||||
@@ -41,6 +53,7 @@ public sealed class VoiceMaskBoundUserInterface : BoundUserInterface
|
||||
return;
|
||||
}
|
||||
|
||||
_window.UpdateState(cast.Name, cast.Verb, cast.Active, cast.AccentHide);
|
||||
_window.UpdateState(cast.Name, cast.Voice, cast.Verb); // Corvax-TTS
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
<Label Text="{Loc 'voice-mask-name-change-speech-style'}" />
|
||||
<OptionButton Name="SpeechVerbSelector" /> <!-- Populated in LoadVerbs -->
|
||||
</BoxContainer>
|
||||
<Button Name="ToggleAccentButton" Text="{Loc 'voice-mask-name-change-accent-toggle'}" HorizontalExpand="True" ToggleMode="True" Margin="5"/>
|
||||
<Button Name="ToggleButton" Text="{Loc 'voice-mask-name-change-toggle'}" HorizontalExpand="True" ToggleMode="True" Margin="5"/>
|
||||
<!-- Corvax-TTS-Start -->
|
||||
<BoxContainer Orientation="Horizontal" Margin="5" Visible="False" Name="TTSContainer">
|
||||
<Label Text="{Loc 'voice-mask-voice-change-info'}" />
|
||||
|
||||
@@ -15,6 +15,8 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
|
||||
{
|
||||
public Action<string>? OnNameChange;
|
||||
public Action<string?>? OnVerbChange;
|
||||
public Action? OnToggle;
|
||||
public Action? OnAccentToggle;
|
||||
public Action<string>? OnVoiceChange; // Corvax-TTS
|
||||
|
||||
private List<(string, string)> _verbs = new();
|
||||
@@ -37,6 +39,9 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
|
||||
SpeechVerbSelector.SelectId(args.Id);
|
||||
};
|
||||
|
||||
ToggleButton.OnPressed += args => OnToggle?.Invoke();
|
||||
ToggleAccentButton.OnPressed += args => OnAccentToggle?.Invoke();
|
||||
|
||||
// Corvax-TTS-Start
|
||||
if (IoCManager.Resolve<IConfigurationManager>().GetCVar(CCCVars.TTSEnabled))
|
||||
{
|
||||
@@ -104,6 +109,8 @@ public sealed partial class VoiceMaskNameChangeWindow : FancyWindow
|
||||
{
|
||||
NameSelector.Text = name;
|
||||
_verb = verb;
|
||||
ToggleButton.Pressed = active;
|
||||
ToggleAccentButton.Pressed = accentHide;
|
||||
|
||||
for (int id = 0; id < SpeechVerbSelector.ItemCount; id++)
|
||||
{
|
||||
|
||||
@@ -1,12 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<OutputPath>..\bin\Content.IntegrationTests\</OutputPath>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<LangVersion>12</LangVersion>
|
||||
<Nullable>disable</Nullable>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="NUnit3TestAdapter" />
|
||||
@@ -17,12 +15,12 @@
|
||||
<ProjectReference Include="..\Content.Server\Content.Server.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Tests\Content.Tests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.CompNetworkGenerator.targets" />
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Testing.props" />
|
||||
</Project>
|
||||
|
||||
130
Content.IntegrationTests/Tests/Atmos/AtmosMonitoringTest.cs
Normal file
130
Content.IntegrationTests/Tests/Atmos/AtmosMonitoringTest.cs
Normal file
@@ -0,0 +1,130 @@
|
||||
using System.Numerics;
|
||||
using Content.Server.Atmos.Monitor.Components;
|
||||
using Content.Shared.Atmos;
|
||||
using Robust.Shared.Console;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
using Robust.Shared.Prototypes;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Atmos;
|
||||
|
||||
/// <summary>
|
||||
/// Test for determining that an AtmosMonitoringComponent/System correctly references
|
||||
/// the GasMixture of the tile it is on if the tile's GasMixture ever changes.
|
||||
/// </summary>
|
||||
[TestOf(typeof(Atmospherics))]
|
||||
public sealed class AtmosMonitoringTest : AtmosTest
|
||||
{
|
||||
// We can just reuse the dP test, I just want a grid.
|
||||
protected override ResPath? TestMapPath => new("Maps/Test/Atmospherics/DeltaPressure/deltapressuretest.yml");
|
||||
|
||||
private readonly EntProtoId _airSensorProto = new("AirSensor");
|
||||
private readonly EntProtoId _wallProto = new("WallSolid");
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the monitor properly nulls out its reference to the tile mixture
|
||||
/// when a wall is placed on top of it, and restores the reference when the wall is removed.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task NullOutTileAtmosphereGasMixture()
|
||||
{
|
||||
// run an atmos update to initialize everything For Real surely
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
var gridNetEnt = SEntMan.GetNetEntity(RelevantAtmos.Owner);
|
||||
TargetCoords = new NetCoordinates(gridNetEnt, Vector2.Zero);
|
||||
var netEnt = await Spawn(_airSensorProto);
|
||||
var airSensorUid = SEntMan.GetEntity(netEnt);
|
||||
Transform.TryGetGridTilePosition(airSensorUid, out var vec);
|
||||
|
||||
// run another one to ensure that the ref to the GasMixture was picked up
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
// should be in the middle
|
||||
Assert.That(vec,
|
||||
Is.EqualTo(Vector2i.Zero),
|
||||
"Air sensor not in expected position on grid (0, 0)");
|
||||
|
||||
var atmosMonitor = SEntMan.GetComponent<AtmosMonitorComponent>(airSensorUid);
|
||||
var tileMixture = SAtmos.GetTileMixture(airSensorUid);
|
||||
|
||||
Assert.That(tileMixture,
|
||||
Is.SameAs(atmosMonitor.TileGas),
|
||||
"Atmos monitor's TileGas does not match actual tile mixture after spawn.");
|
||||
|
||||
// ok now spawn a wall or something on top of it
|
||||
var wall = await Spawn(_wallProto);
|
||||
var wallUid = SEntMan.GetEntity(wall);
|
||||
|
||||
// ensure that atmospherics registers the change - the gas mixture should no longer exist
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
// the monitor's ref to the gas should be null now
|
||||
Assert.That(atmosMonitor.TileGas,
|
||||
Is.Null,
|
||||
"Atmos monitor's TileGas is not null after wall placed on top. Possible dead reference.");
|
||||
// the actual mixture on the tile should be null now too
|
||||
var nullTileMixture = SAtmos.GetTileMixture(airSensorUid);
|
||||
Assert.That(nullTileMixture, Is.Null, "Tile mixture is not null after wall placed on top.");
|
||||
|
||||
// ok now delete the wall
|
||||
await Delete(wallUid);
|
||||
|
||||
// ensure that atmospherics registers the change - the gas mixture should be back
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
// gas mixture should now exist again
|
||||
var newTileMixture = SAtmos.GetTileMixture(airSensorUid);
|
||||
Assert.That(newTileMixture, Is.Not.Null, "Tile mixture is null after wall removed.");
|
||||
// monitor's ref to the gas should be back too
|
||||
Assert.That(atmosMonitor.TileGas,
|
||||
Is.SameAs(newTileMixture),
|
||||
"Atmos monitor's TileGas does not match actual tile mixture after wall removed.");
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Tests if the monitor properly updates its reference to the tile mixture
|
||||
/// when the FixGridAtmos command is called.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task FixGridAtmosReplaceMixtureOnTileChange()
|
||||
{
|
||||
// run an atmos update to initialize everything For Real surely
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
var gridNetEnt = SEntMan.GetNetEntity(RelevantAtmos.Owner);
|
||||
TargetCoords = new NetCoordinates(gridNetEnt, Vector2.Zero);
|
||||
var netEnt = await Spawn(_airSensorProto);
|
||||
var airSensorUid = SEntMan.GetEntity(netEnt);
|
||||
Transform.TryGetGridTilePosition(airSensorUid, out var vec);
|
||||
|
||||
// run another one to ensure that the ref to the GasMixture was picked up
|
||||
SAtmos.RunProcessingFull(ProcessEnt, MapData.Grid.Owner, SAtmos.AtmosTickRate);
|
||||
|
||||
// should be in the middle
|
||||
Assert.That(vec,
|
||||
Is.EqualTo(Vector2i.Zero),
|
||||
"Air sensor not in expected position on grid (0, 0)");
|
||||
|
||||
var atmosMonitor = SEntMan.GetComponent<AtmosMonitorComponent>(airSensorUid);
|
||||
var tileMixture = SAtmos.GetTileMixture(airSensorUid);
|
||||
|
||||
Assert.That(tileMixture,
|
||||
Is.SameAs(atmosMonitor.TileGas),
|
||||
"Atmos monitor's TileGas does not match actual tile mixture after spawn.");
|
||||
|
||||
SAtmos.RebuildGridAtmosphere((ProcessEnt.Owner, ProcessEnt.Comp1, ProcessEnt.Comp3));
|
||||
|
||||
// EXTREMELY IMPORTANT: The reference to the tile mixture on the tile should be completely different.
|
||||
var newTileMixture = SAtmos.GetTileMixture(airSensorUid);
|
||||
Assert.That(newTileMixture,
|
||||
Is.Not.SameAs(tileMixture),
|
||||
"Tile mixture is the same instance after fixgridatmos was ran. It should be a new instance.");
|
||||
|
||||
// The monitor's ref to the tile mixture should have updated too.
|
||||
Assert.That(atmosMonitor.TileGas,
|
||||
Is.SameAs(newTileMixture),
|
||||
"Atmos monitor's TileGas does not match actual tile mixture after fixgridatmos was ran.");
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,7 @@ public abstract class AtmosTest : InteractionTest
|
||||
protected AtmosphereSystem SAtmos = default!;
|
||||
protected EntityLookupSystem LookupSystem = default!;
|
||||
|
||||
protected Entity<GridAtmosphereComponent> RelevantAtmos = default!;
|
||||
protected Entity<GridAtmosphereComponent> RelevantAtmos;
|
||||
|
||||
/// <summary>
|
||||
/// Used in <see cref="AtmosphereSystem.RunProcessingFull"/>. Resolved during test setup.
|
||||
@@ -40,14 +40,35 @@ public abstract class AtmosTest : InteractionTest
|
||||
SAtmos = SEntMan.System<AtmosphereSystem>();
|
||||
LookupSystem = SEntMan.System<EntityLookupSystem>();
|
||||
|
||||
RelevantAtmos = (MapData.Grid, SEntMan.GetComponent<GridAtmosphereComponent>(MapData.Grid));
|
||||
SEntMan.TryGetComponent<GridAtmosphereComponent>(MapData.Grid, out var gridAtmosComp);
|
||||
SEntMan.TryGetComponent<GasTileOverlayComponent>(MapData.Grid, out var overlayComp);
|
||||
SEntMan.TryGetComponent<MapGridComponent>(MapData.Grid, out var mapGridComp);
|
||||
var xform = SEntMan.GetComponent<TransformComponent>(MapData.Grid);
|
||||
|
||||
using (Assert.EnterMultipleScope())
|
||||
{
|
||||
Assert.That(gridAtmosComp,
|
||||
Is.Not.Null,
|
||||
"Loaded map doesn't have a GridAtmosphereComponent on its grid. " +
|
||||
"Did you forget to override TestMapPath with a proper atmospherics testing map?");
|
||||
Assert.That(overlayComp,
|
||||
Is.Not.Null,
|
||||
"Loaded map doesn't have a GasTileOverlayComponent on its grid. " +
|
||||
"Did you forget to override TestMapPath with a proper atmospherics testing map?");
|
||||
Assert.That(mapGridComp,
|
||||
Is.Not.Null,
|
||||
"Loaded map doesn't have a MapGridComponent on its grid. " +
|
||||
"Did you forget to override TestMapPath with a proper atmospherics testing map?");
|
||||
}
|
||||
|
||||
RelevantAtmos = (MapData.Grid, gridAtmosComp);
|
||||
|
||||
ProcessEnt = new Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent>(
|
||||
MapData.Grid.Owner,
|
||||
SEntMan.GetComponent<GridAtmosphereComponent>(MapData.Grid.Owner),
|
||||
SEntMan.GetComponent<GasTileOverlayComponent>(MapData.Grid.Owner),
|
||||
SEntMan.GetComponent<MapGridComponent>(MapData.Grid.Owner),
|
||||
SEntMan.GetComponent<TransformComponent>(MapData.Grid.Owner));
|
||||
gridAtmosComp,
|
||||
overlayComp,
|
||||
mapGridComp,
|
||||
xform);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,15 +1,11 @@
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
using Content.Server.Atmos;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Robust.Shared.EntitySerialization;
|
||||
using Robust.Shared.EntitySerialization.Systems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Map.Components;
|
||||
using Robust.Shared.Utility;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Atmos;
|
||||
@@ -20,7 +16,7 @@ namespace Content.IntegrationTests.Tests.Atmos;
|
||||
/// </summary>
|
||||
[TestFixture]
|
||||
[TestOf(typeof(DeltaPressureSystem))]
|
||||
public sealed class DeltaPressureTest
|
||||
public sealed class DeltaPressureTest : AtmosTest
|
||||
{
|
||||
#region Prototypes
|
||||
|
||||
@@ -92,7 +88,7 @@ public sealed class DeltaPressureTest
|
||||
|
||||
#endregion
|
||||
|
||||
private readonly ResPath _testMap = new("Maps/Test/Atmospherics/DeltaPressure/deltapressuretest.yml");
|
||||
protected override ResPath? TestMapPath => new("Maps/Test/Atmospherics/DeltaPressure/deltapressuretest.yml");
|
||||
|
||||
// TODO ATMOS TESTS
|
||||
// - Check for directional windows (partial airtight ents) properly computing pressure differences
|
||||
@@ -111,40 +107,15 @@ public sealed class DeltaPressureTest
|
||||
[Test]
|
||||
public async Task ProcessingListAutoJoinTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var mapLoader = entMan.System<MapLoaderSystem>();
|
||||
var atmosphereSystem = entMan.System<AtmosphereSystem>();
|
||||
var deserializationOptions = DeserializationOptions.Default with { InitializeMaps = true };
|
||||
|
||||
Entity<MapGridComponent> grid = default;
|
||||
Entity<DeltaPressureComponent> dpEnt;
|
||||
|
||||
// Load our test map in and assert that it exists.
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoadMap(_testMap, out _, out var gridSet, deserializationOptions),
|
||||
$"Failed to load map {_testMap}.");
|
||||
Assert.That(gridSet, Is.Not.Null, "There were no grids loaded from the map!");
|
||||
#pragma warning restore NUnit2045
|
||||
var uid = SEntMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(ProcessEnt, Vector2.Zero));
|
||||
var dpEnt = new Entity<DeltaPressureComponent>(uid, SEntMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
|
||||
grid = gridSet.First();
|
||||
Assert.That(SAtmos.IsDeltaPressureEntityInList(RelevantAtmos, dpEnt), "Entity was not in processing list when it should have automatically joined!");
|
||||
SEntMan.DeleteEntity(uid);
|
||||
Assert.That(!SAtmos.IsDeltaPressureEntityInList(RelevantAtmos, dpEnt), "Entity was still in processing list after deletion!");
|
||||
});
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
var uid = entMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(grid.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, entMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
|
||||
Assert.That(atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was not in processing list when it should have automatically joined!");
|
||||
entMan.DeleteEntity(uid);
|
||||
Assert.That(!atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was still in processing list after deletion!");
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -154,45 +125,27 @@ public sealed class DeltaPressureTest
|
||||
[Test]
|
||||
public async Task ProcessingDeltaStandbyTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var mapLoader = entMan.System<MapLoaderSystem>();
|
||||
var atmosphereSystem = entMan.System<AtmosphereSystem>();
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
var deserializationOptions = DeserializationOptions.Default with { InitializeMaps = true };
|
||||
|
||||
Entity<MapGridComponent> grid = default;
|
||||
Entity<DeltaPressureComponent> dpEnt = default;
|
||||
TileAtmosphere tile = null!;
|
||||
AtmosDirection direction = default;
|
||||
|
||||
// Load our test map in and assert that it exists.
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoadMap(_testMap, out _, out var gridSet, deserializationOptions),
|
||||
$"Failed to load map {_testMap}.");
|
||||
Assert.That(gridSet, Is.Not.Null, "There were no grids loaded from the map!");
|
||||
#pragma warning restore NUnit2045
|
||||
|
||||
grid = gridSet.First();
|
||||
var uid = entMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(grid.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, entMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
var uid = SEntMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(ProcessEnt, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, SEntMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(SAtmos.IsDeltaPressureEntityInList(ProcessEnt, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
});
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
var indices = transformSystem.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = entMan.GetComponent<GridAtmosphereComponent>(grid);
|
||||
var indices = Transform.GetGridOrMapTilePosition(dpEnt);
|
||||
|
||||
direction = (AtmosDirection)(1 << i);
|
||||
var offsetIndices = indices.Offset(direction);
|
||||
tile = gridAtmosComp.Tiles[offsetIndices];
|
||||
tile = RelevantAtmos.Comp.Tiles[offsetIndices];
|
||||
|
||||
Assert.That(tile.Air, Is.Not.Null, $"Tile at {offsetIndices} should have air!");
|
||||
|
||||
@@ -202,19 +155,17 @@ public sealed class DeltaPressureTest
|
||||
tile.Air!.AdjustMoles(Gas.Nitrogen, moles);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
|
||||
// Entity should exist, if it took one tick of damage then it should be instantly destroyed.
|
||||
await server.WaitAssertion(() =>
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(!entMan.Deleted(dpEnt), $"{dpEnt} should still exist after experiencing non-threshold pressure from {direction} side!");
|
||||
Assert.That(!SEntMan.Deleted(dpEnt), $"{dpEnt} should still exist after experiencing non-threshold pressure from {direction} side!");
|
||||
tile.Air!.Clear();
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
}
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -224,47 +175,43 @@ public sealed class DeltaPressureTest
|
||||
[Test]
|
||||
public async Task ProcessingDeltaDamageTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var mapLoader = entMan.System<MapLoaderSystem>();
|
||||
var atmosphereSystem = entMan.System<AtmosphereSystem>();
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
var deserializationOptions = DeserializationOptions.Default with { InitializeMaps = true };
|
||||
|
||||
Entity<MapGridComponent> grid = default;
|
||||
Entity<DeltaPressureComponent> dpEnt = default;
|
||||
TileAtmosphere tile = null!;
|
||||
AtmosDirection direction = default;
|
||||
|
||||
// Load our test map in and assert that it exists.
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoadMap(_testMap, out _, out var gridSet, deserializationOptions),
|
||||
$"Failed to load map {_testMap}.");
|
||||
Assert.That(gridSet, Is.Not.Null, "There were no grids loaded from the map!");
|
||||
#pragma warning restore NUnit2045
|
||||
|
||||
grid = gridSet.First();
|
||||
SAtmos.SetAtmosphereSimulation(ProcessEnt, false);
|
||||
});
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
await server.WaitPost(() =>
|
||||
/*
|
||||
RUNNING REGULAR TICKS USING WaitRunTicks AND GUESSING AS TO HOW MANY ATMOS SIMULATION TICKS ARE HAPPENING
|
||||
WILL CAUSE A RACE CONDITION THAT IS A PAIN IN THE ASS TO DEBUG
|
||||
|
||||
AN ENTITY MAY BE REMOVED AND ADDED BETWEEN A SUBTICK. IF LINDA PROCESSING IS ENABLED IT MIGHT CAUSE
|
||||
AN EQUALIZATION TO PUT AIR IN OTHER TILES IN THE SMALL WINDOW WHERE THE TILE IS NOT AIRTIGHT
|
||||
WHICH WILL THROW OFF DELTAS
|
||||
*/
|
||||
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
SAtmos.RunProcessingFull(ProcessEnt,ProcessEnt.Owner, SAtmos.AtmosTickRate);
|
||||
});
|
||||
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
// Need to spawn an entity each run to ensure it works for all directions.
|
||||
var uid = entMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(grid.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, entMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
var uid = SEntMan.SpawnAtPosition("DeltaPressureSolidTest", new EntityCoordinates(ProcessEnt.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, SEntMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(SAtmos.IsDeltaPressureEntityInList(ProcessEnt.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
|
||||
var indices = transformSystem.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = entMan.GetComponent<GridAtmosphereComponent>(grid);
|
||||
var indices = Transform.GetGridOrMapTilePosition(dpEnt);
|
||||
|
||||
direction = (AtmosDirection)(1 << i);
|
||||
var offsetIndices = indices.Offset(direction);
|
||||
tile = gridAtmosComp.Tiles[offsetIndices];
|
||||
var tile = ProcessEnt.Comp1.Tiles[offsetIndices];
|
||||
|
||||
Assert.That(tile.Air, Is.Not.Null, $"Tile at {offsetIndices} should have air!");
|
||||
|
||||
@@ -274,19 +221,29 @@ public sealed class DeltaPressureTest
|
||||
tile.Air!.AdjustMoles(Gas.Nitrogen, moles);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
|
||||
// Entity should exist, if it took one tick of damage then it should be instantly destroyed.
|
||||
await server.WaitAssertion(() =>
|
||||
// get jiggy with it! hit that dance white boy!
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
Assert.That(entMan.Deleted(dpEnt), $"{dpEnt} still exists after experiencing threshold pressure from {direction} side!");
|
||||
tile.Air!.Clear();
|
||||
SAtmos.RunProcessingFull(ProcessEnt,ProcessEnt.Owner, SAtmos.AtmosTickRate);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
}
|
||||
// need to run some ticks as deleted entities are queued for removal
|
||||
// and not removed instantly
|
||||
await Server.WaitRunTicks(30);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
// Entity shouldn't exist, if it took one tick of damage then it should be instantly destroyed.
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(SEntMan.Deleted(dpEnt), $"{dpEnt} still exists after experiencing threshold pressure from {direction} side!");
|
||||
|
||||
// Double whammy: in case any unintended gas leak occured due to a race condition,
|
||||
// clear out all the tiles.
|
||||
foreach (var mix in SAtmos.GetAllMixtures(ProcessEnt))
|
||||
{
|
||||
mix.Clear();
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -296,39 +253,23 @@ public sealed class DeltaPressureTest
|
||||
[Test]
|
||||
public async Task ProcessingAbsoluteStandbyTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var mapLoader = entMan.System<MapLoaderSystem>();
|
||||
var atmosphereSystem = entMan.System<AtmosphereSystem>();
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
var deserializationOptions = DeserializationOptions.Default with { InitializeMaps = true };
|
||||
|
||||
Entity<MapGridComponent> grid = default;
|
||||
Entity<DeltaPressureComponent> dpEnt = default;
|
||||
TileAtmosphere tile = null!;
|
||||
AtmosDirection direction = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoadMap(_testMap, out _, out var gridSet, deserializationOptions),
|
||||
$"Failed to load map {_testMap}.");
|
||||
Assert.That(gridSet, Is.Not.Null, "There were no grids loaded from the map!");
|
||||
#pragma warning restore NUnit2045
|
||||
grid = gridSet.First();
|
||||
var uid = entMan.SpawnAtPosition("DeltaPressureSolidTestAbsolute", new EntityCoordinates(grid.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, entMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
var uid = SEntMan.SpawnAtPosition("DeltaPressureSolidTestAbsolute", new EntityCoordinates(ProcessEnt.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, SEntMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(SAtmos.IsDeltaPressureEntityInList(ProcessEnt.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
});
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
var indices = transformSystem.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = entMan.GetComponent<GridAtmosphereComponent>(grid);
|
||||
var indices = Transform.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = SEntMan.GetComponent<GridAtmosphereComponent>(ProcessEnt);
|
||||
|
||||
direction = (AtmosDirection)(1 << i);
|
||||
var offsetIndices = indices.Offset(direction);
|
||||
@@ -340,18 +281,16 @@ public sealed class DeltaPressureTest
|
||||
tile.Air!.AdjustMoles(Gas.Nitrogen, moles);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(!entMan.Deleted(dpEnt), $"{dpEnt} should still exist after experiencing non-threshold absolute pressure from {direction} side!");
|
||||
Assert.That(!SEntMan.Deleted(dpEnt), $"{dpEnt} should still exist after experiencing non-threshold absolute pressure from {direction} side!");
|
||||
tile.Air!.Clear();
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
}
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -361,41 +300,21 @@ public sealed class DeltaPressureTest
|
||||
[Test]
|
||||
public async Task ProcessingAbsoluteDamageTest()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var entMan = server.EntMan;
|
||||
var mapLoader = entMan.System<MapLoaderSystem>();
|
||||
var atmosphereSystem = entMan.System<AtmosphereSystem>();
|
||||
var transformSystem = entMan.System<SharedTransformSystem>();
|
||||
var deserializationOptions = DeserializationOptions.Default with { InitializeMaps = true };
|
||||
|
||||
Entity<MapGridComponent> grid = default;
|
||||
Entity<DeltaPressureComponent> dpEnt = default;
|
||||
TileAtmosphere tile = null!;
|
||||
AtmosDirection direction = default;
|
||||
|
||||
await server.WaitPost(() =>
|
||||
{
|
||||
#pragma warning disable NUnit2045
|
||||
Assert.That(mapLoader.TryLoadMap(_testMap, out _, out var gridSet, deserializationOptions),
|
||||
$"Failed to load map {_testMap}.");
|
||||
Assert.That(gridSet, Is.Not.Null, "There were no grids loaded from the map!");
|
||||
#pragma warning restore NUnit2045
|
||||
grid = gridSet.First();
|
||||
});
|
||||
|
||||
for (var i = 0; i < Atmospherics.Directions; i++)
|
||||
{
|
||||
await server.WaitPost(() =>
|
||||
await Server.WaitPost(() =>
|
||||
{
|
||||
// Spawn fresh entity each iteration to verify all directions work
|
||||
var uid = entMan.SpawnAtPosition("DeltaPressureSolidTestAbsolute", new EntityCoordinates(grid.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, entMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(atmosphereSystem.IsDeltaPressureEntityInList(grid.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
var uid = SEntMan.SpawnAtPosition("DeltaPressureSolidTestAbsolute", new EntityCoordinates(ProcessEnt.Owner, Vector2.Zero));
|
||||
dpEnt = new Entity<DeltaPressureComponent>(uid, SEntMan.GetComponent<DeltaPressureComponent>(uid));
|
||||
Assert.That(SAtmos.IsDeltaPressureEntityInList(ProcessEnt.Owner, dpEnt), "Entity was not in processing list when it should have been added!");
|
||||
|
||||
var indices = transformSystem.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = entMan.GetComponent<GridAtmosphereComponent>(grid);
|
||||
var indices = Transform.GetGridOrMapTilePosition(dpEnt);
|
||||
var gridAtmosComp = SEntMan.GetComponent<GridAtmosphereComponent>(ProcessEnt);
|
||||
|
||||
direction = (AtmosDirection)(1 << i);
|
||||
var offsetIndices = indices.Offset(direction);
|
||||
@@ -408,17 +327,15 @@ public sealed class DeltaPressureTest
|
||||
tile.Air!.AdjustMoles(Gas.Nitrogen, moles);
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
Assert.That(entMan.Deleted(dpEnt), $"{dpEnt} still exists after experiencing threshold absolute pressure from {direction} side!");
|
||||
Assert.That(SEntMan.Deleted(dpEnt), $"{dpEnt} still exists after experiencing threshold absolute pressure from {direction} side!");
|
||||
tile.Air!.Clear();
|
||||
});
|
||||
|
||||
await server.WaitRunTicks(30);
|
||||
await Server.WaitRunTicks(30);
|
||||
}
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ namespace Content.IntegrationTests.Tests.Buckle
|
||||
- type: Hands
|
||||
- type: ComplexInteraction
|
||||
- type: InputMover
|
||||
- type: Physics
|
||||
bodyType: KinematicController
|
||||
- type: Body
|
||||
prototype: Human
|
||||
- type: StandingState
|
||||
|
||||
@@ -54,7 +54,7 @@ public sealed class ObjectiveCommandsTest
|
||||
});
|
||||
|
||||
Assert.That(mindEnt, Is.Not.Null);
|
||||
var mindComp = mindEnt.Value.Comp;
|
||||
var mindComp = mindEnt!.Value.Comp;
|
||||
Assert.That(mindComp.Objectives, Is.Empty, "Dummy player started with objectives.");
|
||||
|
||||
await pair.WaitCommand($"addobjective {playerSession.Name} {ObjectiveProtoId}");
|
||||
|
||||
@@ -164,7 +164,7 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
var damageToDeal = FixedPoint2.New(types.Count * 5);
|
||||
DamageSpecifier damage = new(group3, damageToDeal);
|
||||
|
||||
sDamageableSystem.TryChangeDamage(uid, damage, true);
|
||||
sDamageableSystem.ChangeDamage(uid, damage, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -178,7 +178,7 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
});
|
||||
|
||||
// Heal
|
||||
sDamageableSystem.TryChangeDamage(uid, -damage);
|
||||
sDamageableSystem.ChangeDamage(uid, -damage);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -197,7 +197,7 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
Assert.That(types, Has.Count.EqualTo(3));
|
||||
|
||||
damage = new DamageSpecifier(group3, 14);
|
||||
sDamageableSystem.TryChangeDamage(uid, damage, true);
|
||||
sDamageableSystem.ChangeDamage(uid, damage, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -209,7 +209,7 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
});
|
||||
|
||||
// Heal
|
||||
sDamageableSystem.TryChangeDamage(uid, -damage);
|
||||
sDamageableSystem.ChangeDamage(uid, -damage);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -225,7 +225,7 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
|
||||
});
|
||||
damage = new DamageSpecifier(group1, FixedPoint2.New(10)) + new DamageSpecifier(type2b, FixedPoint2.New(10));
|
||||
sDamageableSystem.TryChangeDamage(uid, damage, true);
|
||||
sDamageableSystem.ChangeDamage(uid, damage, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -245,9 +245,9 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
|
||||
|
||||
// Test 'wasted' healing
|
||||
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3a, 5));
|
||||
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(type3b, 7));
|
||||
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -11));
|
||||
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(type3a, 5));
|
||||
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(type3b, 7));
|
||||
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -11));
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
@@ -257,11 +257,11 @@ namespace Content.IntegrationTests.Tests.Damageable
|
||||
});
|
||||
|
||||
// Test Over-Healing
|
||||
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)));
|
||||
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, FixedPoint2.New(-100)));
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
|
||||
|
||||
// Test that if no health change occurred, returns false
|
||||
sDamageableSystem.TryChangeDamage(uid, new DamageSpecifier(group3, -100));
|
||||
sDamageableSystem.ChangeDamage(uid, new DamageSpecifier(group3, -100));
|
||||
Assert.That(sDamageableComponent.TotalDamage, Is.EqualTo(FixedPoint2.Zero));
|
||||
});
|
||||
await pair.CleanReturnAsync();
|
||||
|
||||
@@ -112,7 +112,7 @@ public sealed partial class MindTests
|
||||
Assert.That(entMan.EntityExists(attachedEntity), Is.True);
|
||||
Assert.That(attachedEntity, Is.Not.EqualTo(playerEnt));
|
||||
Assert.That(entMan.HasComponent<GhostComponent>(attachedEntity));
|
||||
var transform = entMan.GetComponent<TransformComponent>(attachedEntity.Value);
|
||||
var transform = entMan.GetComponent<TransformComponent>(attachedEntity!.Value);
|
||||
Assert.That(transform.MapID, Is.Not.EqualTo(MapId.Nullspace));
|
||||
Assert.That(transform.MapID, Is.Not.EqualTo(testMap.MapId));
|
||||
#pragma warning restore NUnit2045
|
||||
@@ -175,7 +175,7 @@ public sealed partial class MindTests
|
||||
Assert.That(player.AttachedEntity, Is.Not.Null);
|
||||
Assert.That(entMan.EntityExists(player.AttachedEntity));
|
||||
#pragma warning restore NUnit2045
|
||||
var originalEntity = player.AttachedEntity.Value;
|
||||
var originalEntity = player.AttachedEntity!.Value;
|
||||
|
||||
EntityUid ghost = default!;
|
||||
await server.WaitAssertion(() =>
|
||||
@@ -248,7 +248,7 @@ public sealed partial class MindTests
|
||||
var mindId = player.ContentData()?.Mind;
|
||||
Assert.That(mindId, Is.Not.Null);
|
||||
|
||||
var mind = entMan.GetComponent<MindComponent>(mindId.Value);
|
||||
var mind = entMan.GetComponent<MindComponent>(mindId!.Value);
|
||||
Assert.That(mind.VisitingEntity, Is.Null);
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
|
||||
186
Content.IntegrationTests/Tests/Power/PowerStateTest.cs
Normal file
186
Content.IntegrationTests/Tests/Power/PowerStateTest.cs
Normal file
@@ -0,0 +1,186 @@
|
||||
using Content.Shared.Coordinates;
|
||||
using Content.Shared.Power.Components;
|
||||
using Content.Shared.Power.EntitySystems;
|
||||
using Robust.Shared.GameObjects;
|
||||
using Robust.Shared.Map;
|
||||
using Robust.Shared.Maths;
|
||||
|
||||
namespace Content.IntegrationTests.Tests.Power;
|
||||
|
||||
[TestFixture]
|
||||
public sealed class PowerStateTest
|
||||
{
|
||||
[TestPrototypes]
|
||||
private const string Prototypes = @"
|
||||
- type: entity
|
||||
id: PowerStateApcReceiverDummy
|
||||
components:
|
||||
- type: ApcPowerReceiver
|
||||
- type: ExtensionCableReceiver
|
||||
- type: Transform
|
||||
anchored: true
|
||||
- type: PowerState
|
||||
isWorking: false
|
||||
idlePowerDraw: 10
|
||||
workingPowerDraw: 50
|
||||
";
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that switching from idle to working updates the power receiver load to the working draw.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_IdleToWorking_UpdatesLoad()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
system.SetWorkingState((ent, powerState), true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that switching from working to idle updates the power receiver load to the idle draw.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_WorkingToIdle_UpdatesLoad()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
Entity<PowerStateComponent> newEnt = (ent, powerState);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(newEnt, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(newEnt, false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Asserts that setting the working state to the current state does not change the power receiver load.
|
||||
/// </summary>
|
||||
[Test]
|
||||
public async Task SetWorkingState_AlreadyInState_NoChange()
|
||||
{
|
||||
await using var pair = await PoolManager.GetServerClient();
|
||||
var server = pair.Server;
|
||||
|
||||
var mapManager = server.ResolveDependency<IMapManager>();
|
||||
var entManager = server.ResolveDependency<IEntityManager>();
|
||||
var mapSys = entManager.System<SharedMapSystem>();
|
||||
|
||||
await server.WaitAssertion(() =>
|
||||
{
|
||||
mapSys.CreateMap(out var mapId);
|
||||
var grid = mapManager.CreateGridEntity(mapId);
|
||||
|
||||
mapSys.SetTile(grid, Vector2i.Zero, new Tile(1));
|
||||
|
||||
var ent = entManager.SpawnEntity("PowerStateApcReceiverDummy", grid.Owner.ToCoordinates());
|
||||
|
||||
var receiver = entManager.GetComponent<Server.Power.Components.ApcPowerReceiverComponent>(ent);
|
||||
var powerState = entManager.GetComponent<PowerStateComponent>(ent);
|
||||
var system = entManager.System<PowerStateSystem>();
|
||||
Entity<PowerStateComponent> valueTuple = (ent, powerState);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, false);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.False);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.IdlePowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
|
||||
system.SetWorkingState(valueTuple, true);
|
||||
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
Assert.That(powerState.IsWorking, Is.True);
|
||||
Assert.That(receiver.Load, Is.EqualTo(powerState.WorkingPowerDraw).Within(0.01f));
|
||||
});
|
||||
});
|
||||
|
||||
await pair.CleanReturnAsync();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -54,6 +54,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
nodeGroupID: HVPower
|
||||
- type: PowerNetworkBattery
|
||||
- type: Battery
|
||||
netsync: false
|
||||
- type: BatteryCharger
|
||||
|
||||
- type: entity
|
||||
@@ -68,6 +69,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
nodeGroupID: HVPower
|
||||
- type: PowerNetworkBattery
|
||||
- type: Battery
|
||||
netsync: false
|
||||
- type: BatteryDischarger
|
||||
|
||||
- type: entity
|
||||
@@ -85,6 +87,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
nodeGroupID: HVPower
|
||||
- type: PowerNetworkBattery
|
||||
- type: Battery
|
||||
netsync: false
|
||||
- type: BatteryDischarger
|
||||
node: output
|
||||
- type: BatteryCharger
|
||||
@@ -110,6 +113,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
maxSupply: 1000
|
||||
supplyRampTolerance: 1000
|
||||
- type: Battery
|
||||
netsync: false
|
||||
maxCharge: 1000
|
||||
startingCharge: 1000
|
||||
- type: Transform
|
||||
@@ -119,6 +123,7 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
id: ApcDummy
|
||||
components:
|
||||
- type: Battery
|
||||
netsync: false
|
||||
maxCharge: 10000
|
||||
startingCharge: 10000
|
||||
- type: PowerNetworkBattery
|
||||
@@ -380,6 +385,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
const float startingCharge = 100_000;
|
||||
|
||||
PowerNetworkBatteryComponent netBattery = default!;
|
||||
EntityUid generatorEnt = default!;
|
||||
EntityUid consumerEnt = default!;
|
||||
BatteryComponent battery = default!;
|
||||
PowerConsumerComponent consumer = default!;
|
||||
|
||||
@@ -395,8 +402,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates());
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
generatorEnt = entityManager.SpawnEntity("DischargingBatteryDummy", grid.Owner.ToCoordinates());
|
||||
consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
|
||||
netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(generatorEnt);
|
||||
battery = entityManager.GetComponent<BatteryComponent>(generatorEnt);
|
||||
@@ -441,7 +448,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
|
||||
// Trivial integral to calculate expected power spent.
|
||||
const double spentExpected = (200 + 100) / 2.0 * 0.25;
|
||||
Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
|
||||
var currentCharge = batterySys.GetCharge((generatorEnt, battery));
|
||||
Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -460,7 +468,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
|
||||
// Trivial integral to calculate expected power spent.
|
||||
const double spentExpected = (400 + 100) / 2.0 * 0.75 + 400 * 0.25;
|
||||
Assert.That(battery.CurrentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
|
||||
var currentCharge = batterySys.GetCharge((generatorEnt, battery));
|
||||
Assert.That(currentCharge, Is.EqualTo(startingCharge - spentExpected).Within(tickDev));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -576,6 +585,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var batterySys = entityManager.System<BatterySystem>();
|
||||
var mapSys = entityManager.System<SharedMapSystem>();
|
||||
EntityUid generatorEnt = default!;
|
||||
EntityUid batteryEnt = default!;
|
||||
PowerSupplierComponent supplier = default!;
|
||||
BatteryComponent battery = default!;
|
||||
|
||||
@@ -591,8 +602,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
entityManager.SpawnEntity("CableHV", grid.Owner.ToCoordinates(0, i));
|
||||
}
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
|
||||
var batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates());
|
||||
batteryEnt = entityManager.SpawnEntity("ChargingBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
var netBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(batteryEnt);
|
||||
@@ -615,7 +626,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
// half a second @ 500 W = 250
|
||||
// 50% efficiency, so 125 J stored total.
|
||||
Assert.That(battery.CurrentCharge, Is.EqualTo(125).Within(0.1));
|
||||
var currentCharge = batterySys.GetCharge((batteryEnt, battery));
|
||||
Assert.That(currentCharge, Is.EqualTo(125).Within(0.1));
|
||||
Assert.That(supplier.CurrentSupply, Is.EqualTo(500).Within(0.1));
|
||||
});
|
||||
});
|
||||
@@ -633,6 +645,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var gameTiming = server.ResolveDependency<IGameTiming>();
|
||||
var batterySys = entityManager.System<BatterySystem>();
|
||||
var mapSys = entityManager.System<SharedMapSystem>();
|
||||
EntityUid batteryEnt = default!;
|
||||
EntityUid supplyEnt = default!;
|
||||
EntityUid consumerEnt = default!;
|
||||
PowerConsumerComponent consumer = default!;
|
||||
PowerSupplierComponent supplier = default!;
|
||||
PowerNetworkBatteryComponent netBattery = default!;
|
||||
@@ -653,9 +668,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
|
||||
batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
|
||||
@@ -694,7 +709,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(200).Within(0.1));
|
||||
|
||||
const int expectedSpent = 200;
|
||||
Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
|
||||
var currentCharge = batterySys.GetCharge((batteryEnt, battery));
|
||||
Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -711,6 +727,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var gameTiming = server.ResolveDependency<IGameTiming>();
|
||||
var batterySys = entityManager.System<BatterySystem>();
|
||||
var mapSys = entityManager.System<SharedMapSystem>();
|
||||
EntityUid batteryEnt = default!;
|
||||
EntityUid supplyEnt = default!;
|
||||
EntityUid consumerEnt = default!;
|
||||
PowerConsumerComponent consumer = default!;
|
||||
PowerSupplierComponent supplier = default!;
|
||||
PowerNetworkBatteryComponent netBattery = default!;
|
||||
@@ -731,9 +750,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var terminal = entityManager.SpawnEntity("CableTerminal", grid.Owner.ToCoordinates(0, 1));
|
||||
entityManager.GetComponent<TransformComponent>(terminal).LocalRotation = Angle.FromDegrees(180);
|
||||
|
||||
var batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
var supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
var consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
|
||||
batteryEnt = entityManager.SpawnEntity("FullBatteryDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
supplyEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
consumerEnt = entityManager.SpawnEntity("ConsumerDummy", grid.Owner.ToCoordinates(0, 3));
|
||||
|
||||
consumer = entityManager.GetComponent<PowerConsumerComponent>(consumerEnt);
|
||||
supplier = entityManager.GetComponent<PowerSupplierComponent>(supplyEnt);
|
||||
@@ -772,7 +791,8 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
Assert.That(netBattery.SupplyRampPosition, Is.EqualTo(400).Within(0.1));
|
||||
|
||||
const int expectedSpent = 400;
|
||||
Assert.That(battery.CurrentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
|
||||
var currentCharge = batterySys.GetCharge((batteryEnt, battery));
|
||||
Assert.That(currentCharge, Is.EqualTo(battery.MaxCharge - expectedSpent).Within(tickDev));
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1223,6 +1243,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
var entityManager = server.ResolveDependency<IEntityManager>();
|
||||
var batterySys = entityManager.System<BatterySystem>();
|
||||
var mapSys = entityManager.System<SharedMapSystem>();
|
||||
EntityUid generatorEnt = default!;
|
||||
EntityUid substationEnt = default!;
|
||||
EntityUid apcEnt = default!;
|
||||
PowerNetworkBatteryComponent substationNetBattery = default!;
|
||||
BatteryComponent apcBattery = default!;
|
||||
|
||||
@@ -1242,9 +1265,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 1));
|
||||
entityManager.SpawnEntity("CableMV", grid.Owner.ToCoordinates(0, 2));
|
||||
|
||||
var generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
var substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1));
|
||||
var apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
generatorEnt = entityManager.SpawnEntity("GeneratorDummy", grid.Owner.ToCoordinates(0, 0));
|
||||
substationEnt = entityManager.SpawnEntity("SubstationDummy", grid.Owner.ToCoordinates(0, 1));
|
||||
apcEnt = entityManager.SpawnEntity("ApcDummy", grid.Owner.ToCoordinates(0, 2));
|
||||
|
||||
var generatorSupplier = entityManager.GetComponent<PowerSupplierComponent>(generatorEnt);
|
||||
substationNetBattery = entityManager.GetComponent<PowerNetworkBatteryComponent>(substationEnt);
|
||||
@@ -1262,8 +1285,9 @@ namespace Content.IntegrationTests.Tests.Power
|
||||
{
|
||||
Assert.Multiple(() =>
|
||||
{
|
||||
var currentCharge = batterySys.GetCharge((apcEnt, apcBattery));
|
||||
Assert.That(substationNetBattery.CurrentSupply, Is.GreaterThan(0)); //substation should be providing power
|
||||
Assert.That(apcBattery.CurrentCharge, Is.GreaterThan(0)); //apc battery should have gained charge
|
||||
Assert.That(currentCharge, Is.GreaterThan(0)); //apc battery should have gained charge
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@ using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Content.Server.GameTicking;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Power.NodeGroups;
|
||||
using Content.Server.Power.Pow3r;
|
||||
using Content.Shared.Maps;
|
||||
@@ -48,6 +49,7 @@ public sealed class StationPowerTests
|
||||
var entMan = server.EntMan;
|
||||
var protoMan = server.ProtoMan;
|
||||
var ticker = entMan.System<GameTicker>();
|
||||
var batterySys = entMan.System<BatterySystem>();
|
||||
|
||||
// Load the map
|
||||
await server.WaitAssertion(() =>
|
||||
@@ -71,7 +73,8 @@ public sealed class StationPowerTests
|
||||
if (node.NodeGroup is not IBasePowerNet group)
|
||||
continue;
|
||||
networks.TryGetValue(group.NetworkNode, out var charge);
|
||||
networks[group.NetworkNode] = charge + battery.CurrentCharge;
|
||||
var currentCharge = batterySys.GetCharge((uid, battery));
|
||||
networks[group.NetworkNode] = charge + currentCharge;
|
||||
}
|
||||
var totalStartingCharge = networks.MaxBy(n => n.Value).Value;
|
||||
|
||||
|
||||
@@ -14,7 +14,7 @@ public sealed class AdminTest : ToolshedTest
|
||||
var toolMan = Server.ResolveDependency<ToolshedManager>();
|
||||
var admin = Server.ResolveDependency<IAdminManager>();
|
||||
var ignored = new HashSet<Assembly>()
|
||||
{typeof(LocTest).Assembly, typeof(Robust.UnitTesting.Shared.Toolshed.LocTest).Assembly};
|
||||
{typeof(LocTest).Assembly};
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
|
||||
@@ -19,7 +19,7 @@ public sealed class LocTest : ToolshedTest
|
||||
var locStrings = new HashSet<string>();
|
||||
|
||||
var ignored = new HashSet<Assembly>()
|
||||
{typeof(LocTest).Assembly, typeof(Robust.UnitTesting.Shared.Toolshed.LocTest).Assembly};
|
||||
{typeof(LocTest).Assembly};
|
||||
|
||||
await Server.WaitAssertion(() =>
|
||||
{
|
||||
|
||||
@@ -3,19 +3,27 @@
|
||||
<OutputType>Exe</OutputType>
|
||||
<OutputPath>..\bin\Content.MapRenderer\</OutputPath>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<Nullable>enable</Nullable>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
<IsTestingPlatformApplication>false</IsTestingPlatformApplication>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.IntegrationTests\Content.IntegrationTests.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.UnitTesting\Robust.UnitTesting.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="NUnit" />
|
||||
<PackageReference Include="Moq" />
|
||||
<PackageReference Include="SixLabors.ImageSharp" />
|
||||
</ItemGroup>
|
||||
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Testing.props" />
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -2,13 +2,13 @@
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<ServerGarbageCollection>True</ServerGarbageCollection>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Packaging\Robust.Packaging.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Packaging.props" />
|
||||
</Project>
|
||||
|
||||
80
Content.Packaging/DepsHandler.cs
Normal file
80
Content.Packaging/DepsHandler.cs
Normal file
@@ -0,0 +1,80 @@
|
||||
using System.Text.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Content.Packaging;
|
||||
|
||||
/// <summary>
|
||||
/// Helper class for working with <c>.deps.json</c> files.
|
||||
/// </summary>
|
||||
public sealed class DepsHandler
|
||||
{
|
||||
public readonly Dictionary<string, LibraryInfo> Libraries = new();
|
||||
|
||||
public DepsHandler(DepsData data)
|
||||
{
|
||||
if (data.Targets.Count != 1)
|
||||
throw new Exception("Expected exactly one target");
|
||||
|
||||
var target = data.Targets.Single().Value;
|
||||
|
||||
foreach (var (libNameAndVersion, libInfo) in target)
|
||||
{
|
||||
var split = libNameAndVersion.Split('/', 2);
|
||||
|
||||
Libraries.Add(split[0], libInfo);
|
||||
}
|
||||
}
|
||||
|
||||
public static DepsHandler Load(string depsFile)
|
||||
{
|
||||
using var f = File.OpenRead(depsFile);
|
||||
var depsData = JsonSerializer.Deserialize<DepsData>(f) ?? throw new InvalidOperationException("Deps are null!");
|
||||
|
||||
return new DepsHandler(depsData);
|
||||
}
|
||||
|
||||
public HashSet<string> RecursiveGetLibrariesFrom(string start)
|
||||
{
|
||||
var found = new HashSet<string>();
|
||||
|
||||
RecursiveAddLibraries(start, found);
|
||||
|
||||
return found;
|
||||
}
|
||||
|
||||
private void RecursiveAddLibraries(string start, HashSet<string> set)
|
||||
{
|
||||
if (!set.Add(start))
|
||||
return;
|
||||
|
||||
var lib = Libraries[start];
|
||||
if (lib.Dependencies == null)
|
||||
return;
|
||||
|
||||
foreach (var dep in lib.Dependencies.Keys)
|
||||
{
|
||||
RecursiveAddLibraries(dep, set);
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class DepsData
|
||||
{
|
||||
[JsonInclude, JsonPropertyName("targets")]
|
||||
public required Dictionary<string, Dictionary<string, LibraryInfo>> Targets;
|
||||
}
|
||||
|
||||
public sealed class LibraryInfo
|
||||
{
|
||||
[JsonInclude, JsonPropertyName("dependencies")]
|
||||
public Dictionary<string, string>? Dependencies;
|
||||
|
||||
[JsonInclude, JsonPropertyName("runtime")]
|
||||
public Dictionary<string, object>? Runtime;
|
||||
|
||||
// Paths are like lib/netstandard2.0/JetBrains.Annotations.dll
|
||||
public IEnumerable<string> GetDllNames()
|
||||
{
|
||||
return Runtime == null ? [] : Runtime.Keys.Select(p => p.Split('/')[^1]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -37,29 +37,9 @@ public static class ServerPackaging
|
||||
.Select(o => o.Rid)
|
||||
.ToList();
|
||||
|
||||
private static readonly List<string> ServerContentAssemblies = new()
|
||||
{
|
||||
// Corvax-Secrets-Start
|
||||
"Content.Corvax.Interfaces.Shared",
|
||||
"Content.Corvax.Interfaces.Server",
|
||||
// Corvax-Secrets-End
|
||||
"Content.Server.Database",
|
||||
"Content.Server",
|
||||
"Content.Shared",
|
||||
"Content.Shared.Database",
|
||||
};
|
||||
|
||||
private static readonly List<string> ServerExtraAssemblies = new()
|
||||
{
|
||||
// Python script had Npgsql. though we want Npgsql.dll as well soooo
|
||||
"Npgsql",
|
||||
"Microsoft",
|
||||
"NetCord",
|
||||
};
|
||||
|
||||
private static readonly List<string> ServerNotExtraAssemblies = new()
|
||||
{
|
||||
"Microsoft.CodeAnalysis",
|
||||
"JetBrains.Annotations",
|
||||
};
|
||||
|
||||
private static readonly HashSet<string> BinSkipFolders = new()
|
||||
@@ -208,27 +188,13 @@ public static class ServerPackaging
|
||||
|
||||
var inputPassCore = graph.InputCore;
|
||||
var inputPassResources = graph.InputResources;
|
||||
var contentAssemblies = new List<string>(ServerContentAssemblies);
|
||||
// Corvax-Secrets-Start
|
||||
if (UseSecrets)
|
||||
contentAssemblies.AddRange(new[] { "Content.Corvax.Shared", "Content.Corvax.Server" });
|
||||
// Corvax-Secrets-End
|
||||
|
||||
// Additional assemblies that need to be copied such as EFCore.
|
||||
var sourcePath = Path.Combine(contentDir, "bin", "Content.Server");
|
||||
|
||||
// Should this be an asset pass?
|
||||
// For future archaeologists I just want audio rework to work and need the audio pass so
|
||||
// just porting this as is from python.
|
||||
foreach (var fullPath in Directory.EnumerateFiles(sourcePath, "*.*", SearchOption.AllDirectories))
|
||||
{
|
||||
var fileName = Path.GetFileNameWithoutExtension(fullPath);
|
||||
var deps = DepsHandler.Load(Path.Combine(sourcePath, "Content.Server.deps.json"));
|
||||
|
||||
if (!ServerNotExtraAssemblies.Any(o => fileName.StartsWith(o)) && ServerExtraAssemblies.Any(o => fileName.StartsWith(o)))
|
||||
{
|
||||
contentAssemblies.Add(fileName);
|
||||
}
|
||||
}
|
||||
var contentAssemblies = GetContentAssemblyNamesToCopy(deps);
|
||||
|
||||
await RobustSharedPackaging.DoResourceCopy(
|
||||
Path.Combine("RobustToolbox", "bin", "Server",
|
||||
@@ -260,5 +226,21 @@ public static class ServerPackaging
|
||||
inputPassResources.InjectFinished();
|
||||
}
|
||||
|
||||
// This returns both content assemblies (e.g. Content.Server.dll) and dependencies (e.g. Npgsql)
|
||||
private static IEnumerable<string> GetContentAssemblyNamesToCopy(DepsHandler deps)
|
||||
{
|
||||
var depsContent = deps.RecursiveGetLibrariesFrom("Content.Server").SelectMany(GetLibraryNames);
|
||||
var depsRobust = deps.RecursiveGetLibrariesFrom("Robust.Server").SelectMany(GetLibraryNames);
|
||||
|
||||
var depsContentExclusive = depsContent.Except(depsRobust).ToHashSet();
|
||||
|
||||
// Remove .dll suffix and apply filtering.
|
||||
var names = depsContentExclusive.Select(p => p[..^4]).Where(p => !ServerNotExtraAssemblies.Any(p.StartsWith));
|
||||
|
||||
return names;
|
||||
|
||||
IEnumerable<string> GetLibraryNames(string library) => deps.Libraries[library].GetDllNames();
|
||||
}
|
||||
|
||||
private readonly record struct PlatformReg(string Rid, string TargetOs, bool BuildByDefault);
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net9.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
|
||||
|
||||
@@ -1,26 +1,23 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Replay\</OutputPath>
|
||||
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
|
||||
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Nett" />
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Client\Robust.Client.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Content.Client\Content.Client.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\XamlIL.targets" />
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Client.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
</Project>
|
||||
|
||||
@@ -1,16 +1,13 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Server.Database\</OutputPath>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<Nullable>enable</Nullable>
|
||||
<NoWarn>RA0003</NoWarn>
|
||||
</PropertyGroup>
|
||||
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.EntityFrameworkCore.Design">
|
||||
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
|
||||
|
||||
@@ -31,6 +31,7 @@ public sealed partial class AdminVerbSystem
|
||||
private static readonly EntProtoId DefaultChangelingRule = "Changeling";
|
||||
private static readonly EntProtoId ParadoxCloneRuleId = "ParadoxCloneSpawn";
|
||||
private static readonly EntProtoId DefaultWizardRule = "Wizard";
|
||||
private static readonly EntProtoId DefaultNinjaRule = "NinjaSpawn";
|
||||
private static readonly ProtoId<StartingGearPrototype> PirateGearId = "PirateGear";
|
||||
|
||||
// All antag verbs have names so invokeverb works.
|
||||
@@ -207,6 +208,21 @@ public sealed partial class AdminVerbSystem
|
||||
};
|
||||
args.Verbs.Add(wizard);
|
||||
|
||||
var ninjaName = Loc.GetString("admin-verb-text-make-space-ninja");
|
||||
Verb ninja = new()
|
||||
{
|
||||
Text = ninjaName,
|
||||
Category = VerbCategory.Antag,
|
||||
Icon = new SpriteSpecifier.Rsi(new("/Textures/Objects/Weapons/Melee/energykatana.rsi"), "icon"),
|
||||
Act = () =>
|
||||
{
|
||||
_antag.ForceMakeAntag<NinjaRoleComponent>(targetPlayer, DefaultNinjaRule);
|
||||
},
|
||||
Impact = LogImpact.High,
|
||||
Message = string.Join(": ", ninjaName, Loc.GetString("admin-verb-make-space-ninja")),
|
||||
};
|
||||
args.Verbs.Add(ninja);
|
||||
|
||||
if (HasComp<HumanoidAppearanceComponent>(args.Target)) // only humanoids can be cloned
|
||||
args.Verbs.Add(paradox);
|
||||
}
|
||||
|
||||
@@ -4,7 +4,6 @@ using System.Numerics;
|
||||
using Content.Server.Cargo.Components;
|
||||
using Content.Server.Doors.Systems;
|
||||
using Content.Server.Hands.Systems;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Server.Station.Systems;
|
||||
using Content.Server.Weapons.Ranged.Systems;
|
||||
@@ -51,8 +50,7 @@ public sealed partial class AdminVerbSystem
|
||||
[Dependency] private readonly AdminTestArenaSystem _adminTestArenaSystem = default!;
|
||||
[Dependency] private readonly StationJobsSystem _stationJobsSystem = default!;
|
||||
[Dependency] private readonly JointSystem _jointSystem = default!;
|
||||
[Dependency] private readonly BatterySystem _batterySystem = default!;
|
||||
[Dependency] private readonly PredictedBatterySystem _predictedBatterySystem = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _batterySystem = default!;
|
||||
[Dependency] private readonly MetaDataSystem _metaSystem = default!;
|
||||
[Dependency] private readonly GunSystem _gun = default!;
|
||||
|
||||
@@ -161,57 +159,6 @@ public sealed partial class AdminVerbSystem
|
||||
args.Verbs.Add(makeVulnerable);
|
||||
}
|
||||
|
||||
if (TryComp<PredictedBatteryComponent>(args.Target, out var pBattery))
|
||||
{
|
||||
Verb refillBattery = new()
|
||||
{
|
||||
Text = Loc.GetString("admin-verbs-refill-battery"),
|
||||
Category = VerbCategory.Tricks,
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/fill_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_predictedBatterySystem.SetCharge((args.Target, pBattery), pBattery.MaxCharge);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-refill-battery-description"),
|
||||
Priority = (int)TricksVerbPriorities.RefillBattery,
|
||||
};
|
||||
args.Verbs.Add(refillBattery);
|
||||
|
||||
Verb drainBattery = new()
|
||||
{
|
||||
Text = Loc.GetString("admin-verbs-drain-battery"),
|
||||
Category = VerbCategory.Tricks,
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/drain_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
_predictedBatterySystem.SetCharge((args.Target, pBattery), 0);
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Priority = (int)TricksVerbPriorities.DrainBattery,
|
||||
};
|
||||
args.Verbs.Add(drainBattery);
|
||||
|
||||
Verb infiniteBattery = new()
|
||||
{
|
||||
Text = Loc.GetString("admin-verbs-infinite-battery"),
|
||||
Category = VerbCategory.Tricks,
|
||||
Icon = new SpriteSpecifier.Texture(new("/Textures/Interface/AdminActions/infinite_battery.png")),
|
||||
Act = () =>
|
||||
{
|
||||
var recharger = EnsureComp<PredictedBatterySelfRechargerComponent>(args.Target);
|
||||
recharger.AutoRechargeRate = pBattery.MaxCharge; // Instant refill.
|
||||
recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
|
||||
Dirty(args.Target, recharger);
|
||||
_predictedBatterySystem.RefreshChargeRate((args.Target, pBattery));
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
|
||||
Priority = (int)TricksVerbPriorities.InfiniteBattery,
|
||||
};
|
||||
args.Verbs.Add(infiniteBattery);
|
||||
}
|
||||
|
||||
if (TryComp<BatteryComponent>(args.Target, out var battery))
|
||||
{
|
||||
Verb refillBattery = new()
|
||||
@@ -254,6 +201,8 @@ public sealed partial class AdminVerbSystem
|
||||
var recharger = EnsureComp<BatterySelfRechargerComponent>(args.Target);
|
||||
recharger.AutoRechargeRate = battery.MaxCharge; // Instant refill.
|
||||
recharger.AutoRechargePauseTime = TimeSpan.Zero; // No delay.
|
||||
Dirty(args.Target, recharger);
|
||||
_batterySystem.RefreshChargeRate((args.Target, battery));
|
||||
},
|
||||
Impact = LogImpact.Medium,
|
||||
Message = Loc.GetString("admin-trick-infinite-battery-object-description"),
|
||||
|
||||
@@ -100,7 +100,7 @@ public sealed partial class AnomalySystem
|
||||
|
||||
// no air-blocked areas.
|
||||
if (_atmosphere.IsTileSpace(grid, xform.MapUid, tile) ||
|
||||
_atmosphere.IsTileAirBlocked(grid, tile, mapGridComp: gridComp))
|
||||
_atmosphere.IsTileAirBlockedCached(grid, tile))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -306,6 +306,8 @@ public partial class AtmosphereSystem
|
||||
/// <param name="directions">The directions to check for air-blockage.</param>
|
||||
/// <param name="mapGridComp">Optional map grid component associated with the grid.</param>
|
||||
/// <returns>True if the tile is air-blocked in the specified directions, false otherwise.</returns>
|
||||
/// <remarks>This rebuilds airtight data on-the-fly. You should only use this if you've just
|
||||
/// invalidated airtight data, and you cannot wait one atmostick to revalidate it.</remarks>
|
||||
[PublicAPI]
|
||||
public bool IsTileAirBlocked(EntityUid gridUid,
|
||||
Vector2i tile,
|
||||
@@ -315,11 +317,35 @@ public partial class AtmosphereSystem
|
||||
if (!Resolve(gridUid, ref mapGridComp, false))
|
||||
return false;
|
||||
|
||||
// TODO ATMOS: This reconstructs the data instead of getting the cached version. Might want to include a method to get the cached version later.
|
||||
var data = GetAirtightData(gridUid, mapGridComp, tile);
|
||||
return data.BlockedDirections.IsFlagSet(directions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a tile on a grid is air-blocked in the specified directions, using cached data.
|
||||
/// </summary>
|
||||
/// <param name="grid">The grid to check.</param>
|
||||
/// <param name="tile">The tile on the grid to check.</param>
|
||||
/// <param name="directions">The directions to check for air-blockage.</param>
|
||||
/// <returns>True if the tile is air-blocked in the specified directions, false otherwise.</returns>
|
||||
/// <remarks>Returns data that is currently cached by Atmospherics.
|
||||
/// You should always use this method over <see cref="IsTileAirBlocked"/> as it's more performant.
|
||||
/// If you need to get up-to-date data because you've just invalidated airtight data,
|
||||
/// use <see cref="IsTileAirBlocked"/>.</remarks>
|
||||
[PublicAPI]
|
||||
public bool IsTileAirBlockedCached(Entity<GridAtmosphereComponent?> grid,
|
||||
Vector2i tile,
|
||||
AtmosDirection directions = AtmosDirection.All)
|
||||
{
|
||||
if (!_atmosQuery.Resolve(grid, ref grid.Comp, false))
|
||||
return false;
|
||||
|
||||
if (!grid.Comp.Tiles.TryGetValue(tile, out var atmosTile))
|
||||
return false;
|
||||
|
||||
return atmosTile.AirtightData.BlockedDirections.IsFlagSet(directions);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Checks if a tile on a grid or map is space as defined by a tile's definition of space.
|
||||
/// Some tiles can hold back space and others cannot - for example, plating can hold
|
||||
|
||||
@@ -19,7 +19,9 @@ public sealed partial class AtmosphereSystem
|
||||
// Fix Grid Atmos command.
|
||||
_consoleHost.RegisterCommand("fixgridatmos",
|
||||
"Makes every tile on a grid have a roundstart gas mix.",
|
||||
"fixgridatmos <grid Ids>", FixGridAtmosCommand, FixGridAtmosCommandCompletions);
|
||||
"fixgridatmos <grid Ids>",
|
||||
FixGridAtmosCommand,
|
||||
FixGridAtmosCommandCompletions);
|
||||
}
|
||||
|
||||
private void ShutdownCommands()
|
||||
@@ -36,42 +38,6 @@ public sealed partial class AtmosphereSystem
|
||||
return;
|
||||
}
|
||||
|
||||
var mixtures = new GasMixture[9];
|
||||
for (var i = 0; i < mixtures.Length; i++)
|
||||
mixtures[i] = new GasMixture(Atmospherics.CellVolume) { Temperature = Atmospherics.T20C };
|
||||
|
||||
// 0: Air
|
||||
mixtures[0].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard);
|
||||
mixtures[0].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard);
|
||||
|
||||
// 1: Vaccum
|
||||
|
||||
// 2: Oxygen (GM)
|
||||
mixtures[2].AdjustMoles(Gas.Oxygen, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 3: Nitrogen (GM)
|
||||
mixtures[3].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 4: Plasma (GM)
|
||||
mixtures[4].AdjustMoles(Gas.Plasma, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 5: Instant Plasmafire (r)
|
||||
mixtures[5].AdjustMoles(Gas.Oxygen, Atmospherics.MolesCellGasMiner);
|
||||
mixtures[5].AdjustMoles(Gas.Plasma, Atmospherics.MolesCellGasMiner);
|
||||
mixtures[5].Temperature = 5000f;
|
||||
|
||||
// 6: (Walk-In) Freezer
|
||||
mixtures[6].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesFreezer);
|
||||
mixtures[6].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesFreezer);
|
||||
mixtures[6].Temperature = Atmospherics.FreezerTemp; // Little colder than an actual freezer but gives a grace period to get e.g. themomachines set up, should keep warm for a few door openings
|
||||
|
||||
// 7: Nitrogen (101kpa) for vox rooms
|
||||
mixtures[7].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellStandard);
|
||||
|
||||
// 8: Air (GM)
|
||||
mixtures[8].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesGasMiner);
|
||||
mixtures[8].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesGasMiner);
|
||||
|
||||
foreach (var arg in args)
|
||||
{
|
||||
if (!NetEntity.TryParse(arg, out var netEntity) || !TryGetEntity(netEntity, out var euid))
|
||||
@@ -92,34 +58,82 @@ public sealed partial class AtmosphereSystem
|
||||
continue;
|
||||
}
|
||||
|
||||
// Force Invalidate & update air on all tiles
|
||||
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> grid =
|
||||
new(euid.Value, gridAtmosphere, Comp<GasTileOverlayComponent>(euid.Value), gridComp, Transform(euid.Value));
|
||||
|
||||
RebuildGridTiles(grid);
|
||||
|
||||
var query = GetEntityQuery<AtmosFixMarkerComponent>();
|
||||
foreach (var (indices, tile) in gridAtmosphere.Tiles.ToArray())
|
||||
{
|
||||
if (tile.Air is not {Immutable: false} air)
|
||||
continue;
|
||||
|
||||
air.Clear();
|
||||
var mixtureId = 0;
|
||||
var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(grid, grid, indices);
|
||||
while (enumerator.MoveNext(out var entUid))
|
||||
{
|
||||
if (query.TryComp(entUid, out var marker))
|
||||
mixtureId = marker.Mode;
|
||||
}
|
||||
|
||||
var mixture = mixtures[mixtureId];
|
||||
Merge(air, mixture);
|
||||
air.Temperature = mixture.Temperature;
|
||||
}
|
||||
RebuildGridAtmosphere((euid.Value, gridAtmosphere, gridComp));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Rebuilds all <see cref="TileAtmosphere"/>s on a grid to have roundstart gas mixes.
|
||||
/// </summary>
|
||||
/// <remarks>Please be responsible with this method. Used only by tests and fixgridatmos.</remarks>
|
||||
public void RebuildGridAtmosphere(Entity<GridAtmosphereComponent, MapGridComponent> ent)
|
||||
{
|
||||
var mixtures = new GasMixture[9];
|
||||
for (var i = 0; i < mixtures.Length; i++)
|
||||
{
|
||||
mixtures[i] = new GasMixture(Atmospherics.CellVolume) { Temperature = Atmospherics.T20C };
|
||||
}
|
||||
|
||||
// 0: Air
|
||||
mixtures[0].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesStandard);
|
||||
mixtures[0].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesStandard);
|
||||
|
||||
// 1: Vaccum
|
||||
|
||||
// 2: Oxygen (GM)
|
||||
mixtures[2].AdjustMoles(Gas.Oxygen, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 3: Nitrogen (GM)
|
||||
mixtures[3].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 4: Plasma (GM)
|
||||
mixtures[4].AdjustMoles(Gas.Plasma, Atmospherics.MolesCellGasMiner);
|
||||
|
||||
// 5: Instant Plasmafire (r)
|
||||
mixtures[5].AdjustMoles(Gas.Oxygen, Atmospherics.MolesCellGasMiner);
|
||||
mixtures[5].AdjustMoles(Gas.Plasma, Atmospherics.MolesCellGasMiner);
|
||||
mixtures[5].Temperature = 5000f;
|
||||
|
||||
// 6: (Walk-In) Freezer
|
||||
mixtures[6].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesFreezer);
|
||||
mixtures[6].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesFreezer);
|
||||
mixtures[6].Temperature = Atmospherics.FreezerTemp; // Little colder than an actual freezer but gives a grace period to get e.g. themomachines set up, should keep warm for a few door openings
|
||||
|
||||
// 7: Nitrogen (101kpa) for vox rooms
|
||||
mixtures[7].AdjustMoles(Gas.Nitrogen, Atmospherics.MolesCellStandard);
|
||||
|
||||
// 8: Air (GM)
|
||||
mixtures[8].AdjustMoles(Gas.Oxygen, Atmospherics.OxygenMolesGasMiner);
|
||||
mixtures[8].AdjustMoles(Gas.Nitrogen, Atmospherics.NitrogenMolesGasMiner);
|
||||
|
||||
|
||||
// Force Invalidate & update air on all tiles
|
||||
Entity<GridAtmosphereComponent, GasTileOverlayComponent, MapGridComponent, TransformComponent> grid =
|
||||
new(ent.Owner, ent.Comp1, Comp<GasTileOverlayComponent>(ent), ent.Comp2, Transform(ent));
|
||||
|
||||
RebuildGridTiles(grid);
|
||||
|
||||
var query = GetEntityQuery<AtmosFixMarkerComponent>();
|
||||
foreach (var (indices, tile) in ent.Comp1.Tiles.ToArray())
|
||||
{
|
||||
if (tile.Air is not {Immutable: false} air)
|
||||
continue;
|
||||
|
||||
air.Clear();
|
||||
var mixtureId = 0;
|
||||
var enumerator = _mapSystem.GetAnchoredEntitiesEnumerator(grid, grid, indices);
|
||||
while (enumerator.MoveNext(out var entUid))
|
||||
{
|
||||
if (query.TryComp(entUid, out var marker))
|
||||
mixtureId = marker.Mode;
|
||||
}
|
||||
|
||||
var mixture = mixtures[mixtureId];
|
||||
Merge(air, mixture);
|
||||
air.Temperature = mixture.Temperature;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Clears & re-creates all references to <see cref="TileAtmosphere"/>s stored on a grid.
|
||||
/// </summary>
|
||||
|
||||
@@ -3,6 +3,7 @@ using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.Reactions;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Reactions;
|
||||
using JetBrains.Annotations;
|
||||
using Robust.Shared.Prototypes;
|
||||
using DependencyAttribute = Robust.Shared.IoC.DependencyAttribute;
|
||||
|
||||
@@ -477,6 +478,26 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
return reaction;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds an array of moles to a <see cref="GasMixture"/>.
|
||||
/// Guards against negative moles by clamping to zero.
|
||||
/// </summary>
|
||||
/// <param name="mixture">The <see cref="GasMixture"/> to add moles to.</param>
|
||||
/// <param name="molsToAdd">The <see cref="ReadOnlySpan{T}"/> of moles to add.</param>
|
||||
/// <exception cref="ArgumentOutOfRangeException">Thrown when the length of the <see cref="ReadOnlySpan{T}"/>
|
||||
/// is not the same as the length of the <see cref="GasMixture"/> gas array.</exception>
|
||||
[PublicAPI]
|
||||
public static void AddMolsToMixture(GasMixture mixture, ReadOnlySpan<float> molsToAdd)
|
||||
{
|
||||
// Span length should be as long as the length of the gas array.
|
||||
// Technically this is a redundant check because NumericsHelpers will do the same thing,
|
||||
// but eh.
|
||||
ArgumentOutOfRangeException.ThrowIfNotEqual(mixture.Moles.Length, molsToAdd.Length, nameof(mixture.Moles.Length));
|
||||
|
||||
NumericsHelpers.Add(mixture.Moles, molsToAdd);
|
||||
NumericsHelpers.Max(mixture.Moles, 0f);
|
||||
}
|
||||
|
||||
public enum GasCompareResult
|
||||
{
|
||||
NoExchange = -2,
|
||||
|
||||
@@ -265,6 +265,7 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
tile.ArchivedCycle = 0;
|
||||
tile.LastShare = 0f;
|
||||
tile.Hotspot = new Hotspot();
|
||||
NotifyDeviceTileChanged((ent.Owner, ent.Comp1, ent.Comp3), tile.GridIndices);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -275,6 +276,10 @@ namespace Content.Server.Atmos.EntitySystems
|
||||
|
||||
if (data.FixVacuum)
|
||||
GridFixTileVacuum(tile);
|
||||
|
||||
// Since we assigned the tile a new GasMixture we need to tell any devices
|
||||
// on this tile that the reference has changed.
|
||||
NotifyDeviceTileChanged((ent.Owner, ent.Comp1, ent.Comp3), tile.GridIndices);
|
||||
}
|
||||
|
||||
private void QueueRunTiles(
|
||||
|
||||
@@ -1,10 +1,8 @@
|
||||
using System.Runtime.CompilerServices;
|
||||
using Content.Server.Atmos.Components;
|
||||
using Content.Server.Maps;
|
||||
using Content.Shared.Atmos;
|
||||
using Content.Shared.Atmos.Components;
|
||||
using Content.Shared.Maps;
|
||||
using Robust.Shared.Map;
|
||||
using Content.Shared.Atmos.Piping.Components;
|
||||
using Robust.Shared.Map.Components;
|
||||
|
||||
namespace Content.Server.Atmos.EntitySystems;
|
||||
@@ -176,4 +174,21 @@ public partial class AtmosphereSystem
|
||||
|
||||
_tile.PryTile(tileRef);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Notifies all subscribing entities on a particular tile that the tile has changed.
|
||||
/// Atmos devices may store references to tiles, so this is used to properly resync devices
|
||||
/// after a significant atmos change on that tile, for example a tile getting a new <see cref="GasMixture"/>.
|
||||
/// </summary>
|
||||
/// <param name="ent">The grid atmosphere entity.</param>
|
||||
/// <param name="tile">The tile to check for devices on.</param>
|
||||
private void NotifyDeviceTileChanged(Entity<GridAtmosphereComponent, MapGridComponent> ent, Vector2i tile)
|
||||
{
|
||||
var inTile = _mapSystem.GetAnchoredEntities(ent.Owner, ent.Comp2, tile);
|
||||
var ev = new AtmosDeviceTileChangedEvent();
|
||||
foreach (var uid in inTile)
|
||||
{
|
||||
RaiseLocalEvent(uid, ref ev);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -64,8 +64,8 @@ public sealed partial class AtmosphereSystem : SharedAtmosphereSystem
|
||||
InitializeGridAtmosphere();
|
||||
InitializeMap();
|
||||
|
||||
_mapAtmosQuery = GetEntityQuery<MapAtmosphereComponent>();
|
||||
_atmosQuery = GetEntityQuery<GridAtmosphereComponent>();
|
||||
_mapAtmosQuery = GetEntityQuery<MapAtmosphereComponent>();
|
||||
_airtightQuery = GetEntityQuery<AirtightComponent>();
|
||||
_firelockQuery = GetEntityQuery<FirelockComponent>();
|
||||
|
||||
|
||||
@@ -43,7 +43,7 @@ public sealed class HeatExchangerSystem : EntitySystem
|
||||
// make sure that the tile the device is on isn't blocked by a wall or something similar.
|
||||
if (args.Grid is {} grid
|
||||
&& _transform.TryGetGridTilePosition(uid, out var tile)
|
||||
&& _atmosphereSystem.IsTileAirBlocked(grid, tile))
|
||||
&& _atmosphereSystem.IsTileAirBlockedCached(grid, tile))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -57,6 +57,13 @@ public sealed class AtmosMonitorSystem : EntitySystem
|
||||
SubscribeLocalEvent<AtmosMonitorComponent, DeviceNetworkPacketEvent>(OnPacketRecv);
|
||||
SubscribeLocalEvent<AtmosMonitorComponent, AtmosDeviceDisabledEvent>(OnAtmosDeviceLeaveAtmosphere);
|
||||
SubscribeLocalEvent<AtmosMonitorComponent, AtmosDeviceEnabledEvent>(OnAtmosDeviceEnterAtmosphere);
|
||||
SubscribeLocalEvent<AtmosMonitorComponent, AtmosDeviceTileChangedEvent>(OnAtmosDeviceTileChangedEvent);
|
||||
}
|
||||
|
||||
private void OnAtmosDeviceTileChangedEvent(Entity<AtmosMonitorComponent> ent, ref AtmosDeviceTileChangedEvent args)
|
||||
{
|
||||
if (!ent.Comp.MonitorsPipeNet)
|
||||
ent.Comp.TileGas = _atmosphereSystem.GetContainingMixture(ent.Owner, true);
|
||||
}
|
||||
|
||||
private void OnAtmosDeviceLeaveAtmosphere(EntityUid uid, AtmosMonitorComponent atmosMonitor, ref AtmosDeviceDisabledEvent args)
|
||||
|
||||
@@ -119,9 +119,9 @@ public sealed partial class CargoSystem
|
||||
label.Id = bounty.Id;
|
||||
label.AssociatedStationId = stationId;
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(Loc.GetString("bounty-manifest-header", ("id", bounty.Id)));
|
||||
msg.AddMarkupOrThrow(Loc.GetString("bounty-manifest-header", ("id", bounty.Id)));
|
||||
msg.PushNewline();
|
||||
msg.AddText(Loc.GetString("bounty-manifest-list-start"));
|
||||
msg.AddMarkupOrThrow(Loc.GetString("bounty-manifest-list-start"));
|
||||
msg.PushNewline();
|
||||
foreach (var entry in prototype.Entries)
|
||||
{
|
||||
|
||||
@@ -77,11 +77,11 @@ public sealed class NanoTaskCartridgeSystem : SharedNanoTaskCartridgeSystem
|
||||
|
||||
printed.Task = item;
|
||||
var msg = new FormattedMessage();
|
||||
msg.AddText(Loc.GetString("nano-task-printed-description", ("description", item.Description)));
|
||||
msg.AddMarkupOrThrow(Loc.GetString("nano-task-printed-description", ("description", FormattedMessage.EscapeText(item.Description))));
|
||||
msg.PushNewline();
|
||||
msg.AddText(Loc.GetString("nano-task-printed-requester", ("requester", item.TaskIsFor)));
|
||||
msg.AddMarkupOrThrow(Loc.GetString("nano-task-printed-requester", ("requester", FormattedMessage.EscapeText(item.TaskIsFor))));
|
||||
msg.PushNewline();
|
||||
msg.AddText(item.Priority switch {
|
||||
msg.AddMarkupOrThrow(item.Priority switch {
|
||||
NanoTaskPriority.High => Loc.GetString("nano-task-printed-high-priority"),
|
||||
NanoTaskPriority.Medium => Loc.GetString("nano-task-printed-medium-priority"),
|
||||
NanoTaskPriority.Low => Loc.GetString("nano-task-printed-low-priority"),
|
||||
|
||||
@@ -46,8 +46,11 @@ internal sealed partial class ChatManager : IChatManager
|
||||
[Dependency] private readonly PlayerRateLimitManager _rateLimitManager = default!;
|
||||
[Dependency] private readonly ISharedPlayerManager _player = default!;
|
||||
[Dependency] private readonly DiscordChatLink _discordLink = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
private ISharedSponsorsManager? _sponsorsManager; // Corvax-Sponsors
|
||||
|
||||
private ISawmill _sawmill = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The maximum length a player-sent message can be sent
|
||||
/// </summary>
|
||||
@@ -67,6 +70,8 @@ internal sealed partial class ChatManager : IChatManager
|
||||
_configurationManager.OnValueChanged(CCVars.OocEnabled, OnOocEnabledChanged, true);
|
||||
_configurationManager.OnValueChanged(CCVars.AdminOocEnabled, OnAdminOocEnabledChanged, true);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("SERVER");
|
||||
|
||||
RegisterRateLimits();
|
||||
}
|
||||
|
||||
@@ -114,7 +119,7 @@ internal sealed partial class ChatManager : IChatManager
|
||||
{
|
||||
var wrappedMessage = Loc.GetString("chat-manager-server-wrap-message", ("message", FormattedMessage.EscapeText(message)));
|
||||
ChatMessageToAll(ChatChannel.Server, message, wrappedMessage, EntityUid.Invalid, hideChat: false, recordReplay: true, colorOverride: colorOverride);
|
||||
Logger.InfoS("SERVER", message);
|
||||
_sawmill.Info(message);
|
||||
|
||||
_adminLogger.Add(LogType.Chat, LogImpact.Low, $"Server announcement: {message}");
|
||||
}
|
||||
|
||||
@@ -745,7 +745,7 @@ public sealed partial class ChatSystem : SharedChatSystem
|
||||
public string TransformSpeech(EntityUid sender, string message)
|
||||
{
|
||||
var ev = new TransformSpeechEvent(sender, message);
|
||||
RaiseLocalEvent(ev);
|
||||
RaiseLocalEvent(sender, ev, true);
|
||||
|
||||
return ev.Message;
|
||||
}
|
||||
|
||||
@@ -26,5 +26,11 @@ namespace Content.Server.Chemistry.Components
|
||||
|
||||
[DataField("clickSound"), ViewVariables(VVAccess.ReadWrite)]
|
||||
public SoundSpecifier ClickSound = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
/// <summary>
|
||||
/// Which source the chem master should draw from when making pills/bottles.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public ChemMasterDrawSource DrawSource = ChemMasterDrawSource.Internal;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
SubscribeLocalEvent<ChemMasterComponent, ChemMasterReagentAmountButtonMessage>(OnReagentButtonMessage);
|
||||
SubscribeLocalEvent<ChemMasterComponent, ChemMasterCreatePillsMessage>(OnCreatePillsMessage);
|
||||
SubscribeLocalEvent<ChemMasterComponent, ChemMasterOutputToBottleMessage>(OnOutputToBottleMessage);
|
||||
SubscribeLocalEvent<ChemMasterComponent, ChemMasterOutputDrawSourceMessage>(OnSetDrawSourceMessage);
|
||||
}
|
||||
|
||||
private void SubscribeUpdateUiState<T>(Entity<ChemMasterComponent> ent, ref T ev)
|
||||
@@ -77,7 +78,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
|
||||
var state = new ChemMasterBoundUserInterfaceState(
|
||||
chemMaster.Mode, chemMaster.SortingType, BuildInputContainerInfo(inputContainer), BuildOutputContainerInfo(outputContainer),
|
||||
bufferReagents, bufferCurrentVolume, chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel);
|
||||
bufferReagents, bufferCurrentVolume, chemMaster.PillType, chemMaster.PillDosageLimit, updateLabel, chemMaster.DrawSource);
|
||||
|
||||
_userInterfaceSystem.SetUiState(owner, ChemMasterUiKey.Key, state);
|
||||
}
|
||||
@@ -135,6 +136,17 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
ClickSound(chemMaster);
|
||||
}
|
||||
|
||||
private void OnSetDrawSourceMessage(Entity<ChemMasterComponent> chemMaster, ref ChemMasterOutputDrawSourceMessage message)
|
||||
{
|
||||
//Ensure draw source is valid, either from the internal buffer or the inserted beaker
|
||||
if (!Enum.IsDefined(message.DrawSource))
|
||||
return;
|
||||
|
||||
chemMaster.Comp.DrawSource = message.DrawSource;
|
||||
UpdateUiState(chemMaster);
|
||||
ClickSound(chemMaster);
|
||||
}
|
||||
|
||||
private void TransferReagents(Entity<ChemMasterComponent> chemMaster, ReagentId id, FixedPoint2 amount, bool fromBuffer)
|
||||
{
|
||||
var container = _itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName);
|
||||
@@ -208,9 +220,9 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
return;
|
||||
|
||||
var needed = message.Dosage * message.Number;
|
||||
if (!WithdrawFromBuffer(chemMaster, needed, user, out var withdrawal))
|
||||
return;
|
||||
|
||||
if (!WithdrawFromSource(chemMaster, needed, user, out var withdrawal))
|
||||
return;
|
||||
_labelSystem.Label(container, message.Label);
|
||||
|
||||
for (var i = 0; i < message.Number; i++)
|
||||
@@ -219,7 +231,10 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
_storageSystem.Insert(container, item, out _, user: user, storage);
|
||||
_labelSystem.Label(item, message.Label);
|
||||
|
||||
_solutionContainerSystem.EnsureSolutionEntity(item, SharedChemMaster.PillSolutionName,out var itemSolution ,message.Dosage);
|
||||
_solutionContainerSystem.EnsureSolutionEntity(item,
|
||||
SharedChemMaster.PillSolutionName,
|
||||
out var itemSolution,
|
||||
message.Dosage);
|
||||
if (!itemSolution.HasValue)
|
||||
return;
|
||||
|
||||
@@ -256,7 +271,7 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
if (message.Label.Length > SharedChemMaster.LabelMaxLength)
|
||||
return;
|
||||
|
||||
if (!WithdrawFromBuffer(chemMaster, message.Dosage, user, out var withdrawal))
|
||||
if (!WithdrawFromSource(chemMaster, message.Dosage, user, out var withdrawal))
|
||||
return;
|
||||
|
||||
_labelSystem.Label(container, message.Label);
|
||||
@@ -270,34 +285,77 @@ namespace Content.Server.Chemistry.EntitySystems
|
||||
ClickSound(chemMaster);
|
||||
}
|
||||
|
||||
private bool WithdrawFromBuffer(
|
||||
private bool WithdrawFromSource(
|
||||
Entity<ChemMasterComponent> chemMaster,
|
||||
FixedPoint2 neededVolume, EntityUid? user,
|
||||
FixedPoint2 neededVolume,
|
||||
EntityUid? user,
|
||||
[NotNullWhen(returnValue: true)] out Solution? outputSolution)
|
||||
{
|
||||
outputSolution = null;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out var solution))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
Solution? solution;
|
||||
Entity<SolutionComponent>? soln = null;
|
||||
|
||||
if (solution.Volume == 0)
|
||||
switch (chemMaster.Comp.DrawSource)
|
||||
{
|
||||
if (user.HasValue)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), user.Value);
|
||||
return false;
|
||||
}
|
||||
case ChemMasterDrawSource.Internal:
|
||||
if (!_solutionContainerSystem.TryGetSolution(chemMaster.Owner, SharedChemMaster.BufferSolutionName, out _, out solution))
|
||||
return false;
|
||||
|
||||
// ReSharper disable once InvertIf
|
||||
if (neededVolume > solution.Volume)
|
||||
{
|
||||
if (user.HasValue)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), user.Value);
|
||||
return false;
|
||||
if (solution.Volume == 0)
|
||||
{
|
||||
if (user is { } uid)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-empty-text"), uid);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (neededVolume > solution.Volume)
|
||||
{
|
||||
if (user is { } uid)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-buffer-low-text"), uid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case ChemMasterDrawSource.External:
|
||||
if (_itemSlotsSystem.GetItemOrNull(chemMaster, SharedChemMaster.InputSlotName) is not {} container)
|
||||
{
|
||||
if (user.HasValue)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-no-beaker-text"), user.Value);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!_solutionContainerSystem.TryGetFitsInDispenser(container, out soln, out solution))
|
||||
return false;
|
||||
|
||||
if (solution.Volume == 0)
|
||||
{
|
||||
if (user is { } uid)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-beaker-empty-text"), uid);
|
||||
|
||||
return false;
|
||||
}
|
||||
if (neededVolume > solution.Volume)
|
||||
{
|
||||
if (user is { } uid)
|
||||
_popupSystem.PopupCursor(Loc.GetString("chem-master-window-beaker-low-text"), uid);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
|
||||
outputSolution = solution.SplitSolution(neededVolume);
|
||||
|
||||
if (soln.HasValue)
|
||||
_solutionContainerSystem.UpdateChemicals(soln.Value);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +0,0 @@
|
||||
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
|
||||
namespace Content.Server.Chemistry.EntitySystems;
|
||||
|
||||
public sealed class InjectorSystem : SharedInjectorSystem;
|
||||
@@ -10,11 +10,11 @@ public sealed partial class CodewordFactionPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// The generator to use for this faction.
|
||||
/// </summary>
|
||||
[DataField(required:true)]
|
||||
public ProtoId<CodewordGeneratorPrototype> Generator { get; } = default!;
|
||||
public ProtoId<CodewordGeneratorPrototype> Generator { get; private set; } = default!;
|
||||
}
|
||||
|
||||
@@ -11,13 +11,13 @@ public sealed partial class CodewordGeneratorPrototype : IPrototype
|
||||
{
|
||||
/// <inheritdoc/>
|
||||
[IdDataField]
|
||||
public string ID { get; } = default!;
|
||||
public string ID { get; private set; } = default!;
|
||||
|
||||
/// <summary>
|
||||
/// List of datasets to use for word generation. All values will be concatenated into one list and then randomly chosen from
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public List<ProtoId<LocalizedDatasetPrototype>> Words { get; } =
|
||||
public List<ProtoId<LocalizedDatasetPrototype>> Words { get; private set; } =
|
||||
[
|
||||
"Adjectives",
|
||||
"Verbs",
|
||||
|
||||
@@ -48,7 +48,7 @@ public sealed partial class BuildMech : IGraphAction
|
||||
|
||||
var cell = container.ContainedEntities[0];
|
||||
|
||||
if (!entityManager.TryGetComponent<PredictedBatteryComponent>(cell, out var batteryComponent))
|
||||
if (!entityManager.TryGetComponent<BatteryComponent>(cell, out var batteryComponent))
|
||||
{
|
||||
Logger.Warning($"Mech construct entity {uid} had an invalid entity in container \"{Container}\"! Aborting build mech action.");
|
||||
return;
|
||||
|
||||
@@ -1,32 +1,31 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
<PropertyGroup>
|
||||
<!-- Work around https://github.com/dotnet/project-system/issues/4314 -->
|
||||
<TargetFramework>$(TargetFramework)</TargetFramework>
|
||||
<LangVersion>12</LangVersion>
|
||||
<IsPackable>false</IsPackable>
|
||||
<AppendTargetFrameworkToOutputPath>false</AppendTargetFrameworkToOutputPath>
|
||||
<OutputPath>..\bin\Content.Server\</OutputPath>
|
||||
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
|
||||
<OutputType Condition="'$(FullRelease)' != 'True'">Exe</OutputType>
|
||||
<NoWarn>1998</NoWarn>
|
||||
<WarningsAsErrors>RA0032;nullable</WarningsAsErrors>
|
||||
<Nullable>enable</Nullable>
|
||||
<ServerGarbageCollection>true</ServerGarbageCollection>
|
||||
</PropertyGroup>
|
||||
<Import Project="../MSBuild/Content.props" />
|
||||
<ItemGroup>
|
||||
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
|
||||
<PackageReference Include="NetCord" />
|
||||
<PackageReference Include="prometheus-net" />
|
||||
<PackageReference Include="Microsoft.Extensions.ObjectPool" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Content.Packaging\Content.Packaging.csproj" />
|
||||
<ProjectReference Include="..\Content.Server.Database\Content.Server.Database.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared.Database\Content.Shared.Database.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Lidgren.Network\Lidgren.Network.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared.Maths\Robust.Shared.Maths.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Shared\Robust.Shared.csproj" />
|
||||
<ProjectReference Include="..\RobustToolbox\Robust.Server\Robust.Server.csproj" />
|
||||
<ProjectReference Include="..\Content.Shared\Content.Shared.csproj" />
|
||||
<ProjectReference Include="..\Corvax\Content.Corvax.Interfaces.Server\Content.Corvax.Interfaces.Server.csproj" />
|
||||
</ItemGroup>
|
||||
<Import Project="..\RobustToolbox\MSBuild\Robust.Properties.targets" />
|
||||
|
||||
<Import Project="..\RobustToolbox\Imports\Lidgren.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Server.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Shared.props" />
|
||||
<Import Project="..\RobustToolbox\Imports\Packaging.props" />
|
||||
</Project>
|
||||
|
||||
@@ -1,42 +1,38 @@
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using Content.Server.Fluids.EntitySystems;
|
||||
using Content.Shared.Fluids.Components;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
using JetBrains.Annotations;
|
||||
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors
|
||||
namespace Content.Server.Destructible.Thresholds.Behaviors;
|
||||
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed partial class SpillBehavior : IThresholdBehavior
|
||||
{
|
||||
[UsedImplicitly]
|
||||
[DataDefinition]
|
||||
public sealed partial class SpillBehavior : IThresholdBehavior
|
||||
/// <summary>
|
||||
/// Optional fallback solution name if SpillableComponent is not present.
|
||||
/// </summary>
|
||||
[DataField]
|
||||
public string? Solution;
|
||||
|
||||
/// <summary>
|
||||
/// When triggered, spills the entity's solution onto the ground.
|
||||
/// Will first try to use the solution from a SpillableComponent if present,
|
||||
/// otherwise falls back to the solution specified in the behavior's data fields.
|
||||
/// The solution is properly drained/split before spilling to prevent double-spilling with other behaviors.
|
||||
/// </summary>
|
||||
/// <param name="owner">Entity whose solution will be spilled</param>
|
||||
/// <param name="system">System calling this behavior</param>
|
||||
/// <param name="cause">Optional entity that caused this behavior to trigger</param>
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
[DataField]
|
||||
public string? Solution;
|
||||
var puddleSystem = system.EntityManager.System<PuddleSystem>();
|
||||
var solutionContainer = system.EntityManager.System<SharedSolutionContainerSystem>();
|
||||
var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
|
||||
|
||||
/// <summary>
|
||||
/// If there is a SpillableComponent on EntityUidowner use it to create a puddle/smear.
|
||||
/// Or whatever solution is specified in the behavior itself.
|
||||
/// If none are available do nothing.
|
||||
/// </summary>
|
||||
/// <param name="owner">Entity on which behavior is executed</param>
|
||||
/// <param name="system">system calling the behavior</param>
|
||||
/// <param name="cause"></param>
|
||||
public void Execute(EntityUid owner, DestructibleSystem system, EntityUid? cause = null)
|
||||
{
|
||||
var solutionContainerSystem = system.EntityManager.System<SharedSolutionContainerSystem>();
|
||||
var spillableSystem = system.EntityManager.System<PuddleSystem>();
|
||||
|
||||
var coordinates = system.EntityManager.GetComponent<TransformComponent>(owner).Coordinates;
|
||||
|
||||
if (system.EntityManager.TryGetComponent(owner, out SpillableComponent? spillableComponent) &&
|
||||
solutionContainerSystem.TryGetSolution(owner, spillableComponent.SolutionName, out _, out var compSolution))
|
||||
{
|
||||
spillableSystem.TrySplashSpillAt(owner, coordinates, compSolution, out _, false, user: cause);
|
||||
}
|
||||
else if (Solution != null &&
|
||||
solutionContainerSystem.TryGetSolution(owner, Solution, out _, out var behaviorSolution))
|
||||
{
|
||||
spillableSystem.TrySplashSpillAt(owner, coordinates, behaviorSolution, out _, user: cause);
|
||||
}
|
||||
}
|
||||
// Spill the solution that was drained/split
|
||||
if (solutionContainer.TryGetSolution(owner, Solution, out _, out var solution))
|
||||
puddleSystem.TrySplashSpillAt(owner, coordinates, solution, out _, false, cause);
|
||||
else
|
||||
puddleSystem.TrySplashSpillAt(owner, coordinates, out _, out _, false, cause);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,8 +33,9 @@ public sealed partial class PlantMutateChemicalsEntityEffectSystem : EntityEffec
|
||||
}
|
||||
else
|
||||
{
|
||||
seedChemQuantity.Min = FixedPoint2.Epsilon;
|
||||
seedChemQuantity.Max = FixedPoint2.Zero + amount;
|
||||
//Set the minimum to a fifth of the quantity to give some level of bad luck protection
|
||||
seedChemQuantity.Min = FixedPoint2.Clamp(pick.Quantity / 5f, FixedPoint2.Epsilon, 1f);
|
||||
seedChemQuantity.Max = seedChemQuantity.Min + amount;
|
||||
seedChemQuantity.Inherent = false;
|
||||
}
|
||||
var potencyDivisor = 100f / seedChemQuantity.Max;
|
||||
|
||||
@@ -29,23 +29,14 @@ public sealed partial class PuddleSystem
|
||||
|
||||
private void SpillOnLand(Entity<SpillableComponent> entity, ref LandEvent args)
|
||||
{
|
||||
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var soln, out var solution))
|
||||
if (!entity.Comp.SpillWhenThrown || Openable.IsClosed(entity.Owner))
|
||||
return;
|
||||
|
||||
if (Openable.IsClosed(entity.Owner))
|
||||
return;
|
||||
|
||||
if (!entity.Comp.SpillWhenThrown)
|
||||
return;
|
||||
|
||||
if (args.User != null)
|
||||
if (TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, out _, out var solution) && args.User != null)
|
||||
{
|
||||
AdminLogger.Add(LogType.Landed,
|
||||
$"{ToPrettyString(entity.Owner):entity} spilled a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} on landing");
|
||||
}
|
||||
|
||||
var drainedSolution = _solutionContainerSystem.Drain(entity.Owner, soln.Value, solution.Volume);
|
||||
TrySplashSpillAt(entity.Owner, Transform(entity).Coordinates, drainedSolution, out _);
|
||||
}
|
||||
|
||||
private void OnDoAfter(Entity<SpillableComponent> entity, ref SpillDoAfterEvent args)
|
||||
|
||||
@@ -369,7 +369,40 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
|
||||
// TODO: This can be predicted once https://github.com/space-wizards/RobustToolbox/pull/5849 is merged
|
||||
/// <inheritdoc/>
|
||||
public override bool TrySplashSpillAt(EntityUid uid,
|
||||
public override bool TrySplashSpillAt(Entity<SpillableComponent?> entity,
|
||||
EntityCoordinates coordinates,
|
||||
out EntityUid puddleUid,
|
||||
out Solution spilled,
|
||||
bool sound = true,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
puddleUid = EntityUid.Invalid;
|
||||
spilled = new Solution();
|
||||
|
||||
if (!Resolve(entity, ref entity.Comp))
|
||||
return false;
|
||||
|
||||
if (!_solutionContainerSystem.TryGetSolution(entity.Owner, entity.Comp.SolutionName, out var solution))
|
||||
return false;
|
||||
|
||||
spilled = solution.Value.Comp.Solution;
|
||||
|
||||
return TrySplashSpillAt(entity, coordinates, solution.Value, out puddleUid, sound, user);
|
||||
}
|
||||
|
||||
private bool TrySplashSpillAt(EntityUid entity,
|
||||
EntityCoordinates coordinates,
|
||||
Entity<SolutionComponent> solution,
|
||||
out EntityUid puddleUid,
|
||||
bool sound = true,
|
||||
EntityUid? user = null)
|
||||
{
|
||||
var result = TrySplashSpillAt(entity, coordinates, solution.Comp.Solution, out puddleUid, sound, user);
|
||||
_solutionContainerSystem.UpdateChemicals(solution);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override bool TrySplashSpillAt(EntityUid entity,
|
||||
EntityCoordinates coordinates,
|
||||
Solution solution,
|
||||
out EntityUid puddleUid,
|
||||
@@ -381,6 +414,7 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
if (solution.Volume == 0)
|
||||
return false;
|
||||
|
||||
var spilled = solution.SplitSolution(solution.Volume);
|
||||
var targets = new List<EntityUid>();
|
||||
var reactive = new HashSet<Entity<ReactiveComponent>>();
|
||||
_lookup.GetEntitiesInRange(coordinates, 1.0f, reactive);
|
||||
@@ -392,26 +426,28 @@ public sealed partial class PuddleSystem : SharedPuddleSystem
|
||||
var owner = ent.Owner;
|
||||
|
||||
// between 5 and 30%
|
||||
var splitAmount = solution.Volume * _random.NextFloat(0.05f, 0.30f);
|
||||
var splitSolution = solution.SplitSolution(splitAmount);
|
||||
var splitAmount = spilled.Volume * _random.NextFloat(0.05f, 0.30f);
|
||||
var splitSolution = spilled.SplitSolution(splitAmount);
|
||||
|
||||
if (user != null)
|
||||
{
|
||||
AdminLogger.Add(LogType.Landed,
|
||||
$"{ToPrettyString(user.Value):user} threw {ToPrettyString(uid):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(solution):solution} onto {ToPrettyString(owner):target}");
|
||||
$"{ToPrettyString(user.Value):user} threw {ToPrettyString(entity):entity} which splashed a solution {SharedSolutionContainerSystem.ToPrettyString(spilled):solution} onto {ToPrettyString(owner):target}");
|
||||
}
|
||||
|
||||
targets.Add(owner);
|
||||
Reactive.DoEntityReaction(owner, splitSolution, ReactionMethod.Touch);
|
||||
Popups.PopupEntity(
|
||||
Loc.GetString("spill-land-spilled-on-other", ("spillable", uid),
|
||||
("target", Identity.Entity(owner, EntityManager))), owner, PopupType.SmallCaution);
|
||||
Popups.PopupEntity(Loc.GetString("spill-land-spilled-on-other",
|
||||
("spillable", entity),
|
||||
("target", Identity.Entity(owner, EntityManager))),
|
||||
owner,
|
||||
PopupType.SmallCaution);
|
||||
}
|
||||
|
||||
_color.RaiseEffect(solution.GetColor(_prototypeManager), targets,
|
||||
Filter.Pvs(uid, entityManager: EntityManager));
|
||||
_color.RaiseEffect(spilled.GetColor(_prototypeManager), targets,
|
||||
Filter.Pvs(entity, entityManager: EntityManager));
|
||||
|
||||
return TrySpillAt(coordinates, solution, out puddleUid, sound);
|
||||
return TrySpillAt(coordinates, spilled, out puddleUid, sound);
|
||||
}
|
||||
|
||||
/// <inheritdoc/>
|
||||
|
||||
@@ -17,6 +17,9 @@ namespace Content.Server.GameTicking.Commands
|
||||
[Dependency] private readonly IPrototypeManager _prototypeManager = default!;
|
||||
[Dependency] private readonly IAdminManager _adminManager = default!;
|
||||
[Dependency] private readonly IConfigurationManager _cfg = default!;
|
||||
[Dependency] private readonly ILogManager _logManager = default!;
|
||||
|
||||
private readonly ISawmill _sawmill;
|
||||
|
||||
public string Command => "joingame";
|
||||
public string Description => "";
|
||||
@@ -25,7 +28,10 @@ namespace Content.Server.GameTicking.Commands
|
||||
public JoinGameCommand()
|
||||
{
|
||||
IoCManager.InjectDependencies(this);
|
||||
|
||||
_sawmill = _logManager.GetSawmill("security");
|
||||
}
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (args.Length != 2)
|
||||
@@ -46,8 +52,8 @@ namespace Content.Server.GameTicking.Commands
|
||||
|
||||
if (ticker.PlayerGameStatuses.TryGetValue(player.UserId, out var status) && status == PlayerGameStatus.JoinedGame)
|
||||
{
|
||||
Logger.InfoS("security", $"{player.Name} ({player.UserId}) attempted to latejoin while in-game.");
|
||||
shell.WriteError($"{player.Name} is not in the lobby. This incident will be reported.");
|
||||
_sawmill.Info($"{player.Name} ({player.UserId}) attempted to latejoin while in-game.");
|
||||
shell.WriteError($"{player.Name} is not in the lobby. This incident will be reported.");
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
using Content.Server.Administration;
|
||||
using Content.Server.GameTicking.Presets;
|
||||
using Content.Shared.Administration;
|
||||
using Linguini.Shared.Util;
|
||||
using Robust.Shared.Console;
|
||||
|
||||
namespace Content.Server.GameTicking.Commands
|
||||
@@ -18,7 +17,7 @@ namespace Content.Server.GameTicking.Commands
|
||||
|
||||
public void Execute(IConsoleShell shell, string argStr, string[] args)
|
||||
{
|
||||
if (!args.Length.InRange(1, 3))
|
||||
if (args.Length is < 1 or > 3)
|
||||
{
|
||||
shell.WriteError(Loc.GetString("shell-need-between-arguments", ("lower", 1), ("upper", 3), ("currentAmount", args.Length)));
|
||||
return;
|
||||
|
||||
@@ -116,7 +116,7 @@ public abstract partial class GameRuleSystem<T> where T: IComponent
|
||||
|
||||
tile = new Vector2i(randomX, randomY);
|
||||
if (_atmosphere.IsTileSpace(targetGrid, Transform(targetGrid).MapUid, tile)
|
||||
|| _atmosphere.IsTileAirBlocked(targetGrid, tile, mapGridComp: gridComp))
|
||||
|| _atmosphere.IsTileAirBlockedCached(targetGrid, tile))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
@@ -49,15 +49,23 @@ public sealed class ToggleableGhostRoleSystem : EntitySystem
|
||||
|
||||
UpdateAppearance(uid, ToggleableGhostRoleStatus.Searching);
|
||||
|
||||
var ghostRole = EnsureComp<GhostRoleComponent>(uid);
|
||||
EnsureComp<GhostTakeoverAvailableComponent>(uid);
|
||||
ActivateGhostRole((uid, component));
|
||||
}
|
||||
|
||||
//GhostRoleComponent inherits custom settings from the ToggleableGhostRoleComponent
|
||||
ghostRole.RoleName = Loc.GetString(component.RoleName);
|
||||
ghostRole.RoleDescription = Loc.GetString(component.RoleDescription);
|
||||
ghostRole.RoleRules = Loc.GetString(component.RoleRules);
|
||||
ghostRole.JobProto = component.JobProto;
|
||||
ghostRole.MindRoles = component.MindRoles;
|
||||
public void ActivateGhostRole(Entity<ToggleableGhostRoleComponent?> ent)
|
||||
{
|
||||
if (!Resolve(ent, ref ent.Comp))
|
||||
return;
|
||||
|
||||
var ghostRole = EnsureComp<GhostRoleComponent>(ent);
|
||||
EnsureComp<GhostTakeoverAvailableComponent>(ent);
|
||||
|
||||
// GhostRoleComponent inherits custom settings from the ToggleableGhostRoleComponent
|
||||
ghostRole.RoleName = Loc.GetString(ent.Comp.RoleName);
|
||||
ghostRole.RoleDescription = Loc.GetString(ent.Comp.RoleDescription);
|
||||
ghostRole.RoleRules = Loc.GetString(ent.Comp.RoleRules);
|
||||
ghostRole.JobProto = ent.Comp.JobProto;
|
||||
ghostRole.MindRoles = ent.Comp.MindRoles;
|
||||
}
|
||||
|
||||
private void OnExamined(EntityUid uid, ToggleableGhostRoleComponent component, ExaminedEvent args)
|
||||
|
||||
@@ -88,7 +88,7 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
|
||||
if (!_doAfter.TryStartDoAfter(args))
|
||||
return;
|
||||
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-needle-injecting-user"), target, user);
|
||||
|
||||
var userName = Identity.Entity(user, EntityManager);
|
||||
_popup.PopupEntity(Loc.GetString("implanter-component-implanting-target", ("user", userName)), user, target, PopupType.LargeCaution);
|
||||
@@ -112,7 +112,7 @@ public sealed partial class ImplanterSystem : SharedImplanterSystem
|
||||
};
|
||||
|
||||
if (_doAfter.TryStartDoAfter(args))
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-injecting-user"), target, user);
|
||||
_popup.PopupEntity(Loc.GetString("injector-component-needle-injecting-user"), target, user);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
using Content.Shared.Kitchen;
|
||||
using Content.Server.Kitchen.EntitySystems;
|
||||
using Robust.Shared.Audio;
|
||||
|
||||
namespace Content.Server.Kitchen.Components
|
||||
{
|
||||
/// <summary>
|
||||
/// The combo reagent grinder/juicer. The reason why grinding and juicing are seperate is simple,
|
||||
/// think of grinding as a utility to break an object down into its reagents. Think of juicing as
|
||||
/// converting something into its single juice form. E.g, grind an apple and get the nutriment and sugar
|
||||
/// it contained, juice an apple and get "apple juice".
|
||||
/// </summary>
|
||||
[Access(typeof(ReagentGrinderSystem)), RegisterComponent]
|
||||
public sealed partial class ReagentGrinderComponent : Component
|
||||
{
|
||||
[DataField]
|
||||
public int StorageMaxEntities = 6;
|
||||
|
||||
[DataField]
|
||||
public TimeSpan WorkTime = TimeSpan.FromSeconds(3.5); // Roughly matches the grind/juice sounds.
|
||||
|
||||
[DataField]
|
||||
public float WorkTimeMultiplier = 1;
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier ClickSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/machine_switch.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier GrindSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/blender.ogg");
|
||||
|
||||
[DataField]
|
||||
public SoundSpecifier JuiceSound { get; set; } = new SoundPathSpecifier("/Audio/Machines/juicer.ogg");
|
||||
|
||||
[DataField]
|
||||
public GrinderAutoMode AutoMode = GrinderAutoMode.Off;
|
||||
|
||||
public EntityUid? AudioStream;
|
||||
}
|
||||
|
||||
[Access(typeof(ReagentGrinderSystem)), RegisterComponent]
|
||||
public sealed partial class ActiveReagentGrinderComponent : Component
|
||||
{
|
||||
/// <summary>
|
||||
/// Remaining time until the grinder finishes grinding/juicing.
|
||||
/// </summary>
|
||||
[ViewVariables]
|
||||
public TimeSpan EndTime;
|
||||
|
||||
[ViewVariables]
|
||||
public GrinderProgram Program;
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,3 @@
|
||||
using Content.Server.Kitchen.Components;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Server.Stack;
|
||||
using Content.Shared.Chemistry.EntitySystems;
|
||||
@@ -20,15 +18,15 @@ using Robust.Shared.Audio.Systems;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Timing;
|
||||
using System.Linq;
|
||||
using Content.Server.Construction.Completions;
|
||||
using Content.Server.Jittering;
|
||||
using Content.Shared.Jittering;
|
||||
using Content.Shared.Kitchen.EntitySystems;
|
||||
using Content.Shared.Power;
|
||||
|
||||
namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
[UsedImplicitly]
|
||||
internal sealed class ReagentGrinderSystem : EntitySystem
|
||||
internal sealed class ReagentGrinderSystem : SharedReagentGrinderSystem
|
||||
{
|
||||
[Dependency] private readonly IGameTiming _timing = default!;
|
||||
[Dependency] private readonly SharedSolutionContainerSystem _solutionContainersSystem = default!;
|
||||
@@ -67,6 +65,8 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
entity.Comp.AutoMode = (GrinderAutoMode) (((byte) entity.Comp.AutoMode + 1) % Enum.GetValues(typeof(GrinderAutoMode)).Length);
|
||||
|
||||
Dirty(entity);
|
||||
|
||||
UpdateUiState(entity);
|
||||
}
|
||||
|
||||
@@ -90,12 +90,7 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
|
||||
foreach (var item in inputContainer.ContainedEntities.ToList())
|
||||
{
|
||||
var solution = active.Program switch
|
||||
{
|
||||
GrinderProgram.Grind => GetGrindSolution(item),
|
||||
GrinderProgram.Juice => CompOrNull<ExtractableComponent>(item)?.JuiceSolution,
|
||||
_ => null,
|
||||
};
|
||||
var solution = GetGrinderSolution(item, active.Program);
|
||||
|
||||
if (solution is null)
|
||||
continue;
|
||||
@@ -218,8 +213,8 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
&& _solutionContainersSystem.TryGetFitsInDispenser(outputContainer.Value, out _, out containerSolution)
|
||||
&& inputContainer.ContainedEntities.Count > 0)
|
||||
{
|
||||
canGrind = inputContainer.ContainedEntities.All(CanGrind);
|
||||
canJuice = inputContainer.ContainedEntities.All(CanJuice);
|
||||
canGrind = inputContainer.ContainedEntities.All(x => CanGrind(x));
|
||||
canJuice = inputContainer.ContainedEntities.All(x => CanJuice(x));
|
||||
}
|
||||
|
||||
var state = new ReagentGrinderInterfaceState(
|
||||
@@ -293,10 +288,10 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
SoundSpecifier? sound;
|
||||
switch (program)
|
||||
{
|
||||
case GrinderProgram.Grind when inputContainer.ContainedEntities.All(CanGrind):
|
||||
case GrinderProgram.Grind when inputContainer.ContainedEntities.All(x => CanGrind(x)):
|
||||
sound = reagentGrinder.GrindSound;
|
||||
break;
|
||||
case GrinderProgram.Juice when inputContainer.ContainedEntities.All(CanJuice):
|
||||
case GrinderProgram.Juice when inputContainer.ContainedEntities.All(x => CanJuice(x)):
|
||||
sound = reagentGrinder.JuiceSound;
|
||||
break;
|
||||
default:
|
||||
@@ -317,29 +312,5 @@ namespace Content.Server.Kitchen.EntitySystems
|
||||
{
|
||||
_audioSystem.PlayPvs(reagentGrinder.Comp.ClickSound, reagentGrinder.Owner, AudioParams.Default.WithVolume(-2f));
|
||||
}
|
||||
|
||||
private Solution? GetGrindSolution(EntityUid uid)
|
||||
{
|
||||
if (TryComp<ExtractableComponent>(uid, out var extractable)
|
||||
&& extractable.GrindableSolution is not null
|
||||
&& _solutionContainersSystem.TryGetSolution(uid, extractable.GrindableSolution, out _, out var solution))
|
||||
{
|
||||
return solution;
|
||||
}
|
||||
else
|
||||
return null;
|
||||
}
|
||||
|
||||
private bool CanGrind(EntityUid uid)
|
||||
{
|
||||
var solutionName = CompOrNull<ExtractableComponent>(uid)?.GrindableSolution;
|
||||
|
||||
return solutionName is not null && _solutionContainersSystem.TryGetSolution(uid, solutionName, out _, out _);
|
||||
}
|
||||
|
||||
private bool CanJuice(EntityUid uid)
|
||||
{
|
||||
return CompOrNull<ExtractableComponent>(uid)?.JuiceSolution is not null;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +153,7 @@ public sealed class EmergencyLightSystem : SharedEmergencyLightSystem
|
||||
}
|
||||
else
|
||||
{
|
||||
_battery.SetCharge((entity.Owner, battery), battery.CurrentCharge + entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency);
|
||||
_battery.ChangeCharge((entity.Owner, battery), entity.Comp.ChargingWattage * frameTime * entity.Comp.ChargingEfficiency);
|
||||
if (_battery.IsFull((entity.Owner, battery)))
|
||||
{
|
||||
if (TryComp<ApcPowerReceiverComponent>(entity.Owner, out var receiver))
|
||||
|
||||
@@ -24,7 +24,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
[Dependency] private readonly ActionContainerSystem _actionContainer = default!;
|
||||
[Dependency] private readonly PopupSystem _popup = default!;
|
||||
[Dependency] private readonly PowerCellSystem _powerCell = default!;
|
||||
[Dependency] private readonly PredictedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedAppearanceSystem _appearance = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedPointLightSystem _lights = default!;
|
||||
@@ -147,7 +147,7 @@ namespace Content.Server.Light.EntitySystems
|
||||
}
|
||||
|
||||
// TODO: Very important: Make this charge rate based instead of instantly removing charge each update step.
|
||||
// See PredictedBatteryComponent
|
||||
// See BatteryComponent
|
||||
public override void Update(float frameTime)
|
||||
{
|
||||
var toRemove = new RemQueue<Entity<HandheldLightComponent>>();
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
using System.Linq;
|
||||
using Content.Server.Atmos.EntitySystems;
|
||||
using Content.Server.Body.Systems;
|
||||
using Content.Server.Mech.Components;
|
||||
@@ -6,6 +5,7 @@ using Content.Shared.ActionBlocker;
|
||||
using Content.Shared.Damage.Systems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.FixedPoint;
|
||||
using Content.Shared.IdentityManagement;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Mech;
|
||||
using Content.Shared.Mech.Components;
|
||||
@@ -25,6 +25,7 @@ using Robust.Server.GameObjects;
|
||||
using Robust.Shared.Containers;
|
||||
using Robust.Shared.Player;
|
||||
using Robust.Shared.Prototypes;
|
||||
using System.Linq;
|
||||
|
||||
namespace Content.Server.Mech.Systems;
|
||||
|
||||
@@ -33,7 +34,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
{
|
||||
[Dependency] private readonly ActionBlockerSystem _actionBlocker = default!;
|
||||
[Dependency] private readonly AtmosphereSystem _atmosphere = default!;
|
||||
[Dependency] private readonly PredictedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly ContainerSystem _container = default!;
|
||||
[Dependency] private readonly DamageableSystem _damageable = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
@@ -88,7 +89,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
if (TryComp<WiresPanelComponent>(uid, out var panel) && !panel.Open)
|
||||
return;
|
||||
|
||||
if (component.BatterySlot.ContainedEntity == null && TryComp<PredictedBatteryComponent>(args.Used, out var battery))
|
||||
if (component.BatterySlot.ContainedEntity == null && TryComp<BatteryComponent>(args.Used, out var battery))
|
||||
{
|
||||
InsertBattery(uid, args.Used, component, battery);
|
||||
_actionBlocker.UpdateCanMove(uid);
|
||||
@@ -109,7 +110,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
|
||||
private void OnInsertBattery(EntityUid uid, MechComponent component, EntInsertedIntoContainerMessage args)
|
||||
{
|
||||
if (args.Container != component.BatterySlot || !TryComp<PredictedBatteryComponent>(args.Entity, out var battery))
|
||||
if (args.Container != component.BatterySlot || !TryComp<BatteryComponent>(args.Entity, out var battery))
|
||||
return;
|
||||
|
||||
component.Energy = _battery.GetCharge((args.Entity, battery));
|
||||
@@ -219,7 +220,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
{
|
||||
BreakOnMove = true,
|
||||
};
|
||||
_popup.PopupEntity(Loc.GetString("mech-eject-pilot-alert", ("item", uid), ("user", args.User)), uid, PopupType.Large);
|
||||
_popup.PopupEntity(Loc.GetString("mech-eject-pilot-alert", ("item", uid), ("user", Identity.Entity(args.User, EntityManager))), uid, PopupType.Large);
|
||||
|
||||
_doAfter.TryStartDoAfter(doAfterEventArgs);
|
||||
}
|
||||
@@ -235,7 +236,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
|
||||
if (_whitelistSystem.IsWhitelistFail(component.PilotWhitelist, args.User))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("mech-no-enter", ("item", uid)), args.User);
|
||||
_popup.PopupEntity(Loc.GetString("mech-no-enter", ("item", uid)), Identity.Entity(args.User, EntityManager));
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -337,7 +338,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
if (battery == null)
|
||||
return false;
|
||||
|
||||
if (!TryComp<PredictedBatteryComponent>(battery, out var batteryComp))
|
||||
if (!TryComp<BatteryComponent>(battery, out var batteryComp))
|
||||
return false;
|
||||
|
||||
_battery.SetCharge((battery.Value, batteryComp), _battery.GetCharge((battery.Value, batteryComp)) + delta.Float());
|
||||
@@ -353,7 +354,7 @@ public sealed partial class MechSystem : SharedMechSystem
|
||||
return true;
|
||||
}
|
||||
|
||||
public void InsertBattery(EntityUid uid, EntityUid toInsert, MechComponent? component = null, PredictedBatteryComponent? battery = null)
|
||||
public void InsertBattery(EntityUid uid, EntityUid toInsert, MechComponent? component = null, BatteryComponent? battery = null)
|
||||
{
|
||||
if (!Resolve(uid, ref component, false))
|
||||
return;
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
using Content.Server.Ninja.Events;
|
||||
using Content.Server.Power.Components;
|
||||
using Content.Server.Power.EntitySystems;
|
||||
using Content.Shared.DoAfter;
|
||||
using Content.Shared.Interaction;
|
||||
using Content.Shared.Ninja.Components;
|
||||
@@ -17,8 +16,7 @@ namespace Content.Server.Ninja.Systems;
|
||||
/// </summary>
|
||||
public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
{
|
||||
[Dependency] private readonly BatterySystem _battery = default!;
|
||||
[Dependency] private readonly PredictedBatterySystem _predictedBattery = default!;
|
||||
[Dependency] private readonly SharedBatterySystem _battery = default!;
|
||||
[Dependency] private readonly SharedAudioSystem _audio = default!;
|
||||
[Dependency] private readonly SharedDoAfterSystem _doAfter = default!;
|
||||
[Dependency] private readonly SharedPopupSystem _popup = default!;
|
||||
@@ -80,20 +78,20 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
protected override bool TryDrainPower(Entity<BatteryDrainerComponent> ent, EntityUid target)
|
||||
{
|
||||
var (uid, comp) = ent;
|
||||
if (comp.BatteryUid == null || !TryComp<PredictedBatteryComponent>(comp.BatteryUid.Value, out var battery))
|
||||
if (comp.BatteryUid == null || !TryComp<BatteryComponent>(comp.BatteryUid.Value, out var battery))
|
||||
return false;
|
||||
|
||||
if (!TryComp<BatteryComponent>(target, out var targetBattery) || !TryComp<PowerNetworkBatteryComponent>(target, out var pnb))
|
||||
return false;
|
||||
|
||||
if (MathHelper.CloseToPercent(targetBattery.CurrentCharge, 0))
|
||||
var available = _battery.GetCharge((target, targetBattery));
|
||||
if (MathHelper.CloseToPercent(available, 0))
|
||||
{
|
||||
_popup.PopupEntity(Loc.GetString("battery-drainer-empty", ("battery", target)), uid, uid, PopupType.Medium);
|
||||
return false;
|
||||
}
|
||||
|
||||
var available = targetBattery.CurrentCharge;
|
||||
var required = battery.MaxCharge - _predictedBattery.GetCharge((comp.BatteryUid.Value, battery));
|
||||
var required = battery.MaxCharge - _battery.GetCharge((comp.BatteryUid.Value, battery));
|
||||
// higher tier storages can charge more
|
||||
var maxDrained = pnb.MaxSupply * comp.DrainTime;
|
||||
var input = Math.Min(Math.Min(available, required / comp.DrainEfficiency), maxDrained);
|
||||
@@ -101,15 +99,13 @@ public sealed class BatteryDrainerSystem : SharedBatteryDrainerSystem
|
||||
return false;
|
||||
|
||||
var output = input * comp.DrainEfficiency;
|
||||
// PowerCells use PredictedBatteryComponent
|
||||
// SMES, substations and APCs use BatteryComponent
|
||||
_predictedBattery.ChangeCharge((comp.BatteryUid.Value, battery), output);
|
||||
_battery.ChangeCharge((comp.BatteryUid.Value, battery), output);
|
||||
// TODO: create effect message or something
|
||||
Spawn("EffectSparks", Transform(target).Coordinates);
|
||||
_audio.PlayPvs(comp.SparkSound, target);
|
||||
_popup.PopupEntity(Loc.GetString("battery-drainer-success", ("battery", target)), uid, uid);
|
||||
|
||||
// repeat the doafter until battery is full
|
||||
return !_predictedBattery.IsFull((comp.BatteryUid.Value, battery));
|
||||
return !_battery.IsFull((comp.BatteryUid.Value, battery));
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user